Wednesday, 25 September 2013 18:40

Using 74HC595 Shift Registers with Arduino

The 74HC595 is a very handy IC used in many microcontroller projects, it is a 8-bit serial-in, serial/parallel-out shift register with output latches.
 
Data is shifted on the positive-going transitions of the shift register clock input (SHCP). The data in each register is transferred to the storage register on a positive-going transition of the storage register clock input (STCP).
 
The shift register has a serial input (DS) and a serial standard output (Q7S) for cascading. Data in the storage register appears at the output whenever the output enable input (OE) is LOW.
 
This is incredibly helpful if you need more digital outputs then the 14+6 that the ATmega328 on the Arduino Uno provides, with only 3 data pins, you can control an almost unlimited amount of outputs.
 
Upon completion of this tutorial, you should be able to controlling the 74HC595 as below:
  • Using 74HC595 Shift Registers with Arduino
  • Daisy chaining 74HC595 shift registers
  • Different ways (binary, decimal, hex) to hold the data using an array
  • Using bitshift, bitwrite operators
  • Direct port access for faster manipulation of the IO pins 
 
 
74HC595 Shift Register
 
The Arduino IDE includes a shiftOut() function that can be used to control 74HC595 Shift Register. Code below is an example of a 8 channel running light. 
  1. const byte COL_COUNT = 8;
  2. //array to hold the data
  3. unsigned char sequence[COL_COUNT] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
  4. //unsigned char sequence[COL_COUNT] = {1, 2, 4, 8, 16, 32, 64, 128};
  5. //unsigned char sequence[COL_COUNT] = {0x01, 0x02, 0x04, 0x8, 0x10, 0x20, 0x40, 0x80};
  6. //Define which pins will be used for the shift register control
  7. //can be any digital pin on the Arduino
  8. int latchPin = 8; //Pin connected to ST_CP(pin 12) of 74HC595
  9. int clockPin = 12; //Pin connected to SH_CP(pin 11) of 74HC595
  10. int dataPin = 11; //Pin connected to DS(pin 14) of 74HC595
  11. void setup() {
  12. pinMode(latchPin, OUTPUT);
  13. pinMode(clockPin, OUTPUT);
  14. pinMode(dataPin, OUTPUT);
  15. }
  16. void loop()
  17. {
  18. for (int col = 0; col < COL_COUNT; col++)
  19. {
  20. digitalWrite(latchPin, LOW); //Pull latch LOW to send data
  21. shiftOut(dataPin, clockPin, MSBFIRST, sequence[col]); //Send the data
  22. digitalWrite(latchPin, HIGH); // Pull latch HIGH to stop sending data
  23. delay(200);
  24. }
  25. }
 
Circuit diagram
Example above shows three different ways (binary, decimal, hex) to hold the data using an array, there is no difference between binary, decimal and hex, they are the same numbers, only presented differently.
  • Line 4 assign variables using binary notation (0 and 1), simply prepend the symbols B to the front of the binary number.
  • Line 5 assign variables using decimal  notation (0 to 9).
  • Line 6 assign variables using hexadecimal notation (0 to 9 and A to F), simply prepend the symbols 0x to the front of the hexadecimal number.
Here is the syntax for shiftOut function:
shiftOut(dataPin, clockPin, bitOrder, value)
where bitOrder is either MSBFIRST(Most Significant Bit Firs) or LSBFIRST(Least Significant Bit First) which order to shift out the bits
 
Look at the code below
  for (int col = 0; col < COL_COUNT; col++)   
  {
    digitalWrite(latchPin, LOW); //Pull latch LOW to send data
    shiftOut(dataPin, clockPin, MSBFIRST, sequence[col]); //Send the data
    digitalWrite(latchPin, HIGH); //// Pull latch HIGH to stop sending data
    delay(200);
  }
 
When col is 7,  shiftOut command will look like this
shiftOut(11, 12, MSBFIRST, sequence[7])
          or
shiftOut(11, 12, MSBFIRST, B10000000) //binary
          or
shiftOut(11, 12, MSBFIRST, 128) //decimal
          or
shiftOut(11, 12, MSBFIRST, 0x80) //hex
shiftOut is written to output 8 bits(1 byte), meaning that each shiftOut executed will only send out 8 bits to the 74HC595 shift register.
 
 
The first example using a single dimension array of  8 set numbers to hold data for the 74HC595 shift register, and here is another example using single dimension array of  24 set numbers (8 sets in 3 groups).
const byte COL_COUNT = 8;
const byte ROW_COUNT = 3;
 
//array to hold the data
unsigned char sequence[24] = {
B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000,
B00000001, B00000011, B00000111, B00001111, B00011111, B00111111, B01111111, B11111111,
B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111
};
 
//Define which pins will be used for the shift register control
//can be any digital pin on the Arduino
int latchPin = 8;  //Pin connected to ST_CP(pin 12) of 74HC595
int clockPin = 12; //Pin connected to SH_CP(pin 11) of 74HC595
int dataPin = 11;  //Pin connected to DS(pin 14) of 74HC595
 
void setup() {    
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}
 
void loop() 
{ 
  for (int row = 0; row < ROW_COUNT; row++) 
  {
    for (int col = 0; col < COL_COUNT; col++) 
    {
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, sequence[col + row*COL_COUNT]);
      digitalWrite(latchPin, HIGH);
      delay(200);
    }
  }
}
 
Of course we can use a two dimension array to hold the data and getting same output.
const byte COL_COUNT = 8;
const byte ROW_COUNT = 3;
 
//array to hold the data
unsigned char sequence[ROW_COUNT][COL_COUNT] = {
B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000,
B00000001, B00000011, B00000111, B00001111, B00011111, B00111111, B01111111, B11111111,
B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111
};
 
//Define which pins will be used for the shift register control
//can be any digital pin on the Arduino
int latchPin = 8;  //Pin connected to ST_CP(pin 12) of 74HC595
int clockPin = 12; //Pin connected to SH_CP(pin 11) of 74HC595
int dataPin = 11;  //Pin connected to DS(pin 14) of 74HC595
 
void setup() {    
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}
 
void loop() 
{ 
  for (int row = 0; row < ROW_COUNT; row++) 
  {
    for (int col = 0; col < COL_COUNT; col++) 
    {
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, sequence[row][col]);
      digitalWrite(latchPin, HIGH);
      delay(200);
    }
  }
}
 
Move the shiftOut() function from main Loop() to another procedure, this can be especially useful when you want to output the sequence in randomly (example: generate matrix fonts).
const byte COL_COUNT = 8;
const byte ROW_COUNT = 3;
 
//array to hold the data
unsigned char sequence[ROW_COUNT][COL_COUNT] = {
B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000,
B00000001, B00000011, B00000111, B00001111, B00011111, B00111111, B01111111, B11111111,
B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111
};
 
//Define which pins will be used for the shift register control
//can be any digital pin on the Arduino
int latchPin = 8;  //Pin connected to ST_CP(pin 12) of 74HC595
int clockPin = 12; //Pin connected to SH_CP(pin 11) of 74HC595
int dataPin = 11;  //Pin connected to DS(pin 14) of 74HC595
 
int row;
 
void setup() {    
  Serial.begin(9600);
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}
 
void loop() 
{ 
    row = 0; col_display(); 
    row = 1; col_display(); 
    row = 2; col_display(); 
    row = 1; col_display(); 
    row = 0; col_display();    
}
 
void col_display()
{
    for (int col = 0; col < COL_COUNT; col++) 
    {
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, sequence[row][col]);
      digitalWrite(latchPin, HIGH);
      delay(200);
    }  
}
 
