If you need to control a lot of inputs / outputs on a MicroController, Shift registers are the way to go. In this project I’m using shift registers to build a circuit that control endless LEDS from 5 pins of a Micro controller
I’m making a LED display inspired by the old-timey neon signs with handwritten font; The LEDs will light up one by one to illuminate the sign.
In this project i used the Digispark attiny85: These tiny boards only have 6 GPIO pins so to manage a large amount of LEDS with a limited number of pins, you’ll need shift registers.
What are shift register?
Shift registers come in multiple flavors, but are in short chips that take data input, store it, and then output it. The shifting the name indicates comes from the way this is done; Data is shifted one position downrange with each clock pulse.
The shift registers used here are SN74HC595N`s made by Texas Instrument. These are what is known as “SIPO” registers, which is short for “Serial In, Parallel Out. These chips allow you to input serial data and get parallel output on the output pins of the register. This is very useful for projects where you need to control a lot of output with a limited amount of pins on your micro controller.
Each IC has 16 pins, with the following ins and outs, according to their data sheet.
8 of these are output pins (Q*).
Here is a short list of what the other 8 do:
VCC = Positive voltage pin
GND = Ground pin
SER = DS = Serial Input
OE = Output Enabled
RCLK = ST_CP = Latch pin
SRCLK = SH_CP = Serial clock pin
SRCLR = MR = Clear register
QH = Output Pin
The shift register works by taking input and shifting the bits one place on the rising edge of the clock pin, meaning that it will shift in one bit with the value of the input pin at the point in time when the value of the clock pin changes from low to high.
If the Input is low at that point, it will shift in a value of 0. If the Input is high, it will shift in a 1.
As each register only has 8 bits of storage, it can only contain 8 binary values, so when one value is pushed in at one end, the last bit in the register gets pushed out on the output pin at other end.
If you only have one shift register, thats it for the poor bit. Its pushed out into the void.
If you need more than 8 bits, you can daisy-chain registers by connecting the Output pin (QH) of one Shiftregister to the input pin of another, and thereby create a 16 bit shift register. In theory, you can do this with as many as you’d like, and make some pretty cool stuff.
The Project
I want the sign i’m making to light one LED at a time until all LEDs are lit, then flash 3 times and reset.
This means i had to access the dataPin, latchPin and clockPin to actually shift data into the registers, but also the outputEnabled to Flash all the LEDs, and the resetPin to reset after each cycle. Thats 5 pins in total. The beauty of shift registers is that the number of LEDs in the finished product is not really relevant; Each successive register after the first one is just connected to the previous one.
Testing the code and circuit, i connected the following on a breadboard:
You can see it in action at the bottom of this post.
Code
There are a lot of libraries available for Shift registers, but since i wanted to learn a bit more about these mysterious parts i have written functions for the events i need and call them to do the actions. This falls into the category of Bit Banging; Using software for serial communication by setting pin values
The functions are:
AddOneOutput,
ResetRegisters
BlinkOutputs
The names should be pretty self explanatory.
//These variables are defined here to be global(ly available)
//The counter is the number of leds you have, 16 would be 2 x 8 SN74HC595N
int dataPin = 0;
int latchPin = 1;
int clockPin = 2;
int resetPin = 3;
int outputEnabled = 4;
int WaitTime = 500;
int Counter = 16;
void setup() {
//Set modes for pins
pinMode(0, OUTPUT);
pinMode(1, OUTPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
//Clear the ShiftRegisters before we start
ResetRegisters();
}
void loop() {
for (int i=0; i <= Counter; i++){
AddOneOutput();
}
for (int i=0; i <= 3; i++){
BlinkOutputs();
}
ResetRegisters();
}
//Functions
void AddOneOutput(){
digitalWrite(latchPin, LOW);
digitalWrite(dataPin, HIGH);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);
digitalWrite(dataPin, LOW);
digitalWrite(latchPin, HIGH);
delay(WaitTime);
}
void ResetRegisters(){
digitalWrite(resetPin, LOW);
digitalWrite(clockPin, HIGH);
digitalWrite(resetPin, HIGH);
digitalWrite(clockPin, LOW);
delay(WaitTime);
}
void BlinkOutputs(){
digitalWrite(outputEnabled, HIGH);
delay(WaitTime);
digitalWrite(outputEnabled, LOW);
delay(WaitTime);
}
The result looks like this: