Path: Home => AVR-EN => Applications => DCF signal analysis => asm code
DCF77 signal analysis logo AVR applications

Analysis for DCF77 receiver signals
Assembler software for analyzing
Logo

Assembler source code for the DCF77 signal analysis

The original assembler source code is here, it needs the LED include to assemble.

;
; *********************************
; * Test an attached DCF77 module *
; * with a tn24_lcd module        *
; * (C)2019 avr-asm-tutorial.net  *
; *********************************
;
.nolist
.include "tn24adef.inc" ; Define device ATtiny24A
.list
;
; **********************************
;    D E B U G   S W I T C H E S
; **********************************
;
.equ Yes = 1 ; Enable switch
.equ No = 0 ; Disable switch
;
; Skip all LCD operations for simulation
.equ Debug_SkipLcd = No
;
; Simulate the DCF duration categories
.equ Debug_DcfCat = No
  .equ Debug_DcfCatD = 200 ; Set the DCF duration
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Device: ATtiny24A, Package: 14-pin-PDIP_SOIC
;             _________
;          1 /         |14
;    +5V o--|VCC    GND|--o 0V
; LCD-RS o--|PB0    PA0|--o DCF
; LCD-RW o--|PB1    PA1|--o NC
;  RESET o--|RESET  PA2|--o NC
;  LCD-E o--|PB2    PA3|--o Key
; 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
; **********************************
;
.equ pDcfKeyO = PORTA ; Define the output port
.equ pDcfKeyD = DDRA ; Define the direction port
.equ pDcfKeyI = PINA ; Define the input port
.equ bDcfO = PORTA0 ; Define the DCF output port
.equ bDcfI = PINA0 ; Define the DCF input port
.equ bKeyO = PORTA3 ; Define the key pins
.equ bKeyI = PINA3 ; Define the key input port
; (LCD pins are defined in Adjustable Constants section)
;
; **********************************
;   A D J U S T A B L E   C O N S T
; **********************************
;
; Delay between initial LCD message and measuring start
.equ tDelay = 5 ; Number of seconds delay
;
; Set pull up resistor on DCF input
.equ DcfPullUp = No ; Yes or no
;
; Time settings for the DCF77 input signals
.equ cDcfTol = 20 ; Tolerance of all times in %
.equ tDcfSp = 10 ; Spurious signals, in ms
.equ tDcf0 = 100 ; Signal duration for a 0, in ms
.equ tDcf1 = 200 ; Signal duration for a 1, in ms
.equ tDcfP = 850 ; Signal duration for a pause, in ms
.equ tDcfM = 1850 ; Signal duration for a missing second pulse, in ms
.equ tDcfTO = 2500 ; DCF signal missing time-out, in ms
;
; Parameter set of properties/definitions for lcd
;   (Needed by LCD include)
.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)
; LCD wait/busy mode
  .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
;
; **********************************
;  F I X  &  D E R I V.  C O N S T
; **********************************
;
; Converting DCF times to TC1 clock cycles with tolerances
.equ ckHz = (clock+500)/1000 ; Clock frequency in kHz
.equ cDcfSp = (tDcfSp*ckHz+512)/1024 ; (0..3)
.equ cDcf0Min = ((tDcf0-cDcfTol*tDcf0/100)*ckHz+512)/1024 ; (4..7)
.equ cDcf0Max1 = ((tDcf0+cDcfTol*tDcf0/100)*ckHz+512)/1024+1 ; (8..11)
.equ cDcf1Min = ((tDcf1-cDcfTol*tDcf1/100)*ckHz+512)/1024 ; (12..15)
.equ cDcf1Max1 = ((tDcf1+cDcfTol*tDcf1/100)*ckHz+512)/1024+1 ; (16..19)
.equ cDcfPMin = ((tDcfP-cDcfTol*tDcfP/100)*ckHz+512)/1024 ; (20..23)
.equ cDcfPMax1 = ((tDcfP+cDcfTol*tDcfP/100)*ckHz+512)/1024+1 ; (24..27)
.equ cDcfMMin = ((tDcfM-cDcfTol*tDcfM/100)*ckHz+512)/1024 ; (28..31)
.equ cDcfMMax1 = ((tDcfM+cDcfTol*tDcfM/100)*ckHz+512)/1024+1 ; (32..35)
.equ cDcfTO = (tDcfTO*ckHz+512)/1024+1 ; (36..39)
;
; Checking of constants
.if cDcfTol == 0
  .error "Tolerance cDcfTol cannot be zero!"
  .endif