Using Bitshift operators
You can have the same output display as example above by using  Bitshift Operators. Code below using a bit shift operator to generate 8 sets of binary patterns, the left shift operator (<<) gives efficient access to exponentiation by 2, so we get those numbers: 1,2,4,8,16,32,64,128 as shown below:
00000001  00000010  00000100  00001000  00010000  00100000  01000000  10000000
const byte COL_COUNT = 8;
int latchPin = 8;  //Pin connected to ST_CP(pin 12) of 74HC595
int clockPin = 12; //Pin connected to SH_CP(pin 11) of 74HC595
int dataPin = 11;  //Pin connected to DS(pin 14) of 74HC595
 
void setup() {    
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}
 
void loop() 
{ 
  for (int col = 0; col < COL_COUNT; col++)   
  {
    int col_data = 1 << col; //bitshift left
    digitalWrite(latchPin, LOW); //Pull latch LOW to send data
    shiftOut(dataPin, clockPin, MSBFIRST, col_data); //Send the data
    digitalWrite(latchPin, HIGH); //Pull latch HIGH to stop sending data
    delay(200);
  }
}
 
 
Using bitWrite operation
Code below using bitWrite operation to generate 16 (8x2) sets of binary patterns as shown below
00000001  00000011  00000111  00001111  00011111  00111111  01111111  11111111
11111110  11111100  11111000  11110000  11100000  11000000  10000000  00000000
const byte COL_COUNT = 8;
int latchPin = 8;  //Pin connected to ST_CP(pin 12) of 74HC595
int clockPin = 12; //Pin connected to SH_CP(pin 11) of 74HC595
int dataPin = 11;  //Pin connected to DS(pin 14) of 74HC595
 
void setup() {    
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}
 
void loop() 
{ 
  int col_data = 0;
  for (int col = 0; col < COL_COUNT; col++)   
  {
    bitWrite(col_data, col, HIGH);
    digitalWrite(latchPin, LOW); //Pull latch LOW to send data
    shiftOut(dataPin, clockPin, MSBFIRST, col_data); //Send the data
    digitalWrite(latchPin, HIGH); //Pull latch HIGH to stop sending data
    delay(200);
  }
 
  for (int col = 0; col < COL_COUNT; col++)   
  {
    bitWrite(col_data, col, LOW);
    digitalWrite(latchPin, LOW); //Pull latch LOW to send data
    shiftOut(dataPin, clockPin, MSBFIRST, col_data); //Send the data
    digitalWrite(latchPin, HIGH); //Pull latch HIGH to stop sending data
    delay(200);
  }  
}
 
 
Controlling two 74HC595 Shift Register
By daisy chaining two 74HC595 shift registers, you can have 16 outputs with only 3 control lines from Arduino.  To output 16 bits (larger than 255) values, shiftOut requires a two step operation since shiftOut is written to output 8 bits (one byte)
const byte COL_COUNT = 8;
unsigned int sequence[COL_COUNT] = {0B0000000100000001, 0B0000001000000011, 0B0000010000000111, 0B0000100000001111, 0B0001000000011111, 0B10000000111111, 0B0100000001111111, 0B1000000011111111};
//unsigned int sequence[COL_COUNT] = {257, 515, 1031, 2063, 4127, 8255, 16511, 33023};
int latchPin = 8;  //Pin connected to ST_CP(pin 12) of 74HC595
int clockPin = 12; //Pin connected to SH_CP(pin 11) of 74HC595
int dataPin = 11;  //Pin connected to DS(pin 14) of 74HC595
 
void setup() {    
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}
 
void loop() 
{ 
  for (int col = 0; col < COL_COUNT; col++)   
  {
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, (sequence[col] >> 8)); //shift out highbyte
    shiftOut(dataPin, clockPin, MSBFIRST,sequence[col]); //shift out lowbyte    
    digitalWrite(latchPin, HIGH);
    delay(200);
  }
}
Note:
  • Change unsigned char sequence[COL_COUNT] to unsigned int sequence[COL_COUNT] for 16 bits serial data
  • Prepend the symbols 0B to the front of the binary number for 16 bit data
 
