RGB driver
Abstract
This project was for a Halloween prop wand. A tiny circuit board with a AA battery pack and RGB LED was held in the hand with a ~10" acrylic light pipe to pose as the wand. The wand had 4 modes.
RGB cycle mode - The wand cycled through the RGB color spectrum slowly fading into surrounding colors.
Color lock - Locks the wand onto the currently displayed color.
"Tink" mode - The wand slowly pulsates a tan color, much like what you would picture Tinkerbell's wand doing.
Off - Off!
Video of said wand: http://www.youtube.com/watch?v=B4l27Mpw3fE
Parts
Attiny45
RGB LED
NOMO button
2 cell battery pack
Light pipe
Code
Currently there is a bug I was unable to trace that causes the light to very quickly go out at what seems to be random intervals. It’s not totally obvious so I let it slide.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m) ((p) ^= (m))
#define bit_write(c,p,m) (c ? bit_set(p,m) : bit_clear(p,m))
#define BIT(x) (0x01 << (x))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))
#define NOP asm("nop")
#define OUT 1
#define IN 0
#define RED_DEF 2
#define BLUE_DEF 0
#define GREEN_DEF 1
#define PRELOAD 0xCE
volatile uint8_t red=0, green=0, blue=0, mode=0, RGB_mode=0, PWM=100, t0_preload=0xCF,
last_mode, tink_play=0;
volatile uint16_t button_cooldown=0;
//this is kind of like a play file
//the rutine will play though this file in order
//format is R, G, B in % of light
static unsigned char file1[] PROGMEM =
{
20,10,0,
22,11,0,
25,12,0,
28,15,0,
31,18,0,
35,23,0,
38,25,0,
42,30,0,
45,35,0,
50,40,0,
55,45,0,
59,50,0,
65,53,0,
70,58,0,
75,60,0,
85,64,0,
90,70,0,
95,73,0,
100,76,0,
100,78,0,
100,82,0,
100,84,0,
100,84,0,
100,84,0,
95,80,0,
92,76,0,
90,73,0,
87,68,0,
82,62,0,
79,60,0,
75,55,0,
72,53,0,
70,50,0,
68,49,0,
65,46,0,
63,44,0,
60,42,0,
50,40,0,
47,39,0,
44,37,0,
41,34,0,
38,32,0,
36,30,0,
34,28,0,
32,26,0,
30,24,0,
28,22,0,
26,20,0,
24,18,0,
22,16,0,
20,14,0
};
ISR(TIMER0_OVF_vect){ //SPECIAL COLOR STUFFF
TCNT0=t0_preload;
if(mode==1){ //rgb mode
switch (RGB_mode){ //0 is red++, 1 is green++, 2 is blue ++;
case 0:
blue--;
red++;
if(red==100){
RGB_mode=1;
}
break;
case 1:
red--;
green++;
if(green==100){
RGB_mode=2;
}
break;
case 2:
green--;
blue++;
if(blue==100){
RGB_mode=0;
}
default:
break;
}
}
else if(mode==3){ //rgb mode
red=pgm_read_byte(&file1[tink_play]);
green=pgm_read_byte(&file1[++tink_play]);
blue=pgm_read_byte(&file1[++tink_play]);
tink_play++;
if(tink_play>147){
tink_play=0;
}
}
}
ISR(TIMER1_OVF_vect){ //COLOR UPDATE
TCNT1=PRELOAD;
if(bit_get(PINB, BIT(3))==0){ //if button pressed
if(button_cooldown==0){
mode++;
button_cooldown=2000;
}
}
if(button_cooldown > 0){
button_cooldown--;
}
if(PWM==red){
bit_set(PORTB, BIT(RED_DEF));
}
if(PWM==green){
bit_set(PORTB, BIT(GREEN_DEF));
}
if(PWM==blue){
bit_set(PORTB, BIT(BLUE_DEF));
}
PWM--;
if (PWM==0){
bit_clear(PORTB, BIT(RED_DEF));
bit_clear(PORTB, BIT(GREEN_DEF));
bit_clear(PORTB, BIT(BLUE_DEF));
PWM=100;
}
}
void timer0_setup(void){
TIMSK|=(1<<TOIE0);
TCNT0=t0_preload;
TCCR0B=(1<<CS02)|(0<<CS01)|(1<<CS00); // 1024 div
}
void timer0_stop(void){
bit_clear(TIMSK, (BIT(TOIE0)));
TCCR0B=(0<<CS13)|(0<<CS12)|(0<<CS11)|(0<<CS10);
}
void timer1_setup(void){ //COLOR UPDATE
TCNT1=PRELOAD;
TIMSK|=(1<<TOIE1);
TCCR1=(0<<CS13)|(1<<CS12)|(0<<CS11)|(1<<CS10); //16 dev
}
void port_setup(void){
DDRB = (IN<<DDB3)|(OUT<<BLUE_DEF)|(OUT<<GREEN_DEF)|(OUT<<RED_DEF);
PORTB = (1<<PORTB3); //enable pullup
}
int main(){
port_setup();
timer1_setup();
sei();
for(;;){
if (mode >= 5){
mode=0;
last_mode=0;
}
switch (mode){
case 0: //off
red=0;
green=0;
blue=0;
break;
case 1: //RGB mode
if(last_mode == 0){
last_mode=mode;
blue=100;
red=0;
green=0;
RGB_mode=0;
t0_preload=0x00; //
NOP;
timer0_setup();
}
break;
case 2: //lock
if(last_mode == 1){
last_mode=mode;
timer0_stop();
}
break;
case 3: //tink mode
if(last_mode == 2){
last_mode=mode;
t0_preload=0x00; //
timer0_setup();
}
break;
case 4: //white
if(last_mode == 3){
last_mode=mode;
timer0_stop();
}
red=100;
green=100;
blue=100;
break;
default:
break;
}
}
}