The VP Process Inc. line of automation expansion modules for the Raspberry Pi uses the Microchip MCP23S08 GPIO Expander IC to maximize the number of modules that can be used with a minimum of Chip Select Lines.
In this example using the PI-SPI-DIN series of I/O modules, the Raspberry Pi reads 2 modules of 8 Isolated Digital Inputs for a total of 16 Inputs and controls 4 modules of 4 Relay Outputs for a total of 16 Relays.
The sample code (written in "C") reads each input from the isolated digital inputs card and turns on the appropriate relay if the value is 1.
For example: If Input 1 on the 8DI Module is High, Relay 1 on the first 4KO module is ON.
The RPi's built in SPI routines only allows for 2 Chip Select lines, CE0 and CE1. Each of the PI-SPI-DIN modules has a jumper selection of 5 Chip selects. The sample code shown here uses a "Hard Coded" function to emulate the SPI function, which allows for any arbitrary GPIO pin to be used as the Chip Select line.
The MCP23S08 GPIO Expander chip has 2 address lines, A0 and A1. These address lines have to be enabled in the setup routine along with determining if the port pins are Inputs or Outputs. For the 8DI modules, the port pins are set to Inputs, and for the relays, the port pins are set to Outputs. Otherwise, the code is pretty much the same.
As an example, if only Digital inputs (-8DI) and Relay outputs (-4KO) modules are used, the Raspberry Pi can drive up to 5 Chip selects and 4 addresses for a total of 20 PI-SPI-DIN modules. That could be the equivalent of:
2 Chip Selects x 4 Addresses x 8 Digital Inputs = 64 Isolated Digital Inputs
3 Chip Selects x 4 Addresses x 4 Relay Outputs = 48 Relay Outputs
for a total of 112 I/O points. Not too shabby!
The Data Sheet pdf for the MCP23S08 can be found here.
Update March 2018
VP Process Inc has created a launched on GitHub a new set of Python libraries for the PI-SPI and PI-SPI-DIN series of products.
https://widgetlords.com/pages/pi-spi-din-applications
The PI-SPI series now has Node-RED support as well:
https://widgetlords.com/pages/node-red-and-the-pi-spi-din-series
"C" SAMPLE CODE:
/*
* pispi_vpl_DIN_16DI_16KO.c
*
* Peripherals
* 2 x 8DI for 16 Digital Inputs Chip Select CE_2 for Inputs 1 to 16
* Address 0 for Inputs 1 to 8
* Address 1 for Inputs 9 to 16
*
* 4 x 4KO for 8 Relay Outputs Chip Select CE_3 for Relays K1 to K16
* Address 0 for Relays 1 thru 4 follow Digital Inputs 1 thru 4
* Address 1 for Relays 1 thru 4 follow Digital Inputs 5 thru 8
* Address 2 for Relays 1 thru 4 follow Digital Inputs 9 thru 12
* Address 3 for Relays 1 thru 4 follow Digital Inputs 13 thru 16
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <wiringPi.h>
// Define Pins as used by Pi-SPi modules
#define CS_4 18
#define CS_3 23
#define CS_2 24
#define CS_0 8
#define CS_1 7
#define vpl_MOSI 10
#define vpl_MISO 9
#define vpl_SCK 11
// Prototypes
void Initialize_Pi_Hardware(void);
void vpl_SPI_tx(unsigned char *tx_buf, unsigned char *rx_buf, char len);
unsigned char Update_8DI(int cs, int address);
void Initialize_8DI(int cs);
void Update_Relays_PISPIDIN(int CS, int address, char relays);
void Initialize_Relays_PISPIDIN(int cs);
// Variables
char Relay_Status_1, Relay_Status_2;
char DI_Status_1,DI_Status_2;
// main
int main(void) {
wiringPiSetupGpio();
Initialize_Pi_Hardware();
digitalWrite(CS_0, HIGH);
digitalWrite(CS_1, HIGH);
digitalWrite(CS_2, HIGH);
digitalWrite(CS_3, HIGH);
digitalWrite(CS_4, HIGH);
digitalWrite(vpl_SCK, LOW);
digitalWrite(vpl_MOSI, HIGH);
Initialize_8DI(CS_2); // Initialize 8DI
Initialize_Relays_PISPIDIN(CS_3); // Intialize 4KO Modules
while(1)
{
// Get Digital Input Module Address = 0
DI_Status_1 = Update_8DI(CS_2, 0);
printf("DI Status 1 = %02x \n", DI_Status_1);
Relay_Status_1 = DI_Status_1;
Update_Relays_PISPIDIN(CS_3, 0, (Relay_Status_1 & 0x0f));
// First 4KO Module, CS_3, Address 0, DI Input 1 thru 4
Update_Relays_PISPIDIN(CS_3, 1, ((Relay_Status_1>>4)&0x0f));
// Second 4KO Module, CS_3, Address 1, DI input 5 thru 8
// Get Digital Input Module Address = 1
DI_Status_2 = Update_8DI(CS_2, 1);
printf("DI Status 2 = %02x \n", DI_Status_2);
Relay_Status_2 = DI_Status_2;
Update_Relays_PISPIDIN(CS_3, 2, (Relay_Status_2 & 0x0f));
// First 4KO Module, CS_3, Address 0, DI Input 1 thru 4
Update_Relays_PISPIDIN(CS_3,3,((Relay_Status_2>>4)&0x0f));
// Second 4KO Module, CS_3, Address 1, DI input 5 thru 8
delay(250);
}
return 0;
}
void Initialize_8DI(int cs) {
unsigned char buf[8], rx_buf[8];
int addr;
addr = 0x40;
buf[0] = addr; // Write command
buf[1] = 0x00; // Start Address 00 IODOR
buf[2] = 0xff; // Set IODIR to Inputs
buf[3] = 0xff; // Set IPOL Polarity to Invert
buf[4] = 0x00; // Set GPINTEN
buf[5] = 0x00; // Set DEFVAL
buf[6] = 0x00; // Set INTCON
buf[7] = 0x08; // Set IOCON to enable Hardware Addressing
buf[8] = 0;
digitalWrite(cs, LOW);
delay(1);
vpl_SPI_tx(buf, rx_buf, 9);
delay(1);
digitalWrite(cs, HIGH);
}
unsigned char Update_8DI(int cs, int address) {
unsigned char data;
unsigned char buf[8], rx_buf[8];
int addr;
if(address > 3 || address < 0)
{
return 0;
}
addr = 0x41 + ((address << 1) & 0x0f);
buf[0] = addr; // Read Command
buf[1] = 0x09; // Address 09 for GPIO Info
buf[2] = 0x00; // Dummy Byte
digitalWrite(cs, LOW);
delay(2);
vpl_SPI_tx(buf, rx_buf, 3);
delay(2);
data = rx_buf[2] & 0xff;
digitalWrite(cs, HIGH);
return data;
}
void Initialize_Relays_PISPIDIN(int cs) {
unsigned char buf[9], rx_buf[8];
int addr;
addr = 0x40;
buf[0] = addr; // Write command
buf[1] = 0x00; // Start Address 00 IODOR
buf[2] = 0x00; // Set IODIR to Outputs
buf[3] = 0xff; // Set IPOL Polarity to Invert
buf[4] = 0x00; // Set GPINTEN
buf[5] = 0x00; // Set DEFVAL
buf[6] = 0x00; // Set INTCON
buf[7] = 0x08; // Set IOCON to enable Hardware Addressing
digitalWrite(cs, LOW);
delay(1);
vpl_SPI_tx(buf, rx_buf, 8);
delay(1);
digitalWrite(cs, HIGH);
}
void Update_Relays_PISPIDIN(int cs, int address, char relays) {
unsigned char buf[9], rx_buf[9];
int addr;
if(address > 3 || address < 0)
{
return;
}
addr = 0x40 + ((address << 1) & 0x0f);
buf[0] = addr; // Read Command
buf[1] = 0x09; // Address 09 for GPIO Info
buf[2] = relays; // Relay Status
digitalWrite(cs, LOW);
delay(2);
vpl_SPI_tx(buf, rx_buf, 3);
delay(2);
digitalWrite(cs, HIGH);
}
// Function that drives the SPI MOSI, MISO and SCK lines
void vpl_SPI_tx(unsigned char *tx_buf, unsigned char *rx_buf, char len) {
int i, bits;
unsigned int data_tx, data_rx;
delay(1);
for(i=0; i<len; i++) {
data_tx = tx_buf[i]; // get data byte to transmit
data_rx=0;
for(bits=0; bits<8; bits++) {
if(data_tx & 0x80) {
digitalWrite(vpl_MOSI, HIGH);
}
else {
digitalWrite(vpl_MOSI, LOW);
}
data_tx <<= 1;
delayMicroseconds(2);
digitalWrite(vpl_SCK, HIGH);
delayMicroseconds(2);
data_rx <<= 1;
if(digitalRead(vpl_MISO) == 1) {
data_rx |= 0x01;
}
else {
data_rx &= ~0x01;
}
digitalWrite(vpl_SCK, LOW);
}
rx_buf[i] = data_rx;
}
delay(1);
}
void Initialize_Pi_Hardware(void) {
// SPI CS Enable Lines
pinMode(CS_4, OUTPUT);
pinMode(CS_3, OUTPUT);
pinMode(CS_2, OUTPUT);
pinMode(CS_0, OUTPUT);
pinMode(CS_1, OUTPUT);
pinMode(vpl_MOSI, OUTPUT);
pinMode(vpl_MISO, INPUT);
pinMode(vpl_SCK, OUTPUT);
pullUpDnControl(vpl_MISO, PUD_DOWN);
}