.if (cDcf0Min <= cDcfSp)
  .error "Minimum for zero less than spurious, reduce tolerance"
  .endif
.if (cDcf0Max1 >= cDcf1Min)
  .error "Zero maximum overlaps One minimum, reduce tolerance"
  .endif
.if (cDcf1Max1 >= cDcfPMin)
  .error "One maximum overlaps Pause minimum, reduce tolerance"
  .endif
.if (cDcfPMax1 >= cDcfMMin)
  .error "Pause maximum overlaps Minute minimum, reduce tolerance"
  .endif
;
; **********************************
;       R E G I S T E R S
; **********************************
;
.def rBinL = R0 ; Binary LSB
.def rBinH = R1 ; dto., MSB
; free: R2 to R14
.def rSreg = R15 ; Save/Restore status port
.def rmp = R16 ; Define multipurpose register
.def rimp = R17 ; Multipurpose inside ints
.def rFlag = R18 ; Flag register
  .equ bDcf = 0 ; DCF flag
  .equ bKey = 1 ; Key input flag
.def rInput = R19 ; Input register
.def rDcfDurL = R20 ; DCF duration, LSB
.def rDcfDurH = R21 ; dto., MSB
; free: R22 to R25
; used: R27:R26 = X as counter pointer
; used: R29:R28 = Y for decimal conversion
; used: R31:R30 = Z for various purposes
;
; **********************************
;           S R A M
; **********************************
;
.dseg
.org SRAM_START
sCounters:
.byte 8 ; Spurious count
  ; 2 bytes description
  ; 2 bytes counter
  ; 4 bytes adder
.byte 8 ; Shorter than zero
.byte 8 ; Correct zero
.byte 8 ; Shorter than one
.byte 8 ; Correct one
.byte 8 ; Shorter than pause
.byte 8 ; Correct pause
.byte 8 ; Shorter than minute
.byte 8 ; Correct minute
.byte 8 ; Longer than minute
sCountersEnd:
;
; **********************************
;         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 Pcint0Isr ; PCI0
	reti ; PCI1
	reti ; WATCHDOG
	reti ; ICP1
	reti ; OC1A
	reti ; OC1B
	reti ; OVF1
	reti ; OC0A
	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
PcInt0Isr:
  in rSreg,SREG ; Save SREG
  in rimp,pDcfKeyI ; Read current pins
  eor rimp,rInput ; Check pins that changed
  sbrc rimp,bKeyI ; Check key pin changed
  rjmp PcInt0Isr1 ; No, skip
  sbis pDcfKeyI,bKeyI ; Check key pin is high
  sbr rFlag,1<<bKey ; No, set key low flag
PcInt0Isr1:
  sbrs rimp,bDcfI ; Check if DCF input changed
  rjmp PcInt0Isr2 ; No, skip
  sbr rFlag,1<<bDcf ; Set DCF flag
  in rDcfDurL,TCNT1L ; Read counter, LSB first
  in rDcfDurH,TCNT1H ; dto., MSB next
  clr rimp ; Restart counter
  out TCNT1H,rimp ; MSB first
  out TCNT1L,rimp ; LSB next
PcInt0Isr2:
  in rInput,pDcfKeyI ; Read input port
  out SREG,rSreg ; Restore SREG
  reti
;
; **********************************
;  M A I N   P R O G R A M   I N I T
; **********************************
;
Main:
	ldi rmp,Low(RAMEND)
	out SPL,rmp ; Init LSB stack pointer
  ; Init ports
  sbi pDcfKeyO,bKeyO ; Set pull up on key pin
  .if DcfPullup == Yes
    sbi pDcfKeyO,bDcfO ; Set pull up on DCF pin
	.endif
.if Debug_SkipLcd == No
  ; Init the LCD
  rcall LcdInit ; Init the LCD
  ldi ZH,High(2*Text_Intro) ; Intro text
  ldi ZL,Low(2*Text_Intro)
  rcall LcdText ; Display intro text
  ; Delay the execution a little bit
  ldi rmp,3*tDelay ; tDelay seconds
