![]() |
Anwendungen von AVR-Einchip-Prozessoren AT90S, ATtiny, ATmega und ATxmega Assembler Quellcode DCF77 Anzeige |
![]() |
;
; ***********************************
; * DCF77 watch ATtiny24 Version 4 *
; * (C)2019 by avr-asm-tutorial.net *
; ***********************************
;
.nolist
.include "tn24def.inc" ; Define device ATtiny24
.list
;
; **********************************
; D E B U G G I N G S W I T C H
; **********************************
;
.equ No = 0 ; Answers
.equ Yes = 1
;
; Debug the display routine
.equ Debug_display = No ; 0=No debugging, 1=debugging only
.if Debug_display == Yes
.equ Debug_displayH = 'm' ; The High parameter
.equ Debug_displayL = 59 ; The Low parameter
.endif
;
; **********************************
; H A R D W A R E
; **********************************
;
; Works with an tiny24-lcd experimental board
; as described under
; http://www.avr-asm-tutorial.net/avr_en/apps/tn24_lcd/tn24_lcd.html
; and one of the DCF77 receivers as decribed under
; http://www.avr-asm-tutorial.net/avr_en/apps/dcf77_rcvr/dcf77_rcvr.html
; Connects with a tn45/85 DCF decoder, receives serial
; data from the tn45/85, decodes and displays it on
; the LCD
;
; Device: ATtiny24, Package: 14-pin-PDIP_SOIC
;
; _________
; 1 / |14
; + 5 V o--|VCC GND|--o 0 V
; LCD-RS o--|PB0 PA0|--o Serial Clock DCF RX
; LCD-R/W o--|PB1 PA1|--o Serial Data DCF RX
; RESET o--|RESET PA2|--o NC
; LCD-E o--|PB2 PA3|--o NC
; LCD-D7 o--|PA7 PA4|--o LCD-D4
; LCD-D6 o--|PA6 PA5|--o LCD-D5
; 7 |__________|8
;
;
; **********************************
; P O R T S A N D P I N S
; **********************************
;
; Serial signal input ports and pins
.equ pSerialClkO = PORTA ; Serial Clk output port
.equ pSerialClkD = DDRA ; Serial Clk direction port
.equ pSerialClkI = PINA ; Serial Clk input port
.equ bSerialClkO = PORTA0 ; Serial Clk output pin
.equ bSerialClkD = DDA0 ; Serial Clk direction pin
.equ bSerialClkI = PINA0 ; Serial Clk input pin
.equ pSerialDataO = PORTA ; Serial Data output port
.equ pSerialDataD = DDRA ; Serial Data direction port
.equ pSerialDataI = PINA ; Serial Data input port
.equ bSerialDataO = PORTA1 ; Serial Data output pin
.equ bSerialDataD = DDA1 ; Serial Data direction pin
.equ bSerialDataI = PINA1 ; Serial Data input pin
;
; *************************************
; L C D . I N C P A R A M E T E R S
; *************************************
;
.equ clock = 1000000 ; Clock frequency of controller in Hz
; LCD size:
.equ LcdLines = 4 ; Number of lines (1, 2, 4)
.equ LcdCols = 20 ; Number of characters per line (8..24)
; LCD bus interface
.equ LcdBits = 4 ; Bus size (4 or 8)
; If 4 bit bus:
.equ Lcd4High = 1 ; Bus nibble (1=Upper, 0=Lower)
.equ LcdWait = 0 ; Access mode (0 with busy, 1 with delay loops)
; LCD data ports
.equ pLcdDO = PORTA ; Data output port
.equ pLcdDD = DDRA ; Data direction port
; LCD control ports und pins
.equ pLcdCEO = PORTB ; Control E output port
.equ bLcdCEO = PORTB2 ; Control E output portpin
.equ pLcdCED = DDRB ; Control E direction port
.equ bLcdCED = DDB2 ; Control E direction portpin
.equ pLcdCRSO = PORTB ; Control RS output port
.equ bLcdCRSO = PORTB0 ; Control RS output portpin
.equ pLcdCRSD = DDRB ; Control RS direction port
.equ bLcdCRSD = DDB0 ; Control RS direction portpin
; If LcdWait = 0:
.equ pLcdDI = PINA ; Data input port
.equ pLcdCRWO = PORTB ; Control RW output port
.equ bLcdCRWO = PORTB1 ; Control RW output portpin
.equ pLcdCRWD = DDRB ; Control RW direction port
.equ bLcdCRWD = DDB1 ; Control RW direction portpin
; If you need binary to decimal conversion:
.equ LcdDecimal = 1 ; If defined: include those routines
; If you need binary to hexadecimal conversion:
.equ LcdHex = 1 ; If defined: include those routines
; If simulation in the SRAM is desired:
;.equ avr_sim = 1 ; 1=Simulate, 0 or undefined=Do not simulate
;
; **********************************
; A D J U S T A B L E C O N S T
; **********************************
;
; Serial time-out
; - previous received bits ignored
; - restarting serial collection process
.equ cSerialTOSelected = 5000 ; Serial timeout, in us
;
; Weekday display language
.equ cLangEn = No ; No=German, Yes=English
;
; **********************************
; F I X & D E R I V. C O N S T
; **********************************
;
; TC0 as seconds clock
.equ cPrescTc0 = 8 ; TC0 prescaler value
.equ cDividerTc0 = 125 ; TC0 CTC divider
.equ cTc0FreqInt = clock/cPrescTc0/cDividerTc0 ; TC0 int frequency
.equ cSec = cTc0FreqInt ; Counter for seconds
;
; TC0 as timeout counter for serial signal
.equ cSerialKBaud = 10 ; Baud rate of serial signal
.equ cSerialTimeBitus = 1000000/16/(cSerialKBaud*1000)*(clock/1000000) ; Time per bit, us
.equ cSerialTimeWordus = 16*cSerialTimeBitus
.equ cSerialTOFreq = 1000000/cSerialTOSelected ; in Hz
.equ cSerialTO = cTc0FreqInt/cSerialTOFreq ; TO Counter value
;
; **********************************
; R E G I S T E R S
; **********************************
;
; free: R0 to R11
.def rDcfBits = R12 ; The last eight bit received
.def rSerialL = R13 ; Serial bits received, LSB
.def rSerialH = R14 ; dto., MSB
.def rSreg = R15 ; Save/Restore status port
.def rmp = R16 ; Define multipurpose register
.def rimp = R17 ; Multipurpose in interrupts
.def rFlag = R18 ; Flags
.equ bSec = 0 ; One second is over
.equ bRxIn = 1 ; 16 bits received
.def rSerialBits = R19 ; Received serial bits
.def rSerialTO = R20 ; Serial time-out counter
.def rRxL = R21 ; Received byte LSB
.def rRxH = R22 ; dto., MSB
; free: R23
.def rSecL = R24 ; 16-bit downcounter seconds, LSB
.def rSecH = R25 ; dto., MSB
; free: R26 to R29
; used: R31:R30 = Z for LCD operations
;
; **********************************
; S R A M
; **********************************
;
.dseg
.org SRAM_START
; Date and time set
sTime:
sTimeWd:
.byte 1
sTimeDay:
.byte 1
sTimeMonth:
.byte 1
sTimeYear:
.byte 1
sTimeHour:
.byte 1
sTimeMinute:
.byte 1
sTimeSecond:
.byte 1
sTimeEnd:
; TimeTable constants
.equ cTimeWd = 0
.equ cTimeDay = 1
.equ cTimeMonth = 2
.equ cTimeYear = 3
.equ cTimeHour = 4
.equ cTimeMinute = 5
.equ cTimeSecond = 6
.equ cTimeNone = 255
;
; **********************************
; C O D E
; **********************************
;
.cseg
.org 000000
;
; **********************************
; R E S E T & I N T - V E C T O R S
; **********************************
rjmp Main ; Reset vector
reti ; EXT_INT0
rjmp Pci0Isr ; PCI0, serial clock pin
reti ; PCI1
reti ; WATCHDOG
reti ; ICP1
reti ; OC1A
reti ; OC1B
reti ; OVF1
rjmp Ocr0aIsr ; OC0A, seconds counter
reti ; OC0B
reti ; OVF0
reti ; ACI
reti ; ADCC
reti ; ERDY
reti ; USI_STR
reti ; USI_OVF
;
; **********************************
; I N T - S E R V I C E R O U T .
; **********************************
;
; PCINT0 Interrupt service routine
Pci0Isr: ; 7 clock cycles for int and vector jump
sbis pSerialClkI,bSerialClkI ; Is clock high? +1/2=8/9
reti ; No, skip, +4=12
; Clock input is high, collect data bit, 9 cycles
in rSreg,SREG ; Save SREG, +1=10
clc ; Clear carry, bit = 0, +1=11
sbic pSerialDataI,bSerialDataI ; Is data low?, +1/2=12/13
sec ; No, high bit received, +1 = 13
rol rSerialL ; Roll into received bits, LSB, +1=14
rol rSerialH ; and into MSB, +1=15
dec rSerialBits ; Down-count bits, +1=16
brne Pci0Isr1 ; Not zero, continue, +1/2=17/18
mov rRxL,rSerialL ; Copy received bits, LSB, +1=18
mov rRxH,rSerialH ; dto., MSB, +1=19
ldi rSerialBits,16 ; Restart counter, +1=20
sbr rFlag,1<<bRxIn ; Set received flag, +1=21
Pci0Isr1: ; 18/21 cycles
ldi rSerialTO,cSerialTO ; Restart time out, +1=19/22
out SREG,rSreg ; Restore SREG, +1=20/23
reti ; +4=24/27
;
; OCR0A Interrupt, seconds counting and serial time-out
Ocr0aIsr:
in rSreg,SREG ; Save SREG
sbiw rSecL,1 ; Down-count seconds counter
brne Ocr0aIsr1 ; Not zero, skip this section
ldi rSecH,High(cSec) ; Restart counter, MSB
ldi rSecL,Low(cSec) ; dto., LSB
sbr rFlag,1<<bSec ; Set seconds flag
Ocr0aIsr1:
dec rSerialTO ; Time-out counter
brne Ocr0aIsr2
ldi rSerialTO,cSerialTO ; Restart counter
ldi rSerialBits,16 ; Restart bit counter
Ocr0aIsr2:
out SREG,rSreg ; Restore SREG
reti
;
; **********************************
; M A I N P R O G R A M I N I T
; **********************************
;
Main:
.ifdef SPH
ldi rmp,High(RAMEND)
out SPH,rmp
.endif
ldi rmp,Low(RAMEND)
out SPL,rmp ; Init LSB stack pointer
;
; **********************************
; D E B U G G I N G
; **********************************
;
.if Debug_display == Yes
ldi rRxH,Debug_displayH ; Load high parameter
ldi rRxL,Debug_displayL ; Load low parameter
rcall DisplayDCF ; Call display
Debug_display_loop:
rjmp Debug_display_loop
.endif
;
; **********************************
; N O R M A L I N I T
; **********************************
; Init the LCD
rcall LcdInit ; Init LCD operation
rcall LcdFrame
; Init serial clock and data pins
sbi pSerialClkO,bSerialClkO ; Set pull-up on serial clock
sbi pSerialDataO,bSerialDataO ; and on serial data
cbi pSerialClkD,bSerialClkD ; Clock pin as input
cbi pSerialDataD,bSerialDataD ; Data pin as input
; Init seconds counting and serial reception
ldi rSecH,High(cSec) ; Start seconds counter, MSB
ldi rSecL,Low(cSec) ; dto., LSB
ldi rSerialTO,cSerialTO ; Serial collection restart counter
; Init serial clock pin as PCINT
ldi rmp,1<<PCINT0 ; Clock pin as external int source
out PCMSK0,rmp ; in int mask 0
ldi rmp,1<<PCIE0 ; Enable pcint 0
out GIMSK,rmp
; Init TC0 as seconds clock and for serial time-out
ldi rmp,cDividerTC0 - 1 ; CTC value to
out OCR0A,rmp ; Compare A
ldi rmp,1<<WGM01 ; CTC on compare A
out TCCR0A,rmp ; to control register A
ldi rmp,1<<CS01 ; Prescaler = 8
out TCCR0B,rmp ; to control register B
ldi rmp,1<<OCIE0A ; Compare A interrupt
out TIMSK0,rmp ; to TC0 int mask register
; Init sleep mode
ldi rmp,1<<SE ; Sleep enable, mode idle
out MCUCR,rmp ; to master control register
; Enable Ints
sei ; Enable interrupts
;
; **********************************
; P R O G R A M L O O P
; **********************************
;
Loop:
sleep ; Go sleeping
nop ; Dummy for wake-up
sbrc rFlag,bRxIn ; No word received?
rcall DisplRx ; No, display that
sbrc rFlag,bSec ; Second over?
rcall Second ; Yes
rjmp loop
;
; ***********************************
; F L A G H A N D L I N G
; ***********************************
;
; A second is over
Second:
cbr rFlag,1<<bSec ; Clear second flag
ldi ZH,2 ; Set second position on LCD
ldi ZL,11
rcall LcdPos ; Set LCD position to Z
lds rmp,sTimeSecond ; Read seconds from SRAM
inc rmp ; Next second
cpi rmp,60 ; End of minute?
brcs Second1 ; No, continue
clr rmp ; Restart seconds
Second1:
sts sTimeSecond,rmp ; Save seconds
rjmp LcdDec2 ; Write seconds as 2-digit decimal
;
; A word has been received, display it
DisplRx:
cbr rFlag,1<<bRxIn ; Clear flag
ldi ZH,RawLine ; Raw data output line
ldi ZL,RawCol ; Raw data output column
rcall LcdPos ; Position to the LCD
mov rmp,rRxH ; Display parameter character
rcall LcdChar ; on LCD position
mov rmp,rRxL ; Display value as hex
rcall LcdHex2 ; on current LCD position
;
; Display received messages
DisplayDCF:
ldi ZH,High(2*PosTable) ; Point Z to table, MSB
ldi ZL,Low(2*PosTable) ; dto., LSB
DisplayDCF1:
lpm rmp,Z+ ; Read character from table
tst rmp ; Zero?
breq DisplayDCFUnknown ; Yes, unknown param
cp rRxH,rmp ; This parameter received?
breq DisplayDCF2 ; Yes, display
adiw ZL,5 ; Point to next parameter in list
rjmp DisplayDCF1 ; Text next parameter
DisplayDCFUnknown:
ret ; Ignore message
DisplayDCF2:
; Display this parameter
lpm XH,Z+ ; Read position on LCD, line
lpm XL,Z+ ; dto., column
push ZH ; Save address on stack
push ZL
mov ZH,XH ; Copy position to Z, line
mov ZL,XL ; dto., column
rcall LcdPos ; Position to the LCD
pop ZL ; Restore address from stack
pop ZH
lpm rmp,Z+ ; Read time position
cpi rmp,cTimeNone ; No time setting?
breq DisplayDCFOutMode ; Yes, skip
cpi rmp,cTimeMinute ; Minute setting?
brne DisplayDCFSetTime
clr XL
sts sTimeSecond,XL ; Clear second
DisplayDCFSetTime:
ldi XH,High(sTime) ; Point X to time in SRAM, MSB
ldi XL,Low(sTime) ; dto., LSB
add XL,rmp ; Add time position
ldi rmp,0 ; Any carry
adc XH,rmp ; add to MSB
st X,rRxL ; Write time
DisplayDCFOutMode:
lpm rmp,Z ; Read output mode
cpi rmp,1 ; 2-digit decimal?
breq DisplayDCF2dig ; 1 = display 2-digit decimal
brcs DisplayDCFAsc ; 0 = display ASCII char
cpi rmp,3 ; Weekday?
breq DisplayDCFWd ; 3 = display weekday
brcs DisplayDCF3dig ; 2 = display 3-digit decimal
; Display the last four DCF bits
mov rmp,rRxL ; Read parameter value
lsr rmp ; A zero or one to carry
rol rDcfBits ; Roll into register
mov rmp,rDcfBits ; Copy DCF bits
swap rmp ; Lower to upper bits
ldi XL,4 ; Four bits to be displayed
DisplayDCFBits:
lsr rmp ; Highest bit to carry
push rmp ; Save bits on stack
ldi rmp,'0' ; A zero to display
brcc DisplayDCF0 ; Display that
inc rmp
DisplayDCF0:
rcall LcdChar ; Display bit on LCD
pop rmp ; Restore bits from stack
dec XL ; Next character
brne DisplayDCFBits ; Continue
rjmp DisplayDCFDone
;
; Display Weekday
DisplayDCFWd:
ldi ZH,High(2*WdTable) ; Point Z to weekday table, MSB
ldi ZL,Low(2*WdTable) ; dto., LSB
mov rmp,rRxL ; Read weekday
lsl rmp ; Two characters per weekday
add ZL,rmp ; Add to table
ldi rmp,0 ; Add carry
adc ZH,rmp ; to ZH
lpm rmp,Z+ ; Read first character
rcall LcdChar ; Write to LCD
lpm rmp,Z ; Read second character
rcall LcdChar ; Write to LCD
rjmp DisplayDCFDone
; Display as ASCII character
DisplayDCFAsc:
mov rmp,rRxL ; Read parameter value
rcall LcdChar
rjmp DisplayDCFDone
; Display 3-digit decimal
DisplayDCF3dig:
mov rmp,rRxL ; Read parameter value
rcall LcdDec3
rjmp DisplayDCFDone
; Display 2-digit decimal
DisplayDCF2dig:
mov rmp,rRxL ; Read parameter value
rcall LcdDec2
DisplayDCFDone:
ret
;
; Position table
; First byte: ASCII char parameter
; Second/third: Line & column to display
; Fourth: Type of display =
; 0=ASCII, 1=2-digit decimal, 2=3-digit decimal
; 3=Weekday (0=Monday), 4=Last four DCF77 digits
PosTable:
.db 'R',2,16,cTimeNone,2,0 ; Restart message
.db 'C',1,16,cTimeNone,2,0 ; Frequency scan complete
.db 'S',2,16,cTimeNone,2,0 ; Signal strength, 3-digit decimal
.db 'F',1,16,cTimeNone,2,0 ; Frequency, 3-digit decimal
.db 'm',2,8,cTimeMinute,1,0 ; Minutes, 2-digit decimal
.db 'h',2,5,cTimeHour,1,0 ; Hours, 2-digit decimal
.db 'W',3,5,cTimeWd,3,0 ; Weekday, Monday based 2 ASCII chars
.db 'D',3,9,cTimeDay,1,0 ; Day, 2-digit decimal
.db 'M',3,12,cTimeMonth,1,0 ; Month, 2-digit decimal
.db 'Y',3,15,cTimeYear,1,0 ; Year, 2-digit decimal
.db '0',0,16,cTimeNone,4,0 ; DCF77-Bit Zero, 4 last DCF77 bits
.db '1',0,16,cTimeNone,4,0 ; dto., One
.db 'E',3,19,cTimeNone,0,0 ; DCF77 error, one ASCII char
.db 'B',1,16,cTimeNone,2,0 ; Number of received bity, 3-digit decimal
.db 'a',1,16,cTimeNone,2,0 ; Debug medium, 3-digit decimal
.db 'd',2,16,cTimeNone,2,0 ; Debug difference, 3-digit decimal
.db 0,0 ; End of table
;
; Table of weekdays
WdTable:
.if cLangEn == Yes
.db "MoTuWdThFrSaSu"
.else
.db "MoDiMiDoFrSaSo"
.endif
;
; Via serial transfer received parameters
; Status reports
; 'R': Restart
; 'C': F scan complete
; 'S': Signal strength
; 'F': Frequency
; Received time
; 'm': Minutes
; 'h': Hours
; 'W': Weekday
; 'D': Day
; 'M': Month
; 'Y': Year
; Bit-Monitoring
; '0': Zero received
; '1': One received
; DCF77 errors
; 'E': DCF77 error
; 'B': Number of bits received <> 59
; Debugging
; 'a': Medium value
; 'd': Difference Max-Min
;
; ***********************************
; L C D R O U T I N E S
; ***********************************
;
LcdFrame:
ldi rmp,1 ; Clear the LCD
rcall LcdCtrl ; as control command
ldi ZH,0 ; To line 1 colukmn 1
ldi ZL,0
rcall LcdPos ; Positioning
ldi ZH,High(2*InitFrame) ; Z to text frame
ldi ZL,Low(2*InitFrame)
rjmp LcdText
;
; 20 chars per line
InitFrame:
.db "DCF77 Serial RX tn24",0x0D,0xFF
.db "Raw data: ___ F:___ ",0x0D,0xFF
.db "Time __:__:__ S:___ ",0x0D,0xFF
.db "Date __, __.__.__ E_",0xFE,0xFF
; 01234567890123456789
.equ RawLine = 1
.equ RawCol = 10
;
; Include lcd.inc
.include "lcd.inc" ; Load include file
;
; End of source code