blingM

For other supported Arduino products from Adafruit: Shields, accessories, etc.

Moderators: adafruit_support_bill, adafruit

blingM

Postby mtbf0 » Thu Feb 14, 2008 2:36 am

Image

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 ...
User avatar
mtbf0
 
Posts: 1642
Joined: Fri Nov 09, 2007 11:59 pm
Location: oakland ca

Postby mtbf0 » Thu Feb 14, 2008 10:50 pm

hey, i got a question...

i'm using pretty low value resistors to limit the current to my leds and they seem to reach their maximum brightness at about a 50% duty cycle, so i've essentially only got seven bits of resolution.

what i was wondering was if i used higher resistor values would i get finer control of the led brightness. right now i'm using 100 Ohms.

took a nap today instead of working on fading and scripting like i was supposed to.

bad mailman.
User avatar
mtbf0
 
Posts: 1642
Joined: Fri Nov 09, 2007 11:59 pm
Location: oakland ca

Postby Entropy » Fri Feb 15, 2008 8:33 am

mtbf0 wrote:hey, i got a question...

i'm using pretty low value resistors to limit the current to my leds and they seem to reach their maximum brightness at about a 50% duty cycle, so i've essentially only got seven bits of resolution.

what i was wondering was if i used higher resistor values would i get finer control of the led brightness. right now i'm using 100 Ohms.

took a nap today instead of working on fading and scripting like i was supposed to.

bad mailman.

If I recall correctly, human vision (esp. perception of brightness) is logarithmic, just like our hearing.

So the difference between 50% and 100% brightness is not nearly as great as you would think it would be.
Entropy
 
Posts: 472
Joined: Mon Jan 07, 2008 11:43 pm
Location: Owego, NY USA

Postby mtbf0 » Sun Feb 17, 2008 1:47 pm

argh.

led fading. conceptually simple. practically annoying.

at its simplest.
Code: Select all
set pwm duty cycle
wait
increase or decrease duty cycle
repeat 'til done

however on the ThingM BlinkM, which i hope to emulate, you have a variable starting and ending brightness and variable duration.

the default duration appears, from the datasheet, to be 5 seconds with the duty cycle updated at 30Hz. so we can calculate the change in intensity at each step as
Code: Select all
deltaIntesity = (startIntensity - endIntensity) / (duration * rate)

and this has to be a floating point operation. and my little arduino is an 8 bit processor. with only integer operations in hardware. and i may be trying to do as many as six fades contemporaneously all while my timer2 interrupt keeps firing 31250 times a second.

i'm thinking of using a 16 bit fixed point scheme. 8 bit integer. 8 bit fraction. it'll require one 16 bit integer divide and duration * rate 16 bit adds, the high order byte of which will give me my duty cycle at each step.

or i could just code up the brute force method and see how it looks.
User avatar
mtbf0
 
Posts: 1642
Joined: Fri Nov 09, 2007 11:59 pm
Location: oakland ca

Postby Entropy » Sun Feb 17, 2008 8:14 pm

There is FP emulation for the AVR. It's big (adds 3-4k flash usage to your code) and slow (??? how long it takes) but should be fine if it's used in code that only runs 30 times per second.
Entropy
 
Posts: 472
Joined: Mon Jan 07, 2008 11:43 pm
Location: Owego, NY USA

Postby mtbf0 » Tue Feb 19, 2008 3:35 pm

Entropy wrote:[
If I recall correctly, human vision (esp. perception of brightness) is logarithmic, just like our hearing.

So the difference between 50% and 100% brightness is not nearly as great as you would think it would be.


so i guess i could increase the values of my current limiting resistors which would decrease the maximum, (somewhat painful to look at), brightness of my leds and increase the apparent sensitivity of my controls.

sounds like a plan.
User avatar
mtbf0
 
Posts: 1642
Joined: Fri Nov 09, 2007 11:59 pm
Location: oakland ca


Return to Other Arduino products from Adafruit

Who is online

Users browsing this forum: No registered users and 1 guest

Stuff to buy from the Adafruit store and links to product documentation!


New Products [103]

Raspberry Pi[80]
 
FLORA[23]
 
Bunnie Studios[9]
 
FPGA[1]
 
mbed[11]
Arduino[60]
 
NETduino[14]
 
BeagleBone[24]
 
Android[6]
 
XBee[10]
More Dev Boards[30]


 
BoArduino[8]
 
SpokePOV[4]
 
TV-B-Gone[4]
 
MiniPOV[3]
 
SIM reader[3]
 
Microtouch[5]
 
Clocks & Watches[18]
 
Drawdio[4]
 
Brain Machine[1]
 
Game of Life[2]
 
MintyBoost[2]
More DIY Kits[16]


 
MaKey MaKey[3]
 
Tweet-a-Watt[5]
 
Young Engineers[33]
 
Discover Electronics[2]
 
Snap Circuits[4]
 
littleBits[3]
 
Project packs[8]


 
Breakout Boards[33]
LCDs & Displays[48]
Components & Parts[69]
Batteries & Power[49]
EL Wire/Tape/Panel[52]
LEDs[109]
 
Wireless[14]
Cables[61]
 
Lasers[6]
Sensors/Parts[145]
 
Enclosures/Cases[11]
 
Solar[11]
 
RFID / NFC[13]
Prototyping[70]
 
iDevices[13]
Tools[71]
 
Wearables[39]
 
CNC[37]
 
Robotics[29]
 
3D printing[1]
 
Materials[24]


 
Stickers[41]
 
Skill badges[55]
 
Books[25]
 
Circuit Playground[7]
 
Gift Certificates[4]