Medienservice

Spenden

In den Projekten auf dieser Seite steckt viel Arbeit. Trotzdem sollen sie ohne irgendwelche Werbetricks frei zugänglich sein. Wer die Entwicklungen unterstützen möchte kann dies gerne hier tun.

Wetter in Chemnitz

Chemnitz Erfenschlag
320m (ü. NN)

Temperatur: 12.8 °C
Luftfeuchte: 86.1 %
Luftdruck: 1015.3 hPa
Niederschlag (24h): 0.0 mm
Niederschlag (30d): 89.1 mm

Über neue Beiträge automatisch informiert werden:

Newsletter abonnieren!





1-Wire Device mit AVR-Mikrocontroller

Beweggründe

Seit ich erfahren habe, das der 1-Wire Baustein DS2423 (Zähler) nicht mehr produziert wird kam mir die Idee, ihn mit Hilfe eines Mikrocontrollers nachzubauen. Anfang 2012 habe ich mich intensiver mit den AVR-Controllern von Atmel befasst und es hat mich ziemlich begeistert, was man damit alles anstellen kann.

Im Internet gab es schon verschiedene Ansätze einen 1-Wire-Slave mit Atmel-Controllern aber die kamen mir alle ein bisschen zu groß und zu unübersichtlich vor. Auch die Vorstellung, selbst eigene Geräte für den 1-Wire-Bus zu kreieren, fasziniert mich sehr. So habe ich probiert, optimiert und korrigiert und heraus kam ein sehr kleines und robustes Stückchen Code, welches auch noch an meinem 50m 1-Wire-Bus mit mehr als 40 Slaves fehlerfrei funktioniert.

{jcomments on}

Der Sourcecode für 1-Wire Devices mit AVR-Mikrocontrollern

Die Aktuelle Version ist nun die 1.2

Changelog:

Version Veränderung zur vorhergehenden Verison
1.2

Verbesserung des Timings
lwmode wird gleich am Anfang von PIN_INT zurückgesetzt dadurch bessere Stabilität bei DS9490

1.1 Umstrukturierung für bessere Anpassung. Skip-Rom und Read-Rom
1.0

Der Code wird wie immer unter GPL-Lizenz veröffentlicht. Eine korrekte 1-Wire-ID für das Gerät kann mit dem CRC Rechner auf meiner Homepage erstellt werden. Das erste Byte beschreibt die Gerätefamilie und sollte nicht verändert werden.

DS18B20 für ATtiny13 und ATtiny25

Zunächst habe ich versuche mit dem DS18B20 unternommen. Hier der Code für den ATTiny13 oder ATTiny25. Er passt gerade so in den 1k Speicher. Als Wert für die Temperatur wird bei dem ATTiny13 die Spannung an PB2 gemessen. Der ATTiny25 hat ein internes Thermometer. Dieses wird 16 mal ausgelesen und summiert. Das LSB entspricht ein Grad Veränderung, durch die Addition wird der Wert mal 16 genommen und entspricht so der Angabe in den Registern des DS18B20. Es muss noch ein Kalibrierwert abgezogen werden. Leider erwärmt sich der ATTiny25 wahrscheinlich selbst ziemlich stark, so dass keine genaue Messung möglich war. Also eher ein Schulbeispiel :-).

//owdevice - A small 1-Wire emulator for AVR Microcontroller
//
//Copyright (C) 2012  Tobias Mueller mail (at) tobynet.de
//
//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
// any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//
//VERSION 1.2 DS18B20  ATTINY13 (AD input) and ATTINY25 (internal sensor)

//FOR MAKE by hand
/*
avr-gcc -mmcu=attiny13 -O2 -c [name].c
avr-gcc.exe -mmcu=attiny13  [name].o -o [name].elf
avr-objcopy -O ihex  [name].elf [name].hex
*/



#include <avr/io.h>
#include <avr/interrupt.h>

#if defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny13__)
// OW_PORT Pin 6  - PB1
//Analog input PB2

//OW Pin
#define OW_PORT PORTB //1 Wire Port
#define OW_PIN PINB //1 Wire Pin as number
#define OW_PORTN (1<<PINB1)  //Pin as bit in registers
#define OW_PINN (1<<PINB1)
#define OW_DDR DDRB  //pin direction register
#define SET_LOW OW_DDR|=OW_PINN;OW_PORT&=~OW_PORTN;  //set 1-Wire line to low
#define RESET_LOW {OW_DDR&=~OW_PINN;}  //set 1-Wire pin as input
//Pin interrupt	
#define EN_OWINT {GIMSK|=(1<<INT0);GIFR|=(1<<INTF0);}  //enable interrupt 
#define DIS_OWINT  GIMSK&=~(1<<INT0);  //disable interrupt
#define SET_RISING MCUCR=(1<<ISC01)|(1<<ISC00);  //set interrupt at rising edge
#define SET_FALLING MCUCR=(1<<ISC01); //set interrupt at falling edge
#define CHK_INT_EN (GIMSK&(1<<INT0))==(1<<INT0) //test if interrupt enabled
#define PIN_INT ISR(INT0_vect)  // the interrupt service routine
//Timer Interrupt
#define EN_TIMER {TIMSK0 |= (1<<TOIE0); TIFR0|=(1<<TOV0);} //enable timer interrupt
#define DIS_TIMER TIMSK0  &= ~(1<<TOIE0); // disable timer interrupt
#define TCNT_REG TCNT0  //register of timer-counter
#define TIMER_INT ISR(TIM0_OVF_vect) //the timer interrupt service routine

//Initializations of AVR
#define INIT_AVR CLKPR=(1<<CLKPCE);\
				   CLKPR=0;/*9.6Mhz*/\
				   TIMSK0=0;\
				   GIMSK=(1<<INT0);/*set direct GIMSK register*/\
				   TCCR0B=(1<<CS00)|(1<<CS01); /*9.6mhz /64 couse 8 bit Timer interrupt every 6,666us*/

//Setup Temp Measurement DS18B20
#define INIT_TEMP   DDRB&=~(1<<PINB2); \
					ADMUX=(1<<MUX0); \
					ADCSRA= (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
#define CONV_TEMP		ADCSRA|=(1<<ADSC); \
						while ((ADCSRA&(1<<ADSC))!=0) {}\
						scratchpad[0]=ADCL;\
						scratchpad[1]=ADCH;\
						



//Times
#define OWT_MIN_RESET 51 //minimum duration of the Reset impulse

#define OWT_RESET_PRESENCE 4 //time between rising edge of reset impulse and presence impulse
#define OWT_PRESENCE 20 //duration of the presence impulse
#define OWT_READLINE 4  //duration from master low to read the state of 1-Wire line
#define OWT_LOWTIME 4 //length of low 
#endif

#ifdef __AVR_ATtiny25__ 
// OW_PORT Pin 7  - PB2


//OW Pin
#define OW_PORT PORTB //1 Wire Port
#define OW_PIN PINB //1 Wire Pin as number
#define OW_PORTN (1<<PINB2)  //Pin as bit in registers
#define OW_PINN (1<<PINB2)
#define OW_DDR DDRB  //pin direction register
#define SET_LOW OW_DDR|=OW_PINN;OW_PORT&=~OW_PORTN;  //set 1-Wire line to low
#define RESET_LOW {OW_DDR&=~OW_PINN;}  //set 1-Wire pin as input
//Pin interrupt	
#define EN_OWINT {GIMSK|=(1<<INT0);GIFR|=(1<<INTF0);}  //enable interrupt 
#define DIS_OWINT  GIMSK&=~(1<<INT0);  //disable interrupt
#define SET_RISING MCUCR=(1<<ISC01)|(1<<ISC00);  //set interrupt at rising edge
#define SET_FALLING MCUCR=(1<<ISC01); //set interrupt at falling edge
#define CHK_INT_EN (GIMSK&(1<<INT0))==(1<<INT0) //test if interrupt enabled
#define PIN_INT ISR(INT0_vect)  // the interrupt service routine
//Timer Interrupt
#define EN_TIMER {TIMSK |= (1<<TOIE0); TIFR|=(1<<TOV0);} //enable timer interrupt
#define DIS_TIMER TIMSK  &= ~(1<<TOIE0); // disable timer interrupt
#define TCNT_REG TCNT0  //register of timer-counter
#define TIMER_INT ISR(TIM0_OVF_vect) //the timer interrupt service routine


#define OWT_MIN_RESET 51
#define OWT_RESET_PRESENCE 4
#define OWT_PRESENCE 20 
#define OWT_READLINE 3 //for fast master, 4 for slow master and long lines
#define OWT_LOWTIME 3 //for fast master, 4 for slow master and long lines 

//Initializations of AVR
#define INIT_AVR CLKPR=(1<<CLKPCE); \
				   CLKPR=0; /*8Mhz*/  \
				   TIMSK=0; \
				   GIMSK=(1<<INT0);  /*set direct GIMSK register*/ \
				   TCCR0B=(1<<CS00)|(1<<CS01); /*8mhz /64 couse 8 bit Timer interrupt every 8us*/
				   
				   

//Setup Temp Measurement DS18B20 intern sensor
#define INIT_TEMP  ADMUX=(1<<REFS1)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0); \
					ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); /*ADC Freq: ~63khz*/ \
					ADCSRB=0;
#define CONV_TEMP	{ uint8_t tc; int16_t sum=0; \
						for(tc=0;tc<0x10;tc++) { \
							ADCSRA|=(1<<ADSC)|(1<<ADIF);\
							while(ADCSRA&(1<<ADSC));\
							sum=sum+ADC; \
						} \
						sum=sum-0xDAF;/*calibration  0x010 are 1 K*/\
						/*sum=(sum<<4);*/  \
						scratchpad[0]=0x00ff&sum;\
						scratchpad[1]=0x00ff&(sum>>8);\
						}			
	
						


#endif // __AVR_ATtiny25__ 


volatile uint8_t scratchpad[9]={0x50,0x05,0x0,0x0,0x7f,0xff,0x00,0x10,0x0}; //Initial scratchpad
volatile uint8_t scrc; //CRC calculation

volatile uint8_t cbuf; //Input buffer for a command
const uint8_t owid[8]={0x28, 0xA2, 0xD9, 0x84, 0x00, 0x00, 0x02, 0xEA};  
//set your own ID http://www.tm3d.de/index.php/tools/14-crc8-berechnung
volatile uint8_t bitp;  //pointer to current Byte
volatile uint8_t bytep; //pointer to current Bit

volatile uint8_t mode; //state
volatile uint8_t wmode; //if 0 next bit that send the device is  0
volatile uint8_t actbit; //current
volatile uint8_t srcount; //counter for search rom

//States / Modes
#define OWM_SLEEP 0  //Waiting for next reset pulse
#define OWM_RESET 1  //Reset pulse received 
#define OWM_PRESENCE 2  //sending presence pulse
#define OWM_READ_COMMAND 3 //read 8 bit of command
#define OWM_SEARCH_ROM 4  //SEARCH_ROM algorithms
#define OWM_MATCH_ROM 5  //test number
#define OWM_READ_SCRATCHPAD 6   
#define OWM_WRITE_SCRATCHPAD 7
#define OWM_CHK_RESET 8  //waiting of rising edge from reset pulse

//Write a bit after next falling edge from master
//its for sending a zero as soon as possible 
#define OWW_NO_WRITE 2
#define OWW_WRITE_1 1
#define OWW_WRITE_0 0



PIN_INT {
	uint8_t lwmode=wmode;  //let this variables in registers
	uint8_t lmode=mode;
	if ((lwmode==OWW_WRITE_0)) {SET_LOW;lwmode=OWW_NO_WRITE;}    //if necessary set 0-Bit 
	DIS_OWINT; //disable interrupt, only in OWM_SLEEP mode it is active
	switch (lmode) {
		case OWM_SLEEP:  
			TCNT_REG=~(OWT_MIN_RESET);
			EN_OWINT; //other edges ?
			break;
		//start of reading with falling edge from master, reading closed in timer isr
		case OWM_MATCH_ROM:  //falling edge wait for receive 
		case OWM_WRITE_SCRATCHPAD:
		case OWM_READ_COMMAND:
			TCNT_REG=~(OWT_READLINE); //wait a time for reading
			break;
		case OWM_SEARCH_ROM:   //Search algorithm waiting for receive or send
			if (srcount<2) { //this means bit or complement is writing, 
				TCNT_REG=~(OWT_LOWTIME);					
			} else 
				TCNT_REG=~(OWT_READLINE);  //init for read answer of master 
			break;
		case OWM_READ_SCRATCHPAD:  //a bit is sending 
			TCNT_REG=~(OWT_LOWTIME);
			break;
		case OWM_CHK_RESET:  //rising edge of reset pulse
			SET_FALLING; 
			TCNT_REG=~(OWT_RESET_PRESENCE);  //waiting for sending presence pulse
			lmode=OWM_RESET;
			break;
	}
	EN_TIMER;
	mode=lmode;
	wmode=lwmode;
	
}			

	

TIMER_INT {
	uint8_t lwmode=wmode; //let this variables in registers
	uint8_t lmode=mode;
	uint8_t lbytep=bytep;
	uint8_t lbitp=bitp;
	uint8_t lsrcount=srcount;
	uint8_t lactbit=actbit;
	uint8_t lscrc=scrc;
	//Ask input line sate 
	uint8_t p=((OW_PIN&OW_PINN)==OW_PINN);  
	//Interrupt still active ?
	if (CHK_INT_EN) {
		//maybe reset pulse
		if (p==0) { 
			lmode=OWM_CHK_RESET;  //wait for rising edge
			SET_RISING; 
		}
		DIS_TIMER;
	} else
	switch (lmode) {
		case OWM_RESET:  //Reset pulse and time after is finished, now go in presence state
			lmode=OWM_PRESENCE;
			SET_LOW;
			TCNT_REG=~(OWT_PRESENCE);
			DIS_OWINT;  //No Pin interrupt necessary only wait for presence is done
			break;
		case OWM_PRESENCE:
			RESET_LOW;  //Presence is done now wait for a command
			lmode=OWM_READ_COMMAND;
			cbuf=0;lbitp=1;  //Command buffer have to set zero, only set bits will write in
			break;
		case OWM_READ_COMMAND:
			if (p) {  //Set bit if line high 
				cbuf|=lbitp;
			} 
			lbitp=(lbitp<<1);
			if (!lbitp) { //8-Bits read
				lbitp=1;
				switch (cbuf) {
					case 0x55:lbytep=0;lmode=OWM_MATCH_ROM;break;
					case 0xF0:  //initialize search rom
						lmode=OWM_SEARCH_ROM;
						lsrcount=0;
						lbytep=0;
						lactbit=(owid[lbytep]&lbitp)==lbitp; //set actual bit
						lwmode=lactbit;  //prepare for writing when next falling edge
						break;
					case 0x4E:
						lmode=OWM_WRITE_SCRATCHPAD;
						lbytep=2;scratchpad[2]=0;  //initialize writing position in scratch pad 
						break;
					case 0x44:  //Start Convert 
					case 0x64:  // some tool uses this command
						CONV_TEMP;
						lmode=OWM_SLEEP;
						break;
					case 0xBE:
						lmode=OWM_READ_SCRATCHPAD; //read scratch pad 
						lbytep=0;lscrc=0; //from first position
						lactbit=(lbitp&scratchpad[0])==lbitp;
						lwmode=lactbit; //prepare for send firs bit 
						break;
					default: lmode=OWM_SLEEP;  //all other commands do nothing
				}		
			}			
			break;
		case OWM_SEARCH_ROM:
			RESET_LOW;  //Set low also if nothing send (branch takes time and memory)
			lsrcount++;  //next search rom mode
			switch (lsrcount) {
				case 1:lwmode=!lactbit;  //preparation sending complement
					break;
				case 3:
					if (p!=(lactbit==1)) {  //check master bit
						lmode=OWM_SLEEP;  //not the same go sleep
					} else {
						lbitp=(lbitp<<1);  //prepare next bit
						if (lbitp==0) {
							lbitp=1;
							lbytep++;
							if (lbytep>=8) {
								lmode=OWM_SLEEP;  //all bits processed 
								break;
							}
						}				
						lsrcount=0;
						lactbit=(owid[lbytep]&lbitp)==lbitp;
						lwmode=lactbit;
					}		
					break;			
			}
			break;
		case OWM_MATCH_ROM:
			if (p==((owid[lbytep]&lbitp)==lbitp)) {  //Compare with ID Buffer
				lbitp=(lbitp<<1);
				if (!lbitp) {
					lbytep++;
					lbitp=1;
					if (lbytep>=8) {
						lmode=OWM_READ_COMMAND;  //same? get next command
						
						cbuf=0;
						break;			
					}
				} 
			} else {
				lmode=OWM_SLEEP;
			}
			break;
		case OWM_WRITE_SCRATCHPAD:
			if (p) {
				scratchpad[lbytep]|=lbitp;
			} 
			lbitp=(lbitp<<1);
			if (!lbitp) {		
				lbytep++;
				lbitp=1;
				if (lbytep==5) {
					lmode=OWM_SLEEP;
					break;
				} else scratchpad[lbytep]=0;
			}		
			break;	
		case OWM_READ_SCRATCHPAD:
			RESET_LOW;
			if ((lscrc&1)!=lactbit) lscrc=(lscrc>>1)^0x8c; else lscrc >>=1;
			lbitp=(lbitp<<1);
			if (!lbitp) {		
				lbytep++;
				lbitp=1;
				if (lbytep>=9) {
					lmode=OWM_SLEEP;
					break;			
				} else if (lbytep==8) scratchpad[8]=lscrc;
			}					
			lactbit=(lbitp&scratchpad[lbytep])==lbitp;
			lwmode=lactbit;
			break;
		}
		if (lmode==OWM_SLEEP) {DIS_TIMER;}
		if (lmode!=OWM_PRESENCE)  { 
			TCNT_REG=~(OWT_MIN_RESET-OWT_READLINE);  //OWT_READLINE around OWT_LOWTIME
			EN_OWINT;
		}
		
		mode=lmode;
		wmode=lwmode;
		bytep=lbytep;
		bitp=lbitp;
		srcount=lsrcount;
		actbit=lactbit;
		scrc=lscrc;
}


