Path: Home => AVR overview => Applications => DCF77 receivers => Superhet with crystal filter => LC-VCO oscillator =>Source code
DCF77-Empfang Applications of
AVR Single Chip controllers AT90S, ATtiny, ATmega and ATxmega
LC-Oscillator for DCF77-Superhet-Receiver
Logo

4 DCF77-Superhet-Receiver with crystal filter

4.1 Oscillators for the DCF77-Superhet-Receiver

4.1.3 LC-VCO-Oszillator with ATtiny25 controller

4.1.3.4 Assembler source code

This is the assembler source code for the LC-VCO's ATtiny25. The original source code in assembler format is here.

;
; *********************************
; * LC Oscillator with frequency  *
; * regulation via PWM&varicap    *
; * (C)2019 avr-asm-tutorial.net  *
; *********************************
;
.nolist
.include "tn25def.inc" ; Define device ATtiny25
.list
;
; **********************************
;  D E B U G G I N G   S W I T C H
; **********************************
;
.equ Yes = 1 ; Set debug on
.equ No = 0 ; Set debug off
;
; Do not compare, blink in 500 ms
.equ debug_blink500ms = No ; Yes = blink
;
; Blink on measuring on analog compare int
;   Red and green LED blink very fast if comparer
;   int occurs, counting is disabled
.equ debug_blinkaci = No ; Yes = blink
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Device: ATtiny25, Package: 8-pin-PDIP_SOIC
;               _________
;            1 /         |8
;    RESET o--|RESET  VCC|--o +5V
; Xtal osc o--|CLKI   PB2|--o SCK/LED
; PWM out  o--|OC1B   PB1|--o MISO/AIN1
;       0V o--|GND    PB0|--o MOSI/AIN0
;           4 |__________|5
;
; **********************************
;  P O R T S   A N D   P I N S
; **********************************
;
.equ pPwmD = PORTB ; PWM direction port
.equ bPwmD = DDB4 ; PWM direction port pin
.equ pLedO = PORTB ; LED output port
.equ pLedD = DDRB ; LED direction port
.equ bLedO = PORTB2 ; LED output port pin
.equ bLedD = DDB2 ; LED direction port pin
.equ pLedI = PINB ; LED blinking port
.equ bLedI = PINB2 ; LED blinking port pin
;
; **********************************
;   A D J U S T A B L E   C O N S T
; **********************************
;
; Clock rate of external crystal oscillator
.equ clock=8000000 ; Define clock frequency
.equ cOscFreq = 77500 + 32768 ; Frequency of the LC oscillator, Hz
.equ cOscTol = 5 ; Frequency tolerance +/-, Hz
.equ cMinVolt = 2000 ; Minimum voltage of PWM, mV
;
; Use sheet clock in dcf77_lcosc_tn25.ods for the following
.equ cGateTime = 500 ; Gate time for frequency measurement in ms
.equ cPresc = 256 ; Prescaler (from spreadsheet)
.equ cCtcDiv = 125 ; CtcDivider (from spreadsheet)
;
; **********************************
;  F I X  &  D E R I V.  C O N S T
; **********************************
;
; Check clock
.set cClockCorrect = clock==4000000
.set cClockCorrect = cClockCorrect || (clock==4194304)
.set cClockCorrect = cClockCorrect || (clock==4915200)
.set cClockCorrect = cClockCorrect || (clock==5120000)
.set cClockCorrect = cClockCorrect || (clock==6553600)
.set cClockCorrect = cClockCorrect || (clock==7372800)
.set cClockCorrect = cClockCorrect || (clock==8000000)
.set cClockCorrect = cClockCorrect || (clock==16000000)
.if cClockCorrect
  .message "Clock is correct"
  .else
  .error "Incorrect clock setting!"
  .endif
;
; Define frequency measurement constants
.equ cTc0Clk = clock / cPresc ; Define prescaler from clock
.equ cDiv = cTc0Clk / cCtcDiv / 2 ; Register divider
.if cDiv > 256
  .error "cDiv is too large!"
  .endif
.if cCtcDiv == 256
  .equ cCtcCmp = 0
  .else
  .equ cCtcCmp = cCtcDiv-1
  .endif
.equ cDelta = cDiv*(cCtcCmp+1)*cPresc ; Clock calculated
.if cDelta != clock
  .message "Clock divider has division rest, inaccurate second"
  .endif
.equ cMeasFreq = 1000 / cGateTime ; Measuring frequency
.if cMeasFreq < 1
  .error "Measuring gate time too long"
  .endif
