Pfad: Home => AVR-DE => Anwendungen => Groß-Uhr m48 => asm-Code
Großuhr-7-Segment-Anzeige AVR-Anwendungen

Groß-Uhr mit ATmega48
Assembler Software für die Groß-Uhr
Logo

Assembler-Quellcode für die Groß-Uhr mit ATmega48

Der Original-Assembler-Quellcode ist hier.

;
; *********************************
; * Grossuhr mit ATmega48         *
; * (C)2019 avr-asm-tutorial.net  *
; *********************************
;
.nolist
.include "m48adef.inc" ; Define device ATmega48A
.list
;
; **********************************
;    D E B U G   S W I T C H E S
; **********************************
;
; Switches debug options on/off
;   Make sure that all switches are off in the final version
;
.equ Yes = 1 ; For debug switches
.equ No = 0
;
; Debug the green led
.equ Debug_ledgreen = No ; Debug the green led only
;
; Debug the leds on start-up
.equ Debug_leds = No ; Debug the leds on start-up
;
; Debug the currents and blink the green led
.equ Debug_current = No ; Switch the current drivers on
;
; Debug the segments of the display
.equ Debug_segments = No ; Switch the segments on
  ; 1 = 9 seconds for all four digits
  ; 10 = slow, 80 seconds for all four digits
  ; 100 = extremely slow, 25 seconds per digit
  .equ cDebug_segDelay = 100 ; Delay the active segment
;
; Debug the muxing
.equ Debug_mux8 = No ; Multiplex the four displays
  ; The mux frequency for all four digits once
  ; Must be between 4 and 10,000 Hz
  .equ cDebug_muxfreq = 150 ; MUX frequency in Hz
;
; Debug the ADC results
.equ Debug_adc = No ; Displays the ADC results
;
; Debug the two keys
.equ Debug_keys = No ; Displays the key status
;
; Debug errors in DCF signal reception
;   Displays E and error number instead of hours
.equ Debug_dcferr = No ; Yes/No
.equ Debug_dcfany = No ; Displays any signals
.equ Debug_dcfdur = No ; Displays signal durations
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Device: ATmega48A, Package: 28-pin-PDIP-S
;
;           __________
;        1 /          |28
;  Res o--|RESET   PC5|--o DCF77-In
;    a o--|PD0     PC4|--o Key1-In
;    b o--|PD1     PC3|--o Key2-In
;    c o--|PD2     PC2|--o Led green cathode
;    d o--|PD3     PC1|--o Optosensor
;    e o--|PD4     PC0|--o Pot
;  +5V o--|VCC     GND|--o 0V
;   0V o--|GND    AREF|--o 100nF
;   X1 o--|PB6    AVCC|--o +5V
;   X2 o--|PB7     PB5|--o SCK
;    f o--|PD5     PB4|--o MISO
;    g o--|PD6     PB3|--o MOSI+A4
;    h o--|PD7     PB2|--o A3
;   A1 o--|PB0     PB1|--o A2
;       14|___________|15
;

; **********************************
;  P O R T S   A N D   P I N S
; **********************************
;
.equ p7SegO = PORTD ; Seven-segment output port
.equ p7SegD = DDRD ; Seven-segment direction port
.equ pAnodeO = PORTB ; Anode driver output port
.equ pAnodeD = DDRB ; Anode driver direction port
.equ pLedGO = PORTC ; Green led output port
.equ pLedGD = DDRC ; Green led direction port
.equ pLedGI = PINC ; Green led input port
.equ bLedGO = PORTC2 ; Green led portbit output
.equ pDcfKeyO = PORTC ; DCF and key port output port
.equ pDcfKeyD = DDRC ; DCF and key direction port
.equ pDcfKeyI = PINC ; DCF&Key input port
.equ bDcfI = PINC5 ; DCF77 input pin
.equ bKey1I = PINC4 ; Key1 input portpin
.equ bKey2I = PINC3 ; Key2 input portpin
;
; **********************************
;   A D J U S T A B L E   C O N S T
; **********************************
;
; Frequency of the external xtal
;   has to be dividable by 2/4, 8 and 256 (=4096/8192)
;   without any fractional remainder
.equ xtal = 4096000 ; in Hz
;
; Clock prescaler applied
.equ cClkDiv = 4 ; Either one, two, four or eight
;
; Start time setting of the clock
;   Packed BCD format
.equ cStartHours = 0x20 ; Start at 20:00 h
.equ cStartMinutes = 0x00
;
; DCF time only (clear display as long as not synced with DCF77)
.equ cDcfOnly = No ; Display/Clear unsynced time
;
; DCF signal durations in ms
.equ dcfdur_personally = No ; No = set default durations
;
.if dcfdur_personally != Yes
  .equ tDcf0 = 100 ; 100 ms for a 0
  .equ tDcf1 = 200 ; 200 ms for a 1
  .equ tDcfP = 850 ; Pause for 0 and 1 to next second
  .equ tDcfM = 1850 ; Pause for 59th second pulse
  .equ tDcfT = 3000 ; Time-out of DCF signal
   ; DCF77 signal duration tolerance
  .equ cDcfTol = 15; Tolerance in %
  .else
  .equ tDcf0 = 80 ; for too short signals
  .equ tDcf1 = 190 ; 200 ms for a 1
  .equ tDcfP = 850 ; Pause for 0 and 1 to next second
  .equ tDcfM = 1850 ; Shorter period for 59th second pulse
  .equ tDcfT = 3000 ; Time-out of DCF signal
  ; DCF77 signal duration tolerance
  .equ cDcfTol = 25; Tolerance in %
  .endif