int main(void) {
	mode=OWM_SLEEP;
	wmode=OWW_NO_WRITE;
	OW_DDR&=~OW_PINN;
	
	SET_FALLING;
	
	INIT_AVR
	INIT_TEMP
	DIS_TIMER;
	
	sei();
	while(1){
	}
}	

 

 

DS2423 auf ATtiny25 und ATtiny2313

Durch die 16-Bit CRC Berechnung passt der DS2423 leider nicht mehr auf den ATtiny13. Wie man es auch dreht und wendet :-). Hier die Variante für den ATtiny25. Der NVRAM ist nicht mit implementiert. Die Trigger-Eingänge für die beiden Zähler sind PB3 und PB4. Eine Schaltung von p4trykx ist im im owfs-Forum zufinden. Zusätzlich habe ich auch die Anpassung für den ATtiny2313 mit angefügt. Der Code beinhaltet auch die Funktionen Skip-Rom und Read-Rom. Allerdings müssen diese erst in Zeile 180 aktiviert werden.

//owdevice - A small 1-Wire emulator for AVR Microcontroller
//
//Copyright (C) 2012  Tobias Mueller mail (at) tobynet.de
//
//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
// any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//
//VERSION 1.2 DS2423  for ATTINY2313 and ATTINY25

//FOR MAKE by hand
/*
avr-gcc -mmcu=[attiny25|attiny2313] -O2 -c ow_slave_DS2423.c
avr-gcc.exe -mmcu=[attiny25|attiny2313]  ow_slave_DS2423.o -o ow_slave_DS2423.elf
avr-objcopy -O ihex  ow_slave_DS2423.elf ow_slave_DS2423.hex
*/



#include <avr/io.h>
#include <avr/interrupt.h>
//does not work here because less memory by ATtiny13
#if defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny13__)
// OW_PORT Pin 6  - PB1
//Analog input PB2

//OW Pin
#define OW_PORT PORTB //1 Wire Port
#define OW_PIN PINB //1 Wire Pin as number
#define OW_PORTN (1<<PINB1)  //Pin as bit in registers
#define OW_PINN (1<<PINB1)
#define OW_DDR DDRB  //pin direction register
#define SET_LOW OW_DDR|=OW_PINN;OW_PORT&=~OW_PORTN;  //set 1-Wire line to low
#define RESET_LOW {OW_DDR&=~OW_PINN;}  //set 1-Wire pin as input
//Pin interrupt	
#define EN_OWINT {GIMSK|=(1<<INT0);GIFR|=(1<<INTF0);}  //enable interrupt 
#define DIS_OWINT  GIMSK&=~(1<<INT0);  //disable interrupt
#define SET_RISING MCUCR=(1<<ISC01)|(1<<ISC00);  //set interrupt at rising edge
#define SET_FALLING MCUCR=(1<<ISC01); //set interrupt at falling edge
#define CHK_INT_EN (GIMSK&(1<<INT0))==(1<<INT0) //test if interrupt enabled
#define PIN_INT ISR(INT0_vect)  // the interrupt service routine
//Timer Interrupt
#define EN_TIMER {TIMSK0 |= (1<<TOIE0); TIFR0|=(1<<TOV0);} //enable timer interrupt
#define DIS_TIMER TIMSK0  &= ~(1<<TOIE0); // disable timer interrupt
#define TCNT_REG TCNT0  //register of timer-counter
#define TIMER_INT ISR(TIM0_OVF_vect) //the timer interrupt service routine

//Initializations of AVR
#define INIT_AVR CLKPR=(1<<CLKPCE);\
				   CLKPR=0;/*9.6Mhz*/\
				   TIMSK0=0;\
				   GIMSK=(1<<INT0);/*set direct GIMSK register*/\
				   TCCR0B=(1<<CS00)|(1<<CS01); /*9.6mhz /64 couse 8 bit Timer interrupt every 6,666us*/


//Times
#define OWT_MIN_RESET 51 //minimum duration of the Reset impulse

#define OWT_RESET_PRESENCE 4 //time between rising edge of reset impulse and presence impulse
#define OWT_PRESENCE 20 //duration of the presence impulse
#define OWT_READLINE 4  //duration from master low to read the state of 1-Wire line
#define OWT_LOWTIME 4 //length of low 
#endif

#ifdef __AVR_ATtiny25__ 
// OW_PORT Pin 7  - PB2


