190 lines
3.5 KiB
C++
190 lines
3.5 KiB
C++
/// Flash an RGB LED through all colours.
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/sleep.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
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;
|
|
}
|