189 lines
3.9 KiB
C++
189 lines
3.9 KiB
C++
/// Christmas star lights.
|
|
///
|
|
/// Mainly white, but transitions white -> colour -> white and cycles
|
|
/// through colours.
|
|
///
|
|
/// Michael Hope <michaelh@juju.net.nz> 2013
|
|
///
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/sleep.h>
|
|
|
|
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;
|
|
}
|