;

;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