Close

Tested outputting to lots of LEDs via shift registers

A project log for Teensy / Arduino eurorack sequencer

Drum / voltage sequencer

lee-sampsonLee Sampson 09/27/2022 at 19:292 Comments

I wanted to figure out the best way to run many LEDs quickly without using loads of IO pins. Here I tested using shift registers. I tried two methods, shiftOut() and SPI. I ran a test setting 16bits and recorded the time. Results:

- shiftOut() = 204 μs

- SPI = 16 μs

Clearly SPI is much faster. Below are both methods.

SPI Schematic:

SPI test code:

#include <SPI.h>

//int pll_d=11; //piedino data
//int pll_c=13; //piedino clock
int pll_l=10; //piedino latch
#define DATAOUT 11//MOSI
#define DATAIN 12//MISO
#define SPICLOCK 13//SCK

int timer = 0;

uint16_t ledNumbers[17] = { 0b0000000000000000, 
                            0b1000000000000000,
                            0b0100000000000000,
                            0b0010000000000000,
                            0b0001000000000000,
                            0b0000100000000000,
                            0b0000010000000000,
                            0b0000001000000000,
                            0b0000000100000000,
                            0b0000000010000000,
                            0b0000000001000000,
                            0b0000000000100000,
                            0b0000000000010000,
                            0b0000000000001000,
                            0b0000000000000100,
                            0b0000000000000010,
                            0b0000000000000001};


void setup () {

  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(pll_l, OUTPUT);

  SPI.begin();
  Serial.begin(9600);
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(LSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV2);


  timer = micros();

  digitalWrite(pll_l,LOW);
  SPI.transfer(ledNumbers[0]);
  SPI.transfer(ledNumbers[0] >> 8);
  digitalWrite(pll_l,HIGH);
  
  timer = micros() - timer;
  Serial.println(timer);

}

void loop() {
  // put your main code here, to run repeatedly:

  for(int x = 0; x < 16; x++){
    turnOn(ledNumbers[x + 1]);
    delay(500);
  }
  
}

void turnOn(uint16_t bitNum){

  digitalWrite(pll_l,LOW);
  SPI.transfer(bitNum);
  SPI.transfer(bitNum >> 8);
  digitalWrite(pll_l,HIGH);

}


shiftOut() Schematic:

shiftOut() Code:

int DS_pin = 11;
int STCP_pin = 12;
int SHCP_pin = 8;

uint16_t ledOutput = 0;

uint16_t ledNumbers[17] = { 0b0000000000000000, 
                            0b1000000000000000,
                            0b0100000000000000,
                            0b0010000000000000,
                            0b0001000000000000,
                            0b0000100000000000,
                            0b0000010000000000,
                            0b0000001000000000,
                            0b0000000100000000,
                            0b0000000010000000,
                            0b0000000001000000,
                            0b0000000000100000,
                            0b0000000000010000,
                            0b0000000000001000,
                            0b0000000000000100,
                            0b0000000000000010,
                            0b0000000000000001};
                      

void setup()
{
  Serial.begin(115200);

  pinMode(DS_pin,OUTPUT);
  pinMode(STCP_pin,OUTPUT);
  pinMode(SHCP_pin,OUTPUT);

  turnOn(4);
  delay(500);
  turnOn(8);
  delay(500);
  turnOn(12);
  delay(500);
  turnOn(16);
  delay(500);

  turnOff(4);
  delay(500);
  turnOff(8);
  delay(500);
  turnOff(12);
  delay(500);
  turnOff(16);
}

void loop()
{


}

void turnOn(int bitNum)
{
  ledOutput |= ledNumbers[bitNum];
  digitalWrite(SHCP_pin, LOW);
  shiftOut(DS_pin, STCP_pin, LSBFIRST, ledOutput); 
  shiftOut(DS_pin, STCP_pin, LSBFIRST, ledOutput  >> 8);
  digitalWrite(SHCP_pin, HIGH);
}

void turnOff(int bitNum)
{
  ledOutput &= ~ledNumbers[bitNum];
  digitalWrite(SHCP_pin, LOW);
  shiftOut(DS_pin, STCP_pin, LSBFIRST, ledOutput); 
  shiftOut(DS_pin, STCP_pin, LSBFIRST, ledOutput  >> 8);
  digitalWrite(SHCP_pin, HIGH);
}

Discussions

Ken Yap wrote 09/28/2022 at 03:19 point

Just a couple of observations on your test program which are not worth worrying about for a test program but might be useful for a production program.

By adding the const qualifier to the lookup table you can avoid allocating two storage areas for the array, one for the mutable table and one for the initialiser. You could even add the non-standard attribute PROGMEM if data memory is running short.

Your bitmasks can actually be computed at runtime using the << operator which on some architectures is just as efficient as table lookup.

  Are you sure? yes | no

Lee Sampson wrote 09/28/2022 at 08:18 point

Thanks for the advice! 

  Are you sure? yes | no