Arduino Source Code: 8x8 RGB LED Matrix - Random Colors using Entropy library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Random LED Dots | |
// Based on Entropy library using watchdog timer jitter | |
// https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library | |
// Ed Nisley - KE4ANU - August 2016 | |
#include <Entropy.h> | |
//---------- | |
// Pin assignments | |
const byte PIN_HEARTBEAT = 8; // DO - heartbeat LED | |
const byte PIN_SYNC = A3; // DO - scope sync | |
const byte PIN_LATCH = 4; // DO - shift register latch clock | |
const byte PIN_DIMMING = 9; // AO - LED dimming control | |
// These are *hardware* SPI pins | |
const byte PIN_MOSI = 11; // DO - data to shift reg | |
const byte PIN_MISO = 12; // DI - data from shift reg (unused) | |
const byte PIN_SCK = 13; // DO - shift clock to shift reg (also Arduino LED) | |
const byte PIN_SS = 10; // DO - -slave select (must be positive for SPI output) | |
//---------- | |
// Constants | |
#define UPDATE_MS 5 | |
//---------- | |
// Globals | |
// LED selects are high-active bits and low-active signals: flipped in UpdateLEDs() | |
// *exactly* one row select must be active in each element | |
typedef struct { | |
const byte Row; | |
byte ColR; | |
byte ColG; | |
byte ColB; | |
} LED_BYTES; | |
// altering the number of rows & columns will require substantial code changes... | |
#define NUMROWS 8 | |
#define NUMCOLS 8 | |
LED_BYTES LEDs[NUMROWS] = { | |
{0x80,0,0,0}, | |
{0x40,0,0,0}, | |
{0x20,0,0,0}, | |
{0x10,0,0,0}, | |
{0x08,0,0,0}, | |
{0x04,0,0,0}, | |
{0x02,0,0,0}, | |
{0x01,0,0,0}, | |
}; | |
byte RowIndex; | |
#define LEDS_ON 0 | |
#define LEDS_OFF 255 | |
unsigned long MillisNow; | |
unsigned long MillisThen; | |
//-- Helper routine for printf() | |
int s_putc(char c, FILE *t) { | |
Serial.write(c); | |
} | |
//-- Useful stuff | |
// Free RAM space monitor | |
// From http://playground.arduino.cc/Code/AvailableMemory | |
uint8_t * heapptr, * stackptr; | |
void check_mem() { | |
stackptr = (uint8_t *)malloc(4); // use stackptr temporarily | |
heapptr = stackptr; // save value of heap pointer | |
free(stackptr); // free up the memory again (sets stackptr to 0) | |
stackptr = (uint8_t *)(SP); // save value of stack pointer | |
} | |
void TogglePin(char bitpin) { | |
digitalWrite(bitpin,!digitalRead(bitpin)); // toggle the bit based on previous output | |
} | |
void PulsePin(char bitpin) { | |
TogglePin(bitpin); | |
TogglePin(bitpin); | |
} | |
//--------- | |
//-- SPI utilities | |
void EnableSPI(void) { | |
digitalWrite(PIN_SS,HIGH); // make sure this is high! | |
SPCR |= 1 << SPE; | |
} | |
void DisableSPI(void) { | |
SPCR &= ~(1 << SPE); | |
} | |
void WaitSPIF(void) { | |
while (! (SPSR & (1 << SPIF))) { | |
// TogglePin(PIN_HEARTBEAT); | |
continue; | |
} | |
} | |
byte SendRecSPI(byte Dbyte) { // send one byte, get another in exchange | |
SPDR = Dbyte; | |
WaitSPIF(); | |
return SPDR; // SPIF will be cleared | |
} | |
void UpdateLEDs(byte i) { | |
SendRecSPI(~LEDs[i].ColB); // low-active outputs | |
SendRecSPI(~LEDs[i].ColG); | |
SendRecSPI(~LEDs[i].ColR); | |
SendRecSPI(~LEDs[i].Row); | |
analogWrite(PIN_DIMMING,LEDS_OFF); // turn off LED to quench current | |
PulsePin(PIN_LATCH); // make new shift reg contents visible | |
analogWrite(PIN_DIMMING,LEDS_ON); | |
} | |
//--------------- | |
// Set LED from integer | |
// On average, this leaves the LED unchanged for 1/8 of the calls... | |
void SetLED(unsigned long Value) { | |
byte Row = Value & 0x07; | |
byte Col = (Value >> 3) & 0x07; | |
byte Color = (Value >> 6) & 0x07; | |
byte BitMask = (0x80 >> Col); | |
// printf("%u %u %u %u\r\n",Row,Col,Color,BitMask); | |
LEDs[Row].ColR &= ~BitMask; | |
LEDs[Row].ColR |= (Color & 0x04) ? BitMask : 0; | |
LEDs[Row].ColG &= ~BitMask; | |
LEDs[Row].ColG |= (Color & 0x02) ? BitMask : 0; | |
LEDs[Row].ColB &= ~BitMask; | |
LEDs[Row].ColB |= (Color & 0x01) ? BitMask : 0; | |
} | |
//------------------ | |
// Set things up | |
void setup() { | |
pinMode(PIN_HEARTBEAT,OUTPUT); | |
digitalWrite(PIN_HEARTBEAT,HIGH); // show we arrived | |
pinMode(PIN_SYNC,OUTPUT); | |
digitalWrite(PIN_SYNC,LOW); | |
pinMode(PIN_MOSI,OUTPUT); // SPI-as-output is not strictly necessary | |
digitalWrite(PIN_MOSI,LOW); | |
pinMode(PIN_SCK,OUTPUT); | |
digitalWrite(PIN_SCK,LOW); | |
pinMode(PIN_SS,OUTPUT); | |
digitalWrite(PIN_SS,HIGH); // OUTPUT + HIGH is required to make SPI output work | |
pinMode(PIN_LATCH,OUTPUT); | |
digitalWrite(PIN_LATCH,LOW); | |
Serial.begin(57600); | |
fdevopen(&s_putc,0); // set up serial output for printf() | |
printf("Random LED Dots - Watchdog Entropy\r\nEd Nisley - KE4ZNU - August 2016\r\n"); | |
Entropy.initialize(); // start up entropy collector | |
//-- Set up SPI hardware | |
SPCR = B01110001; // Auto SPI: no int, enable, LSB first, master, + edge, leading, f/16 | |
SPSR = B00000000; // not double data rate | |
EnableSPI(); // turn on the SPI hardware | |
SendRecSPI(0); // set valid data in shift registers: select Row 0, all LEDs off | |
//-- Dimming pin must use fast PWM to avoid beat flicker with LED refresh rate | |
// Timer 1: PWM 9 PWM 10 | |
analogWrite(PIN_DIMMING,LEDS_OFF); // disable column drive (hardware pulled it low before startup) | |
TCCR1A = B10000001; // Mode 5 = fast 8-bit PWM with TOP=FF | |
TCCR1B = B00001001; // ... WGM, 1:1 clock scale -> 64 kHz | |
//-- lamp test: send a white flash through all LEDs | |
printf("Lamp test begins: white flash each LED..."); | |
digitalWrite(PIN_HEARTBEAT,LOW); // turn off while panel blinks | |
analogWrite(PIN_DIMMING,LEDS_ON); // enable column drive | |
for (byte i=0; i<NUMROWS; i++) { | |
for (byte j=0; j<NUMCOLS; j++) { | |
LEDs[i].ColR = LEDs[i].ColG = LEDs[i].ColB = 0x80 >> j; | |
for (byte k=0; k<NUMROWS; k++) { | |
UpdateLEDs(k); | |
delay(25); | |
} | |
LEDs[i].ColR = LEDs[i].ColG = LEDs[i].ColB = 0; | |
} | |
} | |
UpdateLEDs(NUMROWS-1); // clear the last LED | |
printf(" done!\r\n"); | |
//-- Preload LEDs with random values | |
digitalWrite(PIN_HEARTBEAT,LOW); | |
uint32_t rn = Entropy.random(); | |
printf("Preloading LED array with seed: %08lx\r\n",rn); | |
randomSeed(rn); | |
for (byte Row=0; Row<NUMROWS; Row++) { | |
for (byte Col=0; Col<NUMCOLS; Col++) { // Col runs backwards, but we don't care | |
LEDs[Row].ColR |= random(2) << Col; // random(2) returns 0 or 1 | |
LEDs[Row].ColG |= random(2) << Col; | |
LEDs[Row].ColB |= random(2) << Col; | |
} | |
UpdateLEDs(Row); | |
} | |
check_mem(); | |
printf("SP: %u HP: %u Free RAM: %u\r\n",stackptr,heapptr,stackptr - heapptr); | |
printf("Running...\r\n"); | |
MillisThen = millis(); | |
} | |
//------------------ | |
// Run the test loop | |
void loop() { | |
unsigned long Hash; | |
uint32_t rn; | |
MillisNow = millis(); | |
// Re-seed the generator whenever we get enough entropy | |
if (Entropy.available()) { | |
digitalWrite(PIN_HEARTBEAT,HIGH); | |
rn = Entropy.random(); | |
// printf("Random: %08lx ",rn); | |
randomSeed(rn); | |
digitalWrite(PIN_HEARTBEAT,LOW); | |
} | |
// If it's time for a change, whack a random LED | |
if ((MillisNow - MillisThen) > UPDATE_MS) { | |
MillisThen = MillisNow; | |
SetLED(random()); | |
} | |
// Refresh LED array to maintain the illusion of constant light | |
UpdateLEDs(RowIndex++); | |
if (RowIndex >= NUMROWS) { | |
RowIndex = 0; | |
PulsePin(PIN_SYNC); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More detail on my blog at http://wp.me/poZKh-65t