Main1:
  sbiw ZL,1 ; 2 cycles
  brne Main1 ; 2 cycles for branching, 1 at the end
  dec rmp ; 1 cycle
  brne Main1 ; 2 cycles for branching, 1 at the end
  .endif
  ; Complete time for the loop:
  ;   Inner loop: cycles = 65535 * 4 + 3
  ;   Plus outer loop: cycles = (Inner loop + 3) * (rmp -1) + Innerloop + 2
  ;     = (rmp-1)*(Inner+3) + Inner + 2
  ;     = rmp*Inner + 3*rmp - Inner - 3 + Inner + 2
  ;     = rmp*65535*4 + 3 + 3*rmp - 65535*4 - 12 - 3 + 65535*4 + 3 + 2
  ;     = rmp*262140 + 3*rmp + 3 - 12 - 3 + 3 + 2
  ;     = rmp*(262140+3) - 7
  ;   For one second:
  ;   rmp = (1000000+7)/262143 = 3
  ;
  ; Init TC1 for counting signal durations
  ldi rmp,High(cDcfTO) ; Set time-out for DCF signals, MSB
  out OCR1AH,rmp ; in MSB compare A first
  ldi rmp,Low(cDcfTO) ; dto., LSB
  out OCR1AL,rmp ; in LSB compare next
  clr rmp ; Mode TC1 setting
  out TCCR1A,rmp ; in mode control A
  ldi rmp,(1<<WGM12)|(1<<CS12)|(1<<CS10) ; CTC, prescaler=1024
  out TCCR1B,rmp ; in mode control B
  ; Restart all counters, set standard text, set LCD to standard frame
  rcall ClearOnly ; Restart counting
.if Debug_DcfCat == Yes
  ldi rDcfDurH,High(Debug_DcfCatD)
  ldi rDcfDurL,Low(Debug_DcfCatD)
  rcall DcfSig
  endless: rjmp endless
  .endif
  ; Init ports
;  endless1: rjmp endless1
; Only use this if your module is compatible with
;   the pull-up switched on (mot necessary if you
;   use an NPN driver stage)
;  sbi pDcfKeyO,bDcfO
  ; Init PCINTs on the DCF and Key inputs
  ldi rmp,(1<<bDcfO)|(1<<bKeyO) ; Enable DCF and key ints
  out PCMSK0,rmp
  ldi rmp,1<<PCIE0 ; Enable PCINT0 interrupts
  out GIMSK,rmp ; in General Interrupt Mask
  ; Sleep and interrupts
  ldi rmp,1<<SE ; Sleep mode idle
  out MCUCR,rmp ; in Microcontroller Universal Control Port
  sei ; Enable interrupts
;
; **********************************
;    P R O G R A M   L O O P
; **********************************
;
Loop:
  sleep ; Go to sleep
  nop ; Wake up dummy
  sbrc rFlag,bDcf ; DCF flag clear?
  rcall DcfSig ; Count DCF signal
  sbrc rFlag,bKey ; Key flag clear?
  rcall ClearAll ; No, clear all counters
  rjmp loop
;
; A DCF signal came in
DcfSig:
  cbr rFlag,1<<bDcf ; Clear the flag
  ldi XH,High(sCounters+2) ; Point X to sCounters
  ldi XL,Low(sCounters+2)
  ldi ZH,High(2*DcfDurTab) ; Point Z to duration table
  ldi ZL,Low(2*DcfDurTab)
  lpm rmp,Z+ ; Read table value, LSB
  cp rDcfDurL,rmp ; Compare LSB duration
  lpm rmp,Z+ ; Read table value, MSB
  cpc rDcfDurH,rmp ; dto., MSB with carry
  brcs DcfSigCnt ; Increase count
DcfSig1:
  adiw XL,8 ; Next counter
  cpi XL,Low(sCountersEnd) ; End of valid counters?
  breq DcfSigEnd ; Yes, up-count last
  lpm rmp,Z+ ; Read LSB from table
  cp rDcfDurL,rmp ; Compare LSB
  lpm rmp,Z+ ; Read MSB from table
  cpc rDcfDurH,rmp ; Compare MSB and Carry
  brcs DcfSigCnt ; Shorter than minimum
  adiw XL,8 ; Next counter
  cpi XL,Low(sCountersEnd) ; End of valid counters?
  breq DcfSigEnd ; Yes, up-count last counter
  lpm rmp,Z+ ; Read LSB from table
  cp rDcfDurL,rmp ; Compare LSB
  lpm rmp,Z+ ; Read MSB from table
  cpc rDcfDurH,rmp ; Compare MSB and Carry
  brcs DcfSigCnt ; Shorter than maximum plus one, count
  rjmp DcfSig1 ; Next compare
DcfSigEnd:
;
; Count the signal counts one up
DcfSigCnt:
  ld rmp,X ; Read LSB from SRAM
  inc rmp ; Count LSB one up
  st X+,rmp ; Save LSB
  brne DcfSigCnt1 ; No overflow
  ld rmp,X ; Read MSB from SRAM
  inc rmp ; Incrase MSB
  st X,rmp ; Store MSB
DcfSigCnt1:
  adiw XL,1 ; Point to adder byte 1
  ld rmp,X
  add rmp,rDcfDurL
  st X+,rmp
  ld rmp,X
  adc rmp,rDcfDurH
  st X+,rmp
  ldi rDcfDurL,0
  ld rmp,X
  adc rmp,rDcfDurL
  st X+,rmp
  ld rmp,X
  adc rmp,rDcfDurL
  st X+,rmp
  sbiw XL,6 ; Point back to counter
  ; Position on LCD
  mov ZL,XL ; Copy SRAM position to Z
  mov ZH,XH
  andi ZL,0xFC ; Clear bit 1 and 0
  ldi rmp,Low(sCounters)
  sub ZL,rmp
  ldi rmp,High(sCounters)
  sbc ZH,rmp
  ldi ZH,1 ; Line 2 of the LCD
  lsr ZL ; Divide counter by two
  cpi ZL,20 ; End of line 2?
  brcs DcfSigCnt2
  subi ZH,-2 ; Add two to line number
  subi ZL,20 ; Subtract one line length
DcfSigCnt2:
  rcall LcdPos ; Set LCD position
  ld rBinL,X+ ; Read counter value to binary
  ld rBinH,X
  ldi XH,High(10000) ; Check out of decimal range
  ldi XL,Low(10000)
  cp rBinL,XL
  cpc rBinH,XH
  brcc DcfSigOvf
  set ; Set the T flag
  ldi XH,High(1000) ; Count the thousands
  ldi XL,Low(1000)
  rcall DecDigit ; Calculate and write the decimal digit
  ldi XH,High(100)
  ldi XL,Low(100)
  rcall DecDigit
  ldi XH,High(10)
  ldi XL,Low(10)
  rcall DecDigit
  ldi rmp,'0'
  add rmp,rBinL ; The last decimal digit
  rjmp LcdChar
DcfSigOvf:
  ldi rmp,'9' ; Write 9999 to LCD
.if Debug_SkipLcd == No
  rcall LcdChar
  ldi rmp,'9'
  rcall LcdChar
  ldi rmp,'9'
  rcall LcdChar
  ldi rmp,'9'
  rjmp LcdChar
  .else
  ret
  .endif
;
; Count the decimal digit and write it to LCD
;   Binary in rBinH:rBinL
;   Decimal in X
;   T flag enables leading zero suppression
DecDigit:
  ldi rmp,0xFF
DecDigit1:
  inc rmp
  sub rBinL,XL ; Subtract decimal from binary
  sbc rBinH,XH
  brcc DecDigit1
  add rBinL,XL ; Undo last subtraction
  adc rBinH,XH
  brtc DecDigit3 ; Do not suppress leading zeroes
  tst rmp ; Zero?
  brne DecDigit2 ; Not zero, stop suppression
  ldi rmp,' ' ; Display a blank
.if Debug_SkipLcd == No
  rjmp LcdChar
  .else
  ret
  .endif
DecDigit2:
  clt ; Stop suppression
DecDigit3:
  subi rmp,-'0' ; Add ASCII zero
.if Debug_SkipLcd == No
  rjmp LcdChar
  .else
  ret
  .endif
;
; Table with the DCF77 counter values
DcfDurTab:
  .dw cDcfSp ; Spurious signal, 0..3
  .dw cDcf0Min, cDcf0Max1 ; 4..11
  .dw cDcf1Min, cDcf1Max1 ; 12..19
  .dw cDcfPMin, cDcfPMax1 ; 20..27
  .dw cDcfMMin, cDcfMMax1 ; 28..35
DcfDurTabEnd:
;
; Add strings to SRAM
Str2Sram:
  ldi XH,High(sCounters)
  ldi XL,Low(sCounters)
  ldi ZH,High(2*StrTab)
  ldi ZL,Low(2*StrTab)
