Path: Home => AVR overview => Applications => DCF77 receivers => DCF77 Display => Assembler source code
DCF77 receiver logo Applications of
AVR single chip controllers AT90S, ATtiny, ATmega and ATxmega
Assembler source code DCF77 display
Logo

6.4 Assembler source code for the DCF77 display with ATtiny24

This is the software for the DCF77 display with an ATtiny24. The source code can be downloaded from here in asm format. To assemble it needs the Include routines here.

;
; ***********************************
; * 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




©2019 by http://www.avr-asm-tutorial.net