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