Path: Home => AVR overview => Applications => Digital clock => Source code    (Diese Seite in Deutsch: Flag DE) Logo
Digital clock

Digital clock with an ATmega16 - source code


; *********************************************
; * Digital AVR clock with an ATmega16        *
; * (C)2010 by info (at) avr-asm-tutorial.net *
; *********************************************
;
.nolist
.include "m16def.inc"
.list
;
; ===================================================
;      D e b u g g i n g   p a r a m e t e r s
; ===================================================
;   dbg = 0: debugging disabled, normal program execution
;   dbg = 1: light first display
;   dbg = 2: light second display
;   dbg = 3: light third display
;   dbg = 4: light fouth display
;   dbg = 5: display seconds on alarm-minute position
;   dbg = 6: display ADC results in hex on alarm display
;
.equ dbg = 0
;
; ===================================================
;                H a r d w a r e
; ===================================================
;  Processor type: ATmega16
;               _________
;             /          |
;  Key red --|PB0    ADC0|-- Pot input
;  Key blk --|PB1    ADC1|-- Fototransistor input
;  Key wht --|PB2        |--
;  Speaker --|OC0        |--
;          --|        PA4|-- 1 Digit Anode Driver
; ISP MOSI --|MOSI    PA5|-- 2
;     MISO --|MISO    PA6|-- 3
;      SCK --|SCK     PA7|-- 4
;    RESET --|RESET  AREF|-- +5V
;      VCC --|VCC     GND|-- GND
;      GND --|GND    AVCC|-- +5V
;    Xtal2 --|XTAL2   PC7|-- Large LED points
;    Xtal1 --|XTAL1   PC6|-- g Large
;  Small a --|PD0     PC5|-- f seven
;  seven b --|PD1     PC4|-- e segm.
;  segm. c --|PD2     PC3|-- d displ.
;  displ.d --|PD3     PC2|-- c cath.
;  cath. e --|PD4     PC1|-- b
;        f --|PD5     PC0|-- a
;        g --|PD6     PD7|-- Small LED points
;            |___________
;            
; ===================================================
;   D e s c r i p t i o n   h o w   i t   w o r k s
; ===================================================
; 
; a) Displays and display modes
;    The upper, large 7-segment displays show the time,
;    the lower, small 7-segment displays show the alarm
;    time. The larger double-dot LEDs of the upper display
;    blink in second intervals. The smaller double-dot
;    LEDs of the lower display blink, if the alarm is
;    armed, and are always on, if the alarm is disarmed.
; b) Display multiplexing
;    The diplay uses Counter/Timer 1 in CTC mode to
;    interrupt every 5 ms after reaching its top value.
;    The 4 displays are refreshed in 20 ms, yielding a
;    refresh frequency of 50 cs/s.
;    The display is first switched off, the pointer
;    to the displayed digit is advanced and digit infos
;    in SRAM are written to the Ports C (large display
;    for the time) and D (small display for the alarm
;    time), the appropriate anode driver port bit on
;    the upper nibble of Port A is set active (=0).
; c) Display dimming
;    Display dimming uses the TC1 in CTC mode and Compare
;    Match B interrupt to disable the anode drivers. The
;    compare match value is calculated from the ADC value
;    that results from the fototransistor: the less light
;    is on the transistor the higher its collector voltage
;    and the higher its ADC value and the higher the com-
;    pare match B value. Note that this is a non-linear
;    function.
; d) Time counting
;    The time counting uses TC1 in CTC mode and the
;    Compare Match A interrupt to downcount a counter
;    from 200 to zero. If zero is reached, a flag is set
;    and the 5-ms-counter is restarted.
;    Outside the interrupt service routine, the time is
;    advanced by one second, updating the time after 60
;    seconds. If the alarm is armed, the time is compared
;    with the alarm time and an alarm is triggered when
;    time = alarm time.
; e) Keys
;    The keys are read every time an interrupt wake up
;    took place. Active keys are recognised after 15 ms
;    and stored. The respective action is executed after
;    all keys are inactive for at least 15 ms.
;    The following table gives key reactions in different
;    operation modes.
;    Mode          Key    Action
;    ----------------------------------------------------
;    Normal        Black  Toggle armed bit
;                  Red    Enter time setting mode
;                  White  Enter alarm time setting mode
;    Time Setting  Black  Skip time setting mode
;                  Red    Set time (hour or minute)
;    Alarm Setting Black  Skip alarm time setting mode
;                  White  Set alarm time (hour or minute)
;    Armed         Black  Disable armed
;                  Red    Advance alarm time by snooze
;    Alarmed       Black  Disable alarm and reset alarm
;                         time
;                  Red    Disable alarm and advance alarm
;                         time by snooze
; f) AD conversion
;    The ADC runs with a prescaler of 128, measures two
;    channels and is controlled via interrupt.
;    The analogue voltages of the potentiometer (channel
;    ADC0) and of the collector of the fototransistor
;    (channel ADC1) are measured, the left-adjusted upper
;    result byte is added to a 16-bit sum. If 256 measure-
;    ments per channel took place, the MSB of the 16-bit-
;    result is handed to a routine outside the interrupt
;    service routine. The channel results are used to
;    Ch0: if in time or alarm setting mode, converted to
;         hours or minutes and displayed
;    Ch1: convert to compare match B values to set the dim
;         time and reduce the brightness in a dark
;         environment.
; g) Alarm noise
;    TC0 provides a programmable AF generator by setting
;    the timer in CTC mode and toggling the output pin OC0
;    on compare match. The TC0 runs with a prescaler of 8,
;    providing audio signals between 600 (OCR0=255) and
;    9600 (OCR0=16) cs/s.
;    An interrupt on reaching CTC top plays a melody
;    located in the EEPROM space by reading the CTC value
;    and the duration of the tone. After reaching the table 
;    end, the timer and OC0 output toggling is disabled.
;
; ===================================================
;               C o n s t a n t s
; ===================================================
;
.equ clock = 2457600 ; Xtal frequency
.equ cSnooze = 5 ; snooze alarm time duration
.equ cPauseLong = $4000 ; long pause for melody repeat
;
; ===================================================
;               R e g i s t e r s
; ===================================================
;
; R0 used for LPM to flash and for calculations
; R1 used for calculations
; free R2..R8
.def rFChk = R9 ; check for adjusting dimm
.def rAdcC = R10 ; ADC counter
.def rAdcFL = R11 ; ADC result adder fototransistor, low byte
.def rAdcFH = R12 ; ADC result adder fototransistor, high byte
.def rAdcPL = R13 ; Adc result adder potentiometer, low byte
.def rAdcPH = R14 ; Adc result adder potentiometer, high byte
.def rSreg = R15 ; SREG temp inside ints
.def rmp = R16 ; multipurpose outside ints
.def rimp = R17 ; multipurpose inside ints
.def rFlag = R18 ; flag register
	.equ bArmed = 0 ; Alarm is enabled
	.equ bAlarm = 1 ; Alarm is active
	.equ bSetC  = 2 ; Set clock
	.equ bSetCm = 3 ; Set clock minutes
	.equ bSetA  = 4 ; Set alarm
	.equ bSetAm = 5 ; Set alarm minutes
	.equ bSec   = 6 ; next second reached
	.equ bAdc   = 7 ; new ADC result ready
