Using SPI with shift registers

From HalfgeekKB
Jump to navigation Jump to search

74HC165 and similar

// 74HC165 connection via SPI

#include <SPI.h>

//  UNO   SPI   '165
//  9*    n/a   storage clock (ST_CP, /PL, DIP #1)
//  10*   NSS   shift clock inhibit (CLK INH, /CE, DIP #15)
//  11    MOSI  n/c
//  12    MISO  serial data output (Q7, DIP #9)
//  13    SCK   shift clock (SH_CP, CP, DIP #2)

// Note that the storage clock and the NSS can be anywhere. The UNO pin 10
// must be OUTPUT for the SPI master to work (the "NSS" functionality of the
// pin is applicable for SPI *in slave mode* when it is an input), but it
// need not be the actual select pin.

const uint8_t IM_STO = 9;
const uint8_t IM_NSS = 10;

const unsigned long IM_MAX_SPEED = 4000000; // A guess; could probably do way faster
const uint8_t IM_BIT_ORDER = MSBFIRST;
const uint8_t IM_SPI_MODE = SPI_MODE0;

void setup() {
  pinMode(IM_STO, OUTPUT);
  digitalWrite(IM_STO, true);
  pinMode(IM_NSS, OUTPUT);
  digitalWrite(IM_NSS, true);

  Serial.begin(115200);
  Serial.println("start");

  SPI.begin();
}

void sendStorePulse() {
  digitalWrite(IM_STO, false);
  delayMicroseconds(5);
  digitalWrite(IM_STO, true);
  delayMicroseconds(5);
}

uint8_t loadBySpi() {
  // Select device and begin transaction
  digitalWrite(IM_NSS, false);
  SPI.beginTransaction(SPISettings(IM_MAX_SPEED, IM_BIT_ORDER, IM_SPI_MODE));

  // Perform transfer
  uint8_t receivedData = SPI.transfer(0x00);

  // End transaction and deselect device
  SPI.endTransaction();
  digitalWrite(IM_NSS, true);

  return receivedData;
}

void loop() {
  sendStorePulse();
  uint8_t reading = loadBySpi();

  Serial.print("SPI: ");
  Serial.print(reading, BIN);
  Serial.println();

  delay(1000);
}


74HC595 and similar

#include <SPI.h>

//  UNO   SPI   '595
//  9*    n/a   output disable (/OE, DIP #13)
//  10*   NSS   storage clock (ST_CP, DIP #12)
//  11    MOSI  serial data in (DS, DIP #14)
//  12    MISO  n/c
//  13    SCK   shift clock (SH_CP, CP, DIP #11)

const uint8_t OM_BLK = 9;
const uint8_t OM_STO = 10;

const unsigned long OM_MAX_SPEED = 4000000; // A guess
const uint8_t OM_BIT_ORDER = LSBFIRST;
const uint8_t OM_SPI_MODE = SPI_MODE0;

void setup() {
    pinMode(OM_BLK, OUTPUT);
    digitalWrite(OM_BLK, true);
    pinMode(OM_STO, OUTPUT);
    digitalWrite(OM_STO, true);

    SPI.begin();
}

void sendBySpi(uint8_t value) {
  digitalWrite(OM_STO, false);
  SPI.beginTransaction(SPISettings(OM_MAX_SPEED, OM_BIT_ORDER, OM_SPI_MODE));
  SPI.transfer(value);
  SPI.endTransaction();
  digitalWrite(OM_STO, true);
  digitalWrite(OM_BLK, false);
}

uint8_t c = 0;

void loop() {
  c = (uint8_t)((c + 1) & 0xFF);
  sendBySpi(c);
  delay(100);
}

'165 and '595 in same transaction

The '165 and '595 devices are similar enough that they can be combined as a single conceptual SPI device. One of the select lines from each device can even be combined.

#include <SPI.h>
 
// Where
//   IM means "input module" (e.g. 74HC165) and
//   OM means "output module" (e.g. 74HC595):
 
//  UNO   SPI   Pin description
//  8*          OM output disable ('595 /OE, DIP #13)
//  9*          OM storage clock ('595 ST_CP, DIP #12)
//              IM shift clock inhibit ('165 CLK INH, /CE, DIP #15)
//  10*   *     IM storage clock ('165 ST_CP, /PL, DIP #1)
//  11    MOSI  IM serial data in ('165 DS, DIP #14)
//  12    MISO  OM serial data output ('595 Q7, DIP #9)
//  13    SCK   OM shift clock ('595 SH_CP, CP, DIP #11)
//              IM shift clock ('165 SH_CP, CP, DIP #2)
 
// The functions on UNO #8, #9, and #10 can be moved to any output pins. As
// documented elsewhere, UNO #10 must be set to input mode in order for the
// SPI peripheral to operate in master mode, but need not actually be used
// as a slave select while operating as master.
 
// In many applications the OM output disable can be tied low instead of
// being mapped to a signal, in which case it can be ignored by the program.

// Alternatively, IM storage clock may be tied to OM output disable, in
// which case the outputs are turned off while the inputs are being read
// out. (Though I can't think of a situation where that might be useful at
// the moment.)
 
const uint8_t OM_BLK = 8;
const uint8_t IOM_NSS = 9; // This is OM's STO and IM's DIS
const uint8_t IM_STO = 10;
 
const unsigned long IOM_MAX_SPEED = 4000000;
const uint8_t IOM_BIT_ORDER = LSBFIRST;
const uint8_t IOM_SPI_MODE = SPI_MODE0;
 
inline void setOutput(uint8_t pin, bool state = true) {
  pinMode(pin, OUTPUT);
  digitalWrite(pin, state);
}
 
void setup() {
  setOutput(IOM_NSS);
  setOutput(OM_BLK);
  setOutput(IM_STO);
 
  Serial.begin(115200);
  Serial.println("Starting SPI");
 
  SPI.begin();
}
 
uint8_t exchangeBySpi(uint8_t outValue) {
  digitalWrite(IM_STO, true);
  //delayMicroseconds(5);
 
  digitalWrite(IOM_NSS, false);
  SPI.beginTransaction(SPISettings(IOM_MAX_SPEED, IOM_BIT_ORDER, IOM_SPI_MODE));
  uint8_t inValue = SPI.transfer(outValue);
  SPI.endTransaction();
  digitalWrite(IOM_NSS, true);
   
  digitalWrite(IM_STO, false);
  //delayMicroseconds(5);

  digitalWrite(OM_BLK, false);
  
  return inValue;
}
 
// Writes out the previous reading and reads in a new reading.
// (The output follows the input after a short delay.)
 
uint8_t nextOutValue = 0;
 
void loop() {
  nextOutValue = exchangeBySpi(nextOutValue);
  delay(100);
}