;*************************************************** ;Uhr mit MSF60-Synchronisation ;Anzeige von Zeit, Datum, Wochentag, ;nur A-Channel-Code, ;Ausgabe auf LCD und RS232, ;MSF60-Telegramm bitweise auf RS232 9k6, 8N1 ;ATMEL AVR AtTiny4313 ;Signature: 0x1E,0x92,0x0D ;LCD 2x16, 4-Bit, PB0=RS, PB1=Enable; R/W=GND ;MSF60 input low active = PD6 ;Quarzfrequenz 4,096 MHz (3,686 MHz STK500) ;CPU Takt 4,096 MHz (3,686 MHz STK500) ;*Prog 22032026* ;© frei nach @scott-falk huehn ;ATtiny4313 memory use summary [bytes]: ;Segment Begin End Code Data Used Size Use% ;[.cseg] 0x000000 0x000750 1840 32 1872 4096 45.7 ;[.dseg] 0x000060 0x000079 0 25 25 256 9.8 ;[.eseg] 0x000000 0x000000 0 0 0 256 0.0 ;Assembly complete, 0 errors. 0 warnings ;*************************************************** ; ;Assemblerdirektiven: ; .nolist .include "tn4313def.inc" .list ; ;Definitionen: ;Im SRAM reservierte Bytes ; .dseg ;Datensegment (SRAM) Start bei .org 0x0060 ;Adresse 0x0060 ; sekunde: ;Sekundenpufferbyte binaer .byte 1 minute: ;Minutenpufferbyte binaer .byte 1 stunde: ;Stundenpufferbyte binaer .byte 1 tag: ;Tag-Pufferbyte binaer (1-31) .byte 1 monat: ;Monat-Pufferbyte binaer (1-12) .byte 1 jahr: ;Jahr-Pufferbyte binaer (0-99) .byte 1 ; msfarr: ;MSF60-Schieberegister - Empfangene Nullen .byte 6 ;und Einsen werden in Byte 5, Bit 7 eingelesen ;und pro folgendem Timer-Interrupt in allen ;6 Bytes um eine Bitposition in Richtung LSB ;weitergerueckt. ;Versatz +2 Bits gegenueber DCF77 wegen ;59. Minute und Startbit, Die Wertigkeiten der ;Bits sind gegenueber DCF77 vertauscht => LSB=MSB. ;Das Zeitzonenbit ist im A-Channel-Code nicht ;enthalten. Sonntag=0 anstelle von 7 ;Byte/Bit: ;Byte 0:0bxxx00000 ;Jahr Zehner LSB->MSB ;(Byte 0:J20,J40,J80) ;Byte 1:0b0000000x ;Jahr Zehner LSB ;Byte 1:0b000xxxx0 ;Jahr Einer LSB->MSB ;Byte 1:0b00x00000 ;Monat Zehner ;Byte 1:0bxx000000 ;Monat Einer MSB LSB->MSB ;(Byte 1:M4,M8,M10,J1,J2,J4,J8,J10) ;Byte 2:0b000000xx ;Monat Einer LSB LSB->MSB ;Byte 2:0b0000xx00 ;Kalendertag Zehner LSB->MSB ;Byte 2:0bxxxx0000 ;Kalendertag Einer LSB->MSB ;(Byte 2:K1,K2,K4,K8,K10,K20,M1,M2) ;Byte 3:0b00000xxx ;Wochentag LSB->MSB ;Byte 3:0b000xx000 ;Stunde Zehner LSB->MSB ;Byte 3:0bxxx00000 ;Stunde Einer LSB->MSB ;(Byte 3:S2,S4,S8,S10,S20,W1,W2,W4) ;Byte 4:0b0000000x ;Stunde Einer LSB ;Byte 4:0b0000xxx0 ;Minute Zehner LSB->MSB ;Byte 4:0bxxxx0000 ;Minute Einer LSB->MSB ;(Byte 4:m1,m2,m4,m8,m10,m20,m40,S1) ;Byte 5:0b01111110 ;Synchronisationsbits ; msfbuf1:;MSF-Puffer 1, aktuelle, bcd-decodierte Uhrzeit .byte 12;Byte 0: 0b0000000x nicht benutzt ;Byte 1: 0b0000xxxx Minute Einer ;Byte 2: 0b00000xxx Minute Zehner ;Byte 3: 0b0000xxxx Stunde Einer ;Byte 4: 0b000000xx Stunde Zehner ;Byte 5: 0b0000xxxx Kalendertag Einer ;Byte 6: 0b000000xx Kalendertag Zehner ;Byte 7: 0b00000xxx Wochentag ;Byte 8: 0b0000xxxx Monat Einer ;Byte 9: 0b0000000x Monat Zehner ;Byte 10: 0b0000xxxx Jahr Einer ;Byte 11: 0b0000xxxx Jahr Zehner lockb: ;zusaetzliches Pufferbyte zur Eliminierung der .byte 1 ;(A=0,B=1)-Doppelmarken in 100ms-Distanz ; ;Variablendefinitionen: .equ daten = portb .equ licht = 3 ;0b0000x000 Portbit LCD-Beleuchtung Ladewerte .equ zeit0 = 400 ;fuer LCD-Zeitschleifen .equ zeit1 = 65535 .equ MCUfreq = 4096000 ;Quarzfrequenz 4 MHz(resp.3686000) ; ;(Fuses:0xFD,0xDF,0xFF) .equ ctcomp = 40959 ;Timer compare bei 0,01 sec {(MCUfreq/100)-1} ; ;(resp. bei STK500 => 36859) .equ bdrt = 27 ;(24);Baudratenteiler fuer 9600 Bd festlegen ;MCUfreq/{(Bd*16)-1} ; ;Registerdefinitionen: .def msfsec = r9 ;MSF60 Sekundenmarkenzaehler (0-60) .def msfcnt = r10 ;MSF60-Sampling,PD6 low {x*(1/100) sec} .def flags = r11 ;Verschiedene Flags ;Bit 0: akueller MSF60-Zustand - high oder low ;Bit 1: vorheriger MSF60-Zustand ;Bit 2: Uhr wurde mind. einmal synchronisiert ;Bit 3: Start neue Minute ; TimerISR setzt,standby resettet Bit ;Bit 4: Datum gueltig ;Bit 5: nicht belegt ;Bit 6: nicht belegt ;Bit 7: nicht belegt .def inttmp = r12 ;temporaeres Register fuer Interrupt-;Routinen .def intreg = r15 ;Zwischenspeicher fuer SREG bei ISRs .def temp = r16 ;diverse Universalregister .def temp1 = r17 .def temp2 = r18 ; .cseg ;Codestart .org 0x0000 rjmp RESET ; Interrupt Vektoren Einsprungadressen (default) ;******************************************************************* .org 0x0001 ;ext IRQ0 Flanke rjmp INT00 .org 0x0002 ;ext IRQ1 Flanke rjmp INT10 .org 0x0003 ;Timer 1 capt rjmp ICP10 .org 0x0004 ;Timer 1 comp match A rjmp OC100 .org 0x0005 ;Timer 1 overflow rjmp OVF10 .org 0x0006 ;Timer 0 overflow rjmp OVF00 .org 0x0007 ;UART Receive Complete rjmp URXC0 .org 0x0008 ;UART Data Register Empty rjmp UDRE0 .org 0x0009 ;UART Transmit Complete rjmp UTXC0 .org 0x000A ;Analog Comparator Interrupt rjmp ACI00 .org 0x000B ;Pin Change Interrupt Request B rjmp PCIBA .org 0x000C ;Timer 1 comp match B rjmp OC1BA .org 0x000D ;Timer 0 comp match A rjmp OC0AA .org 0x000E ;Timer 0 comp match B rjmp OC0BA .org 0x000F ;USI Start Condition rjmp USIST .org 0x0010 ;USI Overflow rjmp USIOV .org 0x0011 ;EPROM read ready rjmp ERDYA .org 0x0012 ;Watchdog Timer Overflow rjmp WDTAD .org 0x0013 ;Pin Change Interrupt Request A rjmp PCIAA .org 0x0014 ;Pin Change Interrupt Request D rjmp PCIDA ; ;Interrupt Service Routinen - Interrupt handler ;*********************************************** INT00: ;ISR not in use reti INT10: ;ISR not in use reti ICP10: ;ISR not in use reti OC100: ;ISR Portabfrage MSF60-Status high/low rjmp T1Interr OVF10: ;ISR not in use reti OVF00: ;ISR not in use reti URXC0: ;ISR not in use reti UDRE0: ;ISR not in use reti UTXC0: ;ISR not in use reti ACI00: ;ISR not in use reti PCIBA: ;ISR not in use reti OC1BA: ;ISR not in use reti OC0AA: ;ISR not in use reti OC0BA: ;ISR not in use reti USIST: ;ISR not in use reti USIOV: ;ISR not in use reti ERDYA: ;ISR not in use reti WDTAD: ;ISR not in use reti PCIAA: ;ISR not in use reti PCIDA: ;ISR not in use reti ; RESET: cli ;Interrupts sperren ldi temp, low(ramend) ;Stackpointer setzen out spl, temp ldi temp, high(ramend) out sph, temp ldi yl, low(0x0060) ;<<8 Start SRAM-Bereich low(0x0060) ldi yh, high(0x0060) ;&255 Start SRAM-Bereich high(0x0000) ldi temp, 0x00 init10x: ;SRAM mit Nullen beschreiben st y+, temp ;Speicherzellen loeschen cpi yl, low(ramend+1) ;RAM-Ende (low) erreicht? brne init10x ;nein => Schleife cpi yh, high(ramend+1) ;RAM-Ende (high) erreicht? brne init10x ;nein => Schleife rcall regclr ;Registerreset rcall portinit ;Init I/O-Ports rcall rs232init ;Init serielle Schnittstelle rcall lcdinit ;LCD-Init ldi temp, 'A' ;LCD-Test Buchstaben rcall char2lcd rcall wait1 mov temp1, temp rcall num ;LCD-Test Ziffern rcall wait1 rcall lcd_clear ;LCD Anzeige loeschen rcall wait1 rcall timerinit ;Timerinitialisierung sei ;generelle Interruptfreigabe rjmp standby ;Sprung zum Hauptprogramm ; regclr: clr flags ;Flagregister resetten clr msfsec ;MSF60-Sekundenmarkenzaehler loeschen clr msfcnt ;MSF60-Samplingzaehler loeschen ret ; portinit: ldi temp, 0xFF ;PortB Ausgang LCD out ddrb, temp ;PB0=RS, PB1=Enable, PB3=Hintergrundlicht out portb, temp ;PB4-7=Daten ldi temp, 0b00111010 ;PortD RS232 I/O, MSF60 input out ddrd, temp ;PD0=RXD;PD1=TXD;PD6=MSF60 input(low act.) ldi temp, 0xFF ;Pullups auch fuer RXD out portd, temp ret ; timerinit: ldi temp, high(40959) ;CTC=clear timer on compare match out OCR1AH, temp ;loescht Zaehlregisterinhalt bei ldi temp, low(40959) ;Uebereinstimmung mit dem Vorladewert out OCR1AL, temp ;(STK500=36859;bei 4 MHz=39999) ldi temp, 0x00 out TCCR1A, temp ldi temp, (1< ma1700 und loop cli ;ja-> Interrupts sperren ldi temp,0b11110111 ;Minuten-Flag loeschen and flags, temp ;in Flags speichern sei ;Interrupts wieder aktivieren rcall mdc100 ;MSF60-Auswertung nop rcall msfchk ;MSF60-Check ok? tst r19 brne ma1700 ;nein -> ma1700 und loop rcall mtsync ;ja->Uhr synchronisieren nop rjmp ma1700 ;loop ; ma1700: set bld flags, 2 ;Synchronisationsversuch-Flag setzen clt ;T-Flag loeschen bld flags, 4 ;Datum-gueltig-Flag loeschen clr r19 ;Fehlerzaehler resetten clr r20 rjmp standby ; ;--------------------------------------------------- ; - Pruefung der 8 Synchronisationsbits - ;--------------------------------------------------- ; msfchk: ldi temp, 0b01111110 lds r20, msfarr+5 andi r20, 0x7E cp r20, temp brne msfchk1 ;nicht ok->msfchk1 clr temp ;ok-> Fehlerzaehler etc. resetten clr r19 clr r20 ret ; msfchk1: inc r19 ;Fehlerzaehler erhoehen ret ;--------------------------------------------------- ; - Ablegen der Zeitinformation in Puffer msfbuf1 ;--------------------------------------------------- ; mdc100: ldi zl,low(msfbuf1) ;Zeiger auf MSF60-Puffer 1 ldi zh,high(msfbuf1) lds r23, msfarr ;Byte 0 aus Array holen andi r23, 0x01 ;relevantes Bit filtern st z+,r23 ;in msfbuf1 speichern clr r24 lds r23, msfarr+4 mov r22, r23 andi r22, 0b10000000 ;m1 Minuten Einer ror r22 ror r22 ror r22 swap r22 add r24, r22 lds r23, msfarr+4 mov r22, r23 andi r22, 0b01000000 ;m2 ror r22 swap r22 add r24, r22 mov r22, r23 andi r22, 0b00100000 ;m4 rol r22 swap r22 add r24, r22 mov r22, r23 andi r22, 0b00010000 ;m8 ror r22 add r24, r22 andi r24, 0x0F st z+, r24 ;Minuten Einer speichern clr r24 lds r23,msfarr+4 mov r22, r23 andi r22, 0b00001000 ;m10 ror r22 ror r22 ror r22 add r24, r22 mov r22, r23 andi r22, 0b00000100 ;m20 ror r22 add r24, r22 mov r22, r23 andi r22, 0b00000010 ;m40 rol r22 add r24, r22 andi r24, 0x07 st z+, r24 ;Minuten Zehner speichern clr r24 lds r23, msfarr+4 mov r22, r23 andi r22, 0b00000001 ;S1 add r24, r22 lds r23, msfarr+3 mov r22, r23 andi r22, 0b10000000 ;S2 ror r22 ror r22 swap r22 add r24, r22 mov r22, r23 andi r22, 0b01000000 ;S4 swap r22 add r24, r22 mov r22, r23 andi r22, 0b00100000 ;S8 ror r22 ror r22 add r24, r22 andi r24, 0x0F st z+,r24 ;Stunden Einer speichern clr r24 lds r23, msfarr+3 mov r22, r23 andi r22, 0b00010000 ;S10 swap r22 add r24, r22 mov r22, r23 andi r22, 0b00001000 ;S20 ror r22 ror r22 add r24, r22 andi r24, 0x03 st z+,r24 ;Stunden Zehner speichern clr r24 lds r23, msfarr+2 mov r22, r23 andi r22, 0b10000000 ;K1 ror r22 ror r22 ror r22 swap r22 add r24, r22 mov r22, r23 andi r22, 0b01000000 ;K2 ror r22 swap r22 add r24, r22 mov r22, r23 andi r22, 0b00100000 ;K4 rol r22 swap r22 add r24, r22 mov r22, r23 andi r22, 0b00010000 ;K8 ror r22 add r24, r22 andi r24, 0x0F st z+, r24 ;Kalendertag Einer speichern clr r24 lds r23, msfarr+2 mov r22, r23 andi r22, 0b00001000 ;K10 ror r22 ror r22 ror r22 add r24, r22 mov r22, r23 andi r22, 0b00000100 ;K20 ror r22 add r24, r22 andi r24, 0x03 st z+, r24 ;Kalendertag Zehner speichern clr r24 lds r23, msfarr+3 mov r22, r23 andi r22, 0b00000100 ;W1 ror r22 ror r22 add r24, r22 mov r22, r23 andi r22, 0b00000010 ;W2 add r24, r22 mov r22, r23 andi r22, 0b00000001 ;W4 rol r22 rol r22 add r24, r22 andi r24, 0x07 st z+,r24 ;Wochentag speichern clr r24 lds r23, msfarr+2 mov r22, r23 andi r22, 0b00000010 ;Mo1 ror r22 add r24, r22 mov r22, r23 andi r22, 0b00000001 ;Mo2 rol r22 add r24, r22 lds r23, msfarr+1 mov r22, r23 andi r22, 0b10000000 ;Mo4 ror r22 swap r22 add r24, r22 mov r22, r23 andi r22, 0b01000000 ;Mo8 rol r22 swap r22 add r24, r22 andi r24, 0x0F st z+,r24 ;Monat Einer speichern clr r24 lds r23, msfarr+1 mov r22, r23 andi r22, 0b00100000 ;Mo10 ror r22 swap r22 add r24, r22 andi r24, 0x01 st z+,r24 ;Monat Zehner speichern clr r24 lds r23, msfarr+1 mov r22, r23 andi r22, 0b00010000 ;J1 swap r22 add r24, r22 mov r22, r23 andi r22, 0b00001000 ;J2 ror r22 ror r22 add r24, r22 mov r22, r23 andi r22, 0b00000100 ;J4 add r24, r22 mov r22, r23 andi r22, 0b00000010 ;J8 rol r22 rol r22 add r24, r22 andi r24, 0x0F st z+,r24 ;Jahr Einer speichern clr r24 lds r23, msfarr+1 mov r22, r23 andi r22, 0b00000001 ;J10 add r24, r22 lds r23, msfarr mov r22, r23 andi r22, 0b10000000 ;J20 ror r22 ror r22 swap r22 add r24, r22 mov r22, r23 andi r22, 0b01000000 ;J40 swap r22 add r24, r22 mov r22, r23 andi r22, 0b00100000 ;J80 rol r22 rol r22 swap r22 add r24, r22 andi r24, 0x0F st z+,r24 ;Jahr Zehner speichern ; mdc200: ret ; ------------------------------------------------------ ; Zeitinformationen in die Uhrenpuffer kopieren und ; Uhrzeit bei Synchronisation auf LCD und RS232 ausgeben ; ------------------------------------------------------ mtsync: ldi temp, '<' ;Vorzeichen < nur fuer RS232-Ausgabe rcall senden ;bei MSF60 synchroner Zeit cli ;Interrupts sperren lds r0, msfbuf1+3 ;Stunden Einer holen lds r1, msfbuf1+4 ;Stunden Zehner holen sei rcall locate2a mov temp, r1 subi temp, -0x30 rcall char2lcd rcall senden mov temp, r0 subi temp, -0x30 rcall char2lcd rcall senden rcall bcdbin ;Konvertierung in 8-Bit-Format sts stunde, temp ;in Puffer "stunde" fuer Gangreserve-Puffer ldi temp, ':' ;abspeichern rcall char2lcd rcall senden cli lds r0, msfbuf1+1 ;Minuten Einer holen lds r1, msfbuf1+2 ;Minuten Zehner holen sei mov temp, r1 subi temp, -0x30 ;ASCII "0" addieren rcall char2lcd ;auf LCD ausgeben rcall senden ;auf RS232 ausgeben mov temp, r0 subi temp, -0x30 rcall char2lcd rcall senden rcall bcdbin ;Konvertierung in 8-Bit-Format sts minute, temp ;in Puffer "minute" speichern ldi temp, ':' rcall char2lcd ldi temp, '-' rcall senden cli lds r0, msfbuf1+5 ;Tag Einer holen lds r1, msfbuf1+6 ;Tag Zehner holen sei rcall locate1a mov temp, r1 subi temp, -0x30 rcall char2lcd rcall senden mov temp, r0 subi temp, -0x30 rcall char2lcd rcall senden ldi temp, '.' rcall char2lcd rcall senden cli lds r0, msfbuf1+8 ;Monat Einer holen lds r1, msfbuf1+9 ;Monat Zehner holen sei mov temp, r1 subi temp, -0x30 rcall char2lcd rcall senden mov temp, r0 subi temp, -0x30 rcall char2lcd rcall senden ldi temp, '.' rcall char2lcd rcall senden ldi temp1, 0x14 ;Jahr Tausender/Hunderter rcall num ;ergaenzen - nicht im MSF-Code rcall nums ;auch auf RS232 ausgeben cli lds r0, msfbuf1+10 ;Jahr Einer holen lds r1, msfbuf1+11 ;Jahr Zehner holen sei mov temp, r1 rcall locate1c subi temp, -0x30 rcall char2lcd rcall senden mov temp, r0 subi temp, -0x30 rcall char2lcd rcall senden ldi temp, 0x20 rcall senden rcall wochentag rcall msfid ;Text " MSF" ldi temp, 0x0A ;Zeilenvorschub rcall senden ldi temp, 0x0D ;Wagenruecklauf rcall senden cli ;Interrupts sperren clr temp sts sekunde, temp ;Sekundenpuffer loeschen ldi temp, 0b00010100 ;Synchron- und Datum-Bit... or flags, temp ;...in Flags einsetzen clr temp sei ;Interrupts wieder aktivieren ret ; wochentag: lds r0, msfbuf1+7 mov temp2, r0 cpi temp2, 0x01 breq monta cpi temp2, 0x02 breq diens cpi temp2, 0x03 breq mittw cpi temp2, 0x04 breq donne cpi temp2, 0x05 breq freit cpi temp2, 0x06 breq samst cpi temp2, 0x00 ;(0x07) Sonntag ist Tag "0" breq sonnt ret ; monta: ldi zl, low(mont*2) ;Adresse der Tabelle "mont" ldi zh, high(mont*2);in Z-Pointerregister laden rjmp pmread0 ;Ausgabeschleife aufrufen ; diens: ldi zl, low(dien*2) ;Adresse der Tabelle "dien" ldi zh, high(dien*2);in Z-Pointerregister laden rjmp pmread0 ;Ausgabeschleife aufrufen ; mittw: ldi zl, low(mitt*2) ;Adresse der Tabelle "mitt" ldi zh, high(mitt*2);in Z-Pointerregister laden rjmp pmread0 ;Ausgabeschleife aufrufen ; donne: ldi zl, low(donn*2) ;Adresse der Tabelle "donn" ldi zh, high(donn*2);in Z-Pointerregister laden rjmp pmread0 ;Ausgabeschleife aufrufen ; freit: ldi zl, low(frei*2) ;Adresse der Tabelle "frei" ldi zh, high(frei*2);in Z-Pointerregister laden rjmp pmread0 ;Ausgabeschleife aufrufen ; samst: ldi zl, low(sams*2) ;Adresse der Tabelle "sams" ldi zh, high(sams*2);in Z-Pointerregister laden rjmp pmread0 ;Ausgabeschleife aufrufen ; sonnt: ldi zl, low(sonn*2) ;Adresse der Tabelle "sonn" ldi zh, high(sonn*2);in Z-Pointerregister laden rjmp pmread0 ;Ausgabeschleife aufrufen ; msfid: ldi zl, low(msfid0*2);Adresse der Tabelle "msfid0" ldi zh, high(msfid0*2);in Z-Pointerregister laden rjmp pmread ;Ausgabeschleife aufrufen ; pmread: rcall locate2c rjmp pmread1 ; pmread0: rcall locate1d rjmp pmread1 ; pmread1: lpm ;Kopie Byte 1 des Tabellen-Strings nach r0 tst r0 ;r0 auf 0 testen breq prt_end ;wenn 0, dann zu prt_end mov temp, r0 ;Inhalt von r0 nach temp kopieren rcall char2lcd ;Zeichen-Uebergabe an LCD rcall senden ;Zeichen Uebergabe an RS232 adiw zl:zh, 1 ;Adresse des Z-Pointers um 1 erhoehen rjmp pmread1 ;Sprung wieder zum Anfang (Label "pmread1") ; ;und naechstes Byte aus Tabelle holen prt_end: ;Beendigung der Leseroutine, Ruecksprung ret ; ;Tabellenvariablen Wochentag: mont: .db "Mo ",0 dien: .db "Tu ",0 mitt: .db "We ",0 donn: .db "Th ",0 frei: .db "Fr ",0 sams: .db "Sa ",0 sonn: .db "Su ",0 ; ;Tabellenvariablen MSF-Kennung: msfid0: .db "MSF",0 ; ;------------------------------------------------------------ ; Interrupt-Service-Routine Timer1 CTC, alle 10ms ; MSF60-Telegramm einlesen, Uhrzeit fuer Gangreserve autonom ; hochzaehlen und in Puffern (minute, stunde etc.) speichern ; Register: flags,inttmp,intreg,lockb,msfsec,msfcnt,r14,r25, ; temp,temp1,temp2,xl,xh ;------------------------------------------------------------ T1Interr: in intreg, SREG ;CPU-Flags sichern in r25, pind ;Portbit PD6 MSF60-Pegel einlesen lds temp, lockb ;MSF60 A=0 B=1 Code Doppelmarke sperren or r25, temp ;Bit6 in r25 auf high setzen, sbrc temp, 6 ;wenn vorher in lockb gesetzt rjmp t010 ;Bit6 =1 ->t010 rjmp t030 ;Bit6 =0 ->t030 ; t010: ;Eingang 260ms auf high halten inc r14 ldi temp1, 26 cp r14, temp1 brcc t020 ;260ms erreicht?->t020 rjmp t030 ;nein ->t030 ; t020: ;Zaehler ruecksetzen clr temp1 ;r25 wieder freigeben clr r14 ;fuer Low-Pegel ldi temp, 0x00 sts lockb, temp rjmp t030 ; t030: bst r25, 6 ;MSF60-Pegel holen bld flags, 0 ;und in Flags speichern rjmp t040 ; t040: inc msfcnt ;MSF60-Sampling-Zaehler erhoehen bst flags, 0 ;akuellen MSF60-Pegel pruefen brts t060 ;ist MSF60 High? ja -> t060 bst flags, 1 ;sonst vorh. MSF60-Status pruefen brtc t100 ;war MSF60 vorher High? nein-> Ende rjmp t050 ;ja -> t050 ; t050: clr msfcnt ;MSF60-Zaehler loeschen rjmp t100 ;Ende ; t060: bst flags,1 ;vorherigen MSF-Status pruefen brts t100 ;war MSF60 vorher H? ja -> Ende ; inc msfsec ;sonst Sekunde hochzaehlen ; ldi temp, 0x40 ;Bit6 high setzen sts lockb, temp ;in lockb speichern ; ;r25 wird beim naechsten Interrupt ; ;fuer MSF60 A-Code=0 B-Code=1 gesperrt ldi r25, 46 ;0,5s Austastung testen cp msfcnt, r25 ;MSF60-Minutenbeginn erreicht? brcc t070 ;ja -> t070 rjmp t080 ;nein -> t080 uebrige Dauern auswerten ; t070: set ;T-Flag setzen bld flags, 3 ;Minutenbeginn-Bit setzen clr msfcnt ;Zaehler resetten clr msfsec rjmp t100 ;nur 59 Bits auswerten -> t100 ; t080: ldi r25, 16 ;(14);MSF-Sampling-Zaehler<16 (160ms)? cp msfcnt, r25 ;Ergebnis in Carry - invertiert ; ldi r25, 6 ;6 Pufferbytes bearbeiten ldi xl, low(msfarr+6) ;Zeiger auf Pufferende setzen ldi xh, high(msfarr+6) rol inttmp ;Carry in Register legen (LSB) com inttmp ;alle Bits invertieren mov temp2, inttmp ;Kopie fuer Anzeige 0/1 auf RS232 ror inttmp ;LSB wieder in Carry schieben ; t090: ld inttmp, -x ;Pufferbyte -1 holen ror inttmp ;neues Bit als MSB einschieben st x, inttmp ;LSB in Carry und speichern dec r25 ;alle Pufferbytes bearbeitet? brne t090 ;nein -> nochmals t090 ;---------------- fuer RS232 0/1-Ausgabe --------- andi temp2, 0x01 ;MSF60 Code Nullen/Einsen mov temp, temp2 subi temp, -0x30 rcall senden ;nur auf RS232 ausgeben ;------------------------------------------------- rcall locate2b ;Sekundenanzeige MSF60 synchron mov temp1, msfsec rcall num ;nur auf LCD ausgeben rjmp t100 ; t100: bst flags, 0 ;aktuellen MSF60-Status holen bld flags, 1 ;und als neuen Status speichern rjmp timerend ; timerend: out SREG, intreg ;CPU-Flags wiederherstellen reti ;Ende ; ;********** LCD Initialisierung - LCD Ausgabe-Routinen **** lcdinit: ; rcall wait3 ;LCD power on self reset abwarten rcall wait3 push temp ;Sicherung Temporaerregister... push temp1 push temp2 in temp1, SREG ;...und Statusregister auf Stapel push temp1 rcall lcd_reset ;LCD-Bus-Reset rcall lcd_achtbit ;LCD-Init: acht Bit rcall lcd_acht_vier ;LCD-Init: acht zu vier Bit rcall lcd_vier_zwei ;LCD-Init: vier Bit, 2 Zeilen rcall lcd_on_off ;LCD-Init: Displaypuffer aus rcall lcd_clear ;LCD-Init: Anzeige loeschen rcall lcd_entrymode ;LCD-Init: Entry Mode Set pop temp1 ;Wiederherstellung Temporaerregister out SREG, temp1 ;und Statusregister pop temp2 pop temp1 pop temp ret ; lcd_reset: ;LCD-Bus-Reset: ; push temp ;Temporaeregister auf Stack retten cbi daten, 0 ;RS-Bit auf "low" ldi temp, 0x00 ;Reset des LCD-Ausgabeports out daten, temp rcall enable ;Enable-Impuls toggeln rcall wait3 rcall wait3 pop temp ret ; lcd_achtbit: ;Acht-Bit Init: ; ;dreimal Hex 0x30... push temp ;...immer notwendig, cbi daten, 0 ;wenn einmal 8/4-Bit-Modus ldi temp, 0x30 ;gewechselt wurde out daten, temp ;dabei steht RegisterSelect-Bit rcall enable ;stets auf "low" rcall wait3 ;und jedesmal einen Enable- cbi daten, 0 ;Impuls senden ldi temp, 0x30 out daten, temp rcall enable rcall wait3 cbi daten, 0 ldi temp, 0x30 out daten, temp rcall enable rcall wait3 pop temp ret ; lcd_acht_vier: ;Acht-zu-Vier-Bit-Modus-Wechsel ; push temp cbi daten, 0 ldi temp, 0x20 ;zunaechst nur 4-Bit-Modus, out daten, temp ;untere vier Bit werden rcall enable ;ignoriert, aber immer noch rcall wait3 ;8-Bit-Befehlsbreite pop temp ret ; lcd_vier_zwei: ;Vier-Bit-Modus - zwei Zeilen ; push temp cbi daten, 0 ;dann 4-Bit-Modus ldi temp, 0x28 ;und zweizeilig, rcall comd2lcd ;ab hier bereits Steuerbefehle rcall wait3 ;im Vierbit-Modus senden pop temp ret ; lcd_on_off: ;On/Off Control, ; ;Display ein/ausschalten: push temp cbi daten, 0 ldi temp, 0x08 ;Displaypuffer aus, rcall comd2lcd ;Cursor aus, Blinken aus rcall wait3 pop temp ret ; lcd_clear: ;Anzeige loeschen: ; push temp cbi daten, 0 ;Anzeige wird im "Space"- ldi temp, 0x01 ;Modus geloescht rcall comd2lcd ;braucht mehr Zeit rcall wait3 rcall wait3 pop temp ret ; lcd_entrymode: ;Entry/Shift Mode Set: ; push temp cbi daten, 0 ldi temp, 0x06 ;Cursor von links nach rechts rcall comd2lcd ;kein Schieben rcall wait3 cbi daten, 0 ldi temp, 0x10 ;kein Schieben, Cursorbewegung rcall comd2lcd ;nach rechts rcall wait3 cbi daten, 0 ldi temp, 0x0C ;Anzeige wieder einschalten rcall comd2lcd ;Cursor aus, Blinken aus rcall wait3 cbi daten, 0 ldi temp, 0x01 ;Anzeige nochmals loeschen rcall comd2lcd rcall wait3 pop temp ret ; char2lcd: ;uebergibt Charakter auf "temp" an LCD ; ;--------------------------------------- cbi daten, licht ;Hintergrundbeleuchtung (PB3)"aus" push temp ;rettet temps etc. auf Stapel push temp1 push temp2 mov temp1, temp ;Kopie fuer Operation unten andi temp, 0xF0 ;unteres Nibble ausblenden ori temp, 0x03 ;RS etc. nach Bedarf auf high maskieren sbi daten, 0 ;RS-Portbit (PB0) auf high setzen out daten, temp ;Ausgabe oberes Nibble auf D4-D7 rcall enable ;Enableimpuls senden swap temp1 ;Nibble-Vertauschen,Kopie aus temp andi temp1, 0xF0 ;vorher oberes, jetzt unteres Nibble ; ;ausblenden ori temp1, 0x03 ;RS etc. auf high maskieren sbi daten, 0 ;nochmals RS-Portbit (PB0) auf high out daten, temp1 ;Ausgabe unteres Nibble auf D4-D7 rcall enable ;Enableimpuls nochmals senden sbi daten, licht ;Hintergrundbeleuchtung (PB3)"ein" pop temp2 ;Wiederherstellung von temps pop temp1 pop temp rcall wait2 ;Verarbeitungszeit LCD abwarten ret ; comd2lcd: ;sendet Steuerzeichen auf "temp" an LCD ; ;-------------------------------------- push temp ;rettet temps auf Stapel push temp1 push temp2 mov temp1, temp ;Kopie fuer Operation unten andi temp, 0xF0 ;unteres Nibble ausblenden cbi daten, 0 ;setzt RS-Portbit (PB0) auf low out daten, temp ;Ausgabe oberes Nibble auf D4-D7 rcall enable ;Enableimpuls senden swap temp1 ;Nibble-Vertauschen, Kopie aus temp andi temp1, 0xF0 ;vorher oberes, jetzt unteres Nibble ; ;ausblenden cbi daten, 0 ;nochmals RS-Portbit (PB0) auf low out daten, temp1 ;Ausgabe unteres Nibble auf D4-D7 rcall enable ;Enableimpuls nochmals senden pop temp2 ;Wiederherstellung von temps pop temp1 pop temp rcall wait2 ;Verarbeitungszeit LCD abwarten ret ; enable: ;Enableimpuls-Generierung ; nop sbi daten, 1 ;Enable-Portbit (PB1) auf high setzen nop ;Zeitverzoegerung mindestens nop ;1000 Nanosekunden laut Datenblatt nop ;lieber ein paar "nops" mehr nop ;spendieren nop nop cbi daten, 1 ;Enable-Portbit (PB1) auf low zuruecksetzen nop ret ; ;*************** Verzoegerungsschleifen fuer LCD ************ ; wait1: ;Wiederholtes Aufrufen von unten angeg. push temp ;Verzoegerungsschleifen push temp1 push temp2 in temp2, SREG push temp2 ldi temp, 0x0A ; wait1b: rcall wait3 ; wait1c: dec temp cpi temp, 1 brne wait1b nop pop temp2 out SREG, temp2 pop temp2 pop temp1 pop temp ret ; wait2: ;Verzoegerungsschleife2 fuer LCD: push xl ;Register sichern auf Stapel push xh push temp ;Sicherung von Temporaerregistern... push temp1 ;und Statusregister auf Stapel push temp2 in temp2, SREG push temp2 ldi xl, low(zeit0) ;laedt Zaehlregisterpaar ldi xh, high(zeit0) ;mit oben deklariertem Wert... rjmp repeatx ;..."zeit0" ; wait3: ;Verzoegerungsschleife3 fuer LCD: push xl ;Register sichern auf Stapel push xh push temp ;Sichern von Temporaerregistern push temp1 ;und Statusregister auf Stapel push temp2 in temp2, SREG push temp2 ldi xl, low(zeit1) ;laedt Zaehlregisterpaar ldi xh, high(zeit1) ;mit oben deklariertem Wert... rjmp repeatx ;..."zeit1" ; repeatx: ;16-Bit-Subtraktionsschleife: sbiw xl:xh, 1 ;decrementiert Doppelregister brne repeatx ;zaehlt bis Null pop temp2 ;Wiederherstellung out SREG, temp2 ;von "geretteten" Registern pop temp2 pop temp1 pop temp pop xh pop xl ret ; ;*************** Zahlenumwandlung(1) binaer=>dezimal ****** num: ;wandelt 8 Bit binär auf "temp1" ; ;nach ASCII dezimal und gibt "temp" auf LCD aus ; ;Format: 2 Ziffern mit fuehrender Null push temp ;Eingaberegister "temp1", Ausgaberegister "temp" push temp1 push temp2 mov temp2, temp1 ldi temp1, 0x30 ; num3: subi temp2, 10 ;Berechnung Zehner brcs num4 inc temp1 rjmp num3 ; num4: mov temp, temp1 rcall char2lcd ;Ausgabe Zehnerstelle auf LCD subi temp2, -10 ;Berechnung Einer ldi temp1, 0x30 add temp1, temp2 mov temp, temp1 rcall char2lcd ;Ausgabe Einerstelle auf LCD pop temp2 pop temp1 pop temp ret ; ;**************** Zahlenumwandlung(1a) binaer=>dezimal******** nums: ;wandelt 8 Bit binär auf "temp1" (nur RS232-Ausgabe) ; ;nach ASCII dezimal und uebergibt "temp" RS232 I/O ; ;Format: 2 Ziffern mit fuehrender Null push temp ;Eingaberegister "temp1", Ausgaberegister "temp" push temp1 push temp2 mov temp2, temp1 ldi temp1, 0x30 ; nums3: subi temp2, 10 ;Berechnung Zehner brcs nums4 inc temp1 rjmp nums3 ; nums4: mov temp, temp1 rcall senden ;Ausgabe Zehnerstelle auf RS232 subi temp2, -10 ;Berechnung Einer ldi temp1, 0x30 add temp1, temp2 mov temp, temp1 rcall senden ;Ausgabe Einerstelle auf RS232 pop temp2 pop temp1 pop temp ret ; ;**************** Zahlenumwandlung(1b) binaer=>dezimal******* numx: ;wandelt 8 Bit binär auf "temp1" ; ;(LCD- und RS232-Ausgabe) ; ;nach ASCII dezimal und gibt auf "temp" aus ; ;Format: 2 Ziffern mit fuehrender Null push temp ;Eingaberegister "temp1", Ausgaberegister "temp" push temp1 push temp2 mov temp2, temp1 ldi temp1, 0x30 ; numx3: subi temp2, 10 ;Berechnung Zehner brcs numx4 inc temp1 rjmp numx3 ; numx4: mov temp, temp1 rcall char2lcd ;Ausgabe Zehnerstelle auf LCD rcall senden ;Ausgabe Zehnerstelle auf RS232 subi temp2, -10 ;Berechnung Einer ldi temp1, 0x30 add temp1, temp2 mov temp, temp1 rcall char2lcd ;Ausgabe Einerstelle auf LCD rcall senden ;Ausgabe Einerstelle auf RS232 pop temp2 pop temp1 pop temp ret ; ;*************** Zahlenumwandlung(2) BCD=>binaer ********** ; ;Eingaberegister "temp" bcdbin: ;r1 = Zehner, r0 = Einer clr temp ;Ergebnisregister resetten ldi temp1, 0x0A ;Vorladewert 10 ; bcdbin1: tst r1 ;Zehner = 0 ? breq bcdbin2 ;ja =>Einer direkt bearbeiten add temp, temp1 ;nein=>jedesmal 10 zu Ausgabe add. dec r1 ;so lange Zehner erniedrigen, rjmp bcdbin1 ;bis r1 Null ; bcdbin2: add temp, r0 ;Einer addieren; Ausgabe auf temp ret ; ;******************* LCD Positionierungen (2x16)************ ; locate1: ;Anfang Zeile 1 push temp ldi temp, 0x80 rcall comd2lcd pop temp ret ; locate1a: ;Zeile 1, Spalte 4 push temp ldi temp, 0x83 rcall comd2lcd pop temp ret ; locate1b: ;Zeile 1, Spalte 6 push temp ldi temp, 0x85 rcall comd2lcd pop temp ret ; locate1c: ;Zeile 1, Spalte 12 push temp ldi temp, 0x8B rcall comd2lcd pop temp ret ; locate1d: ;Zeile 1, Spalte 15 push temp ldi temp, 0x8E rcall comd2lcd pop temp ret ; locate2: ;Anfang Zeile 2 push temp ldi temp, 0xC0 rcall comd2lcd pop temp ret ; locate2a: ;Zeile 2, Spalte 4 push temp ldi temp, 0xC3 rcall comd2lcd pop temp ret locate2b: ;Zeile 2, Spalte 10 push temp ldi temp, 0xC9 rcall comd2lcd pop temp ret ; locate2c: ;Zeile 2, Spalte 14 push temp ldi temp, 0xCD rcall comd2lcd pop temp ret ; ;************************************************************