.def rC5ms = R19 ; 5ms counter to seconds
.def rDCnt = R20 ; Display counter anode driver
.def rKey = R21 ; key code storage, last active key
.def rEep = R23 ; EEPROM read address
.def rDurL = R24 ; Duration time counter LSB
.def rDurH = R25 ; Duration time counter MSB
; R27:R26 used for pointing outside ints
; R29:R28 used as display pointer inside ints
; R31:R30 used for pointing outside ints
;
; ===================================================
;           S R A M   l o c a t i o n s
; ===================================================
;
.DSEG
.ORG Sram_Start
sTime:
.byte 4 ; four digit bytes for the large display
sAlarm:
.byte 4 ; four digit bytes for the small display
sCs:
.byte 1 ; clock seconds
sCm:
.byte 1 ; clock minutes
sCh:
.byte 1 ; clock hours
sSm:
.byte 1 ; set alarm minutes
sSh:
.byte 1 ; set alarm hours
sAm:
.byte 1 ; alarm minutes
sAh:
.byte 1 ; alarm hours
sAdcP:
.byte 1 ; Adc result pot
sAdcF:
.byte 1 ; Adc result fototransistor
sKey:
.byte 1 ; pressed key
sKeyC:
.byte 1 ; counter for pressed key
sKeyS:
.byte 1 ; selected key
;
; ===================================================
;                  T i m i n g s
; ===================================================
;
; ADC:
; - MUX channel 0 measures pot, MUX channel 1 measures fototransistor
; - runs at divider = 128
; - is interrupt driven
; - result is left adjust, only the upper 8 bits are used
; - on int, the upper 8 bits of the result are added to a 16-bit-sum
; - after 256 results, the upper 8 bits of the sum are copied
; - when running, one conversion requires 13 ADC clock cycles
; - f = 2457600 / 128 / 13 / 256 = 5.77 cs/s = 173.3 ms
;
; ===================================================
;      R e s e t   a n d   I n t   v e c t o r s 
; ===================================================
;
.cseg
.org $0000
	rjmp start ; Reset vector
	nop
	reti ; INT0
	nop
	reti ; INT1
	nop
	reti ; TC2COMP
	nop
	reti ; TC2OVF
	nop
	rjmp TC1Capt ; TC1CAPT
	nop
	rjmp TC1CompA ; TC1COMPA
	nop
	rjmp TC1CompB ; TC1COMPB
	nop
	reti ; TC1OVF
	nop
	reti ; TC0OVF
	nop
	reti ; SPI, STC
	nop
	reti ; USART RXC
	nop
	reti ; USART UDRE
	nop
	reti ; USART TXC
	nop
	rjmp AdcInt ; ADC
	nop
	reti ; EERDY
	nop
	reti ; ANACOMP
	nop
	reti ; TWI
	nop
	reti ; INT2
	nop
	rjmp Tc0Comp ; TC0COMP
	nop
	reti ; SPM RDY
	nop
