My objective in this design was to simply it as much as possible parts-wise--to minimize the number of discretes. The original design called for the use of MOSFET transistors exclusively to obviate the need for base resistors but I currently don't have any common cathode LED displays on hand and I need a timer ASAP so I had to use PNPs for the common anode display. There is only one pushbutton and it uses the weak pull-up of the MCU, hence a discrete pull-up resistor is unnecessary.
Features:
- Maximum time/readout = 99 minutes
- Display is shut down and MCU goes to sleep 10 seconds after readout becomes zero
- After timing has commenced readout flashes at a frequency of exactly 1Hz and with a duty cycle of 50%. Flashing readout is therefore counting down seconds.
- The pushbutton has multiple functions depending on status of the timer:
- when readout is on, pressing it for a fraction of a second increments readout by one. Keeping it depressed for half a second increments readout by 10 and keeps incrementing by 10 while pressed. No further readout change will occur when readout rolls over to zero. Button must be released and depressed again to increment again.
- when readout is off (MCU is in sleep mode) button acts to wake up the MCU.
- when timer has begun countdown but has not yet reached zero, button acts to abort timing.
- when timer has counted down to zero and buzzer alert is sounding, button acts to stop the buzzer
Detailed description of timer operation and its firmware:
Upon power up, the MCU will be in Standby mode and 2-digit seven-segment LED display will read 0. If PB is momentarily pressed a 7ms beep will sound and the LED display will read 1. Because of the limitations of the display, the maximum number of minutes the system will accommodate is 99 minutes.
After the first keypress (unless it was to awaken the MCU) firmware enters Userinputting mode. Further momentary presses of PB will increment the time by one and a 7ms beep will sound and time will be displayed. Keeping PB depressed for about half a second will increment time by 10 minutes. If time displayed is not a multiple of ten (ie., dividing by ten will result in a remainder), then keeping PB pressed will change time to the next nearest higher multiple of 10. Eg. If current time displayed = 8 then it will be changed to 10. If display = 24 then it will be changed to 30. If PB is kept depressed, increment by 10 will continue until the key is released or until time increments to 0 (there is no 100). A 7ms beep will sound every time heating time is incremented/decremented by 10.
If time displayed is not zero then after no button presses are made for approx 3 seconds from the last button press, countdown will begin and four sets of 50ms beep followed by 50ms silence will sound to indicate that countdown has commenced. Firmware is now in Countingdown mode. The amount of time left will be displayed and display will flash once every second. When time is down to 1 minute, display will change from displaying minutes to seconds. Thus at one minute it will display "60" meaning 60 seconds. Display will cease flashing. Pressing either button while in Countingdown mode will abort countdown and a 7ms beep will sound. Firmware is then returned to Standby mode.
When countdown time has elapsed firmware is changed to Countdownfinished mode. 3 alarm sequences are sounded in succession: 10 sets of 0.1sec beep + 2sec silence, 10 sets of 0.25sec beep + 1.25sec silence, continuous 0.5sec beep + 0.5sec silence. At any time when firmware is in Countdownfinished mode, any button press will terminate the alarm and firmware will be switched back to Standby mode.
When in Standby mode and no button presses are received in approx 10 seconds, LED display is turned off and the MCU goes to sleep. It can be awakened by a button press. The button press that awakes the MCU does not count as time input--the reading is zero when the MCU wakes up. No beep is sounded when a button press wakes up the MCU.
Timer0 is set up to overflow every 8ms. In main() timer0 interrupt flag is continually polled. When it gets set, the input button is checked by the switch debounce routine. This routine determines whether the switch is bouncing or not, and if not whether it is at a high/low level and whether or not a falling/rising edge has occured. A falling edge indicates a valid button press. The routine also keeps track of how long the button has remained pressed and how long it has remained not pressed.
Timer1 is used for countdown timing. Timer1 uses an external 32.768kHz crystal for its clock. With a prescale set to 1:1, TMR1L = 0, and TMR1H set to 192, timer1 overflows every 256 * 64 / 32768 = 0.5sec. Timer1 is turned on only when countdown commences and is stopped after countdown or an aborted countdown.
Firmware uses a state machine implemented using switch()/case statement. There are four possible states: Standby, Userinputting, Countingdown, and Countdownfinished. Upon power up state = Standby. After any button press state changes over to Userinputting. Should countdown commence state changes to Countingdown. Should inputting be canceled states changes to Standby. Upon completion of countdown state changes over to Countdownfinished. When a button is pressed to stop alarm state changes to Standby.
The two digits of the seven segment display are multiplexed and each digit is turned on every time timer0 overflows (8ms). Thus, the refresh rate is 62.5Hz. Because of display flickering problem button beep should be less than timer0 overflow period. When longer beeps are required (eg. to indicate countdown has ended) a state machine implementation (employing switch()/case statement) is used within the main state machine.
Watchdog timer is enabled when MCU is awake and disabled when it goes to sleep. PORTA pull up for input button is enabled. Button is debounced in software. When MCU goes to sleep interrupt-on-change is enabled to allow button press to wake up the MCU. Upon waking IOC is disabled.