With the hardware in hand:

Feeding a stream of avalanche noise from a reverse-biased transistor base-emitter junction into the Arduino’s MISO pin without benefit of a shift register, this Arduino source code extracts nine bit chunks of random data to drive the 8×8 RGB LED matrix:
// Random LED Dots - from noise source
// Ed Nisley - KE4ANU - September 2015
//----------
// 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 - sampled noise input
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 DISPLAY_MS 10000ul
//----------
// Globals
// Input noise bits can produce one of four possible conditions
// Use the von Neumann extractor, discarding 00 and 11 sequences
// https://en.wikipedia.org/wiki/Randomness_extractor#Von_Neumann_extractor
// Sampling interval depends on SPI data rate
// LSB arrives first, so it's the earliest sample
#define VNMASK_A 0x00000001
#define VNMASK_B 0x01000000
enum sample_t {VN_00,VN_01,VN_10,VN_11};
typedef struct {
byte BitCount; // number of bits accumulated so far
unsigned Bits; // random bits filled from low order upward
int Bias; // tallies 00 and 11 sequences to measure analog offset
unsigned SampleCount[4]; // number of samples in each bin
} random_t;
random_t RandomData;
// 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;
} leds_t;
// altering the number of rows & columns will require substantial code changes...
#define NUMROWS 8
#define NUMCOLS 8
leds_t 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 DisplayBase;
//-- 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 DataByte) { // send one byte, get another in exchange
SPDR = DataByte;
WaitSPIF();
return SPDR; // SPIF will be cleared
}
//---------------
// Update LED shift registers with new data
// Returns noise data shifted in through MISO bit
unsigned long UpdateLEDs(byte i) {
unsigned long NoiseData = 0ul;
NoiseData |= (unsigned long) SendRecSPI(~LEDs[i].ColB); // correct for low-active outputs
NoiseData |= ((unsigned long) SendRecSPI(~LEDs[i].ColG)) << 8;
NoiseData |= ((unsigned long) SendRecSPI(~LEDs[i].ColR)) << 16;
NoiseData |= ((unsigned long) SendRecSPI(~LEDs[i].Row)) << 24;
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);
return NoiseData;
}
//---------------
// Extract random data from sampled noise input
// ... tuck it into the global bit structure
// Returns von Neumann status of the sample
byte ExtractRandomBit(unsigned long RawSample) {
byte RetVal;
switch (RawSample & (VNMASK_A | VNMASK_B)) {
case 0: // 00 - discard
RetVal = VN_00;
RandomData.Bias--;
break;
case VNMASK_A: // 10 - true
RetVal = VN_10;
RandomData.BitCount++;
RandomData.Bits = (RandomData.Bits << 1) | 1;
break;
case VNMASK_B: // 01 - false
RetVal = VN_01;
RandomData.BitCount++;
RandomData.Bits = RandomData.Bits << 1;
break;
case (VNMASK_A | VNMASK_B): // 11 - discard
RetVal = VN_11;
RandomData.Bias++;
break;
}
RandomData.Bias = constrain(RandomData.Bias,-9999,9999);
RandomData.SampleCount[RetVal]++;
RandomData.SampleCount[RetVal] = constrain(RandomData.SampleCount[RetVal],0,63999);
return RetVal;
}
//---------------
// Set LED from random bits
// Assumes the Value contains at least nine low-order random bits
// On average, this leaves the LED unchanged for 1/8 of the calls...
void SetLED(unsigned 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("Noisy LED Dots\r\nEd Nisley - KE4ZNU - September 2015\r\n");
//-- Set up SPI hardware
// LSB of SPCR set bit clock speed:
// 00 = f/4
// 01 = f/16
// 10 = f/64
// 11 = f/128
SPCR = B01110011; // Auto SPI: no int, enable, LSB first, master, + edge, leading, speed
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
// collects noise data to get some randomness going
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++) {
ExtractRandomBit(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
// We take whatever number of random bits arrived in RandomData during lamp test
digitalWrite(PIN_HEARTBEAT,LOW);
printf("Preloading LED array\r\nRandom bits %04x\r\n",RandomData.Bits);
randomSeed(RandomData.Bits);
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;
LEDs[Row].ColG |= random(2) << Col;
LEDs[Row].ColB |= random(2) << Col;
}
UpdateLEDs(Row);
}
RandomData.BitCount = 0;
RandomData.Bits = 0;
RandomData.Bias = 0;
for (byte i=0; i<4; i++) {
RandomData.SampleCount[i] = 0;
}
check_mem();
printf("SP: %u HP: %u Free RAM: %u\r\n",stackptr,heapptr,stackptr - heapptr);
printf("Running...\r\n");
DisplayBase = millis();
}
//------------------
// Run the test loop
void loop() {
byte ThisBit;
MillisNow = millis();
if (RowIndex >= NUMROWS) { // set up LED row index for this pass
RowIndex = 0;
PulsePin(PIN_SYNC);
}
if ((MillisNow - DisplayBase) >= DISPLAY_MS) {
analogWrite(PIN_DIMMING,LEDS_OFF); // turn off LED to prevent bright glitch
printf("Bias: %5d of %5u - %5u %5u %5u %5u\r\n",
RandomData.Bias,
RandomData.SampleCount[VN_00] + RandomData.SampleCount[VN_11],
RandomData.SampleCount[0],
RandomData.SampleCount[1],
RandomData.SampleCount[2],
RandomData.SampleCount[3]
);
RandomData.Bias = 0;
for (byte i=0; i<4; i++) {
RandomData.SampleCount[i] = 0;
}
// check_mem();
// printf("SP: %u HP: %u Free RAM: %u\r\n",stackptr,heapptr,stackptr - heapptr);
DisplayBase = MillisNow;
}
// Update one LED row per pass, get at most one random bit
ThisBit = ExtractRandomBit(UpdateLEDs(RowIndex++));
// Update the heartbeat LED to show bit validity
switch (ThisBit) {
case VN_00:
case VN_11:
digitalWrite(PIN_HEARTBEAT,HIGH);
break;
case VN_01:
case VN_10:
digitalWrite(PIN_HEARTBEAT,LOW);
break;
}
// If we have enough random data, twiddle one LED
if (RandomData.BitCount >= 9) {
// analogWrite(PIN_DIMMING,LEDS_OFF); // turn off LED array to prevent bright glitch
SetLED(RandomData.Bits);
RandomData.BitCount = 0;
RandomData.Bits = 0;
}
digitalWrite(PIN_HEARTBEAT,LOW);
}
Now I can point folks at the whole thing…
Comments
3 responses to “Avalanche Noise Amp: Arduino Firmware”
The best companion to this, that I have found is:
Random Sequence Generator based on Avalanche Noise (http://holdenc.altervista.org/avalanche/)
Meanwhile, up there on the right it says “Askimet’s spam filtering ”
Methinks ’tis kismet – the software is “auto kismet” – Akismet.
Good reference: I like the full-frontal analog design using a handful of discrete parts, even though I’m tinkering with the ATmega’s internal Analog Comparator instead. Hard to verify that it works without seeing the output; this may call for a firmware read-and-display loop just for the obligatory scope shot.
As nearly as I can tell, I cannot type Akismet correctly without watching. every. keystroke. Thanks for pointing that out.
[…] It’s the avalanche noise random number generator: […]