hacks/chroma/chroma.cc

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;
}