Pfad: Home => AVR-Übersicht => Anwendungen => DCF77 Uhr

; ***************************************************************
; * 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!at!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"
©2002 by http://www.avr-asm-tutorial.net