Title:    final shower time machine code
 Author:   Rob Duarte <rahji@rahji.com> 
 Date:     04/24/2007
 Software: AVR-GCC 4.1.2 
 Hardware: ATTiny13 using onboard clock
           N.O. momentary switch on INT0 pin
           LEDs no PB2-4 

 Description: led strobe light with a button to control the off-time
              on-time is hard-coded

#include <avr/io.h>
#include <avr/interrupt.h>

#define F_CPU 128000UL // attiny with CKSEL=11 (128KHz clock)

#define LEDS  0x1C // pb2-4

// with 128KHz clock and /256 timer prescale, each clock cycle is 2 ms
#define LED_ON_CYCLES  2 // LEDs are hardcoded to be ON for 2 cycles
#define DELAY_MAX 75     // maximum OFF-cycles (can't be higher than 255 - LED_ON_CYCLES)
#define DELAY_MIN 4      // minimum OFF-cycles
#define DELAY_DEFAULT 20 // LEDs are OFF for 20 cycles by default
#define DELAY_JUMP 1     // increment for changing delay on button press
volatile uint8_t off_until = DELAY_DEFAULT;

// catch a button press interrupt
ISR (INT0_vect)
    // if the button is pressed, increase the delay or start at 0 again if we're near max
    if ((off_until+DELAY_JUMP)>DELAY_MAX) off_until=DELAY_MIN;
    else off_until += DELAY_JUMP; 

    // adjust timer compare values using the new off_until
    OCR0B = off_until;

// when we reach the first compare match (compare reg B)
// turn the LEDs on
ISR (TIM0_COMPB_vect) { PORTB |= LEDS; }

// when we reach the second compare match (compare reg A) turn the LEDs off
// timer is reset after the match with compare register A
ISR (TIM0_COMPA_vect) { PORTB &= ~LEDS; }

int main( void )
    // setup timer for flashing 
    TCCR0A |= _BV(WGM01);  // clear timer on match
    TCCR0B |= _BV(CS02); // added /256 prescaler for timers
    OCR0B   = off_until; // set first compare target
    OCR0A   = OCR0B + LED_ON_CYCLES; // set second compare target, after which the timer zeros
    TIMSK0 |= _BV(OCIE0B) | _BV(OCIE0A); // enable both

    DDRB |= LEDS;  // set LED pins for output
    // setup INT0 interrupt for switch
    GIMSK |= _BV(INT0); // external INT0 interrupt enable 
    MCUCR |= _BV(ISC01); // generate interrupt on falling edge (when the button is pressed) 

    sei(); // enable global interrupts

    while (1); // do nothing but wait for interrupts