/// Flash an RGB LED through all colours. #include #include #include #include class HAL { public: enum Clock { Timer0Prescaler = 64, Timer0Rate = F_CPU / Timer0Prescaler / 256, }; enum class Pin { }; }; class Chroma { public: static void init(); static void run(); static volatile uint8_t ticks; private: struct RGB { int16_t r, g, b; }; static void delay(uint8_t count); static uint8_t clip(int16_t v); static uint8_t correct(int16_t v); static void hsv_to_rgb(int16_t hdash, RGB* rgb); static const int16_t Scale = 256/2; static const int16_t One = Scale - 1; static const uint8_t ciel8[]; }; volatile uint8_t Chroma::ticks; // Output compare comes out of: // OC0A (PB0) // OC0B (PB1) // OC1B (PB4) void Chroma::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 Chroma::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, }; /// Clip a value to 0..1. uint8_t Chroma::clip(int16_t v) { if (v > One) { return One; } else if (v < 0) { return 0; } else { return (uint8_t)v; } } /// Clip and correct a intensity. uint8_t Chroma::correct(int16_t v) { return ciel8[clip(v)/(Scale/64)]; } /// Convert HSV to RGB. /// hdash is 0..6. void Chroma::hsv_to_rgb(int16_t hdash, RGB* rgb) { const int16_t xs = One; const int16_t xv = One; int16_t chroma = xs * xv / Scale; int16_t x = chroma * (1*Scale - abs((hdash % (Scale*2)) - Scale)) / Scale; if (hdash < 1*Scale) { *rgb = { chroma, x, 0 }; } else if (hdash < 2*Scale) { *rgb = { x, chroma, 0 }; } else if (hdash < 3*Scale) { *rgb = { 0, chroma, x }; } else if (hdash < 4*Scale) { *rgb = { 0, x, chroma }; } else if (hdash < 5*Scale) { *rgb = { x, 0, chroma }; } else { *rgb = { chroma, 0, x }; } int16_t min = xv - chroma; rgb->r += min; rgb->g += min; rgb->b += min; } /// Delay the given number of ticks. void Chroma::delay(uint8_t count) { uint8_t now = ticks; while (count != 0) { while (now == ticks) { sleep_mode(); } count--; now++; } } void Chroma::run() { sei(); for (;;) { int16_t step = 1; // Cycle around the hues. for (int16_t h = 0; h < Scale*(360/60); h += step) { RGB rgb; hsv_to_rgb(h, &rgb); OCR0A = correct(rgb.r); OCR0B = correct(rgb.g); OCR1B = 255 - correct(rgb.b); delay(5); } } } ISR(TIMER0_OVF_vect) { Chroma::ticks++; } int main() { Chroma::init(); Chroma::run(); return 0; }