;
; Oscillator constants
.equ cOscLow = cOscFreq - cOscTol ; Smallest tolerable frequency
.equ cOscMax = 2*cOscTol + 1 ; Largest tolerable frequency
;
; Minimum voltage of PWM
.equ cMinPwm = (cMinVolt * 256) / 5000 ; Minimum PWM value
;
; **********************************
;       R E G I S T E R S
; **********************************
;
; free: R0 to R14
.def rSreg = R15 ; Save/Restore status port
.def rmp = R16 ; Define multipurpose register
; free: R17 to R29
.def rFlag = R17 ; Flag register
  .equ bOver = 0 ; Measuring cycle is over flag
; free: R18
.def rDiv = R19 ; Register divider
.def rFrq0 = R20 ; Measured frequency result, byte 1
.def rFrq1 = R21 ; dto., byte 2
.def rFrq2 = R22 ; dto., byte 3
.def rCnt0 = R23 ; LSB of 24 bit counter
.def rCnt1 = R24 ; HSB, used as 16 bit counter
.def rCnt2 = R25 ; MSB of 16 bit counter
; free: R26 to R31
;
; **********************************
;           S R A M
; **********************************
;
; No SRAM used
;
; **********************************
;         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 ; INT0
	reti ; PCI0
	reti ; OC1A
	reti ; OVF1
	reti ; OVF0
	reti ; ERDY
	rjmp AciIsr ; ACI
	reti ; ADCC
	reti ; OC1B
	rjmp Oc0AIsr ; OC0A
	reti ; OC0B
	reti ; WDT
	reti ; USI_START
	reti ; USI_OVF
;
; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************
;
; Counts changes of the analog comparer
;   Occurs every 4.5344 us at 110.268 kHz
AciIsr: ; 7 clocks for int and vector jump
  .if debug_blinkaci == Yes
    sbi pLedI,bLedI ; Blink LED
	reti
    .endif
  in rSreg,SREG ; Save SREG, +1 = 8
  inc rCnt0 ; Count LSB, +1 = 9
  brne AciIsr1 ; +1/2 = 10/11
  adiw rCnt1,1 ; Count HSB/MSB, +2 = 12
AciIsr1: ; 11/12 clocks
  out SREG,rSreg ; Restore SREG, +1 = 12/13
  reti ; +4 = 16/17
; Maximum 17 clock cycles
;   17 clock cycles in 4.5344 us = 4 MHz min.
;
; 2 Hz counter interrupt service routine
;   counts for 0.5 seconds and reads
;   counting result
Oc0AIsr: ; 7 clocks for int and vector jump
  in rSreg,SREG ; Save SREG, +1 = 8
  dec rDiv ; Decrease divider, +1 = 9
  brne Oc0AIsr1 ; Not yet zero, +1/2 = 10/11
  sbr rFlag,1<<bOver ; Set bOver flag, +1 = 11
  ldi rDiv,cDiv ; Restart cDiv, +1 = 12
  mov rFrq0,rCnt0 ; Copy counter, +1 = 13
  mov rFrq1,rCnt1 ; +1 = 14
  mov rFrq2,rCnt2 ; +1 = 15
  clr rCnt0 ; Clear counter, +1 = 16
  clr rCnt1 ; +1 = 17
  clr rCnt2 ; +1 = 18
Oc0AIsr1: ; 11/18 clock cycles
  out SREG,rSreg ; Restore SREG, +1 = 12/19
  reti ; +4 = 16/23
;
; **********************************
;  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 I/O ports
  sbi pLedD,bLedD ; Turn LED output on
  sbi pLedO,bLedO ; Turn red LED on
  ; Start TC1 as async PWM
  sbi pPwmD,bPwmD ; Set OC1B as output
  ldi rmp,255 ; Start with the highest PWM stage
  out OCR1B,rmp ; in compare port B
  ldi rmp,255 ; End value for PWM, 8-Bit PWM
  out OCR1C,rmp ; in output compare register C
  ldi rmp,(1<<PLLE)|(1<<LSM) ; Enable PLL in low speed mode
  out PLLCSR,rmp ; in PLL control register
  ; Wait for 100 microseconds
  ;   n = 2+4*(z16-1) + 3
  ;   n = 2+4*z16-4+3 = 4*z+1
  ;   4*z16 = n-1
  ;   z16 = (n-1)/4
  ;   n @ 8MHz = 800
  .equ z16 = (clock/10000+2)/4
  ldi rCnt2,High(z16) ; Wait for 100 us, MSB
  ldi rCnt1,Low(z16) ; dto., LSB