;
;
; DCF77 signal input pull-up resistor
.equ cDcfPullUp = No ; Yes or no
;
; The anode line to which the double point in the
;   middle is attached to
.equ cAnDp = 3 ; Should be between 1 and 4
;
; Key bouncing period
.equ tBounce = 50 ; Bouncing time, in ms
;
; Skip input mode after inactive time
.equ cSkipInpMinutes = 10 ; Minutes until input has to be finished
;
; Select the dimming source
;   0: Selects the potentiometer
;   1: Selects the opto sensor
.equ cDimOpto = No ; Select the source
;
; **********************************
;  F I X  &  D E R I V.  C O N S T
; **********************************
;
; Clock divider conversion
.if cClkDiv == 1
  .equ cClkPr = 0
  .else
  .if cClkDiv == 2
    .equ cClkPr = 1
    .else
    .if cClkDiv == 4
      .equ cClkPr = 2
      .else
      .if cClkDiv == 8
        .equ cClkPr = 3
        .else
        .error "cClkDiv has illegal value!"
        .endif
      .endif
    .endif
  .endif
;
; Clock frequency
.equ clock=xtal/cClkDiv ; Define clock frequency
;
; Half seconds and minute dividers
.equ cTc0Prsc = 8 ; TC0 prescaler
.equ cTc0Frq = clock / cTc0Prsc / 256 ; TC0 int frequency
.equ cSec2 = (cTc0Frq+1) / 2 ; Half second counter
.equ c75pcon = cSec2 / 4 ; Period over which the
   ; selected digits are displayed when input is
   ; active
   .if c75pcon>255
     .error "Off period too long, reduce c75pcon!" 
	 .endif 
;
; DCF signal counts, with rounding
.equ cTc1Tick = (clock+512) / 1024 ; Timer TC1 tick in Hz, @4.096 MHz = 4000 Hz 
.equ cDcf0Min = ((tDcf0-tDcf0*cDcfTol/100)*cTc1Tick+500)/1000 ; Count 0 minimum
.equ cDcf0Max = ((tDcf0+tDcf0*cDcfTol/100)*cTc1Tick+500)/1000+1 ; Count 0 maximum
.equ cDcf1Min = ((tDcf1-tDcf1*cDcfTol/100)*cTc1Tick+500)/1000 ; Count 1 minimum
.equ cDcf1Max = ((tDcf1+tDcf1*cDcfTol/100)*cTc1Tick+500)/1000+1 ; Count 1 minimum
.equ cDcfPMin = ((tDcfP-tDcfP*cDcfTol/100)*cTc1Tick+500)/1000 ; Count pause minimum
.equ cDcfPMax = ((tDcfP+tDcfP*cDcfTol/100)*cTc1Tick+500)/1000+1 ; Count pause maximum
.equ cDcfMMin = ((tDcfM-tDcfM*cDcfTol/100)*cTc1Tick+500)/1000 ; Count minute minimum
.equ cDcfMMax = ((tDcfM+tDcfM*cDcfTol/100)*cTc1Tick+500)/1000+1 ; Count minute maximum
.equ cDcfT = (tDcfT*cTc1Tick+500)/1000+1 ; Counter time out
.if cDcfT>65535
  .error "Clock frequency too high for DCF duration counting"
  .endif
.if (cDcf0Max>=cDcf1Min)||(cDcf1Max>=cDcfPMin)||(cDcfPMax>=cDcfMMin)
  .error "Overlapping DCF duration(s), reduce cDcfTol!"
  .endif
;
.if cDcfPullUp == Yes
 .equ mDcfKeyO = (1<<PORTC5)|(1<<PORTC4)|(1<<PORTC3) 
 .else
 .equ mDcfKeyO = (1<<PORTC4)|(1<<PORTC3)
 .endif
;
; Key bouncing constant
.equ cTc0Presc = 64 ; TC0 prescaler value
.equ cTc0Mux = clock / cTc0Presc / 256 ; MUX interrupt frequency
.equ cBounce = (tBounce*cTc0Mux+500)/1000 ; Bounce constant
;
; **********************************
;             C L O C K S
; **********************************
;
; Default xtal frequency 4.096 MHz, CLKPR=4, effective clock=1.024 MHz
;
; TC0:
;   Clocked with a prescaler of 8, clock tick = 128 kHz @ 1.024 MHz
;   Fast PWM counting, TOP = 0xFF, overflow int = 500 Hz (2 ms) @ 1.024 MHz
;     MUX-frequency = 500 / 4 = 125 Hz
;   16-Bit-Register downcount from 1,000, yields 2 Hz signal for blinking the double dot
;     If zero: Set bSec flag, register downcount from 120 to yield minute for clock,
;       If zero: Set bMin flag
;   If rBounce not at zero:
;     Both key inputs high: downcount rBounce, otherwise rBounce = cBounce
;   Timer interrupt on overflow 
;   Compare match A: OCR0A interrupt (clears anode driver for dimming the LEDs)
; TC1:
;   Counts the duration of DCF77 signals
;     Clocked with a prescaler of 1,024 = 1 kHz (1.0 ms) @ 1.024 MHz
;   Normal counting (cleared by PCINT on DCF signal input)
;   Timer interrupt on compare match A: sets the bDcfTO flag
; ADC:
;   Converts measurements on ADC0/ADC1 inputs
;   Clock prescaler = 128
;   N clock cycles per conversion = 13
;   Conversion frequency = 615.38 Hz (1.625 ms) @ 1.024 MHz
;   Sums up 64 ADC results = 104 ms @ 1.024 MHz
;   If 64 adders complete: Set bAdc flag
;     ca. 1 MUX cycle per update
;     As the TC0 compare match update is done at the beginning
;     of the next mux event (each 2 ms), no missing compare matches
;     occur (no flickering)
;
; **********************************
;       R E G I S T E R S
; **********************************
;
; used: R1:R0 for DCF signal duration and
      ; for hardw multiplication