;
; ===================================================
; I n t e r r u p t   S e r v i c e   R o u t i n e s
; ===================================================
;
; TC1 ICR period end, display next digit
;
TC1Capt:
	in rsreg,SREG ; save SREG
	ldi rimp,0xF0 ; clear all digit drivers
	out PORTA,rimp
	in rKey,PINB ; read key values
	ori rDCnt,0x08 ; set bit 3
	lsl rDCnt ; shift display counter left
	brcc TC1Capt1 ; end of cycle
	adiw YL,1 ; next digit
	ld rimp,Y ; read next display digit
	out PORTC,rimp ; write to port
	ldd rimp,Y+4 ; read display digit alarm
	out PORTD,rimp ; write to port
	out PORTA,rDCnt ; set active driver
	out SREG,rsreg ; restore SREG
	reti
TC1Capt1:
	ldi YH,HIGH(sTime) ; restart pointer
	ldi YL,LOW(sTime)
	ld rimp,Y ; read first digit
	out PORTC,rimp ; write to port
	ldd rimp,Y+4 ; read first digit alarm time
	out PORTD,rimp ; write to port
	ldi rDCnt,0xE0 ; display starting with bit 4 = 0
	out PORTA,rDCnt
	out SREG,rsreg ; restore SREG
	reti
;
; TC1 Comp A reached
;
TC1CompA:
	in rsreg,SREG ; save SREG
	dec rC5ms ; count down for seconds
	brne TC1CompA1 ; not zero
	sbr rFlag,1<<bSec ; set flag
	ldi rC5ms,200 ; start new
TC1CompA1:
	out SREG,rSreg ; restore SREG
	reti
;
; TC1 Comp B reached
;
TC1CompB:
	ldi rimp,0xF0 ; switch anode drivers off
	out PORTA,rimp
	reti
;
; ADC ready interrupt
;
AdcInt:
	in rsreg,SREG ; save SREG
	in rimp,ADMUX ; low or high byte?
	sbrc rimp,MUX0
	rjmp AdcInt1
	in rimp,ADCH ; read MSB ADC result
	add rAdcPL,rimp ; add to result
	ldi rimp,0 ; add high byte
	adc rAdcPH,rimp
	ldi rimp,(1<<ADLAR)|(1<<MUX0) ; set channel 1
	out ADMUX,rimp
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; start conversion
	out ADCSRA,rimp
	out SREG,rsreg ; restore SREG
	reti
AdcInt1:
	in rimp,ADCH ; read MSB ADC result
	add rAdcFL,rimp ; add to result
	ldi rimp,0 ; add high byte
	adc rAdcFH,rimp
	dec rAdcC ; dec counter
	brne AdcInt2 ; not zero
	sts sAdcP,rAdcPH ; copy result pot
	sts sAdcF,rAdcFH ; copy result fototransistor
	clr rAdcPL ; clear adders
	clr rAdcPH
	clr rAdcFL
	clr rAdcFH
	sbr rFlag,1<<bAdc ; set ADC flag
AdcInt2:
	ldi rimp,1<<ADLAR ; set channel 0
	out ADMUX,rimp
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; start conversion
	out ADCSRA,rimp
	out SREG,rsreg ; restore SREG
	reti
;
; TC0 compare match interrupt
;
Tc0Comp:
	in rSreg,SREG ; save SREG
	tst rDurL ; duration zero?
	brne Tc0Comp0 ; no
	tst rDurH ; MSB zero?
	breq Tc0CompR ; yes, skip
Tc0Comp0:
	sbiw rDurL,1 ; decrease duration counter
	brne Tc0CompR ; not zero, go on
	inc rEep ; increase EEPROM address
	mov rimp,rEep ; copy EEPROM read adress
	lsl rimp ; multiply by 2
	out EEARL,rimp
	ldi rimp,0 ; upper byte
	adc rimp,rimp
	out EEARH,rimp
	ldi rimp,1<<EERE ; read enable EEPROM
	out EECR,rimp
	in rimp,EEDR ; read first byte
	tst rimp ; check for 0
	brne Tc0Comp01
	dec rimp
	out OCR0,rimp ; write to CTC
	ldi rimp,(1<<WGM01)|(1<<COM01)|(1<<CS01) ; clear COM output
	out TCCR0,rimp ; set mode and COM
	rjmp Tc0Comp2