//OW Pin
#define OW_PORT PORTB //1 Wire Port
#define OW_PIN PINB //1 Wire Pin as number
#define OW_PORTN (1<<PINB2)  //Pin as bit in registers
#define OW_PINN (1<<PINB2)
#define OW_DDR DDRB  //pin direction register
#define SET_LOW OW_DDR|=OW_PINN;OW_PORT&=~OW_PORTN;  //set 1-Wire line to low
#define RESET_LOW {OW_DDR&=~OW_PINN;}  //set 1-Wire pin as input
//Pin interrupt	
#define EN_OWINT {GIMSK|=(1<<INT0);GIFR|=(1<<INTF0);}  //enable interrupt 
#define DIS_OWINT  GIMSK&=~(1<<INT0);  //disable interrupt
#define SET_RISING MCUCR=(1<<ISC01)|(1<<ISC00);  //set interrupt at rising edge
#define SET_FALLING MCUCR=(1<<ISC01); //set interrupt at falling edge
#define CHK_INT_EN (GIMSK&(1<<INT0))==(1<<INT0) //test if interrupt enabled
#define PIN_INT ISR(INT0_vect)  // the interrupt service routine
//Timer Interrupt
#define EN_TIMER {TIMSK |= (1<<TOIE0); TIFR|=(1<<TOV0);} //enable timer interrupt
#define DIS_TIMER TIMSK  &= ~(1<<TOIE0); // disable timer interrupt
#define TCNT_REG TCNT0  //register of timer-counter
#define TIMER_INT ISR(TIM0_OVF_vect) //the timer interrupt service routine


#define OWT_MIN_RESET 51
#define OWT_RESET_PRESENCE 4
#define OWT_PRESENCE 20 
#define OWT_READLINE 3 //for fast master, 4 for slow master and long lines
#define OWT_LOWTIME 3 //for fast master, 4 for slow master and long lines

//Initializations of AVR
#define INIT_AVR CLKPR=(1<<CLKPCE); \
				   CLKPR=0; /*8Mhz*/  \
				   TIMSK=0; \
				   GIMSK=(1<<INT0);  /*set direct GIMSK register*/ \
				   TCCR0B=(1<<CS00)|(1<<CS01); /*8mhz /64 couse 8 bit Timer interrupt every 8us*/
				   
#define PC_INT_ISR ISR(PCINT0_vect) { /*ATT25 with 0 by PCINT*/ \
					if (((PINB&(1<<PINB3))==0)&&((istat&(1<<PINB3))==(1<<PINB3))) {	Counter0++;	}		\
					if (((PINB&(1<<PINB4))==0)&&((istat&(1<<PINB4))==(1<<PINB4))) {	Counter1++;	}		\
					istat=PINB;}	\
					
#define INIT_COUNTER_PINS /* Counter Interrupt */\
						GIMSK|=(1<<PCIE);\
						PCMSK=(1<<PCINT3)|(1<<PCINT4);	\
						DDRB &=~((1<<PINB3)|(1<<PINB4)); \
						istat=PINB;\
					
				   


#endif // __AVR_ATtiny25__ 

#if defined(__AVR_ATtiny2313A__) || defined(__AVR_ATtiny2313__)
// OW_PORT Pin 6  - PD2


//OW Pin
#define OW_PORT PORTD //1 Wire Port
#define OW_PIN PIND //1 Wire Pin as number
#define OW_PORTN (1<<PIND2)  //Pin as bit in registers
#define OW_PINN (1<<PIND2)
#define OW_DDR DDRD  //pin direction register
#define SET_LOW OW_DDR|=OW_PINN;OW_PORT&=~OW_PORTN;  //set 1-Wire line to low
#define RESET_LOW {OW_DDR&=~OW_PINN;}  //set 1-Wire pin as input
//Pin interrupt	
#define EN_OWINT {GIMSK|=(1<<INT0);EIFR|=(1<<INTF0);}  //enable interrupt 
#define DIS_OWINT  GIMSK&=~(1<<INT0);  //disable interrupt
#define SET_RISING MCUCR|=(1<<ISC01)|(1<<ISC00);  //set interrupt at rising edge
#define SET_FALLING {MCUCR|=(1<<ISC01);MCUCR&=~(1<<ISC00);} //set interrupt at falling edge
#define CHK_INT_EN (GIMSK&(1<<INT0))==(1<<INT0) //test if interrupt enabled
#define PIN_INT ISR(INT0_vect)  // the interrupt service routine
//Timer Interrupt
#define EN_TIMER {TIMSK |= (1<<TOIE0); TIFR|=(1<<TOV0);} //enable timer interrupt
#define DIS_TIMER TIMSK  &= ~(1<<TOIE0); // disable timer interrupt
#define TCNT_REG TCNT0  //register of timer-counter
#define TIMER_INT ISR(TIMER0_OVF_vect) //the timer interrupt service routine


#define OWT_MIN_RESET 51
#define OWT_RESET_PRESENCE 4
#define OWT_PRESENCE 20 
#define OWT_READLINE 3 //for fast master, 4 for slow master and long lines
#define OWT_LOWTIME 3 //for fast master, 4 for slow master and long lines

//Initializations of AVR
#define INIT_AVR CLKPR=(1<<CLKPCE); \
				   CLKPR=0; /*8Mhz*/  \
				   TIMSK=0; \
				   GIMSK=(1<<INT0);  /*set direct GIMSK register*/ \
				   TCCR0B=(1<<CS00)|(1<<CS01); /*8mhz /64 couse 8 bit Timer interrupt every 8us*/
				   
				   

#define PC_INT_ISR ISR(PCINT_vect) { /*ATT2313 without 0 by PCINT*/ \
					if (((PINB&(1<<PINB3))==0)&&((istat&(1<<PINB3))==(1<<PINB3))) {	Counter0++;	}		\
					if (((PINB&(1<<PINB4))==0)&&((istat&(1<<PINB4))==(1<<PINB4))) {	Counter1++;	}		\
					istat=PINB;}	\

#define INIT_COUNTER_PINS /* Counter Interrupt */\
						GIMSK|=(1<<PCIE);\
						PCMSK=(1<<PCINT3)|(1<<PCINT4);	\
						DDRB &=~((1<<PINB3)|(1<<PINB4)); \
						istat=PINB;\



#endif // __AVR_ATtiny2313__ 


//#define _ONE_DEVICE_CMDS_  //Commands for only one device on bus (Not tested)



typedef union {
	volatile uint8_t bytes[13];//={1,1,2,0,0,0,0,0,0,0,0,5,5};
	struct {
		uint16_t addr;
		uint8_t read;
		uint32_t counter;
		uint32_t zero;
		uint16_t crc;
	};
} counterpack_t;
counterpack_t counterpack;
volatile uint16_t scrc; //CRC calculation

volatile uint8_t lastcps;
volatile uint32_t Counter0;
volatile uint32_t Counter1;
volatile uint8_t istat;


volatile uint8_t cbuf; //Input buffer for a command
const uint8_t owid[8]={0x1D, 0xA2, 0xD9, 0x84, 0x00, 0x00, 0x02, 0x37};    
//set your own ID http://www.tm3d.de/index.php/tools/14-crc8-berechnung
volatile uint8_t bitp;  //pointer to current Byte
volatile uint8_t bytep; //pointer to current Bit

volatile uint8_t mode; //state
volatile uint8_t wmode; //if 0 next bit that send the device is  0
volatile uint8_t actbit; //current
volatile uint8_t srcount; //counter for search rom

//States / Modes
#define OWM_SLEEP 0  //Waiting for next reset pulse
#define OWM_RESET 1  //Reset pulse received 
#define OWM_PRESENCE 2  //sending presence pulse
#define OWM_READ_COMMAND 3 //read 8 bit of command
#define OWM_SEARCH_ROM 4  //SEARCH_ROM algorithms
#define OWM_MATCH_ROM 5  //test number
#define OWM_CHK_RESET 8  //waiting of rising edge from reset pulse
#define OWM_GET_ADRESS 6
#define OWM_READ_MEMORY_COUNTER 7
#define OWM_WRITE_SCRATCHPAD 9
#define OWM_READ_SCRATCHPAD 10

#ifdef _ONE_DEVICE_CMDS_
#define OWM_READ_ROM 50
#endif

//Write a bit after next falling edge from master
//its for sending a zero as soon as possible 
#define OWW_NO_WRITE 2
#define OWW_WRITE_1 1
#define OWW_WRITE_0 0



PIN_INT {
	uint8_t lwmode=wmode;  //let this variables in registers
	uint8_t lmode=mode;
	if ((lwmode==OWW_WRITE_0)) {SET_LOW;lwmode=OWW_NO_WRITE;}    //if necessary set 0-Bit 
	DIS_OWINT; //disable interrupt, only in OWM_SLEEP mode it is active
	switch (lmode) {
		case OWM_SLEEP:  
			TCNT_REG=~(OWT_MIN_RESET);
			EN_OWINT; //other edges ?
			break;
		//start of reading with falling edge from master, reading closed in timer isr
		case OWM_MATCH_ROM:  //falling edge wait for receive 
		case OWM_GET_ADRESS:
		case OWM_READ_COMMAND:
			TCNT_REG=~(OWT_READLINE); //wait a time for reading
			break;
		case OWM_SEARCH_ROM:   //Search algorithm waiting for receive or send
			if (srcount<2) { //this means bit or complement is writing, 
				TCNT_REG=~(OWT_LOWTIME);
			} else 
				TCNT_REG=~(OWT_READLINE);  //init for read answer of master 
			break;
#ifdef _ONE_DEVICE_CMDS_
		case OWM_READ_ROM:
#endif		
		case OWM_READ_MEMORY_COUNTER: //a bit is sending 
			TCNT_REG=~(OWT_LOWTIME);
			break;
		case OWM_CHK_RESET:  //rising edge of reset pulse
			SET_FALLING; 
			TCNT_REG=~(OWT_RESET_PRESENCE);  //waiting for sending presence pulse
			lmode=OWM_RESET;
			break;
	}
	EN_TIMER;
	mode=lmode;
	wmode=lwmode;
	
}			

	

