• PicŪ Basic




  • Port expansion using the MCP23S08

    The MCP23S08 device provides 8-bit, general purpose, parallel I/O expansion for SPI bus applications.
    The MCP23S08 consists of multiple 8-bit configuration registers for input, output and polarity selection.
    The system master can enable the I/Os as either inputs or outputs by writing the I/O configuration bits.
    The polarity of the Input Port register can be inverted with the Polarity inversion register. All registers can be read by the system master.

    The MCP23S08 expander works the same way as the I2C model MCP23008. Please download and read the documentation of the MCP23008.

    I present two libraries to use with the MCP23S08 expander.
    The first library uses hardware MSSP module of the PIC(r). (immovable pins) (MCP23S08-H.inc)
    The second library uses software to create a dialogue on the SPI bus. (configurable pins) (MCP23S08-S.inc)
    In the file "MCP23S08-S.inc", 3 types of code can be used for software SPI communication.

    First:
    Using the SHout/SHin reliable commands.
    This is the SPI code by default.
    Code:
    ' Start the SPI communication
        Low SPI_MCP0_CS
        SHOut SPI_SDO_Pin,SPI_SCK_Pin,MsbFirst,[cCtl23S08ByteMaskRead\8,nRegAddress\8]
        SHIn SPI_SDI_Pin,SPI_SCK_Pin,MsbPre,[nMCPtemp\8]
        Set SPI_MCP0_CS
    ' Stop the SPI communication
    Second: Writing a special code to increase speed.
    I found a SPI code written by TIM and saved for many years on my hard drive. It is time to use it.
    Only I add the Return Variable to read.
    All credit for the SPI routine belongs to TIM.
    Write before the include file:
    $define _SSPI_Medium_Speed_
    Include "MCP23S08-S.inc"
    Code:
    ' Write and Read routine
    $define SSPI_Write(pValue,pReturn) '
        SPI_ByteOut = pValue   '
        GoSub SSPI_Write_Sub   '
        pReturn = SPI_ByteOut
       
    ' Write And Read same time. pValue is the DataOut And DataIn   
    SSPI_Write_Sub:
        Clear SPI_SCK_Pin                           ' Pull the SCK pin Low Mode 0.0   
        nSPI_Loop = 8                               ' 8-bits per write
        Repeat                                      ' Create a loop for all 8-bits           
            Clear SPI_SDO_Pin                       ' \
            If SPI_ByteOut.7 = 1 Then               ' |
                Set SPI_SDO_Pin                     ' | Transfer a bit to the SDO pin
            EndIf                                   ' /
            SPI_ByteOut = SPI_ByteOut << 1          ' Shift the next bit into MSB
            Set SPI_SCK_Pin                         ' Set the SCK pin High
            SPI_ByteOut = SPI_ByteOut | SPI_SDI_Pin ' Capture current bit from SDI
            Clear SPI_SCK_Pin                       ' Pull the SCK pin Low        
            Dec nSPI_Loop                           ' Count down a bit
        Until nSPI_Loop = 0                         ' Until all bits are send to the SPI interface
        Return 
    For the expander is not necessary to read and write at a time. Then I wrote 2 separate routines to increase speed.

    To write:
    Code:
    ' Write routine
    $define SSPI_Write(pValue) '
        SPI_ByteOut = pValue   '
        GoSub SSPI_Write_Sub   
    
    SSPI_Write_Sub:
        nSPI_Loop = 8                  ' 8-bits per write
    SSPI_Write_Sub2:                   ' Create a loop for all 8-bits           
        Clear SPI_SDO_Pin              ' Prepare the SDO_Pin for next loop
        If SPI_ByteOut.7 = 1 Then      ' \
            Set SPI_SDO_Pin            ' | Transfer a bit to the SDO pin
        EndIf                          ' /
        Set SPI_SCK_Pin                ' Set the SCK pin High
        SPI_ByteOut = SPI_ByteOut << 1 ' Shift the next bit into MSB
        Clear SPI_SCK_Pin              ' Pull the SCK pin Low        
        Asm
        Decfsz nSPI_Loop,F,0          ; Decrease loop counter Until all bits are send to the SPI interface
        Bra SSPI_Write_Sub2        
        EndAsm
        Return
    To read:
    Code:
    $define SSPI_Read(pValue) '
        GoSub SSPI_Read_Sub   '
        pValue = SPI_ByteOut
    
    SSPI_Read_Sub:
        Set SPI_SDO_Pin                         ' Read Only, SDO pin is always High
        nSPI_Loop = 8                           ' 8-bits per write
    SSPI_Read_Sub2:                             ' Create a loop for all 8-bits           
        SPI_ByteOut = SPI_ByteOut << 1          ' Shift the next bit into MSB
        Set SPI_SCK_Pin                         ' Set the SCK pin High
        'nop
        SPI_ByteOut = SPI_ByteOut | SPI_SDI_Pin ' Capture current bit from SDI pin
        Clear SPI_SCK_Pin                       ' Pull the SCK pin Low        
        Asm
        Decfsz nSPI_Loop,F,0                    ; Decrease loop counter Until all bits are send to the SPI interface
        Bra SSPI_Read_Sub2
        EndAsm   
        Return  
    Third: I added the Les'code for more speed.
    Please see the sample file "MCP42xxx_Digital_Resistor.bas" in the "PDS\Samples\New Samples" folder.
    Write before the include file:
    $define _SSPI_Fast_Speed_
    Include "MCP23S08-S.inc"

    In all cases the communication to the expander is checked at the initialization time and an ACK is returned.
    The code has been written for the Amicus board and has been tested with real hardware.

    Special features:
    To configure and write bits to the expander every register has a shadow variable.
    The shadow variables GPIO, INTCAP are filtered to avoid seeing the output bits.
    And the OLAT register is filtered too.

    Testing:
    The test files seem complex but it is needed to test all possibilities of the expander.
    Some functionality of the Proteus model does not work properly especially the interrupt function with the DEFVAL register.
    Interrupt On Change with DEFVAL: INTCON Bit = 1
    The operation of this option is very special. If an input is active, the INT interrupt output goes to low level and disables all other inputs. If a sensor stays active for a failure the port inputs are void. I recommend using this option only with fast signals such as pushbuttons.
    Please read carefully the datasheet and application AN1043a. Take care with mechanical sensors for the bounces. Some malfunction occurs. I recommend some RC filters in the inputs.

    CONCLUSION:
    Using the MCP23S08 expander in interrupt mode is very easy if the registers are set well.
    The MCP23S08 expander is a good choice to use a high speed expander.

    Alberto Freixanet
    Updated V1.1 14/04/2015

    MCP23S08_Library.zip