Tc0Comp01:
	out OCR0,rimp ; write to CTC
	ldi rimp,(1<<WGM01)|(1<<COM00)|(1<<CS01) ; toggle COM output
	out TCCR0,rimp ; set mode and COM
Tc0Comp2:
	in rimp,EEARL ; read lower address
	inc rimp ; increase address
	out EEARL,rimp ; set LSB address
	ldi rimp,1<<EERE ; read enable
	out EECR,rimp ; to control register
	in rDurH,EEDR ; read duration
	clr rDurL
	tst rDurH ; check end of melody
	brne Tc0Comp3 ; no, go on
	ldi rimp,(1<<WGM01)|(1<<COM01)|(1<<CS01) ; clear COM output
	out TCCR0,rimp ; set mode and COM
	ldi rimp,0xFF ; long time
	ldi rDurH,High(cPauseLong) ; long duration
	ldi rDurL,Low(cPauseLong)
	ldi rEep,0xFF ; restart melody after pause
	rjmp Tc0CompR
Tc0Comp3:
	lsr rDurH
	ror rDurL
	lsr rDurH
	ror rDurL
	lsr rDurH
	ror rDurL
	lsr rDurH
	ror rDurL
	lsr rDurH
	ror rDurL
Tc0CompR:
	out SREG,rSreg ; restore SREG
	reti
;
; ===================================================
;        M a i n   p r o g r a m   i n i t
; ===================================================
;
Start:
	; init stack
	ldi rmp,HIGH(RAMEND) ; init the MSB of the stack pointer
	out SPH,rmp
	ldi rmp,LOW(RAMEND) ; init the LSB of the stack pointer
	out SPL,rmp
	; init ports
	ldi rmp,0xFF ; set Ports C and D to be outputs and off
	out DDRC,rmp
	out DDRD,rmp
;	out PORTC,rmp
;	out PORTD,rmp
	ldi rmp,0xF0 ; set port A upper four bits to be outputs and off
    out DDRA,rmp
	ldi rmp,0xE0
	out PORTA,rmp
	ldi rmp,0x00
	out PORTC,rmp
	out PORTD,rmp
	; test display
	.if dbg==1
	ldi rmp,0xE0
	out PORTA,rmp
ex1:	rjmp ex1
	.endif
	.if dbg==2
	ldi rmp,0xD0
	out PORTA,rmp
ex2: rjmp ex2
	.endif
	.if dbg==3
	ldi rmp,0xB0
	out PORTA,rmp
ex3: rjmp ex3
	.endif
	.if dbg==4
	ldi rmp,0x70
	out PORTA,rmp
ex4: rjmp ex4
	.endif
    ; end of test
	ldi rmp,0x08 ; set port B bits 0..2 to be input, 3 to output
	out DDRB,rmp
	ldi rmp,0x07 ; set pull-ups on key inputs active
	out PORTB,rmp
	; init display drivers, set all displays to 0
	ldi YH,HIGH(sCs) ; point to time info start
	ldi YL,LOW(sCs)
	clr R0
	ldi rmp,7 ; clear time in SRAM
Start1:
	st Y+,R0
	dec rmp
	brne Start1
	rcall UpDateTime ; set up time display
	rcall UpDateAlarm ; set up alarm display
	out PORTC,rmp ; display ports
	out PORTD,rmp
	ldi rDCnt,0x70 ; set last display to driver
	ldi YH,HIGH(sTime+3) ; point to display start
	ldi YL,LOW(sTime+3)
	clr rFlag ; set flags to zero
	clr rmp ; disable watchdog
	out WDTCR,rmp
	; init ADC
	ldi rmp,1<<ADLAR ; Left adjust result, channel 0, external VREF
	out ADMUX,rmp
	clr rmp ; set SROR
	out SFIOR,rmp
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; enable ADC
	out ADCSRA,rmp ; 
	ldi rmp,1 ; set dimm checking on first ADC cycle
	mov rFChk,rmp
	; init timer 0
	clr rDurL ; set duration zero to block timer
	clr rDurH
	ldi rmp,175 ; set compare value to 175 (880 cs/s)
	out OCR0,rmp
	ldi rmp,(1<<WGM01)|(1<<COM00) ; stop alarm timer
	out TCCR0,rmp ; set mode and COM
	; init timer 1 as clock and digit driver
.equ divider = (5 * clock + 500) / 1000 ; 5 ms time int
	ldi rmp,HIGH(divider) ; set compare match ICR1
	out ICR1H,rmp
	ldi rmp,LOW(divider)
	out ICR1L,rmp
	ldi rmp,HIGH(divider / 2) ; set compare match A as 5-ms-int
	out OCR1AH,rmp
	ldi rmp,LOW(divider / 2)
	out OCR1AL,rmp
	ldi rmp,HIGH(divider - 2) ; set compare match B as dim int
	out OCR1BH,rmp
	ldi rmp,LOW(divider - 2)
	out OCR1BL,rmp
	clr rmp ; clear WGM11 and WGM10
	out TCCR1A,rmp
	ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS10) ; ICR-CTC, div = 1
	out TCCR1B,rmp
	; enable timer interrupts
	ldi rmp,(1<<OCIE1A)|(1<<OCIE1B)|(1<<TICIE1)|(1<<OCIE0) ; ICR1-,COMP1-,TC0 int enable
	out TIMSK,rmp
	; set sleep mode
	ldi rmp,1<<SE ; set sleep enable and sleep mode 0
	out MCUCR,rmp
	; enable interrupts
	sei
; ===================================================
;        M a i n   p r o g r a m   l o o p
; ===================================================
Loop:
	sleep ; go to sleep
	nop ; dummy after wakeup
;
	rcall keys ; check keys after every wakeup
    ; Check for flags that have been set
	sbrc rFlag,bSec ; jump over if second flag not set
	rcall Second
	sbrc rFlag,bAdc ; jump over if no new ADC conversion results
	rcall AdcNew
	rjmp Loop
;
; ===================================================
;            C h e c k   k e y s
; ===================================================
;
.equ cKeyRed = 0x06
.equ cKeyBlack = 0x05
.equ cKeyWhite = 0x03
keys:
	andi rKey,0x07 ; isolate keys
	lds rmp,sKey ; read stored key value
	cp rmp,rKey ; compare key value
	breq key1 ; eqal key
	; key different from last key, store new, clear counter
	sts sKey,rKey ; store key
	ldi rmp,0 ; clear count
	sts sKeyC,rmp
	ret
key1: ; key equals last key
	lds rmp,sKeyC ; read key count
	inc rmp ; increase key count
	sts sKeyC,rmp ; store
	cpi rmp,3 ; three identical key codes
	breq key2 ; identical
	brcs key1a
	ldi rmp,5
	sts sKeyC,rmp
key1a:
	ret
key2: ; key valid
	lds rmp,sKey ; read key code
	cpi rmp,0x07 ; key unpressed?
	breq key3 ; yes
	sts sKeyS,rmp ; store selected key
	ret
key3:
	lds rmp,sKeyS ; read selected key
	ldi ZL,0
	sts sKeyS,ZL
	tst rmp
	brne key4
	ret
key4: ; conditional jumps according to flags
	sbrc rFlag,bAlarm ; currently alarm?
	rjmp keyAlarm ; react to key during alarm
	sbrc rFlag,bArmed ; currently armed?
	rjmp keyArmed ; react to key during armed
	sbrc rFlag,bSetC ; currently on setting clock?
	rjmp keySetC ; react to key during clock setting
	sbrc rFlag,bSetA ; currently on setting alarm time?
	rjmp keySetA ; react to key during alarm time setting
	; normal mode, treat as new key
	cpi rmp,cKeyRed ; red key?
	brne key5 ; no
	sbr rFlag,1<<bSetC ; set flag
	ret
key5:
	cpi rmp,cKeyWhite ; white key?
	brne key6 ; no
	sbr rFlag,1<<bSetA ; set flag
	ret
key6:
	cpi rmp,cKeyBlack ; black key?
	brne key7
	ldi rmp,1<<bArmed ; toggle armed flag
	eor rFlag,rmp
	ret
key7:
	ret
keySetC: ; keys during set C
	cpi rmp,cKeyBlack ; black key pressed = skip
	brne keySetC1
	cbr rFlag,(1<<bSetC)|(1<<bSetCM) ; clear flags
	rjmp UpDateTime
keySetC1:
	cpi rmp,cKeyRed ; red key pressed
	brne keySetC3 ; no
	; red key pressed
	sbrs rFlag,bSetCm ; set clock minutes?
	rjmp keySetC2 ; no, set hours
	; set minutes
	clr rmp ; clear seconds
	sts sCs,rmp
	lds rmp,sAdcP
	ldi ZL,60
	mul rmp,ZL
	sts sCm,R1
	cbr rFlag,(1<<bSetC)|(1<<bSetCm) ; clear flags
	rjmp UpDateTime
keySetC2: ; set hours
	lds rmp,sAdcP
	ldi ZL,24
	mul rmp,ZL
	sts sCh,R1
	clr rmp ; clear seconds
	sts sCs,rmp
	sbr rFlag,(1<<bSetC)|(1<<bSetCm) ; set minute flag
	ret
keySetC3: ; illegal key
	ret
keySetA: ; keys during set A
	cpi rmp,cKeyBlack ; black key pressed = skip
	brne keySetA1 ; no
	cbr rFlag,(1<<bSetA)|(1<<bSetAm) ; clear flags
	rjmp UpDateAlarm
keySetA1:
	cpi rmp,cKeyWhite ; white key pressed?
	brne keySetA3 ; no
	; white key pressed
	sbrs rFlag,bSetAm ; set alarm minutes?
	rjmp keySetA2 ; no, set hours
	; set minutes
	lds rmp,sAdcP ; read pot value
	ldi ZL,60
	mul ZL,rmp
	sts sAm,R1 ; set alarm time
	sts sSm,R1 ; set Set alarm time
	cbr rFlag,(1<<bSetA)|(1<<bSetAm) ; clear set alarm flags
	sbr rFlag,1<<bArmed ; set armed flag
	rjmp UpDateAlarm
keySetA2: ; set hours
	lds rmp,sAdcP
	ldi ZL,24
	mul ZL,rmp
	sts sAh,R1 ; set alarm time
	sts sSh,R1 ; set Set alarm time
	sbr rFlag,1<<bSetAm ; set minutes flag
	ret
keySetA3:
	ret
keyAlarm: ; key during alarm
	cpi rmp,cKeyRed ; red key pressed?
	brne keyAlarm1
	; red key during alarm: snooze function
keyAlarmSnooze:
	lds rmp,sAm ; read alarm minutes
	subi rmp,-cSnooze ; add snooze time
	sts sAm,rmp ; and store
	cpi rmp,60 ; above or equal 60 minutes?
	brcs keyAlarmOff ; no, go on
	subi rmp,60 ; subtract 60 minutes
	sts sAm,rmp ; and store
	lds rmp,sAh ; read hours
	inc rmp ; next hour
	sts sAh,rmp ; and store
	cpi rmp,24 ; greater than or equal 24
	brcs keyAlarmOff ; no, go on
	clr rmp ; restart hours
	sts sAh,rmp ; and store
keyAlarmOff:
	cbr rFlag,1<<bAlarm ; clear alarm flag
	ldi rmp,(1<<WGM01)|(1<<COM00) ; stop alarm timer
	out TCCR0,rmp ; set mode and COM
	cbi PORTB,3 ; clear OC0 port bit
	rjmp UpDateAlarm ; 
keyAlarm1:
	cpi rmp,cKeyBlack ; black key pressed?
	brne keyAlarm2 ; no
	; black key during alarm: set alarm and armed off
	lds rmp,sSm ; read set alarm minutes
	sts sAm,rmp ; write to alarm minutes
	lds rmp,sSh ; read alarm minutes
	sts sAh,rmp ; write to alarm minutes
	cbr rFlag,1<<bArmed ; clear armed flag
	rjmp keyAlarmOff
keyAlarm2:
	; any other key: do nothing
	ret
keyArmed: ; key during armed
	cpi rmp,cKeyBlack ; black key pressed
	brne keyArmed1 ; not pressed
	; black key during armed
	cbr rFlag,1<<bArmed ; set armed off
	lds rmp,sSm ; read set alarm minutes
	sts sAm,rmp ; write to alarm minutes
	lds rmp,sSh ; read alarm minutes
	sts sAh,rmp ; write to alarm minutes
	rjmp UpDateAlarm
keyArmed1:
	cpi rmp,cKeyRed ; red key during armed
	brne keyArmed2 ; not pressed
	; red key during armed: increase alarm time
	rjmp keyAlarmSnooze ; increase alarm time
keyArmed2:
	; any other key: ignore
	ret
;
; ===================================================
;         A   s e c o n d   i s   o v e r
; ===================================================
;
Second:
	cbr rFlag,1<<bSec ; clear second flag
	lds rmp,sCs ; read second
	inc rmp ; next second
	sts sCs,rmp ; store seconds
	.if dbg == 5
	rcall dispsec
	.endif
	cpi rmp,60 ; next minute?
	brcs Second1 ; no, not yet
	clr rmp ; clear seconds
	sts sCs,rmp ; store zero
	lds rmp,sCm ; read minutes
	inc rmp ; next minute
	sts sCm,rmp ; store minutes
	cpi rmp,60 ; next hour?
	brne UpdateTime
	clr rmp ; clear minutes
	sts sCm,rmp ; store minutes
	lds rmp,sCh ; read hours
	inc rmp ; next hour
	sts sCh,rmp ; store hours
	cpi rmp,24 ; next day?
	brne UpdateTime
	clr rmp ; clear hours
	sts sCh,rmp
	rjmp UpdateTime
Second1: ; blink time points
	sbrc rFlag,bSetC
	rjmp Second4
	ror rmp ; lowest bit to carry
	lds rmp,sTime ; load first displayed digit
	brcs Second2 ; low bit = 1
	sbr rmp,1<<7 ; clear led
	rjmp Second3
