SCEA117 July   2022 SN74HCS164 , SN74HCS164-Q1 , SN74HCS165 , SN74HCS165-Q1 , SN74HCS595 , SN74HCS595-Q1

 

  1.   Abstract
  2.   Trademarks
  3. 1Overview
    1. 1.1 Types of Shift Registers
    2. 1.2 Default State of a Shift Register
    3. 1.3 164 Function Shift Registers
    4. 1.4 165 Function Shift Registers
    5. 1.5 595 Function Shift Registers
    6. 1.6 Daisy-Chain Two Shift Registers
  4. 2Design Challenges
    1. 2.1 Controller Loading Limits
    2. 2.2 Operating over Large Distances
    3. 2.3 Data Loss Due to Signal Timing
    4. 2.4 Data Rate Limitations
    5. 2.5 Software Overview
  5. 3Example Design - Daisy Chain 72 Shift Registers
    1. 3.1 System Overview
    2. 3.2 System Design
    3. 3.3 Software Examples
  6. 4References

Software Examples

For this design, the software is relatively simple. For simplicity, we are using Energia and the MSP430G2553 as the controller, which is a simple and widely available microcontroller (MCU).

By using the built-in SPI module for the MCU, much of the code is simplified. The SPI.transfer() function provides both read and write simultaneously, as shown in the provided code.

Example Code for using the SPI module of MSP430G2553 in Energia to load and read shift registers.

/*
 * This is a demo file for using SPI to control 72 parallel-out (595) shift registers
 * and one parallel-in shift register (165)
 * 
 * The hardware used for verification was the MSP-EXP430G2 launchpad with the MSP430G2553 MCU
 * 
 * The USCI_B0 SPI interface is used (which is the default)
 * 
 * P1.5 --> SPI Clock
 * P1.6 --> SPI Data Input (to QH of SN74HCS165)
 * P1.7 --> SPI Data Output (to SER of first SN74HCS595)
 * 
 * P1.2 --> Register Clock and Shift / Load(active low) Output
 */

#include <SPI.h>

byte data[72];   // Data to be sent to the shift registers (1 byte per shift register)
byte sw_val = 0; // DIP switch contents

void setup() {
  // Configure RCLK (also SH/LDn) as output and set default LOW
  pinMode(P1_2, OUTPUT);
  digitalWrite(P1_2, HIGH);

  // Configure SPI module
  // Energia sets MCLK to 16 MHz by default
  // SPI clock is selected at MCLK / 128 = 125 kHz
  SPI.setModule(0);
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV128);
  SPI.begin();

  for(int i = 0; i < 72; i++){
    // This data is generic just for example purposes
    data[i] = i;     // Initialize data contents
    SPI.transfer(0); // Initialize all shift registers to contain '0'
  }

  // Load DIP switch values into the SN74HCS165
  // Also loads register values (0) to all output registers
  digitalWrite(P1_2, LOW);
  delayMicroseconds(10); // Wait 10 us (100 kHz rate)
  digitalWrite(P1_2, HIGH);
}

void loop() {
  // Load values into the shift register
  
  // The first byte of data needs to be retained. This is coming
  // in from the SN74HCS165.
  // The 'SPI.transfer()' function sends and receives simultaneously
  sw_val = SPI.transfer(data[0]);

  // For the remaining data, the received value is discarded
  for(int i = 1; i < 72; i++) {
    SPI.transfer(data[i]);
  }

  // The preceeding 128 data transfers should take ~1.024 ms to complete

  // Pulse RCLK and SH/LDn to complete
  digitalWrite(P1_2, LOW);
  delayMicroseconds(10); // Wait 10 us (100 kHz rate)
  digitalWrite(P1_2, HIGH);

  // 32 ms is added to have a 'frame length' of ~33ms (30 fps)
  delay(32);

  // In a real system, other operations would replace the above delay.
  // For example, the 'data' will likely need to be created for each
  // frame, or loaded from some memory to provide the desired effect.
  
}

