;
;Atmel AVR Design Contest 2006 Registration Number AT3222
;
;***************************************************************************
; Source: Timer.asm
; Application: Teachers Timer.
; Date: 30/04/06.
; Version: 0.1
; Assembler: AVR Studio 4.07.
; Assembler: AVRA: advanced AVR macro assembler Version 1.0.1 Build 777
; Target: Atmel ATtiny13.
; Platform: Any.
;***************************************************************************
.INCLUDE "tn13def.inc"
.def zeroReg =r1 ;keep this register always zero
.DEF tempa =r16 ;temporary register
.DEF tempb =r17 ;another temporary register
;.DEF tempc =r18 ;temporary storage for reset_flags, not used in the
;template, but may be required by application code.
.DEF countL =r19 ;storage for 8bit counter for delay between sample changes
.DEF countH =r20 ;position in sample table
.def bounceL =r21 ;bounce delay count
.def seconds =r22 ;Seconds
.def minutes =r23 ;minutes
.def state =r24 ;machine state
.def alarm =r25 ;minutes for alarm.
.def pwm =r15 ;current pwm value from table
.def bounceH =r18 ;High byte for bounce counter (for hich clock speed)
.equ sCOUNT =0 ;we are counting clicks on thebutton
.equ sALARM =1 ;the alarm is going off *click to cancel
.equ sBUTTON1 =2 ;button1 Status (laser)
.equ sBUTTON2 =3 ;button2 state (control)
.equ sIDLE =4 ;we are in low power.mode
.equ sDEBOUNCE =5 ;we are debouncing a switch
.equ sLASER =6 ;laser state
.equ sMOTOR =7 ;motor state
.equ MOTOR_PWR =0xFF ;pwm level for motor 0xFF=100%
.equ MOTOR_PWROFF =0x00 ;
.equ LASER_PWR =0x7F ;pwm level for laser 0xFF=100%
.equ LASER_PWROFF =0x00 ;
.equ TIME_INC =05 ;number of Minutes to add with each click.
.equ PWM_SPD =05 ;speed to progress through pwm table
.equ PWM_FULL =0x40 ;position in Table of full power
.equ PWM_ZERO =0xC0 ;position in table of zero power
.equ FCPU =9600000 ;(9.6Mhz) remember to change CLKSP
.equ SECONDCOUNT = FCPU/510 ;see page 66 in datasheet
.equ BOUNCECOUNT = SECONDCOUNT/50 ;20ms for debounce timer
.INCLUDE "tiny13_init.asm"
;***************************************************************************
;**************************** Main Program *****************************
;***************************************************************************
MAIN:
; reset power up initialization goes here
; We can jump back to here if we ever want to do a "soft" reset.
; (this reset doesn't effect any IO pin state just internal software)
cli ;stop all interrupts while we do some inits
;reset Stack after a "soft reset"
ldi tempa, low(RAMEND)
out SPL,tempa ; Set Stack Pointer to top of RAM
;reset counters and registers.
clr zeroReg
clr ZH
clr ZL
reg_clr_loop:
st Z+,zeroReg ;clear this mem address (0x00->0x1F = registers)
cpi ZL,0x1E ;stop when we get to Z low byte (r30)
brne reg_clr_loop
ldi state,(0<<sMOTOR)|(0<<sLASER)|(1<<sDEBOUNCE)|(1<<sIDLE)|(0<<sBUTTON2)|(0<<sBUTTON1)|(0<<sALARM)|(0<<sCOUNT)
ldi ZH,high(sine<<1)
ldi ZL,low(sine<<1)
lpm pwm,Z
sei
; sleep enable, power down mode, stop timers, pin change should wake us
ldi tempa,(0<<PUD)|(1<<SE)|(1<<SM1)|(0<<SM0)|(0<<ISC01)|(0<<ISC00)
out MCUCR,tempa
sleep
ldi tempa,(0<<PUD)|(0<<SE)|(0<<SM1)|(0<<SM0)|(0<<ISC01)|(0<<ISC00)
out MCUCR,tempa
cli
;****************************************************************************
; MAIN LOOP
;
loop:
;check for alarm status
check_alm:
mov tempa,state
andi tempa,(1<<sALARM)
breq update_laser
;Update Laser or motor for alarm state
mov tempa,state
andi tempa, (1<<sLASER)
breq alarm_motor
out OCR0B,pwm ;fade laser on
ldi tempa,MOTOR_PWROFF
out OCR0A,tempa ;turn motor off
rjmp check_button1
alarm_motor:
ldi tempa,LASER_PWROFF
out OCR0B,tempa ;turn laser off
out OCR0A,pwm ;turn motor on
rjmp check_button1
;Update Laser and motor for normal state
update_laser:
mov tempa,state
andi tempa, (1<<sLASER)
breq laser_OFF
ldi tempb,LASER_PWR ;turn laser on
out OCR0B,tempb
rjmp update_motor
laser_OFF:
ldi tempb,LASER_PWROFF ;turn laser off
out OCR0B, tempb
update_motor:
mov tempa,state
andi tempa, (1<<sMOTOR)
breq motor_OFF
out OCR0A,pwm
rjmp check_button1
motor_OFF:
ldi tempb, MOTOR_PWROFF ;turn motor off
out OCR0A, tempb
;Read buttons and switches
check_button1: ;laser control switch
mov tempa,state
andi tempa, (1<<sBUTTON1)
breq check_button2
; check for both buttons
mov tempb,state
andi tempb, (1<<sBUTTON2)
breq toggle_laser
; set to idle mode
rjmp MAIN
toggle_laser:
; Button 1 pushed -> toggle laser state
ldi tempa,(1<<sLASER)
eor state,tempa
cbr state,(1<<sBUTTON1) ;clear button press flag
check_button2: ;timing control switch
mov tempa,state
andi tempa, (1<<sBUTTON2)
breq end
; Button 2 pushed -> add alarm time.
cbr state,(1<<sBUTTON2) ;clear button press flag
cbr state,(1<<sIDLE)
;check if alarm is sounding
mov tempa,state
andi tempa,(1<<sALARM)
breq add_time
;stop alarm
cbr state,(1<<sALARM)
clr alarm ;set alarm to 0 minutes
add_time:
;reset timers
clr minutes
clr seconds
clr countL
clr countH
sbr state,(1<<sCOUNT) ;Flag for long key press
;increment the alarm time
ldi tempa,TIME_INC
add alarm,tempa
sbr state,(1<<sMOTOR) ;pulse to acknowledge
ldi ZL, PWM_ZERO
end:
; Go to sleep and wait for an interupt to wake us.
; (IDLE MODE) keep timers running. stop main clock.
sei
ldi tempa,(0<<PUD)|(1<<SE)|(0<<SM1)|(0<<SM0)|(0<<ISC01)|(0<<ISC00)
out MCUCR,tempa
sleep;
; just woke up, disable sleep
ldi tempa,(0<<PUD)|(0<<SE)|(0<<SM1)|(0<<SM0)|(0<<ISC01)|(0<<ISC00)
out MCUCR,tempa
cli
rjmp loop
;****************************************************************************
;ISR for Timer 0
;
; Destroys reg tempa,tempb,
; changes state, countL countH
ISR_T0:
in r0,SREG ;Save status register
ldi tempa,0x01
ldi tempb,0x00
add countL,tempa
adc countH,tempb ;add zero to ensure carry is counted
add bounceL,tempa ;increment debounce timer
adc bounceH,tempb
cpi bounceH, high(BOUNCECOUNT) ;compare bounce time 20mS
brne ISR_T0_one_second
cpi bounceL, low(BOUNCECOUNT)
brne ISR_T0_one_second
;This is run every ~20ms or <20ms after button push
cbr state,(1<<sDEBOUNCE) ;reset debounce status after 20ms
lpm pwm,Z ;read pwm table
ldi tempa,PWM_SPD
add ZL,tempa ;next address(should roll over at 256)
clr bounceL
clr bounceH
ISR_T0_one_second:
cpi countH, high(SECONDCOUNT)
brne ISR_T0_exit
cpi countL, low(SECONDCOUNT)
brne ISR_T0_exit
clr countL ;reset second timers
clr countH ;
inc seconds ;increment second counter and check for
mov tempa,state ;check for long press
andi tempa,(1<<sCOUNT)
breq ISR_T0_checkminute
ISR_T0_longpress:
;If flag is set at end of a second then
;button hasn't been released for 1 second.
;so we do a reset without switching off the
;motor
ldi tempa,MOTOR_PWR ;turn motor on
out OCR0A,tempa ;set motor power
;wait for button to be released before continuing
ISR_T0_btn_on_loop:
sbis PINB,sBUTTON2
rjmp ISR_T0_btn_on_loop
rjmp MAIN
ISR_T0_checkminute:
cpi seconds, 60 ;minutes
breq ISR_T0_one_minute
; This section is executed on seconds 01-59.
cbr state,(1<<sMOTOR) ;Motor OFF, only ever runs for 1s max
rjmp ISR_T0_exit
ISR_T0_one_minute:
; This is executed on second 00 of every minute.
clr seconds ;reset second counter
mov tempa,state ;check state
andi tempa,(1<<sIDLE)
brne ISR_T0_exit ;finished if in sIDLE mode
inc minutes;
tst alarm ;is alarm set?
breq ISR_T0_exit ;no. then exit
;check for alarm condition
cp minutes, alarm
brne ISR_T0_exit
sbr state,(1<<sALARM) ;set alarm condition
ldi ZL,PWM_FULL
ISR_T0_exit:
out SREG,r0
reti
;****************************************************************************
; Pin Change ISR, called on change of status of PORTB2 | PORTB3
; (set by PCIMSK and DDRB)
;
ISR_PC:
in r0,SREG ;Save status register
in tempa,PINB ;read buttons immediatly
;check debounce timer
mov tempb,state
andi tempb,(1<<sDEBOUNCE)
brne ISR_PC_exit ;exit if we are debouncing
ISR_PC_read_buttons:
com tempa ;invert so 1 = closed 0 = open
andi tempa, (1<<PORTB3)|(1<<PORTB2) ;mask buttons only
or state,tempa ;sBUTTON2,sBUTTON1 = PORTB3,PORTB2 ;)
andi tempa, (1<<PORTB3) ;look at button 2 only
brne ISR_PC_debounce_start ;if closed continue
cbr state,(1<<sCOUNT) ;else clear the long press flag
ISR_PC_debounce_start:
clr bounceH ;reset bounce timer
clr bounceL
sbr state,(1<<sDEBOUNCE) ;set machine state
ISR_PC_exit:
out SREG,r0
reti
; Sine wave table from Jesper Hansens mini DDS
; http://www.myplace.nu/avr/minidds/index.htm
.org 0x180
sine: ; 256 step sinewave table
.db 0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae
.db 0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8
.db 0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5
.db 0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff
.db 0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7
.db 0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc
.db 0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3
.db 0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83
.db 0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51
.db 0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27
.db 0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a
.db 0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08
.db 0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23
.db 0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c
.db 0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c