Second2:
	cbr rmp,1<<7 ; set led
Second3:
	sts sTime,rmp ; to first displayed digit
	; blink alarm points
Second4:
	sbrs rFlag,bArmed ; if not armed return
	ret
	lsl rmp ; bit 7 to carry
	lds rmp,sAlarm ; read alarm position
	brcs Second5
	andi rmp,0x7F ; clear bit 7
	rjmp Second6
Second5:
	sbr rmp,1<<7 ; set bit 7
Second6:
	sts sAlarm,rmp ; store alarm byte
	ret
dispsec:
	lds rmp,sCs
	ldi XH,High(sAlarm+2)
	ldi XL,Low(sAlarm+2)
	rcall To7Seg
	lds rmp,sCs
	ret
; ===================================================
;          U p d a t e   d i s p l a y
; ===================================================
;
; Update displayed alarm time
;
UpDateAlarm:
	ldi XH,High(sAlarm)
	ldi XL,Low(sAlarm)
	lds rmp,sAh
	rcall To7Seg
	lds rmp,sAm
	rjmp To7Seg
;
; Updates the time displayed
;
UpdateTime:
	sbrc rFlag,bArmed ; if armed, compare alarm time
	rcall CheckAlarm ; check alarm time
	sbrc rFlag,bSetC ; if Set Clock active, skip
	ret
	ldi XH,High(sTime) ; point to time
	ldi XL,LOW(sTime)
	lds rmp,sCh ; read hours
	rcall To7Seg
	lds rmp,sCm ; read minutes
To7Seg:
	clr R0 ; R0 is counter
To7Seg1:
	subi rmp,10
	brcs To7Seg2
	inc R0
	rjmp To7Seg1
To7Seg2:
	subi rmp,-10
	ldi ZH,HIGH(2*Tab7Seg) ; load table
	ldi ZL,LOW(2*Tab7Seg)
	add ZL,R0
	brcc To7Seg3
	inc ZH
To7Seg3:
	lpm
	st X+,R0
	ldi ZH,HIGH(2*Tab7Seg) ; load table
	ldi ZL,LOW(2*Tab7Seg)
	add ZL,rmp
	brcc To7Seg4
	inc ZH
To7Seg4:
	lpm
	st X+,R0
	ret
; Conversion table decimal to 7-segment
Tab7Seg:
.db 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10
; hex values
.db 0x08,0x03,0x27,0x21,0x06,0x0E
;
; ===================================================
;  C h e c k   a l a r m   t i m e    r e a c h e d
; ===================================================
;
CheckAlarm:
	lds R0,sCh ; read clock hours
	lds rmp,sAh ; read alarm hours
	cp R0,rmp ; equal?
	brne CheckAlarmRet
	lds R0,sCm ; read clock minutes
	lds rmp,sAm ; read alarm minutes
	cp R0,rmp ; equal?
	brne CheckAlarmRet
	sbr rFlag,1<<bAlarm ; set Alarm flag
	ser rEep ; address to end of EEPROM
	ldi rDurL,1 ; start at new address in int cycle
	clr rDurH
	ldi rmp,(1<<WGM01)|(1<<COM01)|(1<<CS01) ; start alarm timer
	out TCCR0,rmp ; set mode and COM and prescale = 8
CheckAlarmRet:
	ret
;
; ===================================================
;   N e w   A d c   r e s u l t   p r o v i d e d
; ===================================================
;
AdcNew:
	cbr rFlag,1<<bAdc ; clear ADC flag
	.if dbg == 6
	rcall dispadc
	.endif
	dec rFChk ; check dim adjust?
	brne AdcNew2 ; don't adjust
	ldi rmp,0x10 ; set down counter
	mov rFChk,rmp
	lds rmp,sAdcF ; read fototransistor value
	cpi rmp,0xFF ; dark?
	brcs AdcNew1 ; no
	ldi rmp,HIGH(divider/2) ; set half time
	out OCR1BH,rmp
	ldi rmp,LOW(divider/2)
	out OCR1BL,rmp
	rjmp AdcNew2
AdcNew1:
	ldi rmp,HIGH(divider-30) ; set full time
	out OCR1BH,rmp
	ldi rmp,LOW(divider-30)
	out OCR1BL,rmp
AdcNew2:
	lds rmp,sAdcP ; read pot value
	sbrs rFlag,bSetC ; C setting?
	rjmp AdcNew4 ; no C
	; set C
	sbrs rFlag,bSetCm ; minutes setting?
	rjmp AdcNew3
	; display minutes from pot setting
	ldi XH,High(sTime+2) ; set pointer
	ldi XL,Low(sTime+2)
	ldi ZL,60 ; 60 minutes
	rjmp Mult ; muliply and display
AdcNew3: ; display hours setting
	ldi XH,High(sTime) ; set pointer
	ldi XL,Low(sTime)
	ldi ZL,24 ; multiply by 24
	rjmp Mult ; multiply and display