.def rAdcCtr = R2 ; ADC sum counter
.def rAdcSumL = R3 ; ADC result sum, LSB
.def rAdcSumH = R4 ; dtp., MSB
.def rAdc = R5 ; ADC result MSB
.def rInput = R6 ; Input pins
.def rDcfBits = R7 ; Counter for DCF bits
.def rDcf3 = R8 ; DCF bits, byte 4
.def rDcf4 = R9 ; DCF bits, byte 5
.def rDcf5 = R10 ; DCF bits, byte 6
.def rDcf6 = R11 ; DCF bits, byte 7
.def rDcf7 = R12 ; DCF bits, byte 8
.def rDcfErr = R13 ; DCF77 signal error
.def rMux = R14 ; Mux channel
.def rSreg = R15 ; Save status register
.def rmp = R16 ; Define multipurpose register
.def rimp = R17 ; Multipurpose inside ints
.def rFlag = R18 ; Flag register
  .equ bMin = 0 ; A minute is over
  .equ bSec2 = 1 ; A half second is over
  .equ bDcf = 2 ; Level change on DCF input
  .equ bDcfTO = 3 ; Time out Dcf input signal
  .equ bKey1 = 4 ; Key1 pressed
  .equ bKey2 = 5 ; Key2 pressed
  .equ bKeyA = 6 ; Key input active
  .equ bKeyM = 7 ; Minute key input active
.def rFlag2 = R19 ; Second flag register
  .equ bAdc = 0 ; An ADC result is available
.def rHours = R20 ; Hours time, packed BCD
.def rMinutes = R21 ; Minutes time, packed BCD
.def rBounce = R22 ; Debouncing key counter
.def rMin = R23 ; Minute counter
.def rSec2L = R24 ; 1/2 seconds counter, LSB
.def rSec2H = R25 ; dto., MSB
; used: R27:R26 = X as pointer
; used: R29:R28 = Y for MUX
; used: R31:R30 = Z for multiple purposes outside int
;
; **********************************
;           S R A M
; **********************************
;
.dseg
.org SRAM_START
sMux:
.byte 4 ; 4 bytes for muxing the display
sMuxEnd:
;
sInpTime:
.byte 2 ; 2 bytes input time buffer
;
sSkipInp:
.byte 1 ; Skip input after inactive time
;
; If debug any DCF signals
sDcfPos:
.byte 1 ; Position of the next display (+1)
;
; **********************************
;         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 ; INT1
	reti ; PCI0
	rjmp Pci1Isr ; PCI1 for DCF and key input changes
	reti ; PCI2
	reti ; WDT
	reti ; OC2A
	reti ; OC2B
	reti ; OVF2
	reti ; ICP1
	rjmp Tc1CmpAIsr ; OC1A, Dcf77 time-out
	reti ; OC1B
	reti ; OVF1
	rjmp Tc0CmpAIsr ; OC0A: clear anode driver
	reti ; OC0B
	rjmp Tc0OvfIsr ; MUX and time
	reti ; SPI
	reti ; URXC
	reti ; UDRE
	reti ; UTXC
	rjmp AdcIsr ; ADCC, Conversion complete
	reti ; ERDY
	reti ; ACI
	reti ; TWI
	reti ; SPMR
;
; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************
;
; PCI1 Interrupt service routine
Pci1Isr:
  in rSreg,SREG ; Save SREG
  in rimp,pDcfKeyI ; Read DCF signal and keys
  eor rimp,rInput ; Compare with last input
  sbrc rimp,bDcfI ; DCF77 bit set?
  sbr rFlag,1<<bDcf ; Set DCF flag
Pci1Isr1:
  tst rBounce ; Check bouncing counter
  brne Pci1Isr3 ; Still bouncing, ignore
  ; rInput key bit was 1/0, now is 0/1: EOR bit is one
  sbrs rimp,bKey1I ; Key 1 input changed?
  rjmp Pci1Isr2 ; No, skip
  sbis pDcfKeyI,bKey1I ; Input is one?
  sbr rFlag,1<<bKey1 ; Set key 1 flag
Pci1Isr2:
  sbrs rimp,bKey2I ; Key 2 input changed?
  rjmp Pci1Isr3 ; No, skip
  sbis pDcfKeyI,bKey2I ; Input is one?
  sbr rFlag,1<<bKey2 ; Set key 2 flag
Pci1Isr3:
  in rInput,pDcfKeyI ; Read DCF signal and keys again
  out SREG,rSreg ; Restore SREG
  reti
;
; TC0 overflow interrupt service routine
Tc0OvfIsr:
  in rSreg,SREG ; Save SREG
  clr rimp ; Anodes off
  out pAnodeO,rimp
  ld rimp,-Y ; Read next mux byte
  out p7SegO,rimp ; Cathodes out
  out pAnodeO,rMux ; Set anodes
  lsr rMux ; Next lower anode
  brcc Tc0OvfIsr1 ; Not at the end
  ldi rimp,0b00001000 ; Start with anode 4
  mov rMux,rimp ; in rMux
  ldi YH,High(sMuxEnd) ; Restart from the end
  ldi YL,Low(sMuxEnd)
Tc0OvfIsr1:
  tst rBounce ; Check if bouncing active
  breq Tc0OvfIsr4 ; No, skip
  sbis pDcfKeyI,bKey1I ; Key 1 not pressed?
  rjmp Tc0OvfIsr2 ; No, restart bouncing
  sbic pDcfKeyI,bKey2I ; Key 2 pressed?
  rjmp Tc0OvfIsr3 ; No, decrease bounce count
Tc0OvfIsr2:
  ldi rBounce,cBounce ; Restart bouncing
  rjmp Tc0OvfIsr4 ; Continue ISR
Tc0OvfIsr3:
  dec rBounce ; Down count rBounce
Tc0OvfIsr4:
  sbiw rSec2L,1 ; Decrease seconds divider
  brne Tc0OvfIsr5 ; Not zero, skip
  ldi rSec2H,High(cSec2) ; Restart seconds divider
  ldi rSec2L,Low(cSec2)
  sbr rFlag,1<<bSec2 ; Set half second flag
  dec rMin ; Decrease minute divider
  brne Tc0OvfIsr5 ; Not zero, skip
  ldi rMin,120 ; Restart minute counter divider
  sbr rFlag,1<<bMin ; Set minute flag
Tc0OvfIsr5:
  out SREG,rSreg ; Restore SREG
  reti
;
Tc1CmpAIsr:
  in rSreg,SREG ; Save SREG
  sbr rFlag,1<<bDcfTO ; Set DCF time-out flag
  out SREG,rSreg ; Restore SREG
  reti
;
; TC0 Compare A Interrupt service routine
Tc0CmpAIsr:
  ldi rimp,0 ; Clear anode driver
  out pAnodeO,rimp ; in anode port
  reti
;
; ADC conversion complete interrupt service routine
AdcIsr:
  in rSreg,SREG ; Save SREG
  lds rimp,ADCL ; Read LSB result
  add rAdcSumL,rimp ; Add to LSB sum
  lds rimp,ADCH ; Read MSB
  adc rAdcSumH,rimp ; Add this and carry to MSB sum 
  dec rAdcCtr ; Decrease counter
  brne AdcIsr1 ; Not zero
  sbr rFlag2,1<<bAdc ; Set flag
  out SREG,rSreg ; Restore SREG
  reti
AdcIsr1:
  ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  sts ADCSRA,rimp ; Restart ADC
  out SREG,rSreg ; Restore SREG
  reti
;
; **********************************
;  M A I N   P R O G R A M   I N I T
; **********************************
;
Main:
  ; Init stack
	ldi rmp,High(RAMEND)
	out SPH,rmp ; Init MSB stack pointer
	ldi rmp,Low(RAMEND)
	out SPL,rmp ; Init LSB stack pointer
  ; Init clock prescaler
  ldi rmp,1<<CLKPCE ; Activate clock prescaler
  sts CLKPR,rmp ; in clock prescaler port
  ldi rmp,cClkPr ; Set new prescaler
  sts CLKPR,rmp ; in clock prescaler port
;
; ******************************
;  H A R D W A R E   D E B U G
; ******************************
;
; Debug hardware options
;
; Debug the current drivers
.if Debug_current == Yes
  ldi rmp,0xFF ; All driver pins as output
  out p7SegD,rmp ; in direction port
  out p7SegO,rmp ; and activated
  .endif
;
; Blink the green LED
.if (Debug_ledgreen == Yes)||(Debug_current == Yes)
    sbi pLedGD,bLedGO ; enable output
  Debug_ledgreen1:
    rcall ToggleGreen
  Debug_ledgreen2:
    sbiw ZL,1
    brne Debug_ledgreen2
    rjmp Debug_ledgreen1
  .endif
;
; Debug the segments
.if Debug_segments == Yes
  ; Hint: uses rHours as anode driver and
  ; rMinutes as cathode driver
    ldi rmp,1<<bLedGO ; Green led as output
	out pLedGD,rmp ; Set portpin direction
    rcall ToggleGreen ; Toggle the green led
    ldi rmp,0xFF ; All cathode pins as outputs
    out p7SegD,rmp ; in direction port
    clr rmp ; All cathodes off
    ldi rmp,0b00001000 ; Anode driver 4 as output
    out pAnodeD,rmp ; in direction port
    clr rmp ; Anode drivers off
    out pAnodeO,rmp ; in anode port
  Debug_seg1:
    ; Start with digit 1
    ldi rHours,0x01 ; Start with digit 1 anode
  Debug_seg2:
    ldi rmp,0
	out pAnodeO,rmp
    ldi rMinutes,0x01 ; and with the first segment
  Debug_seg3:
    out p7SegO,rMinutes ; Activate the cathodes
	out pAnodeO,rHours ; and the anode
    ldi rmp,cDebug_segDelay ; Load segment delay counter
  Debug_seg4:
    sbiw ZL,1 ; down-count delay
    brne Debug_seg4 ; until zero
    dec rmp ; Repeat counter
    brne Debug_seg4 ; Additional delay
    lsl rMinutes ; next segment
    brcc Debug_seg3
    lsl rHours
    sbrs rHours,4 ; Bit 4 zero?
    rjmp Debug_seg2 ; No, next digit
    rjmp Debug_seg1 ; Restart with digit 1
  .endif
;
; Debug muxing
.if Debug_mux8 == Yes ; Multiplex the four displays
  ; Wait time for MUX frequency
  ;   Delay of loop is N = 2 + 4 * (c-1) + 3
  ;   c = (N-5) / 4 + 1
  ;   c= (clock/fMux/4-5)/4+1
  .equ cDebug_muxdelay =(clock/cDebug_muxfreq/4-5)/4+1
  ; Hint: uses rHours as anode driver
    ldi rmp,1<<bLedGO ; Green led as output
	out pLedGD,rmp ; Set portpin direction
    rcall ToggleGreen ; Toggle the green led
    ldi rmp,0xFF ; All cathode pins as outputs
    out p7SegD,rmp ; in direction port
    ldi rmp,0xFF ; All cathodes on
	out p7SegO,rmp
    ldi rmp,0b00001000 ; Anode driver 4 as output
    out pAnodeD,rmp ; in direction port
  Debug_mux8a:
	ldi rHours,0x01 ; Anode driver 1 on
  Debug_mux8b:
	out pAnodeO,rHours
    ldi ZH,High(cDebug_muxdelay)
	ldi ZL,Low(cDebug_muxdelay)
  Debug_mux8c:
    sbiw ZL,1
	brne Debug_mux8c
	lsl rHours
	sbrs rHours,4
	rjmp Debug_mux8b
	rjmp Debug_mux8a
  .endif
