Skip to content

Instantly share code, notes, and snippets.

@aperiodic
Created December 14, 2012 19:26
Show Gist options
  • Save aperiodic/4287932 to your computer and use it in GitHub Desktop.
Save aperiodic/4287932 to your computer and use it in GitHub Desktop.
Driving a 5x5 RGB LED grid with 5-bit PWM using only some shift registers, a demuxer, and a timer interrupt.
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#define __spi_clock 13 // SCK - hardware SPI
#define __spi_latch 10
#define __spi_data 11 // MOSI - hardware SPI
#define __spi_data_in 12 // MISO - hardware SPI (unused)
#define __brightness_levels 32 // 0...15 above 28 is bad for ISR ( move to timer1, lower irq freq ! )
#define __max_brightness __brightness_levels-1
#define __TIMER1_MAX 0xFFFF // 16 bit CTR
#define __TIMER1_CNT 0x0130 // 32 levels --> 0x0130; 38 --> 0x0157 (flicker)
#define __TIMER2_MAX 0xFF // 8 bit CTR
#define __TIMER2_CNT 0xFF // max 28 levels !
/* PHYSICAL DISPLAY ATTRIBUTES */
#define ROWS 5
#define COLUMNS 5
#define LEDS ROWS*COLUMNS
/* FRAMEBUFFER MACROS */
#define FLIP_BUFFER() which_buffer=1-which_buffer
long count, ms;
byte row;
// framebuffer arrays
byte fb_r[ROWS * COLUMNS * 2], fb_g[ROWS * COLUMNS * 2], fb_b[ROWS * COLUMNS * 2], which_buffer;
int outs[COLUMNS];
void setup(void) {
Serial.begin(9600);
// setup shift register pins
pinMode(__spi_clock,OUTPUT);
pinMode(__spi_latch,OUTPUT);
pinMode(__spi_data,OUTPUT);
pinMode(__spi_data_in,INPUT);
digitalWrite(__spi_latch,LOW);
digitalWrite(__spi_data,LOW);
digitalWrite(__spi_clock,LOW);
// setup 3:8 selector pins, initialize to row 0
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
digitalWrite(2,HIGH);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
row = 0;
// setup frame buffer
which_buffer = 0;
for (int i = 0; i < LEDS * 2; i++) {
fb_r[i] = 0;
fb_g[i] = 0;
fb_b[i] = 0;
outs[i % ROWS] = 0;
}
// setup program state
count = 0;
ms = 0;
// setup hardware spi. for some reason, if this is put
// at the beginning of setup(), everything breaks.
setup_hardware_spi();
delay(10);
// setup the timer1 interrupt, which drives the LED display
setup_timer1_ovf();
}
void loop(void) {
count++;
int dly = 0;
// omitted: filling the framebuffer w/stuff
}
if (ms < 3000 || durations[program] - ms < 3000) {
int diff = min(ms, durations[program] - ms);
float t = diff/3000.0;
fb_uniform_mul(t);
}
FLIP_BUFFER();
delay(dly);
}
byte spi_transfer(byte data)
{
SPDR = data; // Start the transmission
while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
{
};
return SPDR; // return the received byte, we don't need that
}
void setup_hardware_spi(void) {
byte clr;
// spi prescaler:
// SPI2X SPR1 SPR0
// 0 0 0 fosc/4
// 0 0 1 fosc/16
// 0 1 0 fosc/64
// 0 1 1 fosc/128
// 1 0 0 fosc/2
// 1 0 1 fosc/8
// 1 1 0 fosc/32
// 1 1 1 fosc/64
SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
clr=SPSR; // clear SPI status reg
clr=SPDR; // clear SPI data reg
SPSR |= (1<<SPI2X); // set prescaler bits
}
void setup_timer1_ovf(void) {
// Arduino runs at 16 Mhz...
// Timer1 (16bit) Settings:
// prescaler (frequency divider) values: CS12 CS11 CS10
// 0 0 0 stopped
// 0 0 1 /1
// 0 1 0 /8
// 0 1 1 /64
// 1 0 0 /256
// 1 0 1 /1024
// 1 1 0 external clock on T1 pin, falling edge
// 1 1 1 external clock on T1 pin, rising edge
//
TCCR1B &= ~ ( (1<<CS12) );// | ( 1<<CS10 ));
TCCR1B |= ( (1<<CS11) | (1<<CS10) );
//normal mode
TCCR1B &= ~ ( (1<<WGM13) | (1<<WGM12) );
TCCR1A &= ~ ( (1<<WGM11) | (1<<WGM10) );
//Timer1 Overflow Interrupt Enable
TIMSK1 |= (1<<TOIE1);
TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
// enable all interrupts
sei();
}
ISR(TIMER1_OVF_vect) {
TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
byte cycle;
byte fb_off = (1-which_buffer) * LEDS;
for(cycle = __max_brightness; cycle > 0; cycle--) {
for (int i = 0; i < COLUMNS; i++) {
if (cycle == fb_r[row*COLUMNS + i + fb_off]) {
outs[row] |= 1 << (i*3);
}
if (cycle == fb_g[row*COLUMNS + i + fb_off]) {
outs[row] |= 1 << ((i*3) + 1);
}
if (cycle == fb_b[row*COLUMNS + i + fb_off]) {
outs[row] |= 1 << ((i*3) + 2);
}
}
digitalWrite(__spi_latch,LOW);
spi_transfer(outs[row] >> 8);
spi_transfer(outs[row]);
digitalWrite(__spi_latch,HIGH);
}
digitalWrite(__spi_latch,LOW);
spi_transfer(0);
spi_transfer(0);
digitalWrite(__spi_latch,HIGH);
next_row();
}
void next_row(void) {
row++;
if (row == ROWS) {
row = 0;
for (int i = 0; i < ROWS; i++) {
outs[i] = 0;
}
}
digitalWrite(2, LOW);
switch(row) {
case 0:
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, LOW);
break;
case 1:
digitalWrite(3, HIGH);
digitalWrite(4, LOW);
digitalWrite(5, LOW);
break;
case 2:
digitalWrite(3, LOW);
digitalWrite(4, HIGH);
digitalWrite(5, LOW);
break;
case 3:
digitalWrite(3, HIGH);
digitalWrite(4, HIGH);
digitalWrite(5, LOW);
break;
case 4:
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, HIGH);
break;
}
digitalWrite(2, HIGH);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment