/****************************************************************************
Title: zoombot
Author: Rob Duarte <rahji@rahji.com>
Date: 02/05/2008
Software: AVR-GCC 4.1.2
Hardware: ATTiny13 using onboard clock
microswitch on pb2 active-low
pb3, pb4 3mm leds sourcing active-high
pb0, pb1 pc123 optoisolators
Description: pressing the microswitch cycles through four states:
pre-zoomin, zoomin, pre-zoomout, zoomout, etc
button debouncing and the flashing of leds during the "pre" states
is handled in the timer/counter match interrupt - in fact,
the entire program is in that interrupt, for the most part
*****************************************************************************/
// attiny with CKSEL=11 (128KHz clock)
// this interrupt triggers every time the counter reaches 70 (from 0)
// (this happens every 35ms -- .035 seconds)
// it takes care of both button debouncing and led flashing
ISR
{
static volatile uint8_t zoom_state, button_count, bar_count, led_count, led_flash;
// if the button state is low (pressed), advance the count variable.
// the count continues to go up only if it's low for every consecutive check.
// if this happens 3 times in a row (~10ms), it's debounced.
if (bit_is_clear(PINB,SWITCH_PIN)) button_count++;
else button_count = 0; // reset the count - we're bouncing or not pressed
// if the button is low and stable, accept it as a click and handle it.
// interrupts are disabled inside this ISR, so we can take our time.
// that's not normal practice, but in this application nothing else will be
// effected by our timing - the whole app is in this ISR!
// btw, bar_count makes sure that there's a mandatory delay between successful clicks
// this specific application shouldn't have clicks anywhere near each other (they're SECONDS apart!)
if {
switch
{
case STATE_PREZOOMIN:
// everything off, this is a pause state
PORTB &= ~(_BV(ZOOMIN_PIN) | _BV(ZOOMOUT_PIN) | _BV(LEDZOOMOUT_PIN) | _BV(LEDZOOMIN_PIN));
led_flash = LEDZOOMIN_PIN; // flash the zoom-in led to indicate the next state
zoom_state = STATE_ZOOMIN; // the zoom state to enter the next time the button is pressed
break;
case STATE_ZOOMIN:
PORTB |= _BV(ZOOMIN_PIN) | _BV(LEDZOOMIN_PIN); // zoom in
led_flash = NO_LEDFLASHING; // we want the zoom-in led to stay on now, no flashing
zoom_state = STATE_PREZOOMOUT; // the state to enter next time
break;
case STATE_PREZOOMOUT:
// everything off, this is a pause state
PORTB &= ~(_BV(ZOOMIN_PIN) | _BV(ZOOMOUT_PIN) | _BV(LEDZOOMOUT_PIN) | _BV(LEDZOOMIN_PIN));
led_flash = LEDZOOMOUT_PIN; // flash the zoom-out led to indicate the next state
zoom_state = STATE_ZOOMOUT; // the state to enter next time
break;
case STATE_ZOOMOUT:
PORTB |= _BV(ZOOMOUT_PIN) | _BV(LEDZOOMOUT_PIN); // zoom out
led_flash = NO_LEDFLASHING; // we want the zoom-out led to stay on now, no flashing
zoom_state = STATE_PREZOOMIN; // the state to enter next time (loops back to the 1st state)
break;
}
button_count = 0; // reset the debounce counter for the next time
bar_count = 0; // reset the counter that gives us a period of no-clicks-allowed time after a successful click
}
// handle the led flashing
// basically, if neither led is ON then one of them will be flashing,
// as described in the state machine above.
// led_flash evaluates to either false or the pin name of one of the two leds
if {
if { // if the 35ms counter int has triggered 10 times
PORTB ^= _BV(led_flash); // then toggle the appropriate led pin
led_count = 0; // and reset the counter for the next toggle
}
}
}
void
{
cli(); // disable interrupts, just in case
// define pin data directions
DDRB &= ~_BV(SWITCH_PIN); // pb2 is input - the toggler switch
PORTB |= _BV(SWITCH_PIN); // enable pull-up resistor on pb2
DDRB |= _BV(ZOOMIN_PIN) | _BV(ZOOMOUT_PIN); // output pins - the optoisolators
DDRB |= _BV(LEDZOOMIN_PIN) | _BV(LEDZOOMOUT_PIN); // output pins - the led indicators
// setup the only 8-bit timer, used for button debounce and led blinking
TCCR0B |= _BV(CS00) | _BV(CS01); // add /64 prescaler
TCCR0A |= _BV(WGM01); // clear timer on compare match
TIMSK0 |= _BV(OCIE0A); // enable compare match a
OCR0A = 70; // set compare match value to 70 - matches every 35ms (.035 sec)
sei(); // enable global interrupts
}
int
{
setup_hardware();
while (1); // do nothing but wait for interrupts
}