TIMER_INT {
	uint8_t lwmode=wmode; //let this variables in registers
	uint8_t lmode=mode;
	uint8_t lbytep=bytep;
	uint8_t lbitp=bitp;
	uint8_t lsrcount=srcount;
	uint8_t lactbit=actbit;
	uint16_t lscrc=scrc;
	//Ask input line sate 
	uint8_t p=((OW_PIN&OW_PINN)==OW_PINN);  
	//Interrupt still active ?
	if (CHK_INT_EN) {
		//maybe reset pulse
		if (p==0) { 
			lmode=OWM_CHK_RESET;  //wait for rising edge
			SET_RISING; 
		}
		DIS_TIMER;
	} else
	switch (lmode) {
		case OWM_RESET:  //Reset pulse and time after is finished, now go in presence state
			lmode=OWM_PRESENCE;
			SET_LOW;
			TCNT_REG=~(OWT_PRESENCE);
			DIS_OWINT;  //No Pin interrupt necessary only wait for presence is done
			break;
		case OWM_PRESENCE:
			RESET_LOW;  //Presence is done now wait for a command
			lmode=OWM_READ_COMMAND;
			cbuf=0;lbitp=1;  //Command buffer have to set zero, only set bits will write in
			break;
		case OWM_READ_COMMAND:
			if (p) {  //Set bit if line high 
				cbuf|=lbitp;
			} 
			lbitp=(lbitp<<1);
			if (!lbitp) { //8-Bits read
				lbitp=1;
				switch (cbuf) {
					case 0x55:lbytep=0;lmode=OWM_MATCH_ROM;break;
					case 0xF0:  //initialize search rom
						lmode=OWM_SEARCH_ROM;
						lsrcount=0;
						lbytep=0;
						lactbit=(owid[lbytep]&lbitp)==lbitp; //set actual bit
						lwmode=lactbit;  //prepare for writing when next falling edge
						break;
					case 0xA5:
						lmode=OWM_GET_ADRESS; //first the master send an address 
						lbytep=0;lscrc=0x7bc0; //CRC16 of 0xA5
						counterpack.bytes[0]=0;
						break;
#ifdef _ONE_DEVICE_CMDS_
					case 0xCC:
						lbytep=0;cbuf=0;lmode=OWM_READ_COMMAND;break;
					case 0x33:
						lmode=OWM_READ_ROM;
						lbytep=0;	
						break;
#endif											
					default: lmode=OWM_SLEEP;  //all other commands do nothing
				}		
			}			
			break;
		case OWM_SEARCH_ROM:
			RESET_LOW;  //Set low also if nothing send (branch takes time and memory)
			lsrcount++;  //next search rom mode
			switch (lsrcount) {
				case 1:lwmode=!lactbit;  //preparation sending complement
					break;
				case 3:
					if (p!=(lactbit==1)) {  //check master bit
						lmode=OWM_SLEEP;  //not the same go sleep
					} else {
						lbitp=(lbitp<<1);  //prepare next bit
						if (lbitp==0) {
							lbitp=1;
							lbytep++;
							if (lbytep>=8) {
								lmode=OWM_SLEEP;  //all bits processed 
								break;
							}
						}				
						lsrcount=0;
						lactbit=(owid[lbytep]&lbitp)==lbitp;
						lwmode=lactbit;
					}		
					break;			
			}
			break;
		case OWM_MATCH_ROM:
			if (p==((owid[lbytep]&lbitp)==lbitp)) {  //Compare with ID Buffer
				lbitp=(lbitp<<1);
				if (!lbitp) {
					lbytep++;
					lbitp=1;
					if (lbytep>=8) {
						lmode=OWM_READ_COMMAND;  //same? get next command
						
						cbuf=0;
						break;			
					}
				} 
			} else {
				lmode=OWM_SLEEP;
			}
			break;
		case OWM_GET_ADRESS:  
			if (p) { //Get the Address for reading
				counterpack.bytes[lbytep]|=lbitp;
			}  
			//address is part of crc
			if ((lscrc&1)!=p) lscrc=(lscrc>>1)^0xA001; else lscrc >>=1;
			lbitp=(lbitp<<1);
			if (!lbitp) {	
				lbytep++;
				lbitp=1;
				if (lbytep==2) {
					lmode=OWM_READ_MEMORY_COUNTER;
					lactbit=(lbitp&counterpack.bytes[lbytep])==lbitp;
					lwmode=lactbit;
					lsrcount=(counterpack.addr&0xfe0)+0x20-counterpack.addr; 
					//bytes between start and Counter Values, Iam never understanding why so much???
					break;
				} else counterpack.bytes[lbytep]=0;
			}			
			break;	
		case OWM_READ_MEMORY_COUNTER:
			RESET_LOW;
			//CRC16 Calculation
			if ((lscrc&1)!=lactbit) lscrc=(lscrc>>1)^0xA001; else lscrc >>=1;
			p=lactbit;
			lbitp=(lbitp<<1);
			if (!lbitp) {		
				lbytep++;
				lbitp=1;
				if (lbytep==3) {
					lsrcount--;
					if (lsrcount) lbytep--;
					else  {//now copy counter in send buffer
						switch (counterpack.addr&0xFe0) {
						case 0x1E0:
							counterpack.counter=Counter0;
							break;
						case 0x1C0:
							counterpack.counter=Counter1;
							break;
						default: counterpack.counter=0;
						}
					}
				}
				if (lbytep>=13) { //done sending
					lmode=OWM_SLEEP;
					break;			
				}  		 
				if ((lbytep==11)&&(lbitp==1)) { //Send CRC
					counterpack.crc=~lscrc; 
				}			
					 
			}					
			lactbit=(lbitp&counterpack.bytes[lbytep])==lbitp;
			lwmode=lactbit;
			
			break;
#ifdef _ONE_DEVICE_CMDS_	
		case OWM_READ_ROM:
			RESET_LOW;
			lbitp=(lbitp<<1);
			if (!lbitp) {		
				lbytep++;
				lbitp=1;
				if (lbytep>=8) {
					lmode=OWM_SLEEP;
					break;			
				} 
			}					
			lactbit=(lbitp&owid[lbytep])==lbitp;
			lwmode=lactbit;
			break;
#endif		
		}
		if (lmode==OWM_SLEEP) {DIS_TIMER;}
		if (lmode!=OWM_PRESENCE)  { 
			TCNT_REG=~(OWT_MIN_RESET-OWT_READLINE);  //OWT_READLINE around OWT_LOWTIME
			EN_OWINT;
		}
		mode=lmode;
		wmode=lwmode;
		bytep=lbytep;
		bitp=lbitp;
		srcount=lsrcount;
		actbit=lactbit;
		scrc=lscrc;
}



PC_INT_ISR  //for counting  defined for specific device



int main(void) {
	mode=OWM_SLEEP;
	wmode=OWW_NO_WRITE;
	OW_DDR&=~OW_PINN;
	
	uint8_t i;
	for(i=0;i<sizeof(counterpack);i++) counterpack.bytes[i]=0;
	Counter0=0;
	Counter1=0;

	
	SET_FALLING;
	
	INIT_AVR
	DIS_TIMER;
	
	INIT_COUNTER_PINS

	sei();
	
	while(1){
		
	}
}	

