Difference between revisions of "Using SPI with shift registers"
Jump to navigation
Jump to search
Line 125: | Line 125: | ||
<syntaxhighlight lang=cpp> | <syntaxhighlight lang=cpp> | ||
− | ... | + | #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); | ||
+ | } | ||
</syntaxhighlight> | </syntaxhighlight> |
Revision as of 13:01, 26 February 2018
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);
}