;
; ********************************
;     N O R M A L   I N I T
; ********************************
;
  ; Init ports
  clr rmp ; Anodes off
  out pAnodeO,rmp ; in Anode port
  ldi rmp,0b00001000 ; Anode 4 as output pin
  out pAnodeD,rmp ; in anode port
  clr rmp ; Outputs cathodes low
  out p7SegO,rmp ; to portpins
  ldi rmp,0xFF ; Port as output
  out p7SegD,rmp ; to direction port
  clr rmp ; DCF and key inputs off
  out pDcfKeyD,rmp ; in DCF and key inputs
  ldi rmp,mDcfKeyO ; Set pull-ups
  out pDcfKeyO,rmp ; in DCF and key pins
  ; Init TC0 for MUX
  ldi rmp,0x01 ; Start with very short dim period
  out OCR0A,rmp
  ldi rmp,(1<<WGM00)|(1<<WGM01) ; Fast PWM mode
  out TCCR0A,rmp
  ldi rmp,(1<<CS01) ; Prescaler=8
  out TCCR0B,rmp
  ldi rmp,(1<<TOIE0)|(1<<OCIE0A) ; Interrupt on overflow and compare A
  sts TIMSK0,rmp
  ; Init TC1
  ldi rmp,High(cDcfT) ;  DCF time-out, MSB
  sts OCR1AH,rmp
  ldi rmp,Low(cDcfT) ; dto, LSB
  sts OCR1AL,rmp
  clr rmp ; CTC mode on compare A
  sts TCCR1A,rmp
  ldi rmp,(1<<CS10)|(1<<CS12)|(1<<WGM12) ; CTC on compare A, presc=1024
  sts TCCR1B,rmp
  ldi rmp,1<<OCIE1A ; Timer int mask for compare A
  sts TIMSK1,rmp ; to int mask port TC1
  ; Init ADC
  ldi rmp,64 ; Start ADC counter
  mov rAdcCtr,rmp ; ... with 64
  ldi rmp,(1<<REFS0) ; MUX to channel ADC0
  sts ADMUX,rmp ; Set channel selection
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  sts ADCSRA,rmp ; Restart ADC
  ; Init flags and other parameters
  clr rFlag
  ldi rSec2H,High(cSec2) ; Init second counter, MSB
  ldi rSec2L,Low(cSec2) ; dto., LSB
  ldi rMin,120 ; Init minute counter
  ldi rHours,cStartHours ; Set initial time, hours
  ldi rMinutes,cStartMinutes ; dto., minutes
  rcall SetTime ; Convert time to display mux
  ldi YH,High(sMuxEnd)
  ldi YL,Low(sMuxEnd)
  ldi rmp,0b00010000
  mov rMux,rmp
  ldi rmp,Low(sMuxEnd) ; DCF output position
  sts sDcfPos,rmp
  ; Init PCINT
  in rInput,pDcfKeyI ; Read inputs
  ldi rmp,(1<<PCINT13)|(1<<PCINT12)|(1<<PCINT11) ; The interrupt generators
  sts PCMSK1,rmp
  ldi rmp,1<<PCIE1 ; PCINT1 enable
  sts PCICR,rmp
  ; Sleep mode
  ldi rmp,1<<SE ; Sleep mode idle
  out SMCR,rmp ; in SMCR
  ; Enable interrupts
	sei ; Enable interrupts
;
; **********************************
;    P R O G R A M   L O O P
; **********************************
;
Loop:
  sleep ; Go to sleep
  nop ; after wake-up
  sbrc rFlag,bDcfTo ; DCF77 time-out clear?
  rcall DcfTimeOut ; Yes, time out
  sbrc rFlag,bDcf ; DCF input level change clear?
  rcall Dcf ; Yes, analyze
  sbrc rFlag,bKey1 ; Key 1 pressed?
  rcall Key1 ; Yes, react
  sbrc rFlag,bKey2 ; Key 2 pressed?
  rcall Key2 ; Yes, react
  sbrc rFlag,bMin ; Minute flag clear?
  rcall Minute ; Set, go to minutes
  sbrc rFlag,bSec2 ; Second flag clear?
  rcall Second ; Set, go to seconds
  sbrc rFlag2,bAdc ; ADC flag clear?
  rcall AdcFlag ; Set, go to ADC conversion
  .if Debug_keys == Yes
    rcall KeyDisplay
	.endif
  rjmp loop ; Restart loop from the beginning
;
; **********************************
;    F L A G   R E A C T I O N S
; **********************************
;
; **********************************
;  H A L F   S E C O N D   O V E R
; **********************************
;
; Half second over, blink double point
Second:
  cbr rFlag,1<<bSec2 ; Clear flag
  lds rmp,sMux+cAnDp-1 ; Read mux byte where double point is attached to
  sbrc rmp,7 ; Seventh bit clear?
  rjmp Second1
  ori rmp,0x80 ; Set seventh bit
  sts sMux+cAnDp-1,rmp ; Bit 7 high to mux
  ret
Second1:
  andi rmp,0x7F ; Clear seventh bit
  sts sMux+cAnDp-1,rmp ; Bit 7 low to mux
  ret