Example Code for using GPIOs of MSP430G2553 in Energia to load and read shift registers.

/*
 * This is a demo file for using GPIOs to control shift registers.
 * 
 * This code loads values into four shift registers then reads back the four values.
 * The shift registers are loaded as though they are driving seven-segment displays.
 * 
 * The hardware used was the MSP-EXP430G2 launchpad with the MSP430G2553 MCU
 * Four 595-type shift registers are connected in series
 * 
 * P1.2 --> Register Clock
 * P1.5 --> Serial Clock
 * P1.6 --> Data Input
 * P1.7 --> Data Output
 */

// The following pin definitions are chosen arbitrarily.
// Any GPIO can be mapped to any of the following signals.
static const uint8_t RCLK   = P1_2; // Output -- Register Clock (rising edge)
static const uint8_t SRCLK  = P1_5; // Output -- Serial Clock (rising edge)
static const uint8_t DI     = P1_6; // Input  -- Serial data from shift registers
static const uint8_t DO     = P1_7; // Output -- Serial data to shift registers

#include <SPI.h>

// Seven segment interface data
// digit[#] will display # on the seven segment display
byte digit[] =  {0b00111111, 
                 0b00000110, 
                 0b01011011, 
                 0b01001111, 
                 0b01100110, 
                 0b01101101,
                 0b01111101,
                 0b00000111,
                 0b01111111,
                 0b01101111};
// dp only lights up the decimal point for the seven segment
// this is intended to be used with digit[] and a logical OR
byte dp = 0b10000000;

// Global counter
int i = 0;
int value = 0;

void pulseSRCLK() {
  // Advance the shift registers when called
  digitalWrite(SRCLK, HIGH);
  delayMicroseconds(1); // Wait 1 us
  digitalWrite(SRCLK, LOW);
}

void pulseRCLK() {
  // Loads serial register data into the output registers when called
  digitalWrite(RCLK, HIGH);
  delayMicroseconds(1); // Wait 1 us
  digitalWrite(RCLK, LOW);
}

int SR_transfer_byte(byte tx_val) {
  // This function writes 'tx_val' to the first shift register
  // while simultaneously reading 'rx_val' in from the last shift register
  int rx_val = 0;   // Initialize receive value
  int read_val = 0; // Initialize read value
  
  for( int i = 0; i < 8; i++ ) {
    // Loop over eight bits
    if( (0b00000001 << (7-i)) & tx_val )
      // When the 'i'th bit in tx_val is 'high'
      digitalWrite( DO, HIGH );
    else
      // When the 'i'th bit in tx_val is 'low'
      digitalWrite( DO, LOW );

  read_val = digitalRead( DI );
  rx_val = (read_val << (7-i)) | rx_val;

  // Pulse the serial clock
  pulseSRCLK();
  
  } // byte send/receive complete

  return rx_val;
  
}

// The setup() function is called once at startup and is used for configuration.
void setup() {
  // Configure pin modes
  pinMode(SRCLK, OUTPUT);
  pinMode(RCLK, OUTPUT);
  pinMode(DO, OUTPUT);
  pinMode(DI, INPUT);

  // Initialize output values
  digitalWrite(SRCLK, LOW);
  digitalWrite(RCLK, LOW);
  digitalWrite(DO, LOW);

  // Load in values to all 4 shift registers
  SR_transfer_byte(digit[1]);
  SR_transfer_byte(digit[2]);
  SR_transfer_byte(digit[3]);
  SR_transfer_byte(digit[4]);
  pulseRCLK();

  // Initialize 'value' for first loop
  value = digit[5];
  delay(500);
}

// The loop() function is called after the setup() function and loops forever
void loop() {
  value = SR_transfer_byte(value);
  pulseRCLK();
  delay(500);
  // For this example, the values are looped and the imaginary 
  // 4 digit 7-segment display would show:
  // 1 2 3 4
  // 2 3 4 5
  // 3 4 5 1
  // 4 5 1 2
  // 5 1 2 3 -- this would loop forever
}