Str2Sram1:
  lpm rmp,Z+
  st X+,rmp
  lpm rmp,Z+
  st X+,rmp
  adiw XL,6
  cpi XH,High(sCountersEnd)
  brne Str2Sram1
  cpi XL,Low(sCountersEnd)
  brne Str2Sram1
  ret
;
; Table with the text strings in SRAM
StrTab:
  .db "Sp<0 0<1 1<P P<M M>M"
;
; The clear all key has been pressed
ClearAll:
  cbr rFlag,1<<bKey ; Clear the key flag
  ; Write SRAM content to EEPROM
  clr ZL ; EEPROM address, LSB
  clr ZH ; dto., MSB
  ldi XH,High(sCounters) ; X points to Sram counters, MSB
  ldi XL,Low(sCounters) ; dto., LSB
Copy2Eep:
  ; Wait for EEPROM programming clear
  sbic EECR,EEPE ; Wait for EEPE bit clear
  rjmp Copy2Eep ; Not yest
  ldi rmp,(0<<EEPM1)|(0<<EEPM0) ; Atomic Write mode
  out EECR,rmp  ; to EEPROM control
  out EEARH,ZH ; Set write address, MSB
  out EEARL,ZL ; dto., LSB
  adiw ZL,1 ; Next address
  ld rmp,X+ ; Read Sram byte
  out EEDR,rmp ; to data register
  cli ; Do not interrupt
  sbi EECR, EEMPE ; Write program mode enable
  sbi EECR, EEPE ; Write program enable
  sei ; Interrupts allowed again
  cpi XL,Low(sCountersEnd) ; End of the Sram?
  brne Copy2Eep ; No, continue
Copy2EepEnd:
  sbic EECR,EEPE ; Wait for the last EEPE bit clear
  rjmp Copy2EepEnd ; Not yest
; Clear the SRAM counters
ClearOnly:
  ldi XH,High(sCounters) ; Point X to counters, MSB
  ldi XL,Low(sCounters) ; dto., LSB
  clr rmp ; Zero to all registers
ClearAll1:
  st X+,rmp ; Clear counter
  cpi XL,Low(sCountersEnd) ; All counters cleared?
  brne ClearAll1 ; Still counters to be cleared
  rcall Str2Sram ; Add strings to SRAM
.if Debug_SkipLcd == No
  ldi ZH,0
  ldi ZL,0
  rcall LcdPos
  ldi ZH,High(2*Text_Template) ; Z to text table
  ldi ZL,Low(2*Text_Template)
  rcall LcdText ; Display the complete text
  .endif
WaitKey:
  ; Wait for key release
  ldi ZH,High(16666) ; For 100 ms delay after last key event
  ldi ZL,Low(16666) ;  (See below for the formula)
WaitKey1:
  sbis pDcfKeyI,bKeyI ; Read key input pin, +2 = 2
  rjmp WaitKey ; Key input low, restart toggle counter
  sbiw ZL,1 ; Count toggle counter down, +2 = 4
  brne WaitKey1 ; Wait o, +1/2 = 5/6
  ; Time required:
  ;   clocks = 6 * (Z - 1) + 5 = 6 * Z - 1
  ;   Z = (clocks + 1) / 6
  ;   clocks = 100,000 for 100 ms
  ;   Z = 8334
  clr rFlag ; Clear the flag
  clr rmp
  out TCNT1H,rmp ; Clear the signal counter
  out TCNT1L,rmp
  ret

;
; *****************************************
;      T E X T   T E M P L A T E S
; *****************************************
;
; Initial text,  20 chars per line
Text_Intro:
  .db " DCF77 signal check ",0x0D,0xFF
  .db "  tn24_lcd module   ",0x0D,0xFF
  .db "                    ",0x0D,0xFF
  .db "   (C)2019 DG4FAC   ",0xFE,0xFF
;      01234567890123456789
;
Text_Template:
  .db "  Sp  <0   0  <1   1",0x0D,0xFF
  .db "   0   0   0   0   0",0x0D,0xFF
  .db "  <P   P  <M   M  >M",0x0D,0xFF
  .db "   0   0   0   0   0",0xFE,0xFF
;      01234567890123456789
;
; LCD routines include
.include "lcd.inc"
;
; End of source code



Praise, error reports, scolding and spam please via the comment page to me.

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