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
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
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
Thanks for the advice!
Are you sure? yes | no