/* * AHAB camera trigger source code. A simple timer to drive an output * line at a given interval. * * This source is for an ATtiny85 microcontroller, clocked at 4.9152MHz. * Set the low fuse byte on the MCU to 0xDF for proper operation. * * Wiring: * B.2: Button tied to ground * B.1: LED tied high * B.0: Trigger, floating. * * Compile this code with avr-gcc, giving it the -mmcu=attiny85 option. * * Copyright 2007, Jon McClintock. * * This software is licensed under the CC-GNU GPL. */ // The CPU clock frequency, in Hz. #define F_CPU 4915200UL #include #include #include #include #include #include // I/O line definitions. Bits on PORTB. #define BUTTON_LINE 2 #define LED_LINE 1 #define OUTPUT_LINE 0 // Timer 0 is used to clock the camera trigger and button scan interval. // It is configured to overflow roughly 30 times per second, and generate // an interrupt upon overflow. #define TIMER0_TCCR0B 0x05 // ck/1024 = 4800 ticks/sec #define TIMER0_VALUE 0x56 // 160 ticks to overflow: 30 overflows/sec // The button press types. #define BUTTON_NONE 0 #define BUTTON_SHORT 1 #define BUTTON_LONG 2 // Intervals of note #define CAMERA_INTERVAL 7 // Seconds #define LED_SLOW 3 // Flash the LED slowly until 3 seconds #define LED_FAST 1 // Flash the LED quickly at less than 1 second /////////////////////////////////// // GLOBALS /////////////////////////////////// volatile unsigned char seconds; // Counts seconds in this cycle volatile unsigned char ticks; // Counts 30th's of a second /////////////////////////////////// // METHOD PROTOTYPES /////////////////////////////////// unsigned char read_button(); void idle_tick(); void run_tick(); /////////////////////////////////// // MAIN ROUTINE /////////////////////////////////// int main(void) { unsigned char run; // Set to true if the trigger is enabled. // Set the data direction bits on port B DDRB = _BV(LED_LINE) | _BV(OUTPUT_LINE); PORTB = _BV(BUTTON_LINE) | _BV(LED_LINE); // Initialize our counters run = seconds = ticks = 0; // Setup Timer 0 and start it running TCNT0 = TIMER0_VALUE; TIMSK |= _BV(TOIE0); TCCR0B = TIMER0_TCCR0B; // Enable interrupts sei(); // Main processing loop. Sleep, and do stuff when we are awoken. while(1) { unsigned char button; // Read the button and process any press. button = read_button(); if ( button == BUTTON_SHORT ) { run = ~run; seconds = ticks = 0; } while ( ticks >= 30 ) { seconds++; ticks -= 30; } if ( run ) { run_tick(); } else { idle_tick(); } sleep_mode(); } return 0; } /////////////////////////////////// // SIGNAL HANDLERS /////////////////////////////////// // The Timer 0 overflow handler. This is when we scan the button // input. It simply sets the tick flag to let the mainloop know that // it should run. ISR(SIG_OVERFLOW0) { TCNT0 = TIMER0_VALUE; ticks++; } /////////////////////////////////// // UTILITY METHODS /////////////////////////////////// // Performs the button scan procedure unsigned char read_button() { static unsigned char keycount = 0; unsigned char button = BUTTON_NONE; if ( ! (PINB & _BV(BUTTON_LINE)) ) { keycount++; // If the button's been held down for a full second, register // a long press. if ( keycount == 30 ) { button = BUTTON_LONG; } if ( keycount >= 31 ) { keycount = 31; } } else { // We require that the button be held for two 30th's of a // second for the press to register. if ( keycount >= 1 && keycount < 30 ) { button = BUTTON_SHORT; } keycount = 0; } return button; } // Handle an idle timer tick. Slowly flash the LED. void idle_tick() { if ( seconds == 0 ) { PORTB ^= _BV(LED_LINE); } } // Displays the current target time, hours in long pulses, minutes // in short pulses. This is implemented as a mini state machine, // driven monotonically by the quick timer. void update_display() { unsigned char modulus; // Glow solid if we're triggering the cameras. if ( seconds >= CAMERA_INTERVAL ) { PORTB = PORTB & ~_BV(LED_LINE); return; } if ( seconds <= LED_SLOW ) { modulus = 15; } else if ( seconds <= (CAMERA_INTERVAL - LED_FAST) ) { modulus = 5; } else { modulus = 3; } if ( (ticks % modulus) == 0 ) { PORTB ^= _BV(LED_LINE); } } // Handle a running timer tick. Update the display and fire the // cameras off if necessary. void run_tick() { update_display(); if ( seconds >= CAMERA_INTERVAL ) { // If we're just past the interval, turn on the output line. if ( (seconds == CAMERA_INTERVAL) && (ticks < 1) ) { PORTB |= _BV(OUTPUT_LINE); } else { PORTB &= ~_BV(OUTPUT_LINE); seconds = ticks = 0; } } }