i've started work on a six led clone of the BlinkM. i used the method described in atmel's avr appnote 136 to get 18 channels of 8 bit pwm at a frequency of round about 120Hz. at present it accepts commands over the serial port to select an led and set its color. for instance, "da" selects the first led, (led "A"), then n800080 sets the color to magenta. the argument is a hexadecimal sextet. tomorrow, i begin work on color fading. there's also a command "r", for report, that dumps some debug info because i was being phenominally stupid there for a while.
anyway, here's some code for anyone interested. it doesn't use much of the arduino api, because i needed low latency on my timer interrupt.
- Code: Select all
/* BlingM - 24 bit control of 6 RGB leds.
based on ATMEL AVR Appnote 136 and BlinkM by ThingM
by bobby cossum for theEntropyWorks.com
*/
#define BAUD 9600
#define PINS 18
volatile unsigned char iteration;
volatile unsigned char LEDBuffer[PINS];
volatile unsigned char foo1, foo2, foo3, foo4;
ISR (TIMER2_OVF_vect) {
static unsigned char portBuffD, portBuffC, portBuffB;
static unsigned char CompBuffer[PINS];
//
// first do port i/o so there's no variable latency caused by conditionals below
//
PORTD = (PORTD && 0x03) | portBuffD; // step lightly over serial RX and TX
PORTC = portBuffC;
PORTB = portBuffB;
if (iteration == 0) {
//
// don't be tempted to use a loop here. because we're in an isr we're looking for
// the fastest possible execution amd this way the array references are resolved at
// compile time.
//
CompBuffer[0] = LEDBuffer[0]; // led A - R, G, then B
CompBuffer[1] = LEDBuffer[1];
CompBuffer[2] = LEDBuffer[2];
CompBuffer[3] = LEDBuffer[3]; // led B
CompBuffer[4] = LEDBuffer[4];
CompBuffer[5] = LEDBuffer[5];
CompBuffer[6] = LEDBuffer[6]; // led C
CompBuffer[7] = LEDBuffer[7];
CompBuffer[8] = LEDBuffer[8];
CompBuffer[9] = LEDBuffer[9]; // led D
CompBuffer[10] = LEDBuffer[10];
CompBuffer[11] = LEDBuffer[11];
CompBuffer[12] = LEDBuffer[12]; // led E
CompBuffer[13] = LEDBuffer[13];
CompBuffer[14] = LEDBuffer[14];
CompBuffer[15] = LEDBuffer[15]; // led F
CompBuffer[16] = LEDBuffer[16];
CompBuffer[17] = LEDBuffer[17];
//
// set alls pins high at start of pwm period
//
portBuffD = 0xfc;
portBuffB = 0x3f;
portBuffC = 0x3f;
}
//
// as on time for each pin expires, set pin low
//
if (CompBuffer[0] == iteration) portBuffD &= ~(1 << PD2);
if (CompBuffer[1] == iteration) portBuffD &= ~(1 << PD3);
if (CompBuffer[2] == iteration) portBuffD &= ~(1 << PD4);
if (CompBuffer[3] == iteration) portBuffD &= ~(1 << PD5);
if (CompBuffer[4] == iteration) portBuffD &= ~(1 << PD6);
if (CompBuffer[5] == iteration) portBuffD &= ~(1 << PD7);
if (CompBuffer[6] == iteration) portBuffB &= ~(1 << PB0);
if (CompBuffer[7] == iteration) portBuffB &= ~(1 << PB1);
if (CompBuffer[8] == iteration) portBuffB &= ~(1 << PB2);
if (CompBuffer[9] == iteration) portBuffB &= ~(1 << PB3);
if (CompBuffer[10] == iteration) portBuffB &= ~(1 << PB4);
if (CompBuffer[11] == iteration) portBuffB &= ~(1 << PB5);
if (CompBuffer[12] == iteration) portBuffC &= ~(1 << PC0);
if (CompBuffer[13] == iteration) portBuffC &= ~(1 << PC1);
if (CompBuffer[14] == iteration) portBuffC &= ~(1 << PC2);
if (CompBuffer[15] == iteration) portBuffC &= ~(1 << PC3);
if (CompBuffer[16] == iteration) portBuffC &= ~(1 << PC4);
if (CompBuffer[17] == iteration++) portBuffC &= ~(1 << PC5);
//
// and increment counter
//
foo1 = iteration;
foo2 = portBuffD;
foo3 = portBuffB;
foo4 = portBuffC;
}
void setup (void) {
//
// set data direction registers
//
DDRD = 0xFE; // all output, except serial RX
DDRC = 0x3F; // all output, except RESET
DDRB = 0x3F; // all output, except XTAL1 & XTAL2
//
// initialize data for led buffer
//
iteration = 0;
for (int i = 0; i < PINS; i+=1)
LEDBuffer[i] = 80; // initialize all to 50% duty cycle
TCCR0B = 0; // stop timer 0
TCCR0A = 0; // get back hardware pwm pins
TCCR1B = 0; // stop timer 1
TCCR1A = 0; // get back hardware pwm pins
//
// set up timer 2, normal mode, prescale = 1, overflow interrupt enabled
//
TCCR2B = 0; // stop timer 2
TCCR2A = (1 << WGM21) | (1 << WGM20); // diasble output compare pins
TCNT2 = 0; // reset counter
TIMSK2 &= !((1 << OCIE2A) | (1 << OCIE2B)); // disable output compare match interrupts
TIMSK2 |= (1 << TOIE2); // enable timer overflow interrupt
OCR2A = 63; // timer freq = 16M / 8/ 64, ca 122Hz
TCCR2B = (1 << CS21) | (1 << WGM22); // start timer in fast pwm mode 7, with prescaler = 8
//
// set up baud rate
//
UBRR0H = ((F_CPU / 16 + BAUD / 2) / BAUD - 1) >> 8;
UBRR0L = ((F_CPU / 16 + BAUD / 2) / BAUD - 1);
//
// enable rx and tx
//
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
//
// 8 bits, no parity, 1 stop bit
//
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
unsigned char XMITBuffer[64]; // circular transmit buffer length is a power of
// to avoid using modulus operator
unsigned char XMITHead, XMITTail; // xmit buffer head and tail pointers
void USARTTransmit( unsigned char data ) {
//
// wait for empty transmit buffer
//
while ( !( UCSR0A & (1<<UDRE0)) );
//
// Send it
//
UDR0 = data;
}
unsigned char USARTReceive( void ) {
//
// wait for data to be received
//
while ( !(UCSR0A & (1<<RXC0)) );
//
// return received data
//
return UDR0;
}
//
// output a byte in hex format to USART
//
void USARTHex (unsigned char ch) {
unsigned char temp;
temp = ch >> 4;
temp += (temp > 9) ? 'a' - 10 : '0';
USARTTransmit (temp);
temp = ch & 0x0f;
temp += (temp > 9) ? 'a' - 10 : '0';
USARTTransmit (temp);
USARTTransmit (' ');
}
unsigned char ch; // input character
unsigned char args[4]; // input arguments
unsigned char argnum; // input argument counter
unsigned char diode; // currently selected led
enum {START, // enumerated input states
SELECT_DIODE,
SET_RGB1,
SET_RGB2,
SET_RGB3,
SET_RGB4,
SET_RGB5,
SET_RGB6,
ERROR=255};
unsigned char state; // input state
void loop (void) {
USARTTransmit (ch = USARTReceive ());// wait for character from USART
switch (state) {
case START :
if (ch == 'd') { // select diode, single character argument a-f
state = SELECT_DIODE;
break;
}
if (ch == 'r') { // report current state
USARTTransmit ('\n');
USARTHex (diode);
USARTTransmit ('-');
USARTHex (LEDBuffer [diode]);
USARTHex (LEDBuffer [diode + 1]);
USARTHex (LEDBuffer [diode + 2]);
USARTTransmit ('\n');
USARTHex (foo1); // iteration, from isr
USARTTransmit ('-');
USARTHex (foo2); // PORTD
USARTHex (foo3); // PORTB
USARTHex (foo4); // PORTC
USARTTransmit ('\n');
break;
}
if (ch == 'n') { // set rgb, get three undelimited hex values
state = SET_RGB1;
break;
}
state = ERROR;
break;
case SELECT_DIODE : // selects current led a-f
if ((ch >= 'a') && (ch <= 'f')) { // sanity check
diode = (ch - 'a') * 3; // set pointer to red position in le bBuffer
state = START;
}
else state = ERROR;
break;
case SET_RGB1 : // get high nibble of argument byte
argnum = 0; // set argument pointer
case SET_RGB3 :
case SET_RGB5 :
if ((ch >= '0') && (ch <= '9')) ch -= '0'; // sanity check
else if ((ch >= 'a') && (ch <= 'f')) ch -= 'a' - 10;
else {
state = ERROR;
break;
}
args[argnum] = ch << 4; // multiply by 16 and save
state += 1; // proceed to next state
break;
case SET_RGB2 : // get low nibble of argument
case SET_RGB4 :
case SET_RGB6 :
if ((ch >= '0') && (ch <= '9')) ch -= '0'; // sanity check
else if ((ch >= 'a') && (ch <= 'f')) ch -= 'a' - 10;
else {
state = ERROR;
break;
}
args[argnum++] += ch; // add to saved high nibble & increment ptr
if (state == SET_RGB6) { // got enough values ...
LEDBuffer[diode] = args[0]; // ... so save to led led buffers
LEDBuffer[diode + 1] = args[1];
LEDBuffer[diode + 2] = args[2];
state = START;
}
else state += 1; // or proceed to next state
break;
}
if (state == ERROR) { // oops, give error indication
USARTTransmit ('*');
USARTTransmit ('\n');
state = START;
}
}
// ad nauseum ...