Here is the output:
Seq. First 74HC595 Second 74HC595
1 00000001 00000001
2 00000011 00000010
3 00000111  00000100 
4 00001111  00001000 
5 00011111  00010000 
6 00111111  00100000 
7 01111111  01000000 
8 11111111  10000000
Controlling two 74HC595 shift register 
 
 
 
Another example to controlling two 74HC595 shift register
  • The serial data for first shift register is getting from a two dimension array, and therefore its output is exactly sames as sequence[][] array
  • The serial data for second shift register is gemerated using a left shift (<<) operator
const byte COL_COUNT = 8;
const byte ROW_COUNT = 3;
 
//array to hold the data
unsigned char sequence[ROW_COUNT][COL_COUNT] = {
B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000,
B00000001, B00000011, B00000111, B00001111, B00011111, B00111111, B01111111, B11111111,
B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111
};
 
//Define which pins will be used for the shift register control
//can be any digital pin on the Arduino
int latchPin = 8;  //Pin connected to ST_CP(pin 12) of 74HC595
int clockPin = 12; //Pin connected to SH_CP(pin 11) of 74HC595
int dataPin = 11;  //Pin connected to DS(pin 14) of 74HC595
int row;
 
void setup() {    
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}
void loop() 
{ 
    row = 0; col_display(); //display first row data
    row = 1; col_display(); //display second row data
    row = 2; col_display(); //display third row data
}
 
void col_display()
{
    for (int col = 0; col < COL_COUNT; col++) 
    {
      int second74HC595_data = 1 << col+8; //serial data for 2nd 74HC595
      int data = sequence[row][col] + second74HC595_data; //1st 74HC595 serial data + 2nd 74HC595r serial data
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, (data >> 8)); //shift out highbyte (2nd 74HC595)
      shiftOut(dataPin, clockPin, MSBFIRST,data); //shift out lowbyte (1st 74HC595)
      digitalWrite(latchPin, HIGH);
      delay(200);
    }  
}
 
 
 
Direct Port access
Port registers allow for lower-level and faster manipulation of the IO pins and if you are running low on program memory, you can use these tricks to make your code smaller. 

const byte BIT_COUNT = 8;
// latchPin=Digital 8(74HC595 pin 12)
// clockPin=Digital 12(74HC595 pin 11)
// dataPin=Digital 11(74HC595 pin 14)

void setup() {
//PORTB maps to Arduino digital pins 8 to 13
//set bits 0,3,4 high, this will set digital pin 8,11,12 as output & all other pins as input
DDRB=B00011001;
}

void loop() {
  for (int col = 0; col < BIT_COUNT; col++) {
    PORTB &= ~(1<<0); //set latch pin (bit 0) low, all other pin unchange
    int data = 1 << col; //bitshift left to generate required serial data
    ShiftOutLSBFIRST(data);
    PORTB |= 1<<0; //set latch pin (bit 0) high, all other pin unchange
    delay(200);
  }
}


void ShiftOutLSBFIRST(int data) {
  for(int i = 0; i < BIT_COUNT; i++) {
    if (data & (1<<i)) { //LSB first 
      PORTB |= 1<<3; //set data pin (bit 3) high, all other pin unchange
    } else {
      PORTB &= ~(1<<3); //set data pin (bit 3) low, all other pin unchange

  }

    // generate a pulse for SERIAL CLOCK
    PORTB &= ~(1 << 4); // Set the clockPin low
    PORTB |= (1 << 4); // Set the clockPin high
    // it can also use the code below to generate SERIAL CLOCK
    // ^= toggle bit, doing this twice to generate a high low pulse
    // PORTB ^= 1 << 4; //toogle bit 3, all other pin unchange
    // PORTB ^= 1 << 4;
  }
}

 

  • If you want to control two shift registers (16 bits), chang the BIT_COUNT to 16
  • If you want MSBFIRST, change if (data & (1<<i)) {  to code below
if (data & (0B1000000000000000>>i)) //most significant bit is sent first.
 

 

 

Read 36510 times Last modified on Sunday, 12 June 2016 11:08
Back to Top