Raspberry Pi and GPIO Expansion Using the MCP23S08

Posted by Vytas Sinkevicius on

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.

PI-SPI-DIN Raspberry Pi Automation Expansion Modules

 

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.

"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);
}

 

SaveSaveSaveSaveSave
SaveSave

Share this post



← Older Post


Leave a comment

Please note, comments must be approved before they are published.