;
; **********************************
;      M I N U T E   O V E R
; **********************************
;
; A minute is over, increase time
Minute:
  cbr rFlag,1<<bMin ; Clear flag
  lds rmp,sSkipInp ; Read skip input time
  tst rmp ; At zero?
  breq Minute0
  dec rmp
  sts sSkipInp,rmp
  brne Minute0
  cbr rFlag,(1<<bKeyA)|(1<<bKeyM) ; Clear input flags
Minute0:
  ldi rmp,0x07 ; Add 7 to BCD
  add rMinutes,rmp ; to minutes
  brhs Minute1 ; Half overflow
  ldi rmp,0x06 ; Subtract 6
  sub rMinutes,rmp ; from minutes
Minute1:
  cpi rMinutes,0x60 ; 60 minutes over?
  brcs SetTime ; Set the time
  clr rMinutes ; Restart at zero
  ldi rmp,0x07 ; Add 7 to BCD
  add rHours,rmp ; to hours
  brhs Minute2 ; Half overflow
  ldi rmp,0x06 ; Subtract 6
  sub rHours,rmp ; from hours
Minute2:
  cpi rHours,0x24 ; Next day?
  brcs SetTime ; No
  clr rHours ; Restart day
;
; **********************************
;      D I S P L A Y   T I M E
; **********************************
;
; Convert the hhmm time and display
SetTime:
  .if cDcfOnly == Yes
    ; Do not update time
    ret
    .endif
SetTime1:
  sbrc rFlag,bKeyA ; Is a key input active?
  ret ; Yes, skip time output
  ldi XH,High(sMux)
  ldi XL,Low(sMux)
  mov rmp,rHours ; Read hours
  rcall Convert2Seven
  mov rmp,rMinutes
  rjmp Convert2Seven
;
; *******************************
;    D C F 7 7   S I G N A L S
; *******************************
;
; DCF77 Time-Out signal input
DcfTimeOut:
  cbr rFlag,1<<bDcfTo ; Clear flag
  .if cDcfOnly == Yes
    ldi rmp,0 ; Clear the MUX area
    sts sMux,rmp
    sts sMux+1,rmp
    sts sMux+2,rmp
    sts sMux+3,rmp
    .endif
  ret
;
; Active DCF signal
Dcf:
  cbr rFlag,1<<bDcf ; Clear flag
  lds XL,TCNT1L ; Read TC1 count, LSB first
  lds XH,TCNT1H ; dto., MSB next
  tst XH ; Check MSB
  brne Dcf1 ; Larger than zero, fine
  cpi XL,4 ; Minimum is 1 ms
  brcc Dcf1 ; Ok
  ret ; Ignore pulse, too short!
Dcf1:
  .if Debug_dcfdur == Yes
    ; Display signal duration in hex
	mov R1,XH ; Copy duration to R1:R0
	mov R0,XL
	ldi XH,High(sMux) ; X to sMux
	ldi XL,Low(sMux)
	mov rmp,R1 ; MSB first
	rcall Convert2Seven ; Write first two nibbles
	mov rmp,R0 ; LSB next
	rcall Convert2Seven ; Write second two nibbles
	mov XL,R0 ; Copy duration to X again
	mov XH,R1
	.endif
  ldi rmp,0xFF ; Counter for compares
  clr rDcfErr ; Error counter
  ldi ZH,High(2*DcfDur) ; Point Z to value table
  ldi ZL,Low(2*DcfDur)
Dcf2:
  inc rDcfErr ; Next DCF error
  inc rmp ; Next count
  cpi rmp,5 ; Maximum correct count = 4
  brcc DcfErr9 ; Error 9 (Signal too long)
  lpm R0,Z+ ; Read LSB min from table
  cp XL,R0 ; Compare with LSB min
  lpm R0,Z+ ; Read MSB min from table
  cpc XH,R0 ; Compare with MSB min
  brcs DcfError ; Error, signal too short
  lpm R0,Z+ ; Read LSB max from table
  cp XL,R0 ; Compare with LSB min
  lpm R0,Z+ ; Read MSB max from table
  brcc Dcf2 ; Larger than max table value
  cpi rmp,2 ; Zero or one?
  brcc Dcf4 ; No
  ; Received a correct bit
  .if Debug_dcfAny == Yes
    push rmp
    lsr rmp
	ldi rmp,0b01011100
	brcc Dcf2a
	ldi rmp,0b00000100
  Dcf2a:
    rcall DcfReport
	pop rmp
	.endif
  lsr rmp ; Shift counter bit 0 to carry
  ror rDcf7 ; Roll carry into DCF bit buffer
  ror rDcf6
  ror rDcf5
  ror rDcf4
  ror rDcf3
  inc rDcfBits
  rjmp DcfErrClear
Dcf3:
  .if Debug_dcfAny == Yes
    ldi rmp,0b01110011
    rcall DcfReport
    .endif
  rjmp DcfErrClear
Dcf4:
  cpi rmp,4
  brcs Dcf3 ; Pause, ignore
  ; Received a correct minute signal
  inc rDcfErr ; Next error
  ldi rmp,59 ; 59 bits received?
  cp rmp,rDcfBits ; Number of bits
  ldi rmp,0 ; Clear number of bits
  mov rDcfBits,rmp ; in counter register
  brne DcfError ; Next error
  inc rDcfErr ; DCF error 7
  lsr rDcf5 ; Shift Parity2 to carry
  ror rDcf4 ; and into Byte 5
  ror rDcf3 ; Minute 40s to byte 4
  lsr rDcf4 ; Shift hours right
  ror rDcf3 ; Shift parity1 to minutes
  mov rmp,rDcf4 ; Minutes to rmp
  rcall Parity ; Check parity in rmp
  brne DcfError ; Parity odd
  inc rDcfErr ; Next error
  mov rmp,rDcf3 ; Check parity minutes
  rcall Parity ; Check parity in rmp
  brne DcfError ; Parity odd
  ; All checks performed and errorfree
  mov rHours,rDcf4 ; Read hours
  andi rHours,0x3F ; Remove upper two bits
  mov rMinutes,rDcf3
  andi rMinutes,0x7F ; Isolate minutes
  rcall DcfErrClear
  rjmp SetTime1 ; Display time