Vielen Dank für die netten Kommentare. Ich freue mich immer über Rückmeldungen, Wünsche, Anregungen usw.

{jcomments on}

Erklärungen zum 1-Wire Emulator

Einige Erklärungen zum grundsätzlichen Ablauf:

Reset-Impuls erkennen

Eine 1-Wire-Sequenz beginnt immer mit einem Reset-Impuls. Dies ist der längste Low-Pegel im ganzen Protokoll. Im "Ruhezustand" (OWM_SLEEP) wird bei jeder fallenden Flanke ein Interrupt ausgelöst. Gleichzeitig wird ein Timer gestartet der nach der angegebenen minimalen Länge des Reset-Impulses eine Leitungsanfrage auslöst. Ist die Leitung dann immer noch auf Low, so wird der Pegel-Interrupt auf die Detektion einer steigenden Flanke umgestellt und sozusagen auf das Ende des Reset-Impulses gewartet.

Was passiert, wenn das 1-Wire Device im Ruhezustand ist, weil der 1-Wire Master mit einem anderem Gerät kommuniziert?
Jede fallende Flanke löst einen Interrupt aus. Der Timer wird immer wieder zurückgesetzt, oder am Ende der Kommunikation wird ein High-Pegel detektiert.

UML des Kommunikationsbeginns  UML für den Kommunikationsbeginn (pdf)

Presence-Impuls

Nach dem Reset-Impuls mus jedes angeschlossene Gerät den 1-Wire Bus eine bestimmte Zeit auch Low ziehen. So erkennt der Master, ob überhaupt Geräte angeschlossen sind. Dieser Vorgang ist komplett über den Timer-Interrupt gesteuert. Der Interrupt für den 1-Wire Eingang ist dabei deaktiviert.

1-Wire Sequenz und Ablauf im ProgrammZeitlicher Ablauf und Zustände im Mikrocontroller (pdf)

Empfangen von Bits und Bytes

Die Übertragung eines Bits im 1-Wire-Bus geht immer mit einer fallenden Flanke einher, die vom Master vorgegeben wird. Damit wird der Bus synchronisiert. Diese Flanke löst den Interrupt aus und stellt den Timer entsprechen ein für die Überprüfung ob das entsprechende Bit eine 1 (High-Pegel) oder eine 0 (Low-Pegel) ist. Der Interupt für das Bus-Pin wird dann deaktiviert und auf den Timer-Interupt gewartet. Die Timer-ISR list dann das Bit aus Speichert es und aktiviert wieder den Pin-Change-Interrupt. Das gleiche wiederholt sich für alle 8 Bits. Danach wird in der Timer-ISR der Befehl geprüft und die Zustandsvariable entsprechen gesetzt.

1-Wire Timing

Fünf Zeiten müssen für das Protokoll eingestellt werden:

Name Beschreibung Vorgegebene Zeit Wert bei 8 MHz/64 Erklärungen
OWT_MIN_RESET Minimale Dauer des Reset-Impulses 480 µs normal Wert Annahme 440 µm 55
OWT_RESET
_PRESENCE
Minimale Dauer des Reset-Impulses Master samples den Presence-Impuls nach
70 µs
4 (32µs) Basierent auf Messwerte
OWT_PRESENCE Pause zwischen Reset Presence-Impuls 410 µs bis Master fallende sendet 20 (80µs) Bei Problemen erhöhen
OWT_READLINE Zeit zwischen fallander Flanke vom Master und dem Auslesen 15 µs 3-4 (24-32 µs) Für lange Leitungen sind längere Zeiten sinnvoll
OWT_LOWTIME Dauer des Slave Low-Pegels 60 µs 3-4 (24-32 µs) Schnelle Master (z.B. DS9490) funktionieren nur mit 24 µs korrekt, da der AVR für die Berechnungen für das nächste Bit Zeit braucht (DS2423 read, Countercopy, CRC16...)

 

Zu Problemen kam es bei der DS2423-Emulation bei zu langer LOW-TIME. Offensichtlich ist der AVR dann noch nicht fertig mit der Berechnung, die erst nach dem Ende des Low-Signals erfolgt. Mit 24 µs funktionierte es aber bisher ganz gut. Es besteht aber noch die Möglichkeit, Berechnungen oder das Kopieren der Zählervariablen in die PIN_INT Funktion zu verschieben.  Weiterhin gibt es auch die Möglichkeit die LOW-TIME zu splitten, so dass die Berechnungen wärend des Low-Signals ausgeführt werden. Wer Probleme hat, soll sich bitte melden, dann pflege ich die Änderungen ein.

Der 1-Wire Search Algorithmus aus Sicht der 1-Wire Slaves

Hier wird der Search Rom Algorithmus aus sich eines 1-Wire Gerätes erklärt. Im Internet wird meistens die Sichtweise des Masters beschrieben (z.B. APPLICATION NOTE 187). Der Search-Rom Algorithmus besteht aus drei Stufen:

  1. Slave sendet ein Bit
  2. Slave sendet Bit negierte
  3. Slave empfängt das Kontrollbit vom Master

Entspricht das Kontrollbit dem gesendeten Bit, sogeht kann das nächste Bit gesendet werden. Andernfalls geht der 1-Wire Slave in den Ruhezustand (OW_SLEEP) über.

 1wird3UML für den Search-Rom Algorithmus (pdf)

Alle angeschlossenen Geräte durchlaufen die Search-Rom Prozedur gleichzeitig. Aus Sicht des Masters werden also die Antworten der Slaves mit der logischen Funktion AND verknüpft (sobald ein Slave die Leitung auf Low zieht ist wird ein Low gelesen).