; *********************************************
; * 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
ut 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
di 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
No comments:
Post a Comment