SCEA117 July 2022 SN74HCS164 , SN74HCS164-Q1 , SN74HCS165 , SN74HCS165-Q1 , SN74HCS595 , SN74HCS595-Q1
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
}