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.

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.

Kommentare  

0 #10 DS2413IPRalf 2014-05-29 20:56
Danke für den Code.

Die DS2413 waren nicht lieferbar.
Also habe ich deine Code angepasst.
Die ATtiny45 sind sogar billiger.
Können zwar keine 28V/20mA schalten, aber für einfache Fensterkontakte ist das Ideal.

Gruß Ralf

PS:
Falls du den Code haben möchtest, bitte melden.
Ich würde auch gerne den Code mit den Lizenshinweisen auf meiner Homepage veröffentlichen.
Darf ich einen Link auf deine Seite setzen?
Zitieren
0 #9 Best version of gcc-avrTobias Müller 2014-04-14 07:16
I use actual the version 3.4.2.1002 of gcc.It is important to use -O1 or -O2 to compile not -Os!!
Zitieren
0 #8 Best version of gcc-avrMirceaC 2014-04-07 20:08
Hello Tobias, thank you for the nice code. I want to compile it but the problem is that there are so many versions of gcc-avr :(, could you please tell me what is the most recommended version ( a Windows version will be nice) ?
Also, do I need any other libraries for the code co compile, is there an archive holding all the necessary packages ?

Cheers, Mircea
Zitieren
0 #7 RE: Der Sourcecode für 1-Wire Devices mit AVR-MikrocontrollernJakob 2013-10-27 21:36
Hi All
I would very much like to program a "fake" DS2423 on a ATTiny 25.
Normaly I do not program avr chips so I have only little experience on this. Can you please tell me what software is god for programming the chip. I do have a AVR programmer for the job.

You can answer in german if yo like, I mostly do understand. :)

Thanks

Jakob
Zitieren
0 #6 inerne Pull-UpsTobias 2013-08-19 12:33
Hallo Eddy,
Das PUD-Flag im MCUCR Register ist "0" und die entsprechenden PORTxn sind auch "0". Daraus folgt, dass kein Pull-Up geschaltet ist. Die Konfiguration ist ein bisschen durcheinander. Bei der nächsten Version werde ich mal Ordnung in das Chaos bringen. Um einen Pull-Up zu erreichen sollte PORTB3 und PORTB4 auf "1" gesetzt werden.
Viele Grüße
Tobias
Zitieren
0 #5 inerne Pull-UpsEddy 2013-08-19 10:58
Hallo Tobias, danke für den Code.
Sind die ATmel internen Pull-Up Widerstände an den beiden Zähleingängen mit deinem Code aktiv oder nicht ?
Zitieren
0 #4 SKIP_ROMMarkus 2012-09-02 11:56
Hallo Tobias,
danke für diesen interessanten Code. Könnte man das ganze auch noch um IO's ergänzen an dem AVR?

Grüße
Markus
Zitieren
0 #3 SKIP_ROMTobias 2012-06-24 12:46
Hallo Peter,
SKIP_ROM habe ich weggelassen, weil ich es noch nie verwendet habe. Das gleiche gilt für READ_ROM (Funktioniert auch nur mit einem Gerät am Bus).
Der Buffer für den Befehl cbuf muss noch auf 0 gesetzt werden, da beim Lesen eines Befehls nur die gesetzten Bits eingetragen werden.
Also:
case 0xCC:lbytep=0;cbuf=0;lmode=OWM_READ_COMMAND;break;

PS:
Ich überarbeite den Code gerade ein bischen um in leichter auf andere AVRs übertragen zu können. Ich werde die beiden Befehle als Option mit integrieren.
Zitieren
0 #2 RE: Der Sourcecode für 1-Wire Devices mit AVR-MikrocontrollernPeter Jung 2012-06-24 01:53
case 0x55:lbytep=0;lmode=OWM_MATCH_ROM;break;
+ case 0xCC:lbytep=0;lmode=OWM_READ_COMMAND;break;

um SKIP_ROM zu implementieren oder kanns leider nicht testen?
Zitieren
0 #1 RE: Der CodeKoplimi 2012-06-24 01:32
Thank you very much for the code!

This will solve a lot of my problems with onewire-hardware.

Thanks!
Zitieren
Zum Seitenanfang