.include "tn24def.inc" .equ crystal=3686400 //querzfrequenz .equ baudrate=1200 .equ bitlength= crystal/baudrate .equ startbl = 3*bitlength/2 //nach 1.5 Bitzeiten erstes Datenbit sampeln .equ AM_NOP = $00 // No operation .equ AM_CLOCK_SEC = $01 // Sekundenüberlauf .equ AM_UART_RX = $02 // zeichen empfangen .equ AM_NEWLINE= $03 // Eingabetaste wurde gedrückt //arbeitsregister, werden in den IRQ-Routinen gesichert .def SREG_TMP = r1 //Ringpuffer für TX .def TXPTR_LO = r2 .def TXPTR_HI = r3 .def RXPTR_LO= r4 .def RXPTR_HI = r5 .def MSGPTR_LO= r6 .def MSGPTR_HI = r7 //internes Statusregister .def STATUS = r26 .equ CLOCK_HALT = 0 .def TEMP1 = r16 .def TEMP2 = r17 .def TEMP3 = r18 .def TEMP4 = r19 .def TEMP5 = r25 .def TEMP6 = r9 .def TEMP7 = r10 .def TEMP8 = r11 .def BITCNT_OUT = r20 .def DATA_OUT = r21 .def BITCNT_IN = r22 .def DATA_IN = r23 .def CMD_PTR = r24 .org $0000 rjmp reset .org OVF1addr rjmp OVF1hdlr .org OC1Aaddr rjmp OC1Ahdlr .org OC1Baddr rjmp OC1Bhdlr .org EXT_INT0addr rjmp INT0hdlr .org INT_VECTORS_SIZE*2 reset: //init SP ldi temp1,low(RAMEND) out spl,temp1 rcall init_wdt clr status ldi temp1,$00 mov TXPTR_LO,temp1 mov TXPTR_HI,temp1 mov RXPTR_LO,temp1 mov RXPTR_HI,temp1 mov MSGPTR_LO,temp1 mov MSGPTR_HI,temp1 mov CMD_PTR,temp1 sts cmdbuf,cmd_ptr //pda2 als AUSGANG, der RS232-Ausgang ldi temp1,0b01000000 out ddra,temp1 //Timer 1 Init //ovf1 INterrupt Enable sbi timsk1,0 //Timer an ldi temp1,0b00000001 out tccr1b,temp1 //int0 init //INT0 on falling edge ldi temp1,0b00000010 out mcucr, temp1 //int0 an ldi temp1,0b01000000 out gimsk,temp1 rcall initclock sei rcall crlf rcall crlf ldi zl,low(msg_welcome*2) ldi zh,high(msg_welcome*2) rcall sendmessage rcall crlf rcall newline endless_sleep: ldi temp2,0b00100010 out mcucr,temp2 sleep endless: rcall popmessage breq endless_sleep cpi temp1,AM_CLOCK_SEC breq do_clock cpi temp1,AM_UART_RX breq do_uart_rx cpi temp1,AM_NEWLINE breq do_newline rjmp endless //eine Sekunde ist vergangen... do_clock: rcall check_alarm // rcall sendtime rjmp endless //ein Zeichen wurde empfangen do_uart_rx: rcall uart_rx rcall transmit cpi temp1,$0d //wenn es ein CR war, dann Zeileneingabe ausführen breq donewline rcall cmd_char rjmp endless donewline: ldi temp1,$0a rcall transmit rcall newline cli ldi temp1,AM_NEWLINE rcall postmessage sei rjmp endless do_newline: //eine Zeile wurde eingegeben ldi temp1,0 tokenize: //erstes Token finden rcall findtoken //es war eine leerzeile breq noerr //start des Tokens nun in temp1 //länge des tokens nun in temp2 findcommand: //aktuelles Kommando ldi temp4,$00 clr temp3 cmdloop: mov temp7,temp2 ldi yl,low(cmdbuf) ldi yh,high(cmdbuf) add yl,temp1 adc yh,temp3 ldi zl,low(commands*2) ldi zh,high(commands*2) add zl,temp4 adc zh,temp3 //y steht nun am Anfang des cmd-puffers, //z steht am Anfang von Kommando Nr Temp 4 // das ende der kommando-Tabelle ist erreicht cmdchrloop: lpm temp5,z+ cpi temp5,$ff //wenn zeichen $ff gefunden, dann ende der Kommandotabelle breq endcommand cpi temp5,' ' breq ignorespace ld temp6,y+ cp temp5,temp6 breq ignorespace subi temp4,-4 // 4 zu temp4 hinzu->nächstes Kommando prüfen rjmp cmdloop ignorespace: dec temp7 brne cmdchrloop //kommando wurde gefunden rcall execute_command brcs error brne noerr //OK-Message unterdrücken ldi zl,low(msg_ok*2) ldi zh,high(msg_ok*2) rcall sendmessage rjmp noerr error: ldi zl,low(msg_error*2) ldi zh,high(msg_error*2) rcall sendmessage rjmp noerr endcommand: ldi zl,low(msg_invalid*2) ldi zh,high(msg_invalid*2) rcall sendmessage noerr: rcall crlf rcall newline clr CMD_PTR sts cmdbuf,cmd_ptr rjmp endless execute_command: //nummer des kommandos durch 2 Teilen ergibt den Zeiger auf die Adresse lsr temp4 execute_command_direct: ldi zl,low(commandhandler*2) ldi zh,high(commandhandler*2) add zl,temp4 adc zh,temp3 //temp 3 ist immer noch null lpm temp3,z+ lpm temp4,z+ // Sprungadresse aus Tabelle lesen mov zl,temp3 mov zh,temp4 ijmp //ret wird von kommand ausgeführt.. //liefert in temp1 die startadresse eines eingabetokens //und in temp2 die Länge desselben findnexttoken: add temp1,temp2 findtoken: clr temp2 ldi zl,low(cmdbuf) ldi zh,high(cmdbuf) add zl,temp1 adc zh,temp2 tokenloop: ld temp3,z+ cpi temp3,$00 breq endtoken cpi temp3,' ' brne nowhitespace cpi temp2,$00 brne endtoken inc temp1 dec temp2 nowhitespace: inc temp2 cpi temp2,$1f brne tokenloop endtoken: tst temp2 ret //liest ein nibble auf dem kommandopuffer readnib: push temp1 push temp2 clr temp2 ldi zl,low(cmdbuf) ldi zh,high(cmdbuf) add zl,temp1 adc zh,temp2 ld temp2,z+ rcall nib2bin rjmp exit_read //liest eine dezimalzahl 00-99 aus dem Puffer readhex: push temp1 push temp2 clr temp2 ldi zl,low(cmdbuf) ldi zh,high(cmdbuf) add zl,temp1 adc zh,temp2 ld temp1,z+ ld temp2,z+ rcall a2bin rjmp exit_read readdec: push temp1 push temp2 clr temp2 ldi zl,low(cmdbuf) ldi zh,high(cmdbuf) add zl,temp1 adc zh,temp2 ld temp1,z+ ld temp2,z+ rcall dec2bin exit_read: mov temp3,temp1 pop temp2 pop temp1 ret //gibt die aktuelle Zeit auf dem UART aus sendalarm: ldi zl,low(alarm+3) ldi zh,high(alarm+3) rjmp dosend sendtime: ldi zl,low(current_time+3) ldi zh,high(current_time+3) dosend: push zl push zh ld temp3,-z rcall senddec rcall sendcolon ld temp3,-z rcall senddec rcall sendcolon ld temp3,-z rcall senddec ldi temp1,' ' rcall transmit pop zh pop zl ld temp3,z+ rcall senddec rcall senddot ld temp3,z+ rcall senddec rcall senddot ld temp3,z+ rcall senddec ldi temp1,' ' rcall transmit //Kommando senden ld temp3,z+ rcall senddec rcall crlf rjmp newline senddot: ldi temp1,'.' rjmp transmit sendcolon: ldi temp1,':' rjmp transmit senddec: rcall bin2dec rcall transmit mov temp1,temp2 rjmp transmit crlf: ldi temp1,$0d rcall transmit ldi temp1,$0a rjmp transmit newline: ldi temp1,'>' rjmp transmit //gibt den Text ab dem Z-Pointer aus, nullterminiert sendmessage: lpm temp1,z+ tst temp1 breq endsend rcall transmit rcall sendmessage endsend: ret //schreibt ein zeichen in die Befehlsque postmessage: mov temp2,MSGPTR_HI sub temp2,MSGPTR_LO cpi temp2,$07 brsh exitpost push zh push zl //Zeichen an Pufferstelle schreiben ldi temp2,$00 mov temp3,MSGPTR_HI ldi zl,LOW(msgqueue) ; ldi zh,HIGH(msgqueue) add zl,temp3 adc zh,temp2 st z,temp1 //pufferzeiger erhöhen inc temp3 andi temp3,0b00000111 mov MSGPTR_HI,TEMP3 pop zl pop zh exitpost: ret //liest ein zeichen aus dem UART-Empfangspuffer uart_rx: cli //prüfen, ob Zeichen im Puffer cp RXPTR_LO,RXPTR_HI breq exit_uart_rx ldi temp1,$00 mov temp2,RXPTR_LO ldi zl,LOW(rxbuf) ; ldi zh,HIGH(rxbuf) add zl,temp2 adc zh,temp1 ld temp1,z //puffer inc temp2 andi temp2,0b00000111 mov RXPTR_LO,TEMP2 clz exit_uart_rx: sei ret //holt eine Message aus der Messagequeue popmessage: cli //prüfen, ob neue nachricht in der Queue cp MSGPTR_LO,MSGPTR_HI breq exit_popmessage ldi temp1,$00 mov temp2,MSGPTR_LO ldi zl,LOW(msgqueue) ; ldi zh,HIGH(msgqueue) add zl,temp2 adc zh,temp1 ld temp1,z //puffer inc temp2 andi temp2,0b00000111 mov MSGPTR_LO,TEMP2 clz exit_popmessage: sei ret //schreibt ein zeichen in den Kommandopuffer cmd_char: //wenn puffer voll, dann message verwerfen cpi CMD_PTR,$1f brsh exit_cmd_char //Zeichen an Pufferstelle schreiben ldi temp2,$00 ldi zl,LOW(cmdbuf) ; ldi zh,HIGH(cmdbuf) add zl,CMD_PTR adc zh,temp2 st z+,temp1 clr temp2 // end-zeichen setzen st z,temp2 //pufferzeiger erhöhen inc CMD_PTR exit_cmd_char: ret //stellt ein Zeichen in den RS232 Sendepuffer // zeichen im TEMP1 register transmit: // warten, bis Puffer voll ist push zh push zl push temp2 push temp3 waittxbuf: mov temp2,txptr_hi sub temp2,txptr_lo cpi temp2,$07 brsh waittxbuf //interrupts aus cli //Zeichen an Pufferstelle schreiben ldi temp2,$00 mov temp3,TXPTR_HI ldi zl,LOW(txbuf) ; ldi zh,HIGH(txbuf) add zl,temp3 adc zh,temp2 st z,temp1 //pufferzeiger erhöhen inc temp3 andi temp3,0b00000111 mov TXPTR_HI,TEMP3 sei pop temp3 pop temp2 pop zl pop zh ret receive: //wenn puffer voll, dann zeichen verwerfen mov temp2,rxptr_hi sub temp2,rxptr_lo cpi temp2,$07 brsh exitreceive push zh push zl //Zeichen an Pufferstelle schreiben ldi temp2,$00 mov temp3,RXPTR_HI ldi zl,LOW(rxbuf) ; ldi zh,HIGH(rxbuf) add zl,temp3 adc zh,temp2 st z,DATA_IN //pufferzeiger erhöhen inc temp3 andi temp3,0b00000111 mov RXPTR_HI,TEMP3 pop zl pop zh exitreceive: ret //wird nur von Interrupt-Routine aufgerufen //startet den Transfer eines Bytes transmitbyte: push zl push zh //Zeichen an unterster Puffer-Stelle holen und übertragen ldi temp1,$00 mov temp2,TXPTR_LO ldi zl,LOW(txbuf) ; ldi zh,HIGH(txbuf) add zl,temp2 adc zh,temp1 ld data_out,z //puffer inc temp2 andi temp2,0b00000111 mov TXPTR_LO,TEMP2 ldi BITCNT_OUT,0 rcall settxmatch sbi timsk1,1 sbi tifr1,1 pop zh pop zl ret //Handler für Timer 0 Overflow OVF1hdlr: in sreg_tmp,sreg push temp1 push temp2 push temp3 push temp4 //wenn gerade TX übertragung läuft, dann nichs senden sbic timsk1,1 rjmp skip_tx //wenn zeichen im Sendepuffer, dann senden cp TXPTR_HI,TXPTR_LO breq skip_tx rcall transmitbyte skip_tx: rcall clock rjmp exit_ovf1 exit_ovf1: pop temp4 pop temp3 pop temp2 pop temp1 out sreg,sreg_tmp reti //Handler füt OCRA, Diese Interrupt-Routine sendet die RS232-Daten OC1Ahdlr: in sreg_tmp,sreg push temp1 push temp2 push temp3 push temp4 rcall settxmatch cpi BITCNT_OUT,$00 breq write0 //startbit cpi BITCNT_OUT,$09 brsh write1 //stopbit lsr data_out brcc write0 write1: sbi porta,6 rjmp done_write write0: cbi porta,6 done_write: inc BITCNT_OUT cpi BITCNT_OUT,$0a brne exitoc1a //stop timer cbi timsk1,1 exitoc1a: pop temp4 pop temp3 pop temp2 pop temp1 out sreg,sreg_tmp reti //Handler füt OCRB, Diese Interrupt-Routine Empfängt die RS232-Daten OC1Bhdlr: in sreg_tmp,sreg push temp1 push temp2 push temp3 push temp4 rcall setrxmatch clc sbic pinb,2 sec ror DATA_IN inc BITCNT_IN cpi BITCNT_IN,$08 brne exitOC1Bhdlr //OC Interrupt off, Timer Interrupt ON cbi timsk1,2 ldi temp1,0b01000000 out gifr,temp1 out gimsk,temp1 //zeichen in puffer schreiben rcall receive ldi temp1,AM_UART_RX rcall postmessage exitOC1Bhdlr: pop temp4 pop temp3 pop temp2 pop temp1 out sreg,sreg_tmp reti //Handler füt OCRB, Diese Interrupt-Routine Empfängt die RS232-Daten //Beim eintreffen einer fallenden Flanke wird der OCRB-Interrupt aktiviert //und 8 * eine Bitlänge abgetastet INT0hdlr: in sreg_tmp,sreg push temp1 push temp2 push temp3 push temp4 //nächster interrupt in 1.5 Bitzeiten, dh wir treffen die itte des ersten Bits ldi temp3,low(startbl) ldi temp4,high(startbl) rcall addmatch clr BITCNT_IN out ocr1bh,temp2 out ocr1bl,temp1 //clear pending irqs sbi tifr1,2 sbi timsk1,2 //int0 aus ldi temp1,0b00000000 out gimsk,temp1 pop temp4 pop temp3 pop temp2 pop temp1 out sreg,sreg_tmp reti //Hilfroutinen, welche die Zählerwerte für Sende-und Empfangsinterrupt berechen setrxmatch: rcall getnextmatch out ocr1bh,temp2 out ocr1bl,temp1 ret //nächsten interrupt setzen settxmatch: rcall getnextmatch out ocr1ah,temp2 out ocr1al,temp1 ret getnextmatch: ldi temp3,low(bitlength) ldi temp4,high(bitlength) addmatch: in temp1,tcnt1l in temp2,tcnt1h add temp1,temp3 adc temp2,temp4 ret /* reset: ldi temp1,low(ramend) out spl,temp1 // ldi temp1,high(ramend) // out sph,temp1 rcall initclock ldi temp1,0 endless: rcall clock ldi temp1,$00 rcall check_alarm rjmp endless */ initclock: //Start-Uhrzeit aus EEprom lesen ldi temp2,$00 rcall gettime //und ins SRAM schreiben lds temp1,alarm sts seconds,temp1 lds temp1,alarm+1 sts minutes,temp1 lds temp1,alarm+2 sts hours,temp1 lds temp1,alarm+3 sts day,temp1 lds temp1,alarm+4 sts month,temp1 lds temp1,alarm+5 sts year,temp1 ldi temp1,crystal & $ff sts tick_cnt+2,temp1 ldi temp1,(crystal/256) & $ff sts tick_cnt+1,temp1 ldi temp1,(crystal/65536) & $ff sts tick_cnt,temp1 ret clock: sbrc status,CLOCK_HALT rjmp endclock rcall ticks brne endclock //kein Sekundenüberlauf ldi temp1,AM_CLOCK_SEC rcall postmessage //sekunden lds temp1,seconds inc temp1 sts seconds,temp1 cpi temp1,60 brne endclock clr temp1 sts seconds,temp1 //minuten lds temp1,minutes inc temp1 sts minutes,temp1 cpi temp1,60 brne endclock clr temp1 sts minutes,temp1 //stunden lds temp1,hours inc temp1 sts hours,temp1 cpi temp1,24 brne endclock clr temp1 sts hours,temp1 //datum ändern //aktuelle Monatslänge holen rcall month_length //tag lds temp1,day inc temp1 sts day,temp1 cp temp1,temp2 brne endclock ldi temp1,1 sts day,temp1 //monat lds temp1,month inc temp1 sts month,temp1 cpi temp1,13 brne endclock ldi temp1,1 sts month,temp1 //jahr lds temp1,year inc temp1 sts year,temp1 endclock: ret //zählt Systemtakte //setzt Zeroflag, wenn 1 Sekundenüberlauf erfolgt ist ticks: lds temp1,tick_cnt dec temp1 sts tick_cnt,temp1 brne end_ticks ldi temp2,tick_count+2 rcall readeeprom lds temp2,tick_cnt+2 add temp2,temp1 sts tick_cnt+2,temp2 ldi temp2,tick_count+1 rcall readeeprom lds temp2,tick_cnt+1 adc temp2,temp1 sts tick_cnt+1,temp2 ldi temp2,tick_count rcall readeeprom lds temp2,tick_cnt adc temp2,temp1 sts tick_cnt,temp2 sez end_ticks: ret //Monatslänge,inkl einfacher Schaltjahr-Berechnung //liefert die Anzahl der Tage des Monats +1 //wird dann direkt zum Vergleich verwendet month_length: //zuerst alle mit 30 Tagen prüfen ldi temp2,31 lds temp1,month cpi temp1,4 breq end_length cpi temp1,6 breq end_length cpi temp1,9 breq end_length cpi temp1,11 breq end_length //wenns ein Februar ist, auf Schaltjahr prüfen cpi temp1,2 breq testleap //Nicht 30 Tage und kein Februar->Monat hat 31 Tage ldi temp2,32 rjmp end_length testleap: //februar hat erstmal 28 Tage ldi temp2,29 lds temp1,year andi temp1,0b00000011 brne end_length //die letzten beiden Bits sind null, also durch 4 teilbar: //es ist ein Schaltjahr inc temp2 end_length: ret //Prüft ob Alarm aufgetreten // //prüft alle 8 alarme durch und führt bei übereinstimmung das hinterlegte kommando aus check_alarm: ldi temp5,$08 check_alarm_loop: mov temp2,temp5 rcall gettime ldi zl,LOW(current_time) ; ldi zh,HIGH(current_time) ldi yl,low(alarm) ldi yh,high(alarm) ldi temp3,6 check_alarm_bytes: ld temp1,z+ ld temp2,y+ cp temp1,temp2 brne end_check_alarm dec temp3 brne check_alarm_bytes ld temp4,y+ //wichtig.Temp3 muss 0 sein für funktion execute_command_direct lsl temp4 push temp5 rcall execute_command_direct pop temp5 end_check_alarm: dec temp5 brne check_alarm_loop ret dec2bin: //nur für 00 bis 99 subi temp1,48 subi temp2,48 mov temp3,temp1 lsl temp3 // *8 lsl temp3 lsl temp3 lsl temp1//+*2 add temp1,temp3 //=*10 add temp1,temp2 ret //wandelt 2 ascii zeichen in eine Binärzahl (00-ff) nib2bin: //selbiges für nibble, hier high-nibble einfach löschen ldi temp1,'0' a2bin: subi temp1,48 cpi temp1,10 brlo nochar1 subi temp1,39 nochar1: swap temp1 subi temp2,48 cpi temp2,10 brlo nochar2 subi temp2,39 nochar2: add temp1,temp2 ret writehex: rcall bin2hex rcall transmit mov temp1,temp2 rcall transmit ret bin2hex: mov temp2,temp1 swap temp1 andi temp1,$0f subi temp1,-48 cpi temp1,$3a brlo nochar3 subi temp1,-39 nochar3: andi temp2,$0f subi temp2,-48 cpi temp2,$3a brlo nochar4 subi temp2,-39 nochar4: ret //bin2asci, funktioniert NUR für Zahlen! bin2dec: ldi temp1,-1 mov temp2,temp3 divloop: inc temp1 subi temp2,10 ;-10 brcc divloop subi temp2,-58 ; subi temp1,-48 ; ret gettime: //holt eine Time-Struktur aus dem EEprom lsl temp2 lsl temp2 lsl temp2 ldi temp3,low(times) add temp2,temp3 ldi yl,low(alarm) ldi yh,high(alarm) ldi temp4,$08 //8 zeichen schreiben getloop: rcall readeeprom st y+,temp1 inc temp2 dec temp4 brne getloop ret //speichert den aktuellen alarm-puffer im eeprom an stelle TEMP2*8 storetime: lsl temp2 lsl temp2 lsl temp2 ldi temp3,low(times) add temp2,temp3 ldi yl,low(alarm) ldi yh,high(alarm) ldi temp4,$08 //8 zeichen schreiben writeloop: ld temp1,y+ rcall writeeeprom inc temp2 dec temp4 brne writeloop ret //adresse in temp2, daten kommen dann in temp1 readeeprom: sbic eecr,eepe rjmp readeeprom out eearl,temp2 sbi eecr,eere in temp1,eedr ret //adresse in temp2, daten in temp1 writeeeprom: sbic eecr,eepe rjmp writeeeprom ldi temp3,$00 out eecr,temp3 out eearl,temp2 out eedr,temp1 sbi eecr,eempe sbi eecr,eepe endwrite: ret SALARMHANDLER: cli rcall readtime breq no_alarm rcall findnexttoken breq no_alarm rcall readdec dosetalarm: sts alarm+6,temp3 rcall findnexttoken breq no_alarm rcall readnib mov temp2,temp3 inc temp2 rcall storetime no_alarm: sei clc clz ret CLAHANDLER: cli clr temp3 sts alarm+0,temp3 sts alarm+1,temp3 sts alarm+2,temp3 sts alarm+3,temp3 sts alarm+4,temp3 sts alarm+5,temp3 rjmp dosetalarm RESETHANDLER: rjmp reset //liest 3 bytes ein und schreibt sie als neuen Tick-Zählerwert ins eeprom XTALHANDLER: cli rcall findnexttoken breq exit_tick_err rcall readhex sts alarm,temp3 rcall findnexttoken breq exit_tick_err rcall readhex sts alarm+1,temp3 rcall findnexttoken breq exit_tick_err rcall readhex mov temp1,temp3 ldi temp2,tick_count+2 rcall writeeeprom dec temp2 lds temp1,alarm+1 rcall writeeeprom dec temp2 lds temp1,alarm rcall writeeeprom exit_tick: sei clc clz ret exit_tick_err: sei sec sez ret STIMEHANDLER: cli rcall readtime breq no_stime exit_stime: ldi temp2,$00 rcall storetime rcall initclock no_stime: sei clc clz ret readtime: rcall findnexttoken breq exit_readtime rcall readdec sts alarm+2,temp3 rcall findnexttoken breq exit_readtime rcall readdec sts alarm+1,temp3 rcall findnexttoken breq exit_readtime rcall readdec sts alarm,temp3 rcall findnexttoken breq exit_readtime rcall readdec sts alarm+3,temp3 rcall findnexttoken breq exit_readtime rcall readdec sts alarm+4,temp3 rcall findnexttoken breq exit_readtime rcall readdec sts alarm+5,temp3 clz exit_readtime: ret TIMEHANDLER: rcall sendtime clc clz ret SHOWHANDLER: ldi temp2,$00 showloop: push temp2 rcall gettime rcall sendalarm pop temp2 inc temp2 cpi temp2,$09 brne showloop clc clz ret STOPHANDLER: sbr status,(1<