;
; DCF signal errors
;   0: No error
;   1: Signal shorter than 0
;   2: Signal shorter than 1
;   3: Signal shorter than pause
;   4: Signal shorter than missing second
;   5: Signal longer than missing second
;   6: Not 59 seconds received
;   7: Minute parity is odd
;   8: Hour parity is odd
;   9: Time out of signal input
;
DcfErr9:
  ; Error 9: signal too long
  ldi rmp,9
  mov rDcfErr,rmp
DcfError:
.if Debug_dcferr==Yes
    ldi XH,High(sMux)
    ldi XL,Low(sMux)
    mov rmp,rDcfErr
    ori rmp,0xE0 ; Error sign
    rcall Convert2Seven
    clr rmp ; Clear the last two digits
    st X+,rmp
    st X+,rmp
  .endif
  ret
;
; Clear the DCF error number
DcfErrClear:
  .if Debug_dcferr == Yes
    ldi rmp,0
    sts sMux+1,rmp
	.endif 
  ret
;
; Display DCF report in rmp
.if Debug_dcfAny == Yes
  DcfReport:
    ldi XH,High(sMux)
    lds XL,sDcfPos
    st -X,rmp
    cpi XL,Low(sMux)
    brne DcfReport1
    ldi XL,Low(sMuxEnd)
  DcfReport1:
    sts sDcfPos,XL
    ret
  .endif
;
; Check parity of rmp
Parity:
  clr ZL ; ZL is bit counter
Parity1:
  lsr rmp
  brcc Parity2
  inc ZL ; Count
Parity2:
  brne Parity1
  andi ZL,1
  ret
;
; DCF77 signal durations
DcfDur:
.dw cDcf0Min,cDcf0Max
.dw cDcf1Min,cDcf1Max
.dw cDcfPMin,cDcfPMax
.dw cDcfMMin,cDcfMMax
DcfDurEnd:
;
; *******************************
;   K E Y   P R O C E S S I N G
; *******************************
;
; Key1 is pressed
Key1:
  cbr rFlag,1<<bKey1 ; Clear flag
  ldi rmp,0b10101010
  sts sMux,rmp
  ldi rBounce,cBounce ; Start bouncing period
  sbrc rFlag,bKeyA ; Key input inactive?
  rjmp Key1Active ; Yes, key flag is active
  sbr rFlag,1<<bKeyA ; Set key flag active
  ret
Key1Active:
  sbrc rFlag,bKeyM ; Minute flag clear?
  rjmp Key1Minute ; No, go to minute
  sbr rFlag,1<<bKeyM ; Set M flag
  ldi rmp,24 ; Convert ADC to hours
  mul rmp,rAdc
  ldi XH,High(sInpTime) ; Point to hours input
  ldi XL,Low(sInpTime)
  rcall ToBcd ; Convert binary in rmp to packed BCD
  mov rmp,ZL ; Result to rmp
  ldi XH,High(sMux) ; Hours display
  ldi XL,Low(sMux)
  rjmp Convert2Seven ; Display the selected hours
;
Key1Minute:
  ldi rmp,60 ; Convert ADC to minutes
  mul rmp,rAdc
  ldi XH,High(sInpTime+1) ; Point X to minutes
  ldi XL,Low(sInpTime+1)
  rcall ToBcd ; Convert to BCD
  adiw XL,1 ; Point to behind minutes
  ld rMinutes,-X ; Result to minutes
  ld rHours,-X ; and hours
  ldi rSec2L,Low(cSec2) ; Restart half second divider
  ldi rSec2H,High(cSec2)
  ldi rMin,120 ; Restart minute counter
  cbr rFlag,(1<<bMin)|(1<<bSec2)|(1<<bKeyA)|(1<<bKeyM)
  rjmp SetTime ; Set the current time
;
; Key2 is pressed
Key2:
  cbr rFlag,1<<bKey2 ; Clear flag
  ldi rBounce,cBounce ; Start bouncing period
  sbrs rFlag,bKeyA ; Key input active?
  ret ; No, ignore key
  sbrc rFlag,bKeyM ; Minute active?
  rjmp Key2Minute ; Yes
  cbr rFlag,1<<bKeyA ; Stop input
  rjmp SetTime
;
Key2Minute:
  cbr rFlag,1<<bKeyM ; Return to hour input
  rjmp SetTime
;
; Convert the binary number in R1 to packed BCD in ZL
;   and write result to the X location
ToBcd:
  ldi ZL,-0x10
  ldi rmp,10
ToBcd1:
  subi ZL,-0x10
  sub R1,rmp
  brcc ToBcd1
  add R1,rmp
  add ZL,R1 ; Add the ones
  st X,ZL ; Store packed BCD in SRAM
  ret
;
; Displays the keys
KeyDisplay:
  ldi rmp,0b01011100 ; Small 0
  sbic pDcfKeyI,bKey1I ; Key 1
  ldi rmp,0b00000100 ; Small 1
  sts sMux,rmp
  ldi rmp,0b01011100 ; Small 0
  sbic pDcfKeyI,bKey2I ; Key 1
  ldi rmp,0b00000100 ; Small 1
  sts sMux+1,rmp
  ret
