; *************************************************************** ; * DCF-synchronized Digital Clock for RS232 communication on * ; * a 2313-Experimental-Board, Version 0.2 as of 12.01.2001 * ; * Features: XTal driven digital clock for exact date and time * ; * information, adjusted and read over a SIO-I/O 9k6 8N1 * ; * connection, Self-adjusting date- and time-synchronisation * ; * to a connected receiver for the Frankfurt/Germany based * ; * official clock reference DCF77 (optional) * ; * (C)2001 by Gerhard Schmidt * ; * report bugs to info@avr-asm-tutorial.net * ; *************************************************************** ; ; Hardware requirements: ; - 2313 board (see extra doc) ; - RS232 compatible terminal, e.g. PC+Win+HyperTerminal to adjust/read ; the date and time informationen ; - (RXD/TXD, RTS/CTS)-crosswired RS232 cable connection between the ; 2313-board and the terminal ; - Optional: DCF77 clock with active low receiver signal (second ticks) ; ; Software features: ; - Interrupt-driven and buffered SIO-I/O with RTS/CTS hardware protocol ; - Interrupt-driven clock signals with exact timing ; - SLEEP mode for reduced power consumption of the MPU ; - Exact date calculation from 2001 up to the year 2099 ; - Transmits ANSI color codes for terminal control ; - DCF synchronisation: self-adjusting to unexact signal lengthes and ; spurious signals by software, loss-of-signal-detection, full parity bit ; checking, convenient hardware debugging opportunities by terminal display ; of the signal length and all detected errors during the sampling process ; .NOLIST .INCLUDE "C:\avrtools\appnotes\2313def.inc" .LIST ; ; Constants ; ; Constants for Sio properties .EQU fq=10000000; Xtal frequency on board in Hz .EQU baud=9600; Baudrate for SIO communication .EQU bddiv=(fq/(16*baud))-1; Baudrate divider .EQU ticks=fq/16000; ticks per second for the timing functions ; Constants for Sio communications .EQU ccr=0x0D; Carriage return character .EQU clf=0x0A; Line feed character .EQU cnul=0x00; NUL character .EQU cesc=0x1B; ESCAPE character .EQU cBs=0x08; Backspace character ; Bit assignment of the RTS and CTS pins on Port B .EQU bIRts = 2 .EQU bOCts = 4 ; Locations in the Internal SRAM .EQU sDTF = 0x60 ; Date/Time information, first location in SRAM .EQU dDDT = 0 ; relative distance, Tens of Days .EQU dDD = 1 ; relative distance, Days .EQU dDMT = 3 ; relative distance, Tens of Monthes .EQU dDM = 4 ; relative distance, Month .EQU dDY = 9 ; relative distance, Years .EQU dTHT = 12 ; relative disance, Tens of Hours .EQU dTH = 13 ; relative distance, Hours .EQU dTMT = 15 ; relative distance, Tens of Minutes .EQU dTM = 16 ; relative distance, Minutes .EQU dTST = 18 ; relative distance, Tens of Seconds .EQU dTS = 19 ; relative distance, Seconds .EQU sDTL = 0x74 ; Date/Time information, last location in SRAM .EQU sDTT = 0x6C ; Position of Time .EQU sSec = 0x73 ; Adress of seconds .EQU sSecT = 0x72 ; Adress of tens of seconds .EQU sMin = 0x70 ; Adress of minutes ; Constants for Sio Rx- and Tx-Buffers .EQU RxBuF = 0x75 ; Sio Rx-Buffer, first location in SRAM .EQU RxBuL = 0x84 ; Sio Rx-Buffer, last location in SRAM (16 Bytes) .EQU TxBuF = 0x85 ; Sio Tx-Buffer, first location in SRAM .EQU TxBuL = 0xA4 ; Sio Tx-Buffer, last location in SRAM (32 Bytes) ; ; Used registers ; ; Register mainly for Program Memory Read Operations .DEF rlpm = R0 ; Used for read operations with LPM ; SIO Tx Buffer In pointer .DEF rsiotxin = R1 ; Registers for the DCF77-Receiver option .DEF rdcfp = R2 ; Parity bit counter for DCF signals .DEF rdcf1 = R3 ; Last Receiver Shift Register .DEF rdcf2 = R4 ; Receiver Shift register .DEF rdcf3 = R5 ; Receiver Shift register .DEF rdcf4 = R6 ; Receiver Shift register .DEF rdcf5 = R7 ; First Receiver Shift Register .DEF rDcfCmp = R8 ; Compare length of DCF pulse (self-adjusted) .DEF rDcfLc = R9 ; Last count length detected .EQU cDcfCmpDflt = 125 ; Default length for DCF signals (self-adjusted) .DEF rDcfCnt = R10 ; DCF length count, interrupt driven .DEF rDcfL0 = R11 ; Distance of last short pulse from medium count length .DEF rDcfL1 = R12 ; Distance of last long pulse from medium count length ; For all purposes .DEF rmpr = R16 ; Multi-purpose register ; Low level ticker for seconds counting by interrupt (1.6 ms) .DEF rtckh = R17 ; MSB .DEF rtckl = R18 ; LSB ; DCF77 Tick register for signal length, driven by timer interrupt (1.6 ms) .DEF rDcfl = R19 ; Timer 0 flag register with the following bits: .DEF rdtf = R20 ; Date/Time Flag register .EQU bEos = 0 ; Bit 0: End of second reached .EQU mEos = 1 .EQU bMin = 1 ; Bit 1: Echo minutes only .EQU mMin = 2 .EQU bDTm = 2 ; Bit 2: DT mode active .EQU mDTm = 4 ; Bits used by DCF77: .EQU bDcfm = 3 ; Bit 3: DCF mode active .EQU mDcfm = 8 .EQU bDcfCtm = 4 ; Bit 4: DCF echo counters .EQU mDcfCtm = 16 .EQU bDcfSync = 5 ; Bit 5: DCF synch tickers at next second .EQU mDcfSync = 32 .EQU bDcfRdy = 6 ; Bit 6: DCF bit ready .EQU mDcfRdy = 64 .EQU bDcfOk = 7 ; Bit 7: DCF Signal is ok .EQU mDcfOk = 128 ; SIO flag register ; Bit 5: Unused ; Bit 6: Unused ; Bit 7: Unused .DEF rsioflg = R21 ; SIO flag register .EQU bEcho = 0 ; Bit 0: Echo all incoming characters .EQU mEcho = 1 .EQU bAddLf = 1 ; Bit 1: Insert LF after CR .EQU mAddLf = 2 .EQU bTxBAct = 2 ; Bit 2: Transmitter buffer active .EQU mTxBAct = 4 .EQU bTxAct = 3 ; Bit 3: Character transmission active .EQU mTxAct = 8 .EQU bRxCmp = 4 ; Bit 4: Rx-Line complete .EQU mRxCmp = 16 ; DCF error flag register .DEF rDcfErr = R22 ; Last DCF-error .EQU bDcfPem = 0 ; Bit 0: Parity error minute .EQU mDcfPem = 1 .EQU bDcfPeh = 1 ; Bit 1: Parity error hour .EQU mDcfPeh = 2 .EQU bDcfPed = 2 ; Bit 2: Parity error date .EQU mDcfPed = 4 .EQU bDcfCts = 3 ; Bit 3: Count too short .EQU mDcfCts = 8 .EQU bDcfCtl = 4 ; Bit 4: Count too long .EQU mDcfCtl = 16 .EQU bDcfSok = 5 ; Bit 5: Synchronisation ( not an error!) .EQU mDcfSOk = 32 .EQU bDcfEtr = 6 ; Bit 6: Error to be reported .EQU mDcfEtr = 64 .EQU bDcfAny = 7 ; Bit 7: Any error .EQU mDcfAny = 128 ; DCF shift register counter .DEF rDcfs = R23 ; Shift Register Bit Counter ; ; Code starts here ; .CSEG .ORG $0000 ; ; Reset- and Interrupt-vectors ; rjmp Start ; Reset-vector rjmp IInt0 ; External Interrupt Request 0 rjmp IInt1 ; External Interrupt Request 1 rjmp TCpt1 ; Timer/Counter1 Capture event rjmp TCmp1 ; Timer/Counter1 Compare match rjmp TOvf1 ; Timer/Counter1 Overflow rjmp TOvf0 ; Timer/Counter0 Overflow rjmp URxAv ; Uart Rx char available rjmp UTxDe ; Uart Tx data register empty rjmp UTxCp ; Uart Tx complete rjmp AnaCp ; Analog comparator ; ; ************** Interrupt service routines ******** ; ; External Interrupt 0: Started by a negative edge on the DCF input ; IInt0: push rmpr in rmpr,SREG sbrs rdtf,bDcfSync rjmp IInt01 cbr rdtf,mDcfSync ; Synchronize DCF and internal tickers clr rtckl clr rtckh IInt01: clr rDcfl out SREG,rmpr pop rmpr reti ; ; External Interrupt 1 : Started by a positive edge on the DCF input ; IInt1: push rmpr in rmpr,SREG cpi rDcfl,10 ; Exclude short signals brcs IInt1r mov rDcfCnt,rDcfl ; Store count length inc rDcfs sbr rdtf,mDcfRdy ; Flag received bit ready IInt1r: out SREG,rmpr pop rmpr reti ; ; Timer/Counter 1, Capture event interrupt, not used ; TCpt1: reti ; ; Timer/Counter 1, Compare match interrupt, not used ; TCmp1: reti ; ; Timer/Counter 1, Overflow interrupt, not used ; TOvf1: reti ; ; Timer/Counter 0, Overflow interrupt, used to count times ; TOvf0: push rmpr ; Save Register rmpr ldi rmpr,6 ; Set Counter to 6 to int after 250 ticks out TCNT0,rmpr in rmpr,SREG ; Save status register inc rtckl brne TOvf0a inc rtckh TOvf0a: cpi rtckl,LOW(ticks) ; End of second reached? brne TOvf0b cpi rtckh,HIGH(ticks) brne TOvf0b sbr rdtf,mEos ; Set End of second flag clr rtckl clr rtckh TOvf0b: inc rDcfl ; DCF77 counter tick brne TOvf0c dec rDcfl TOvf0c: out SREG,rmpr ; Restore anything pop rmpr reti ; ; Uart Rx Complete Interrupt ; URxAv: push rmpr ; Save mpr register in rmpr,SREG ; Save SREG push rmpr sbic USR,FE ; Framing error? rjmp URxAv2 in rmpr,UDR ; Read Char sbrc rsioflg,bEcho rcall siotxch ; Echo character cpi rmpr,cBs ; Backspace? brne URxAv0 cpi XL,RxBuF+1 ; Backspace brcs URxAv3 dec XL rjmp URxAv3 URxAv0: st X+,rmpr ; Store in RX buffer cpi XL,RxBuL+1 ; End of buffer reached? brcs URxAv1 ldi XL,RxBuF URxAv1: cpi rmpr,cCr ; End of input line? brne URxAv3 sbr rSioFlg,mRxCmp ; Set Line complete flag rjmp URxAv3 URxAv2: in rmpr,UDR ; Clear Framing error bit URxAv3: pop rmpr ; Restore SREG out SREG,rmpr pop rmpr ; Restore rmpr reti ; ; Uart Data register empty interrupt ; UTxDe: push rmpr ; Save register in rmpr,SREG ; Save status register push rmpr cp YL,rsiotxin ; Compare Buffer In and Out brne UTxDeCh UTxDeOff: cbr rSioFlg,mTxBAct ; No more chars to send rjmp UTxDeRet UTxDeCh: sbic PortB,bIRts ; RTS input ready? rjmp UTxDeOff ld rmpr,Y+ ; Read char out UDR,rmpr cpi YL,TxBuL+1 ; Check end of buffer brcs UTxDeRet ldi YL,TxBuF ; Point to buffer start UTxDeRet: pop rmpr ; Restore status register out SREG,rmpr pop rmpr ; Restore register reti ; ; Uart Tx complete interrupt ; UTxCp: push rmpr in rmpr,SREG cbr rsioflg,mTxAct ; Clear the flag (not used here) out SREG,rmpr pop rmpr reti ; ; Analog comparator interrupt ; AnaCp: reti ; ; ******* End of interrupt service routines *********** ; ; Sio service subroutines to be called from various sources ; ; ; Char in rmpr to Tx-Buffer ; siotxch: sbic PortB,bIRts ; Send only, if RTS is active ret push ZH push ZL clr ZH ; Point to TX buffer input position mov ZL,rSioTxIn st Z+,rmpr cpi ZL,TxBuL+1 ; End of buffer reached? brcs siotxchx ldi ZL,TxBuF siotxchx: ; Wait here to avoid buffer overrun cp ZL,YL breq siotxchx cli ; Enter critical situation, disable interrupts sbrc rsioflg,bTxBAct rjmp sioTxChy sbr rsioflg,mTxBAct out UDR,rmpr rjmp sioTxChn sioTxChy: mov rsioTxIn,ZL sioTxChn: sei ; End of critical situation pop ZL pop ZH cpi rmpr,cCr ; Add linefeeds after carriage return? brne sioTxChz sbrs rsioflg,bAddLf rjmp sioTxChz ldi rmpr,cLf rcall sioTxCh ldi rmpr,cCr sioTxChz: ret ; ; Transmits a null-terminated text from memory that Z points to ; TxTxt: push rlpm push rmpr TxTxt1: lpm ; Read a char from the program memory at Z mov rmpr,rlpm cpi rmpr,cnul ; End of text? breq TxTxtz rcall siotxch adiw ZL,1 rjmp TxTxt1 TxTxtz: pop rmpr pop rlpm ret ; ; Send date/time to SIO ; DispDT: rcall DcfErr clr ZH ; Send time info in SRAM to SIO ldi ZL,sDTF DispDT1: ld rmpr,Z+ ; Read from SRAM rcall siotxch ; Transmit cpi rmpr,cCr ; Last char? brne DispDT1 ret ; ; Send a byte as decimal number to the SIO ; DispByte: rcall siotxch ; preface ldi rmpr,' ' rcall siotxch ldi rmpr,'=' rcall siotxch ldi rmpr,' ' rcall siotxch ldi ZH,100 ; send 100's sub ZL,ZH brcs DispByte1 ldi rmpr,'1' sub ZL,ZH brcs DispByte1 sub ZL,ZH inc rmpr DispByte1: rcall siotxch add ZL,ZH ldi ZH,10 ; send 10's sub ZL,ZH brcs DispByte3 ldi rmpr,'0' DispByte2: inc rmpr sub ZL,ZH brcc DispByte2 rjmp DispByte4 DispByte3: cpi rmpr,' ' breq DispByte4 ldi rmpr,'0' DispByte4: add ZL,ZH rcall siotxch ldi rmpr,'0' ; send 1's add rmpr,ZL rcall siotxch ldi rmpr,' ' rcall siotxch rjmp siotxch ; ************** End of SIO subrourines ******************* ; ; ***************** Various subroutines ******************* ; ; DT mode active, display date/time ; DTModeX: sbrs rdtf,bMin ; Minutes only? rjmp DTModeX1 lds rmpr,sSec ; End of minute? cpi rmpr,'0' brne DTModeX2 lds rmpr,sSecT cpi rmpr,'0' brne DTModeX2 DTModeX1: rcall DispDT ; Display date and time DTModeX2: ret ; Return to loop ; ; DCF mode active, display DCF characteristics ; DCFModeX: rcall DcfErr ; Report any DCF77 errors first sbrc rdtf,bDcfCtm rjmp DCFModeX2 or rDcfs,rDcfs ; Report DCF signals bitwise ldi rmpr,cCr breq DCFModeX1 dec rDcfLc ldi rmpr,'1' cp rDcfLc,rDcfCmp brcc DCFModeX1 dec rmpr DCFModeX1: rjmp siotxch DCFModeX2: ldi rmpr,'b' ; Report signal number mov ZL,rDcfs rcall DispByte ldi rmpr,'c' ; Report detected signal length in ticks mov ZL,rDcfLc rcall DispByte ldi rmpr,'m' ; Report current discriminating value mov ZL,rDcfCmp rcall DispByte ldi rmpr,cCr rjmp siotxch ; ; Reports any DCF errors ; DcfErr: sbrs rDcfErr,bDcfEtr ; Any unreported errors? ret cbr rDcfErr,mDcfEtr ldi ZH,HIGH(2*TxtDcfErr) ; Error text intro ldi ZL,LOW(2*TxtDcfErr) rcall TxTxt mov rmpr,rDcfErr andi rmpr,0x3F DcfErr1: adiw ZL,1 clc ; Identify next error bit ror rmpr brcc DcfErr3 rcall TxTxt DcfErr2: or rmpr,rmpr ; No more bits set? brne DcfErr1 andi rDcfErr,0x80 ldi rmpr,cCr rjmp siotxch DcfErr3: adiw ZL,1 ; Point to next text sequence lpm or rlpm,rlpm brne DcfErr3 rjmp DcfErr2 ; ; DCF synchronisation ; Dcf: clr rDcfs ; End of minute, clear bit counter sbr rDcfErr,(mDcfSOk | mDcfEtr) ; Set synch to be reported ldi rmpr,'0' clr ZH ldi ZL,sSec ; Second st Z,rmpr st -Z,rmpr dec ZL dec ZL mov rmpr,rDcf1 ; Minute ror rmpr ror rmpr andi rmpr,0x0F ori rmpr,0x30 st Z,rmpr mov rmpr,rDcf2 ; Tens of minutes ror rmpr mov rmpr,rDcf1 ror rmpr ror rmpr swap rmpr andi rmpr,0x07 ori rmpr,0x30 st -Z,rmpr dec ZL ; Hour mov rmpr,rDcf2 ror rmpr ror rmpr andi rmpr,0x0F ori rmpr,0x30 st -Z,rmpr mov rmpr,rDcf2 ; Tens of hours rol rmpr rol rmpr rol rmpr andi rmpr,0x03 ori rmpr,0x30 st -Z,rmpr ldi ZL,sDTF+dDD ; Day mov rmpr,rDcf3 ror rmpr andi rmpr,0x0F ori rmpr,0x30 st Z,rmpr mov rmpr,rDcf3 ; Tens of Days ror rmpr swap rmpr andi rmpr,0x03 ori rmpr,0x30 st -Z,rmpr adiw ZL,4 ; Month mov rmpr,rDcf4 ror rmpr ror rmpr andi rmpr,0x0F ori rmpr,0x30 st Z,rmpr mov rmpr,rDcf4 ; Tens of monthes swap rmpr ror rmpr ror rmpr andi rmpr,0x01 ori rmpr,0x30 st -Z,rmpr adiw ZL,6 ; Years mov rmpr,rDcf4 rol rmpr mov rmpr,rDcf5 rol rmpr andi rmpr,0x0F ori rmpr,0x30 st Z,rmpr mov rmpr,rDcf5 ; Tens of years rol rmpr swap rmpr andi rmpr,0x0F ori rmpr,0x30 st -Z,rmpr ret ; ; Next second ; ChkDcf: ; Check DCF77 info complete cbr rdtf,mEos ; Clear seconds flag bit sbrc rdtf,bDcfOk ; Last DCF tick was ok? rjmp NewDcfSec sbr rdtf,mDcfSync ; Minute is over mov rmpr,rDcfs ; Have all 59 DCF ticks been received? cpi rmpr,59 brcs CntTooShort ; Less than 59 ticks brne CountTooLong ; More than 59 ticks sbrs rDcfErr,bDcfAny ; Any errors in parity? rjmp Dcf rjmp CntReset ; No DCF synch, clear all CountTooLong: sbrs rdtf,bDcfm ; DCF echo mode on? rjmp CntReset sbr rDcfErr,(mDcfCtl | mDcfEtr | mDcfAny) ; Set DCF error type rjmp CntReset CntTooShort: sbr rDcfErr,mDcfCts ; Set DCF error type or rDcfs,rDcfs ; DCF shift register totally empty? breq CntReset sbr rDcfErr,(mDcfEtr | mDcfAny) ; Set error to report CntReset: clr rDcfs ; Clear the DCF shift counter cbr rDcfErr,mDcfAny ; Clear the global DCF error bit NewDcfSec: cbr rdtf,mDcfOk ; Clear the DCF tick ok bit IncSec: clr ZH ; Point to Date/Time info in SRAM ldi ZL,sDTF+dTS ; Second rcall IncNmbr ; Next second and handle overflow cpi rmpr,60 ; end of minute? brcc IncMin IncRet: ret IncMin: ldi rmpr,'0' ; Clear seconds st Z,rmpr st -Z,rmpr ldi ZL,sDTF+dTM ; Next minute rcall IncNmbr cpi rmpr,60 ; End of the hour? brcs IncRet IncHour: ldi rmpr,'0' ; Clear minutes st Z,rmpr st -Z,rmpr ldi ZL,sDTF+dTH ; Next hour rcall IncNmbr cpi rmpr,24 ; End of the day? brcs IncRet ldi rmpr,'0' ; Clear hours st Z,rmpr st -Z,rmpr IncDay: ldi ZL,sDTF+dDD ; Next day rcall IncNmbr cpi rmpr,32 ; End of month? brcc IncMonth cpi rmpr,31 ; End of month for short monthes brne ChkFeb ldi ZL,sDTF+dDM ; Get days rcall GetByte subi rmpr,4 ; Before April? brcs IncRet cpi rmpr,3 ; April or June? brcs IncDay1 inc rmpr ; Later than June IncDay1: sbrc rmpr,0 ; Even month? ret rjmp IncMonth ; End of a short month ChkFeb: ldi ZL,sDTF+dDM ; Get current month rcall GetByte cpi rmpr,2 ; February? brne IncRet ldi ZL,sDTF+dDY ; Get year rcall GetByte andi rmpr,0x03 ; February with 29 days? brne ChkFeb1 ldi ZL,sDTF+dDD ; Get current day rcall GetByte cpi rmpr,30 ; Long February ends with 29 rjmp ChkFeb2 ChkFeb1: ldi ZL,sDTF+dDD ; Short February, get actual day rcall GetByte cpi rmpr,29 ; End of month? ChkFeb2: brcs IncRet IncMonth: ldi ZL,sDTF+dDD ; Next month, clear days ldi rmpr,'1' st Z,rmpr ldi rmpr,'0' st -Z,rmpr ldi ZL,sDTF+dDM ; Next month rcall IncNmbr cpi rmpr,13 ; End of the year? brcs IncRet IncYear: ldi rmpr,'1' ; next year, clear month st Z,rmpr ldi rmpr,'0' st -Z,rmpr ldi ZL,sDTF+dDY ; Inc years by running into the following ; ; Inc Number at Z and Z-1 and return with the number in one byte ; IncNmbr: ld rmpr,Z ; Inc's a number in SRAM and its tens, if necessary inc rmpr cpi rmpr,'9'+1 brcs IncNmbr1 ld rmpr,-Z inc rmpr st Z+,rmpr ldi rmpr,'0' IncNmbr1: st Z,rmpr ; ; Get byte from Z and Z-1 ; GetByte: ld rmpr,-Z ; Two digit number to binary, load first digit subi rmpr,'0' mov rlpm,rmpr ; Multiply by 10 add rmpr,rmpr add rmpr,rmpr add rmpr,rlpm add rmpr,rmpr mov rlpm,rmpr ; Store result in rlpm inc ZL ; Add second digit ld rmpr,Z subi rmpr,'0' add rmpr,rlpm ret ; **************** End of the subroutine section *************** ; ; ******************** Main program loop *********************** ; ; Main program routine starts here ; Start: cli ; Disable interrupts ldi rmpr,RAMEND ; Set stack pointer out SPL,rmpr rcall InitDT ; Init Date/Time-Info in SRAM rcall InitIo ; Init the I/O properties rcall InitSio ; Init the SIO properties rcall InitDcf rcall InitTimer0 ; Init the timer 0 rcall InitAna ; Init the Analog comparator ; General Interrupt Mask Register ; External Interrupt Request 1 Enable ; External Interrupt Request 0 Enable ldi rmpr,0b11000000 out GIMSK,rmpr ; Timer/Counter Interrupt register ; Disable all TC1 Ints ; Enable TC0 Ints ldi rmpr,0b00000010 out TIMSK,rmpr ; Enable interrupts (Master Int Enable) sei ; Enable all interrupts ; Master Control register settings ; Sleep Enable, Sleep Mode = Idle ; Ext Int 1 on rising edges ; Ext Int 0 on falling edges ldi rmpr,0b00101110 out MCUCR,rmpr Loop: sleep ; Sleep until interrupt occurs nop ; needed to wakeup sbrs rdtf,bDcfRdy ; Check if DCF signal has ended rjmp ChkEos ; no, inc the seconds cbr rdtf,mDcfRdy ; DCF: clear active signal ended mov rDcfLc,rDcfCnt cp rDcfCmp,rDcfLc ; Count parity information, is it a 1 or a 0? brcc DcfPar inc rDcfp ; Count 1's DcfPar: cpi rDcfs,21 ; Start of minute information? brcs DcfCrct brne DcfPar1 clr rDcfp ; Clear parity counter rjmp DcfCrct DcfPar1: cpi rDcfs,29 ; Minute parity to check? brne DcfPar2 sbrc rDcfp,0 ; Parity even? sbr rDcfErr,(mDcfPem | mDcfEtr | mDcfAny) ; Parity error clr rDcfp ; Clear parity counter rjmp DcfCrct DcfPar2: cpi rDcfs,36 ; Hour parity to check? brne DcfPar3 sbrc rDcfp,0 ; Even? sbr rDcfErr,(mDcfPeh | mDcfEtr | mDcfAny) ; Parity error clr rDcfp ; Clear parity counter rjmp DcfCrct DcfPar3: cpi rDcfs,59 ; Date parity to check? brne DcfCrct sbrc rDcfp,0 ; Even? sbr rDcfErr,(mDcfPed | mDcfEtr | mDcfAny) ; Parity error clr rDcfp ; Clear Parity counter DcfCrct: cp rDcfCmp,rDcfLc ; Compare DCF signal length with medium value ror rDcf5 ; Shift the result into the DCF 40-bit storage ror rDcf4 ror rDcf3 ror rDcf2 ror rDcf1 sbr rdtf,mDcfOk ; Set the DCF signal ok bit sub rDcfCnt,rDcfCmp ; Calc distance of signal length from medium value brcs DcfCrct0 ; Underflow = short pulse? mov rDcfL1,rDcfCnt ; Store this difference in the 1-length-byte mov rmpr,rDcfL0 ; Has ever a 0-signal been received? cpi rmpr,0 brne DcfCrct2 DcfCrctUp: inc rDcfCmp ; Only 1-signals received so far, adjust higher medium rjmp Loop DcfCrct0: com rDcfCnt ; Underflow = Signal 0, negative to positive distance mov rDcfL0,rDcfCnt ; Store the difference in the 0-length-byte or rDcfl1,rDcfL1 ; Has ever been received a 1-signal? brne DcfCrCt2 DcfCrctDwn: dec rDcfCmp ; All 0's, reduce medium value rjmp Loop DcfCrct2: cp rDcfL1,rDcfL0 ; Compare the differences of the last 0 and 1 received breq Loop brcs DcfCrctDwn ; Adjust the medium value according to the difference rjmp DcfCrctUp ChkEos: sbrs rdtf,bEos ; End of a timer second? rjmp Loop2 rcall ChkDcf ; Check if DCF is to sychronize sbrs rDTf,bDTm ; DT mode active? rjmp Loop1 rcall DTModeX ; Output the results ModeOff: cpi XL,RxBuF ; In the time- and dcf-echo-mode only blanks are to be breq Loop ; reacted upon. Has a char been sent over the SIO? clr XH ; Reset RX-buffer to the start ldi XL,RxBuF ld rmpr,X ; Read character cpi rmpr,' ' ; Check for BLANK brne StartLoop bld rsioflg,bEcho ; Restore the echo bit cbr rDTf,(mDTm | mDcfm) ; Clear the mode bits rjmp CursorOn ; send cursor on the SIO Loop1: sbrs rdtf,bDcfm ; DCF mode active? rjmp Loop2 rcall DCFModeX ; DCF mode execution rjmp ModeOff Loop2: sbrs rSioFlg,bRxCmp ; Line of chars from the SIO complete? rjmp Loop cbr rSioFlg,mRxCmp ; Clear line available bit sbi PortB,bOCts ; Set hardware CTS off cpi XL,RxBuF+1 ; Has only a carriage return been sent? brne Cmnd ldi ZH,HIGH(2*TxtIntro) ; Empty line, transmit help text ldi ZL,LOW(2*TxtIntro) rcall TxTxt CursorOn: ldi ZH,HIGH(2*TxtCursor) ; Display cursor line again ldi ZL,LOW(2*TxtCursor) rcall TxTxt clr XH ; Set SIO-RX buffer to start ldi XL,RxBuF cbi PortB,bOCts ; Set hardware clear to send active StartLoop: rjmp Loop Cmnd: ldi ZH,HIGH(2*CmdTab) ; Line received, search for command in table ldi ZL,LOW(2*CmdTab) Cmnd1: clr XH ; Receive buffer to start ldi XL,RxBuF Cmnd2: lpm ; Read a char from the command table adiw ZL,1 ldi rmpr,cCr ; end of the command? cp rmpr,rlpm brne Cmnd4 lpm ; next byte in command table a Carriage return char? cp rmpr,rlpm brne Cmnd3 adiw ZL,1 ; Jump over that Cmnd3: lpm ; Load the command adress from the tabel and push it to the stack adiw ZL,1 push rlpm lpm adiw ZL,1 push rlpm ldi ZH,HIGH(2*TxtYellow) ; Set terminal to different color ldi ZL,LOW(2*TxtYellow) rjmp TxTxt ; after transmit the return jumps to the command adress! Cmnd4: ld rmpr,X+ ; compare the next char in the RX buffer cp rmpr,rlpm breq Cmnd2 ; equal, compare next Cmnd5: ldi rmpr,cCr ; not equal, search for next command in table cp rmpr,rlpm ; search end of the current command name breq Cmnd6 lpm ; read until a carriage return in the command table is found adiw ZL,1 rjmp Cmnd5 Cmnd6: lpm ; read over additional carriage returns cp rmpr,rlpm brne Cmnd7 adiw ZL,1 rjmp Cmnd6 Cmnd7: adiw ZL,2 ; ignore the following word (command adress) lpm ldi rmpr,cnul ; check if the end of tabel is reached cp rmpr,rlpm brne Cmnd1 ldi ZH,HIGH(2*TxtError) ; end of table, command is unknown ldi ZL,LOW(2*TxtError) rcall TxTxt rjmp CursorOn ; receive next command line ; ************************* End of program loop ***************** ; ; ********************** Initialisation routines **************** ; ; Init the Date/Time-Info in the SRAM ; InitDT: ldi ZH,HIGH(2*DfltDT) ; Point Z to default value in program memory ldi ZL,LOW(2*DfltDT) clr XH ; Point X to date/time info in SRAM ldi XL,sDTF clr rmpr ; Test for NUL = end of default? InitDT1: lpm ; Read from program memory adiw ZL,1 ; Inc Z pointer cp rmpr,rlpm ; Test for end brne InitDT2 ret InitDT2: st X+,rlpm ; Copy to SRAM location and inc X pointer rjmp InitDT1 ; Next location ; ; Initialize the IO-properties ; InitIo: ; Pins on Port D used: ; Bit 0: Input RxD Sio ; 1: Output TxD Sio ; 2: Input External interrupt 0, DCF signal ; 3: Input External Interrupt 1, DCF signal ; 4: Output TO, not used ; 5: Output T1, not used ; 6: ICP Input Capture Pin, not used ldi rmpr,0b00110010 ; Port D Control out DDRD,rmpr ; Data direction register ldi rmpr,0b00001100 ; Pullup resistors on DCF input on to avoid glitches out PortD,rmpr ; on open inputs ; Pins on Port B: ; Bit 0: Input AIN2, not used ; 1: Input AIN1, not used ; 2: Input SIO incoming RTS signal ; 3: Output OC1, not used ; 4: Output SIO outgoing CTS signal ; 5: Input MOSI, programming interface ; 6: Input MISO, programming interface ; 7: Input SCK, programming interface ldi rmpr,0b00011000 ; Port B Control out DDRB,rmpr ; Data Direction Register ldi rmpr,0b00000000 ; No pullup resistors out PortB,rmpr ret ; ; Initialize the SIO properties ; InitSio: ldi rsioflg,0b00000011 ; Echo on, insert LF after CR on clr XH ; Set Rx Buffer ldi XL,RxBuF ldi rmpr,TxBuF ; Set Tx Buffer In mov rsiotxin,rmpr clr YH ; Set Tx Buffer Out ldi YL,TxBuF ldi rmpr,bddiv ; Init baud generator out UBRR,rmpr ; set divider in UART baud rate register ldi rmpr,0b00011000 ; Enable TX and RX, disable Ints ldi ZL,0 ; Wait for 65 ms ldi ZH,0 InitSio1: sbiw ZL,1 brne InitSio1 ldi rmpr,0b11111000 ; Enable TX und RX 8 Bit and Ints out UCR,rmpr ; in UART Control Register cbi PortB,bOCts ; Set Clear to sent active ret ; ; Init the Timer 0 ; InitTimer0: clr rmpr ; Stop the Timer out TCCR0,rmpr ldi rmpr,6 ; Start with 6 to get 250 steps @ 10 MHz and div=64 out TCNT0,rmpr ; to Timer register clr rtckl ; Clear the low-level-ticker clr rtckh clr rdtf ; Clear the flags ldi rmpr,3 ; Divide Clock by 64 out TCCR0,rmpr ; to Timer 0 Control register ret ; ; Init the Analog comparator ; InitAna: ; Analog comparator is disabled and switched off ldi rmpr,0b00000000 out ACSR,rmpr ret ; ; Init DCF ; InitDcf: ldi rmpr,cDcfCmpDflt ; Set medium value to default value mov rDcfCmp,rmpr clr rDcfL0 ; Clear the distances clr rDcfL1 clr rDcfErr ; Clear the error flags ret ; *************** End of init routines ********************* ; ; ********************* Commands *************************** ; ; Command Table, holds the command text and the command adress ; CmdTab: .DB '?',cCr ; Display list of commands .DW Help .DB 'd','?',cCr,cCr .DW DateOut .DB 'd','s',cCr,cCr ; Mode echo seconds .DW DS .DB 'd','m',cCr,cCr ; Mode echo minutes .DW DM .DB 'd','a','t','e','=',cCr ; Set Date .DW Date .DB 't','i','m','e','=',cCr ; Set Time .DW Time .DB 'd','c','f','b',cCr,cCr ; Enter DCF bit pattern mode .DW DCFMode .DB 'd','c','f','c',cCr,cCr ; Enter DCF counter mode .DW DCFCntMode .DB cnul,cnul,cnul,cnul ; ; Display list of commands Help: ldi ZH,HIGH(2*TxtHelp) ldi ZL,LOW(2*TxtHelp) rcall TxTxt rjmp CursorOn ; Display date and time DateOut: rcall DispDT rjmp CursorOn ; Set mode to echo date and time every second DS: cbr rdtf,mMin rcall DTMode rjmp CursorOn ; Set mode to echo date and time every minute only DM: sbr rdtf,mMin ; Enter Date/Time-Echo-Mode DTMode: sbr rdtf,mDTm ; Set the mode bit ldi ZH,HIGH(2*TxtDtm) ; Display the info text ldi ZL,LOW(2*TxtDtm) SetMode: rcall TxTxt bst rsioflg,bEcho ; Store the echo bit and switch it off cbr rsioflg,mEcho cbi PortB,bOCts ; Clear to send active to receive characters clr XH ; Set RX-buffer to start ldi XL,RxBuF rjmp Loop ; ; Enter DCFMode ; DCFMode: cbr rdtf,mDcfCtm ; clear the mode bit DCFMode1: sbr rdtf,mDcfm ; set echo mode ldi ZH,HIGH(2*TxtDcf) ; Display the info text ldi ZL,LOW(2*TxtDcf) rjmp SetMode ; ; Enter DCF counter echo mode ; DCFCntMode: sbr rdtf,mDcfCtm ; set the DCF echo mode bit rjmp DCFMode1 ; ; Set date ; Date: clr ZH ; Point Z to Date in SRAM ldi ZL,sDTF Date1: ld rmpr,X+ ; Take over char from Rx-buffer cpi rmpr,cCr breq Date2 st Z+,rmpr ld rmpr,Z ; End reached? cpi rmpr,',' breq Date2 cpi rmpr,cCr brne Date1 Date2: rcall DispDT ; send date and time to SIO rjmp CursorOn ; Cursor on and back to loop ; ; Set time ; Time: clr ZH ; Point Z to Time in SRAM ldi ZL,sDTT rjmp Date1 ; ; Texts to be displayed ; TxtIntro: .DB cesc,'[','0',59,'1',59,'3','7',59,'4','0','m'; Set screen colours .DB cesc,'[','H',cesc,'[','J' ; ANSI Clear screen .DB "Hello world!" .DB ccr,'T' .DB "his is the experimental 2313 board (C)DG4FAC at work. ? for help. " .DB cnul,cnul TxtCursor: .DB ccr,cesc,'[','3','2','m','*','>',cesc,'[','3','6','m',cnul TxtYellow: .DB cesc,'[','3','3','m',cnul TxtError: .DB ccr,cesc,'[','3','1','m' .DB "Error! Unknown command! " .DB cCr,cnul TxtHelp: .DB "List of commands" .DB ':',cCr .DB " d?: Display date/time." .DB cCr,' ' .DB " date=dd.yy.yyyy: Set date" .DB '.',cCr .DB " time=hh:mm:ss: Set time." .DB cCr,' ' .DB " ds: Display seconds" .DB '.',cCr .DB " dm: Display minutes." .DB cCr,' ' .DB " dcfb: DCF77 bit pattern echo." .DB cCr,' ' .DB " dcfc: DCF77 counter echo." .DB cCr,' ' .DB " ?: Display this list." .DB cCr,cNul TxtDtm: .DB "Date/Time echo. SPACE to leave" .DB '.',cesc,'[','3','5','m',cCr,cnul TxtDcf: .DB "DCF echo mode. SPACE to leave." .DB cesc,'[','3','4','m',cCr,cnul,cnul TxtDcfErr: .DB ' ',' ','D','C','F',':',' ',cNul .DB "Minutes wrong!" .DB ' ',cNul .DB "Hours wrong!" .DB ' ',cNul .DB "Date wrong" .DB '!',cNul .DB "Count too short!" .DB ' ',cNul .DB "Count too long" .DB '!',cNul .DB "Synchronisation." .DB cNul,cNul ; ; Default date and time ; DfltDT: .DB "11.01.2001, 23:12:58" .DB cCr,cNul ; ; End of code segment ; ; 1015 words, only a few left in the 2313. Enough for a copyright. ; Copyright: .DB " 2C00 1GDF4CA"