This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ----------------------------------------------------------------------- | |
* Title: advent.c | |
* Flicker 4 LEDs | |
* | |
* Author: Alexander Weber | |
* http://tinkerlog.com | |
* Date: 22.11.2009 | |
* Hardware: ATtiny13v | |
* Software: CrossPack-AVR-20090415 | |
* | |
* Credits: | |
* This code is based heavily on sritesmods version. | |
* Find the original at http://spritesmods.com/?art=minimalism&f=gpl | |
* | |
* Changes: | |
* - support 4 LEDs | |
* - added a bit of sampling for light detection | |
* - moved the "power down" out of the ISR, was always resetting | |
* - removed callibration, replaced by hardwired value. | |
*/ | |
#include <avr/io.h> | |
#include <util/delay.h> | |
#include <avr/interrupt.h> | |
#include <avr/eeprom.h> | |
#include <avr/pgmspace.h> | |
#include <avr/sleep.h> | |
#include <avr/wdt.h> | |
#define LED1 PB4 | |
#define LED2 PB3 | |
#define LED3 PB2 | |
#define LED4 PB1 | |
#define ADC2 2 | |
#define AMBIENT_LIGHT 300 | |
#define TRUE 1 | |
#define FALSE 0 | |
//Bunch o' random numbers: I'm too lazy to write or port a real random number | |
//generator. | |
//generated using bash: | |
//for x in `seq 0 255`; do echo -n $(($RANDOM%256)),; done | |
uint8_t const randomvals[] PROGMEM = { | |
234,191,103,250,144,74,39,34,215,128,9,122,144,74,137,105,123,218,158,175,205, | |
118,149,13,98,7,173,179,194,97,115,110,213,80,220,142,102,102,36,152,90,135, | |
105,176,173,49,6,197,48,140,176,122,4,53,83,216,212,202,170,180,214,53,161, | |
225,129,185,106,22,12,190,97,158,170,92,160,194,134,169,98,246,128,195,24, | |
198,165,156,77,126,113,136,58,156,196,136,41,246,164,84,138,171,184,42,214, | |
203,128,89,39,198,85,140,148,149,36,215,78,170,234,131,124,152,239,154,214, | |
130,194,49,3,69,248,120,179,101,163,131,124,184,148,213,118,213,81,177,149, | |
58,213,33,201,63,10,195,215,190,7,86,245,128,9,8,40,102,51,125,94,92,5,159, | |
75,253,158,40,4,6,178,241,92,124,73,248,1,157,61,50,86,136,113,22,16,171,209, | |
230,144,240,14,188,2,167,22,88,57,50,86,171,73,114,175,34,226,245,57,180,111, | |
220,186,170,242,141,229,49,158,30,82,161,49,124,65,139,24,95,14,133,65,238, | |
116,180,190,49,130,30,30,59,93,173,139,19,187,2,163,102,26,255,23,239,196,19, | |
6,162 | |
}; | |
uint8_t mode_ee EEMEM = 0; // stores the mode in eeprom | |
static volatile uint8_t sleep = 0; | |
// Gets a semi-random number between 0 and 255 | |
uint8_t getRandom(void) { | |
//This'll probably give a warning because we use it uninitialised. Little | |
//does the compiler know: that's actually what we _want_ :) | |
static uint8_t random1, random2; | |
random1++; | |
if (random1 == 0) { | |
random2 += 0x41; | |
} | |
return pgm_read_byte(randomvals + random1) ^ random2; | |
} | |
uint16_t getLight(void) { | |
uint16_t val = 0; | |
uint8_t i; | |
// measure pb4 using internal ref | |
ADMUX = (1 << REFS0) | ADC2; | |
// enable ADC, prescaler 8 | |
ADCSRA = (1 << ADEN) | 3; | |
// kill all leds | |
PORTB &= ~((1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4)); | |
_delay_ms(5); | |
// let led generate some voltage | |
DDRB &= ~(1 << LED1); | |
_delay_ms(5); | |
// warm up the ADC, discard the first conversion | |
ADCSRA |= (1 << ADSC); | |
while (ADCSRA & (1 << ADSC)); | |
for (i = 0; i < 4; i++) { | |
_delay_ms(5); | |
ADCSRA |= (1 << ADSC); | |
while (ADCSRA & (1 << ADSC)); | |
val += ADC; | |
} | |
val >>= 2; | |
ADCSRA = 0; // disable adc | |
DDRB |= (1 << LED1); // re-enable led | |
return val; | |
} | |
void powerDown(void) { | |
// Go to sleep until we're woken up by the wdt. | |
} | |
ISR(WDT_vect) { | |
//check if it's still dark | |
sleep = (getLight() > AMBIENT_LIGHT) ? TRUE : FALSE; | |
} | |
int main(void) { | |
uint8_t lval1, lval2, lval3, lval4; | |
uint8_t i, x, y; | |
uint8_t mode; | |
// set up wdt | |
wdt_enable(WDTO_2S); | |
WDTCR |= 0x40; // WDT generates interrupts instead of resets now. | |
// We want interrupts because a reset clears our nice random | |
// seeds, and an interrupt doesn't. | |
for (i = 0; i < 10; i++) { | |
_delay_ms(100); | |
} | |
// retrieve mode from eeprom and write back mode + 1 | |
mode = eeprom_read_byte(&mode_ee); | |
mode = mode % 4; | |
eeprom_write_byte(&mode_ee, mode + 1); | |
sei(); | |
// go directly into sleep mode and lets wake up by the wdt | |
sleep = TRUE; | |
// enable leds | |
DDRB = (1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4); | |
while (1) { | |
WDTCR |= 0x40; // make sure wdt keeps generating an int instead of a reset | |
if (sleep) { | |
// switch off all LEDs and power down | |
PORTB &= ~((1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4)); | |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); | |
sleep_mode(); | |
} | |
else { | |
// get a random value for the leds intensity | |
lval1 = getRandom(); | |
lval2 = getRandom(); | |
lval3 = getRandom(); | |
lval4 = getRandom(); | |
// Manually do some pwm | |
for (x = 0; x < 20; x++) { | |
if (mode == 0) { | |
PORTB |= (1 << LED1); | |
} | |
else if (mode == 1) { | |
PORTB |= (1 << LED1) | (1 << LED2); | |
} | |
else if (mode == 2) { | |
PORTB |= (1 << LED1) | (1 << LED2) | (1 << LED3); | |
} | |
else if (mode == 3) { | |
PORTB |= (1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4); | |
} | |
for (y = 0; y != 255; y++) { | |
if (y == lval1) { | |
PORTB &= ~(1 << LED1); | |
} | |
if (y == lval2) { | |
PORTB &= ~(1 << LED2); | |
} | |
if (y == lval3) { | |
PORTB &= ~(1 << LED3); | |
} | |
if (y == lval4) { | |
PORTB &= ~(1 << LED4); | |
} | |
_delay_us(5); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment