/*
* Blink'n'man
* A blinkenbuttonleds gadget
*
* Copyright (C) 2011 B. Stultiens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#define BIT_LR 0 /* left on port B */
#define BIT_LG 1
#define BIT_LB 2
#define BIT_LIN 3
#define BIT_RR 5 /* right on port D */
#define BIT_RG 4
#define BIT_RB 3
#define BIT_RIN 2
#define BIT_BFLAG 7 /* input change flag */
#define MAX_HUE 1541
#define bl_pressed() ((buttonl & (_BV(BIT_LIN) | _BV(BIT_BFLAG))) == (_BV(BIT_LIN) | _BV(BIT_BFLAG)))
#define bl_released() ((buttonl & (_BV(BIT_LIN) | _BV(BIT_BFLAG))) == _BV(BIT_BFLAG))
#define br_pressed() ((buttonr & (_BV(BIT_RIN) | _BV(BIT_BFLAG))) == (_BV(BIT_RIN) | _BV(BIT_BFLAG)))
#define br_released() ((buttonr & (_BV(BIT_RIN) | _BV(BIT_BFLAG))) == _BV(BIT_BFLAG))
#define bl_reset() do { buttonl &= ~_BV(BIT_BFLAG); } while(0)
#define br_reset() do { buttonr &= ~_BV(BIT_BFLAG); } while(0)
static volatile uint8_t r[2], g[2], b[2];
static volatile uint8_t plr, plg, plb;
static volatile uint8_t prr, prg, prb;
static volatile uint8_t buttonl, buttonr;
static volatile uint8_t bstatel, bstater;
static volatile uint8_t bcntl, bcntr;
static volatile uint8_t timer1_fired;
ISR(TIMER0_OVF0_vect)
{
uint8_t t;
t = _BV(BIT_LIN) | _BV(BIT_LR) | _BV(BIT_LG) | _BV(BIT_LB);
if(++plr < r[0] ) t &= ~_BV(BIT_LR);
if(++plg < g[0] ) t &= ~_BV(BIT_LG);
if(++plb < b[0] ) t &= ~_BV(BIT_LB);
PORTB = t;
t = _BV(BIT_RIN) | _BV(BIT_RR) | _BV(BIT_RG) | _BV(BIT_RB);
if(++prr < r[1]) t &= ~_BV(BIT_RR);
if(++prg < g[1]) t &= ~_BV(BIT_RG);
if(++prb < b[1]) t &= ~_BV(BIT_RB);
PORTD = t;
t = PINB;
if((t ^ buttonl) & _BV(BIT_LIN))
bcntl = 0;
if(!--bcntl)
buttonl = (~t & _BV(BIT_LIN)) | _BV(BIT_BFLAG);
t = PIND;
if((t ^ buttonr) & _BV(BIT_RIN))
bcntr = 0;
if(!--bcntr)
buttonr = (~t & _BV(BIT_RIN)) | _BV(BIT_BFLAG);
}
ISR(TIMER1_COMP1_vect)
{
timer1_fired++;
}
#define NUMB (8)
#define NUM (1 << (NUMB))
#define NUMN(n) ((NUM + 1) * n)
static void hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v, uint8_t n)
{
if(h < NUMN(1)) {
h -= NUMN(0);
r[n] = v;
g[n] = (v * (uint8_t)(~((uint16_t)(s * (NUM-h)) >> NUMB))) >> 8;
b[n] = (v * (uint8_t)(~s + 1)) >> 8;
} else if(h < NUMN(2)) {
h -= NUMN(1);
r[n] = (v * (uint8_t)(~((uint16_t)(s * h) >> NUMB))) >> 8;
g[n] = v;
b[n] = (v * (uint8_t)(~s + 1)) >> 8;
} else if(h < NUMN(3)) {
h -= NUMN(2);
r[n] = (v * (uint8_t)(~s + 1)) >> 8;
g[n] = v;
b[n] = (v * (uint8_t)(~((uint16_t)(s * (NUM-h)) >> NUMB))) >> 8;
} else if(h < NUMN(4)) {
h -= NUMN(3);
r[n] = (v * (uint8_t)(~s + 1)) >> 8;
g[n] = (v * (uint8_t)(~((uint16_t)(s * h) >> NUMB))) >> 8;
b[n] = v;
} else if(h < NUMN(5)) {
h -= NUMN(4);
r[n] = (v * (uint8_t)(~((uint16_t)(s * (NUM-h)) >> NUMB))) >> 8;
g[n] = (v * (uint8_t)(~s + 1)) >> 8;
b[n] = v;
} else {
h -= NUMN(5);
r[n] = v;
g[n] = (v * (uint8_t)(~s + 1)) >> 8;
b[n] = (v * (uint8_t)(~((uint16_t)(s * h) >> NUMB))) >> 8;
}
}
static uint16_t pph;
static uint8_t ppv;
static uint8_t pps;
static int8_t ppincs;
static int8_t ppincv;
static void ping_pong_step(void)
{
hsv_to_rgb(pph, pps, ppv, 0);
hsv_to_rgb(pph, pps, ~ppv, 1);
if(!(ppv & 0x0f)) {
pph++;
if(pph > MAX_HUE)
pph = 0;
pps += ppincs;
if(pps == 210 || pps == 255)
ppincs = -ppincs;
}
ppv += ppincv;
if(ppv == 255 || ppv == 3)
ppincv = -ppincv;
}
static void color_step(void)
{
hsv_to_rgb(pph, 255, ppv, 0);
hsv_to_rgb(pph, 255, ppv, 1);
ppv += ppincv;
if(ppv == 255 || ppv == 3)
ppincv = -ppincv;
if(ppv == 3) {
pph += (MAX_HUE+1)/17;
if(pph > MAX_HUE)
pph -= MAX_HUE+1;
}
}
static uint16_t chl;
static uint16_t chr;
static void cycle_step(void)
{
hsv_to_rgb(chl++, 255, 255, 0);
hsv_to_rgb(chr++, 255, 255, 1);
if(chl > MAX_HUE)
chl = 0;
if(chr > MAX_HUE)
chr = 0;
}
static uint32_t rand_val;
static void lfsrrandom(void)
{
int c = rand_val & 1;
rand_val >>= 1;
if(c)
rand_val ^= 0xa6a6a6a6;
}
static void random_step(void)
{
lfsrrandom();
r[0] = rand_val;
g[0] = rand_val >> 8;
b[0] = rand_val >> 16;
lfsrrandom();
r[1] = rand_val;
g[1] = rand_val >> 8;
b[1] = rand_val >> 16;
}
#define MDOT (128+1) /* Dot */
#define MDASH (128+2) /* Dash */
#define MDP ( 0+1) /* Dot pause */
#define MLP ( 0+3) /* Letter pause */
#define MWP ( 0+5) /* Word pause (should be 7) */
static const prog_uint8_t sos[] = {
MDOT, MDP, MDOT, MDP, MDOT, /* S */
MLP,
MDASH, MDP, MDASH, MDP, MDASH, /* O */
MLP,
MDOT, MDP, MDOT, MDP, MDOT, /* S */
MWP,
MDASH, MDP, MDOT, /* N */
MLP,
MDOT, MDP, MDOT, /* I */
MLP,
MDOT, MDP, MDOT, MDP, MDOT, /* S */
MLP,
MDOT, MDP, MDOT, MDP, MDOT, /* S */
MLP,
MDOT, /* E */
MWP,
MDASH, MDP, MDASH, /* M */
MLP,
MDOT, MDP, MDASH, /* A */
MLP,
MDASH, MDP, MDOT, /* N */
MLP,
MDASH, MDP, MDOT, MDP, MDOT, /* D */
MWP,
};
static void handle_bl(void)
{
uint8_t i;
for(i = 0; i < sizeof(sos); i++) {
uint8_t m = pgm_read_byte(&sos[i]);
if(m & 0x80) {
hsv_to_rgb(pph, 255, 255, 0);
hsv_to_rgb(pph, 255, 255, 1);
} else {
hsv_to_rgb(0, 0, 0, 0);
hsv_to_rgb(0, 0, 0, 1);
}
m = (m & 0x7f) * 50;
while(timer1_fired < m) {
if(bl_released())
return;
}
timer1_fired = 0;
pph += 31;
if(pph > MAX_HUE)
pph -= MAX_HUE+1;
}
}
int main()
{
cli();
/* Processor init */
GIMSK = 0;
MCUCR = 0;
/* Leds output and button input with pull-up */
PORTB = _BV(BIT_LR) | _BV(BIT_LG) | _BV(BIT_LB) | _BV(BIT_LIN);
DDRB = _BV(BIT_LR) | _BV(BIT_LG) | _BV(BIT_LB);
PORTD = _BV(BIT_RR) | _BV(BIT_RG) | _BV(BIT_RB) | _BV(BIT_RIN);
DDRD = _BV(BIT_RR) | _BV(BIT_RG) | _BV(BIT_RB);
TCCR0 = 1; /* Prescale 1:1 -> 15625Hz -> PWM @ 61Hz */
TIMSK = _BV(TOIE0) | _BV(OCIE1A);
//OCR1A = 6250; /* 80Hz */
OCR1A = 10000; /* 50Hz */
TCCR1A = 0;
TCCR1B = _BV(CTC1) | _BV(CS10); /* CTC mode prescale 1:8 */
/* Set PWM counters at intervals to prevent surge currents */
plr = 0;
plg = 1;
plb = 2;
prr = 3;
prg = 4;
prb = 5;
rand_val = 0xdeadbeef;
sei(); /* Go running */
pph = 0;
ppv = 100;
pps = 250;
ppincs = -1;
ppincv = 1;
chl = 0;
chr = MAX_HUE / 6;
/* Main loop */
while(1) {
while(!br_pressed()) {
if(bl_pressed())
handle_bl();
if(timer1_fired >= 2) {
timer1_fired = 0;
ping_pong_step();
}
}
br_reset();
while(!br_pressed()) {
if(bl_pressed())
handle_bl();
if(timer1_fired >= 4) {
timer1_fired = 0;
cycle_step();
}
}
br_reset();
while(!br_pressed()) {
if(bl_pressed())
handle_bl();
if(timer1_fired >= 80) {
timer1_fired = 0;
random_step();
}
}
br_reset();
while(!br_pressed()) {
if(bl_pressed())
handle_bl();
if(timer1_fired >= 2) {
timer1_fired = 0;
color_step();
}
}
br_reset();
}
}