/// Christmas star lights. /// /// Mainly white, but transitions white -> colour -> white and cycles /// through colours. /// /// Michael Hope 2013 /// #include #include #include class HAL { public: enum Clock { Timer0Prescaler = 64, Timer0Rate = F_CPU / Timer0Prescaler / 256, }; enum class Pin { }; }; class Christmas { public: static void init(); static void run(); static volatile uint8_t ticks; private: /// A single point in the cycle. struct Point { /// How long to stay at this point. uint8_t seconds; uint8_t r, g, b; }; static void delay(uint16_t count); static uint8_t correct(uint8_t v); static uint8_t project(uint8_t from, uint8_t to, uint8_t at, uint8_t limit); static const uint8_t ciel8_[]; static const Point points_[]; }; volatile uint8_t Christmas::ticks; // Output compare comes out of: // OC0A (PB0) // OC0B (PB1) // OC1B (PB4) void Christmas::init() { DDRB = _BV(0) | _BV(1) | _BV(4); // Clear the output on compare. TCCR0A = 0 | (3 << COM0A0) | (3 << COM0B0) // Fast PWM. | (3 << WGM00) ; static_assert(HAL::Timer0Prescaler == 64, "Update the prescaler below."); TCCR0B = 0 // Prescale by 64. | (3 << CS00) ; TIMSK = _BV(TOIE0); TCCR1 = 0 | (3 << CS10) ; GTCCR = 0 | (1 << PWM1B) | (2 << COM1B0) ; } /// Convert an intensity to PWM. const uint8_t Christmas::ciel8_[] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 27, 30, 33, 36, 39, 43, 47, 51, 56, 61, 67, 73, 80, 88, 96, 105, 115, 125, 137, 149, 163, 178, 195, 213, 233, 255, }; #define MAX 63 /// All points to cycle through. const Christmas::Point Christmas::points_[] = { { 60, MAX, MAX, MAX }, // White { 10, 0, 0, MAX }, // Blue { 60, MAX, MAX, MAX }, { 10, MAX, 0, MAX }, // Purple { 60, MAX, MAX, MAX }, { 10, MAX, 0, 0 }, // Red { 60, MAX, MAX, MAX }, { 10, MAX, MAX, 0 }, // Yellow { 60, MAX, MAX, MAX }, { 10, 0, MAX, 0 }, // Green { 60, MAX, MAX, MAX }, { 10, 0, MAX, MAX }, // Cyan // End of list. { 0 }, }; /// Convert intensity to PWM. uint8_t Christmas::correct(uint8_t level) { return ciel8_[level]; } /// Mix the from and to levels. uint8_t Christmas::project(uint8_t from, uint8_t to, uint8_t at, uint8_t limit) { int delta = to - from; delta = delta * at / limit; return (uint8_t)(from + delta); } /// Delay the given number of ticks. void Christmas::delay(uint16_t count) { uint8_t now = ticks; while (count != 0) { while (now == ticks) { sleep_mode(); } count--; now++; } } void Christmas::run() { sei(); const Point* plast = points_ + 0; for (;;) { for (const Point* ppoint = points_; ppoint->seconds != 0; ppoint++) { const int steps = 64; // Transition between colours. for (int i = 0; i < steps; i++) { OCR0A = correct(project(plast->r, ppoint->r, i, steps)); OCR0B = correct(project(plast->g, ppoint->g, i, steps)); OCR1B = 255 - correct(project(plast->b, ppoint->b, i, steps)); // Change over 10s. delay(HAL::Clock::Timer0Rate * 10 / steps); } plast = ppoint; // Delay on this colour. delay(HAL::Clock::Timer0Rate * ppoint->seconds); } } } ISR(TIMER0_OVF_vect) { Christmas::ticks++; } int main() { Christmas::init(); Christmas::run(); return 0; }