AdcNew4:
	sbrs rFlag,bSetA ; A setting ?
	ret ; no
	sbrs rFlag,bSetAm ; minutes?
	rjmp AdcNew5 ; no
	ldi XH,High(sAlarm+2) ; set pointer
	ldi XL,Low(sAlarm+2)
	ldi ZL,60
	rjmp Mult ; multiply and display
AdcNew5:
	ldi XH,High(sAlarm) ; set pointer
	ldi XL,Low(sAlarm)
	ldi ZL,24
	rjmp Mult
;
; Multiply and display
;
Mult:
	mul ZL,rmp
	mov rmp,R1
	rjmp To7Seg
;
; ===================================================
;  D e b u g :   D i s p l a y   a d c   r e s u l t
; ===================================================
;
dispadc:
	ldi XH,HIGH(sAlarm)
	ldi XL,LOW(sAlarm)
	lds rmp,sAdcP
	rcall dispadc1
	lds rmp,sAdcF
dispadc1:
	push rmp
	swap rmp
	rcall dispadc2
	pop rmp
dispadc2:
	andi rmp,0x0F
	ldi ZH,HIGH(2*Tab7Seg)
	ldi ZL,LOW(2*Tab7Seg)
	add ZL,rmp
	brcc dispadc3
	inc ZH
dispadc3:
	lpm
	st X+,R0
	ret
;
; ===================================================
;      C o p y r i g h t   i n f o r m a t i o n
; ===================================================
;
.db "C(2)10 0ybh tt:p//ww.wva-rsa-mutotirlan.te"
;
; ===================================================
;         M e l o d y   i n   E E P R O M
; ===================================================
;
.eseg
.org $0000
;
.equ cPa = 10 ; pause duration
.equ cD = 9216 ; tone duration constant
;
; Frequency table
.equ cf2e = 659
.equ cf2f = 698
.equ cf2g = 784
.equ cf2a = 880
.equ cf2h = 988
.equ cf3c = 1047
.equ cf3d = 1175
.equ cf3e = 1319
.equ cf3f = 1397
.equ cf3g = 1568
.equ cf3a = 1760
.equ cf3h = 1976
.equ cf4c = 2093
.equ cf4d = 2349
.equ cf4e = 2637
.equ cf4f = 2794
.equ cf4g = 3136
.equ cf4a = 3520
.equ cf4h = 3951
.equ cf5c = 4186
; CTC table for these frequencies
.equ cc2e = clock/16/cf2e
.equ cc2f = clock/16/cf2f
.equ cc2g = clock/16/cf2g
.equ cc2a = clock/16/cf2a
.equ cc2h = clock/16/cf2h
.equ cc3c = clock/16/cf3c
.equ cc3d = clock/16/cf3d
.equ cc3e = clock/16/cf3e
.equ cc3f = clock/16/cf3f
.equ cc3g = clock/16/cf3g
.equ cc3a = clock/16/cf3a
.equ cc3h = clock/16/cf3h
.equ cc4c = clock/16/cf4c
.equ cc4d = clock/16/cf4d
.equ cc4e = clock/16/cf4e
.equ cc4f = clock/16/cf4f
.equ cc4g = clock/16/cf4g
.equ cc4a = clock/16/cf4a
.equ cc4h = clock/16/cf4h
.equ cc5c = clock/16/cf5c
; tone duration constants
.equ cd2e = cd/cc2e
.equ cd2f = cd/cc2f
.equ cd2g = cd/cc2g
.equ cd2a = cd/cc2a
.equ cd2h = cd/cc2h
.equ cd3c = cd/cc3c
.equ cd3d = cd/cc3d
.equ cd3e = cd/cc3e
.equ cd3f = cd/cc3f
.equ cd3g = cd/cc3g
.equ cd3a = cd/cc3a
.equ cd3h = cd/cc3h
.equ cd4c = cd/cc4c
.equ cd4d = cd/cc4d
.equ cd4e = cd/cc4e
.equ cd4f = cd/cc4f
.equ cd4g = cd/cc4g
.equ cd4a = cd/cc4a
.equ cd4h = cd/cc4h
.equ cd5c = cd/cc5c
; melody
; 4g  4g  4f   3a
; 4e  4d 4d
; 4c  3h  4c   4f
; 4f 4g-4f
; table
.db cc3g,cd3g,cc3g,cd3g,cc3f,cd3f,cc2a,cd2a
.db cc3e,cd3e,cc3d,cd3d,cc3d,cd3d
.db cc3c,cd3c,cc2h,cd2h,cc3c,cd3c,cc3f,cd3f
.db cc3f,cd3f,cc3g,cd3g,cc3f,cd3f
; end of table
.db 0,0
;
; End of source code
;



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