;
; **************************************
;     A D C   R E S U L T   R E A D Y
; **************************************
;
AdcFlag:
  cbr rFlag2,1<<bAdc ; Clear ADC flag
  mov rAdc,rAdcSumH ; Copy MSB sum
  clr rAdcSumL ; Restart sum
  clr rAdcSumH
  ldi rmp,64 ; Restart counter
  mov rAdcCtr,rmp ; into rAdcCtr
.if cDimOpto == Yes
  ldi rmp,(1<<REFS0)|(1<<MUX0) ; Mux to channel ADC1
  sbrc rFlag,bKeyA ; Clock setting active?
  ldi rmp,(1<<REFS0) ; Yes, MUX to channel ADC0
  .else
  ldi rmp,(1<<REFS0) ; MUX to channel ADC0
  .endif
  sts ADMUX,rmp ; Set new channel selection
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  sts ADCSRA,rmp ; Restart ADC
.if Debug_Adc == Yes
  rjmp AdcOut ; Display the ADC result
  .endif
  sbrc rFlag,bKeyA ; Key input active?
  rjmp AdcFlag2 ; Update the current input digit
  .if cDimOpto == Yes
    com rAdc ; Invert result
	.endif
AdcFlag1:
  out OCR0A,rAdc ; Write result to compare port TC0
    ; for dimming
  ret
AdcFlag2:
  tst rSec2H ; MSB larger than 0?
  brne AdcFlag3 ; Yes, display number
  cpi rSec2L,c75pcon ; LSB smaller than 75%
  brcc AdcFlag3
  ; Switch the two digits off
  ldi XH,High(sMux) ; Point X to hours
  ldi XL,Low(sMux)
  sbrc rFlag,bKeyM ; Minute key clear?
  ldi XL,Low(sMux+2) ; No, point to minutes
  clr rmp ; Write zeroes to digit
  st X+,rmp
  st X,rmp
  ret
AdcFlag3:
  ; Key input is active, calculate digit
  ldi rmp,24 ; Multiply ADC result by 24
  sbrc rFlag,bKeyM ; Minute flag clear?
  ldi rmp,60 ; Multiply ADC result by 60
  mul rmp,rAdc ; Hardware multiplication
  ; Convert MSB result to packed BCD 
  ldi ZL,-0x10 ; Start with minus 10
  ldi rmp,10
AdcFlag4:
  subi ZL,-0x10 ; Add ten to result
  sub R1,rmp ; Subtract 10 from ADC MSB
  brcc AdcFlag4 ; If not carry continue subtracting
  add R1,rmp ; Undo last subtraction
  add ZL,R1 ; Add rest to result
  mov rmp,ZL
  ldi XH,High(sMux) ; Point X to hours
  ldi XL,Low(sMux)
  sbrc rFlag,bKeyM ; Minute key clear?
  ldi XL,Low(sMux+2) ; No, point to minutes
  rjmp Convert2Seven ; Convert to display
;
; Display the ADC results
AdcOut:
  ldi XH,High(sMux) ; Point X to sMux
  ldi XL,Low(sMux)
  ldi rmp,0b01110111 ; A sign
  st X+,rmp ; to hour tens
  mov R1,rAdc ; Move the rAdc to R1
  ldi rmp,100 ; Hundreds
  rcall AdcOutDec ; Count hundreds
  ldi rmp,10
  rcall AdcOutDec
  mov R0,R1 ; Display the rest
  rjmp AdcOutDec2
;
; Convert R1 to a decimal and display
;   rmp is the decimal (100, 10)
;   Uses R0
AdcOutDec:
  clr R0
  dec R0
AdcOutDec1:
  inc R0
  sub R1,rmp
  brcc AdcOutDec1
  add R1,rmp
; Convert R0 to 7segment and display
AdcOutDec2:
  ldi ZH,High(2*SevenSeg)
  ldi ZL,Low(2*SevenSeg)
  add ZL,R0
  ldi rmp,0
  adc ZH,rmp
  lpm rmp,Z
  st X+,rmp
  ret
;
; **************************************
;    B A S I C   S U B R O U T I N E S
; **************************************
;
; Toggles the green led
;   by outputting on the in port
ToggleGreen:
  ldi rmp,1<<bLedGO ; The green led
  out pLedGI,rmp ; Toggle the led
  ret
;
; Convert rmp to 7segment and write result to X
Convert2Seven:
  push rmp ; Save for LSB
  swap rmp
  rcall Convert2SevenDigit
  pop rmp
Convert2SevenDigit:
  andi rmp,0x0F
  ldi ZH,High(2*SevenSeg) ; Load table, MSB
  ldi ZL,Low(2*SevenSeg) ; dto., LSB
  add ZL,rmp ; Add number, LSB
  ldi rmp,0 ; Zero
  adc ZH,rmp ; Add carry
  lpm rmp,Z ; Read from flash
  st X+,rmp
  ret
;
; Seven-segment table
;     ---- a     hgfedcba
;  f |    | b
;     -g--
;  e |    | c
;     ----  d
;
SevenSeg:
.db 0b00111111,0b00000110 ; 0+1
.db 0b01011011,0b01001111 ; 2+3
.db 0b01100110,0b01101101 ; 4+5
.db 0b01111101,0b00000111 ; 6+7
.db 0b01111111,0b01101111 ; 8+9
.db 0b01110111,0b01111100 ; 10(A)+11(b)
.db 0b00111001,0b01011110 ; 12(C)+13(d)
.db 0b01111001,0b01110001 ; 14(E)+15(F)
;
; End of source code
; Copyright
.db "(C)2019 by avr-asm-tutorial.net "
.db "C(2)10 9yba rva-mst-turoai.lrn t"
;



Lob, Tadel, Fehlermeldungen, Genöle und Geschimpfe oder Spam bitte über das Kommentarformular an mich.

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