PllWait:
  sbiw rCnt1,1 ; Count down
  brne PllWait ; Wait further
  ldi rmp,(1<<PLLE)|(1<<LSM)|(1<<PCKE) ; and PCK
  out PLLCSR,rmp ; in PLL control register
  ldi rmp,(1<<PWM1B)|(1<<COM1B1) ; PWM B enabled, High to low
  out GTCCR,rmp ; in general timer control register
  ldi rmp,(1<<PWM1A)|(1<<CS12) ; PWM A, High/Low, Prescaler=8
  out TCCR1,rmp ; in TC1 control register
  ; Start TC0 as gate timer
  ldi rDiv,cDiv ; Start software divider
  ldi rmp,cCtcCmp ; Set compare A value
  out OCR0A,rmp ; in compare A
  ldi rmp,1<<WGM01 ; Set CTC mode 3
  out TCCR0A,rmp ; in TC0 control port
  clr rmp
  .if (cPresc == 1) || (cPresc == 64) || (cPresc == 1024)
    sbr rmp,1<<CS00
    .endif
  .if (cPresc == 8) || (cPresc == 64)
    sbr rmp,1<<CS01
    .endif
  .if (cPresc == 256) || (cPresc == 1024)
    sbr rmp,1<<CS02
    .endif
  out TCCR0B,rmp ; to TC0 control port B
  ldi rmp,1<<OCIE0A ; Enable interrupt on compare A
  out TIMSK,rmp ; in timer int mask
; Sleep mode idle
  ldi rmp,1<<SE ; Sleep enable
  out MCUCR,rmp ; in microcontroller control port
  ; Init analog comparer as frequency input
  ldi rmp,(1<<AIN1D)|(1<<AIN0D) ; Disable digital inputs
  out DIDR0,rmp ; in analog disable port register
  ldi rmp,1<<ACIE ; Enable analog comparator interrupts
  out ACSR,rmp ; in analog comparer status register
;
; Enable interrupts
	sei ; Enable interrupts
;
; **********************************
;    P R O G R A M   L O O P
; **********************************
;
Loop:
  sleep ; Go to sleep
  nop ; Delay on wake-up
  sbrc rFlag,bOver ; bOver flag clear?
  rcall Measured ; Frequency measurement
  rjmp Loop
;
; Frequency measurement complete
Measured:
  cbr rFlag,1<<bOver ; Clear flag
.if debug_blink500ms == Yes
  sbi pLedI,bLedI
  ret
  .endif
  ldi rmp,Byte1(cOscFreq) ; Byte 1 of cOscLow
  sub rFrq0,rmp ; Subtract measured frequency, LSB
  ldi rmp,Byte2(cOscFreq) ; Byte 2 of cOscLow
  sbc rFrq1,rmp ; dto., HSB
  ldi rmp,Byte3(cOscFreq) ; Byte 3 of cOscLow
  sbc rFrq2,rmp ; dto., MSB
  brcs MeasuredLow ; Frequency too small, increase
  ldi rmp,Byte1(cOscMax) ; Byte 1 of upper bound
  sub rFrq0,rmp ; Subtract upper bound, LSB
  ldi rmp,Byte2(cOscMax) ; Byte 2 of upper bound
  sbc rFrq1,rmp ; Subtract upper bound, HSB
  ldi rmp,Byte3(cOscMax) ; Byte 3 of upper bound
  sbc rFrq2,rmp ; Subtract upper bound, MSB
  brcc MeasuredHigh ; Frequency too high, decrease
  cbi pLedD,bLedD ; LED off
  ret
MeasuredLow:
  ; Measured frequency too low, increase
  in rmp,OCR1B ; Read compare value
  inc rmp ; Increase compare value
  brne MeasuredSetPwm ; Not the max. value, set PWM
  dec rmp ; Decrease again
  cbi pLedO,bLedO ; LED to yellow
  rjmp MeasuredSetPwm
MeasuredHigh:
  ; Measured frequency too high, decrease
  in rmp,OCR1B ; Read compare value
  dec rmp ; Decrease
  cpi rmp,cMinPwm ; Smaller than minimum PWM
  brcc MeasuredSetPwm ; Not smaller than min., set PWM
  inc rmp ; Increase again
  sbi pLedO,bLedO ; LED to red
MeasuredSetPwm:
  out OCR1B,rmp ; Write new value to TC1 compare B
  sbi pLedD,bLedD ; LED pin as output
  ret
;
; End of source code
;
Copyright:
.db "(C)2019 by Gerhard Schmidt",0,0
.db "C(2)10 9ybG reahdrS hcimtd",0,0


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