; A 90 Day timer with 4 LEDs based on the AVR 90S2343 or Tiny22 ; Inputs - RESET button - pulls to ground when pressed. ; Outputs - Status LED - Blinks to show the timer is running ; 30 Day LED - Blinks when 30 days have passed ; 60 Day LED - Blinks when 60 days have passed ; 90 Day LED - Blinks when 90 days have passed ; This is based on an idea from a Fairchild Semiconductor Application ; Note for the ACE1101 processor. This design improves on the App ; note by: ; 1) Using timer0 and sleep mode to use less power. ; 2) Using a possibly lower cost AVR part. ; 3) Offering blinking LEDs to reduce power consumption. (Future implementation) ; A constant-on mode is also available. ; Written 8/12/99 by Brian Hammill (hammill@ipass.net) This code is ; copyright (C) 1999 by Brian Hammill. Please give credit to the ; author whenever using or distributing this code. Otherwise, you ; are free to use and make changes to this code. If you make ; an improvement, be nice and send a copy to me. ; Using the 1 MHz Internal OSC and a timer prescaler of CK/1024 ; Timer0 will overflow approximately every 250 mS. ; We start up, init the timer, and put the chip to sleep. ; When the timer overflows the chip wakes up, increments the 1 mS ; counter and goes back to sleep. ; When the 250 mS counter reaches 240 (1 Minute ), then the 1 min. counter is ; incremented and the 250 mS counter is reset to 0. ; When the 1 minute counter reached 60 (1 hour) then we increment ; the 1 hour counter and reset the 1 minute counter to 0 ; when the 1 hour counter reaches 240 (10 days) increment the ; 10 day counter and reset the 1 hour counter. The 1 hour and 10 day count ; is stored in EEPROM so that if the power fails, at most an hour's count ; is lost. ; The timer ISR will check the EEPROM to see when the elapsed ; time reaches 30, 60, and 90 days and set a flag to blink the ; appropriate LED. The reset button will clear all timer values and the ; EEPROM location and start counting from 0 again. ; Blinking the LEDs is handled in the 250 mS timer ISR. A low duty cycle is ; used to flash the LEDs so that power can be reduced. ; ; Written for the Atmel AVR Assembler version 1.30. ; This code illustrates a way to generate extremely long delays. ; It also shows how to read/write the AVR EEPROM and ; how to use timer0 with interrupts. This program uses 6 registers ; and 2 EEPROM locations. No SRAM is used or needed. It is approximately ; 123 words of code space. ; ; The external interrupt for reset operation is not yet active. ; For some reason the hour location in EEPROM gets incremented ; about 4 times as often as it should. I think the timer is ; running faster than CK/1024 or CK is faster than 1MHz (internal RC) ; So I changed the hour counter to 240 minutes instead of 60 minutes. ; Can trim the minute and hour count compare values to improve the ; timer accuracy. ; Make sure to set the RCEN flag when programming the 2343. .include "2343def.inc" .def count_250ms = r16 .def count_10s = r17 .def count_1m = r18 .def temp = r19 .def led_mask = r20 .def led_counter = r21 ; used to flash the LEDs every n times through ISR. .equ reset_pin = 1 ; PB1 is the reset (INPUT) .equ status_led = 0 ; PB0 is the status LED indicator. (OUTPUT) .equ days30_led = 2 ; PB2 is the 30 day LED indicator. (OUTPUT) .equ days60_led = 3 ; PB3 is the 60 day LED indicator. (OUTPUT) .equ days90_led = 4 ; PB4 is the 90 day LED indicator. (OUTPUT) ; There are no more I/O pins on the 2343. ; the Interrupt Vector table .org 0x00 ; Power up reset rjmp POWER_ON ; External HW Int ;rjmp reset_button reti ; Internal Timer0 Overflow rjmp Timer0 POWER_ON: ; Initialize the Stack Pointer, WDT, Interrupts, I/O Pins, and Timer0 cli ; Make sure interrupts are disabled for now. ldi temp, 14 ;15 = 2048 mS, 14 = 1024, 13 = 512 mS out WDTCR, temp ; Watchdog timer is a good idea here. ldi ZL,low(RAMEND) out SPL,ZL ;init Stack Pointer clr temp out GIMSK, temp ; don't allow External INT0 ldi temp, 2 out TIMSK, temp ; allow Timer0 overflow interrupts. ldi temp,0b11111111 out DDRB, temp ; all outputs except for PB1 ldi temp, 0b101 out TCCR0, temp ; timer prescaler = CK/1024 ldi temp, 0b00100000 out MCUCR, temp ; sleep mode enabled, Idle mode selected ;ldi temp,$80 ; ;out ACSR,temp ; Comparator Disabled to save power ; All LEDs OFF ser led_mask out PORTB, led_mask clr led_counter zero_timer: clr temp out TCNT0, temp ; start timer at 0 sleep_and_wait: ; check the 10 days location in EEPROM to see if we need to light any LEDs ldi temp, 1 ; put address 0 in the EEPROM address register out EEAR, temp sbi EECR, EERE Get_READ_POLL: ; this will poll the EECR read enable bit until it is cleared. Then sbic EECR, EERE ; the EEPROM data will be available at the EEPROM data register. rjmp Get_READ_POLL in temp, EEDR ; copy the contents to our local variable. ; Now the 10 days value is in temp. cpi temp, 3 ; 30 days brlt done_setting_bits ;cbr led_mask, days30_led cbi PORTB, days30_led cpi temp, 6 ; 60 days brlo done_setting_bits ;cbr led_mask, days60_led cbi PORTB, days60_led cpi temp, 9 ; 90 days brlo done_setting_bits ;cbr led_mask, days90_led cbi PORTB, days90_led done_setting_bits: sei ; make sure interrupts are enabled at this time. sleep ; put the CPU in idle mode. The Timer0 still runs. nop nop rjmp sleep_and_wait ;The following is the Timer0 ISR where all the work gets done. ;At 1 MHz the timer will overflow approximately every 250 mS. Timer0: cli ; disable further interrupts just in case. We have 250 mS to do all this. inc led_counter sbis PORTB, status_led rjmp led_now_on rjmp led_now_off led_now_off: ; we want to turn it on when count gets to 8 cpi led_counter, 8 ;rjmp turn_on_status_led breq turn_on_status_led rjmp skip_over turn_on_status_led: cbi PORTB, status_led clr led_counter rjmp skip_over led_now_on: ; it is on, so turn it off sbi PORTB, status_led skip_over: ;cpi led_counter, 30 ;brlt dont_clear ;clr led_counter dont_clear: ; com led_mask ; temp test code. ;out PORTB, led_mask ; turn on the LEDs for number of days. ms250_handler: wdr ; don't forget to reset the WDT inc count_250ms cpi count_250ms, 240 ; has the count reached 1 minute? Adjust for accuracy. breq one_minute ; flash the LED's in this section. rjmp exit_interrupt one_minute: clr count_250ms inc count_1m cpi count_1m, 240 ; has 1 hour passed? breq hour_handler rjmp exit_interrupt hour_handler: ; this routine has to write the EEPROM. Put in the checks in the 100ms section ; to flash the corresponding LEDs when EEPROM value reaches specific numbers. clr count_1m ; set aside two EEPROM locations. Location 0 counts the hours up to 240 hours (10 days). ; then location 1 is incremented to count tens of days. When location 0 reaches 240 ; hours, then we increment location 1 and clear location 0. ; start by getting the value of location 0. If it is less than 240 increment it. Else ; clear and increment location 1. ldi temp, 0 ; put address 0 in the EEPROM address register out EEAR, temp ldi temp, 1 ; enable EEPROM read out EECR, temp EE_READ_POLL: ; this will poll the EECR read enable bit until it is cleared. Then sbic EECR, 0 ; the EEPROM data will be available at the EEPROM data register. rjmp EE_READ_POLL in temp, EEDR ; copy the contents to our local variable. cpi temp, 240 ; see if 10 days have passed breq TEN_DAYS inc temp ; if not 10 days, then increment the hour counter and write back to EEPROM out EEDR, temp sbi EECR, EEMWE ; set the EEPROM master write enable sbi EECR, EEWE ; set the EEPROM write enable bit EE_WRITE_POLL: sbic EECR, EEWE ; this will poll on the write enable bit until it clears (write complete) rjmp EE_WRITE_POLL cbi EECR, EEMWE ; clear the master write enable bit to prevent any further write. rjmp exit_interrupt TEN_DAYS: ; ten days has passed if we reach here (240 hours). If we get here, then we need to clear ; EEPROM location 0 and increment EEPROM location 1. ldi temp, 1 ; put address 1 in the EEPROM data register out EEAR, temp sbi EECR, EERE ; set the read enable bit READ_POLL: sbic EECR, EERE ; poll the read bit rjmp READ_POLL in temp, EEDR ; get the EEPROM data into local variable temp. inc temp ; add 1 to the temp register. ; need to set flags for LEDs here, now that we have the number of 10 day periods in temp out EEDR, temp ; get ready to write back to EEPROM. sbi EECR, EEMWE ; set master write enable sbi EECR, EEWE WRITE_POLL: sbic EECR, EEWE ; poll the write bit rjmp WRITE_POLL ; now we have to clear the hours location in EEPROM to start over. clr temp out EEAR, temp ; set EEPROM address to 0. out EEDR, temp ; set data register to 0. sbi EECR, EEMWE ; set the master write enable. sbi EECR, EEWE ; set the write bit strobe. POLL_WRITE: sbic EECR, EEWE ; poll the write bit rjmp POLL_WRITE cbi EECR, EEMWE ; clear the master write enable bit ; now the 10 day location in EEPROM has been updated. Next we reset the counter re-enable ; interrupts, and return from the interrupt. exit_interrupt: ;sbi PORTB, status_led ; turn off the status LED before we leave. ;sbi PORTB, days30_led ;sbi PORTB, days60_led ;sbi PORTB, days90_led ; turn off the day count LEDs. clr temp out TCNT0, temp sei reti reset_button: ; if the reset button is pressed, we will come here. Reset the EEPROM locations ; reset the register counters, and jump back to the beginning. ldi temp, 0 ; put address 0 in the EEPROM address register out EEAR, temp out EEDR, temp sbi EECR, EEMWE ; set the EEPROM master write enable sbi EECR, EEWE ; set the EEPROM write enable bit CLEAR_LOCATION_ZERO: sbic EECR, EEWE ; this will poll on the write enable bit until it clears (write complete) rjmp CLEAR_LOCATION_ZERO out EEDR, temp inc temp out EEAR, temp sbi EECR, EEMWE sbi EECR, EEWE CLEAR_LOCATION_ONE: sbic EECR, EEWE rjmp CLEAR_LOCATION_ONE cbi EECR, EEMWE ; clear the master write enable bit to prevent any furthe rjmp POWER_ON ; start over.