728x90

728x90

Sunday, February 15, 2026

ATMega644P SPI and MCP23S17 GPIO Expansion Example

Overview 

The MCP23S17 is an SPI 16-bit (two ports) bi-directional GPIO expansion chip. Its TWI version is the MCP23017. It's rich of functions, data direction, control, distinct input and output registers, programmable weak pull up resistors, interrupt capabilities etc. Its configuration setting is very similar to most of PIC micro-controller since it is produced by Microchip Technology.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
MCP23S17 I/O Ports Programming

 

It contains two 8-bit bi-directional port, GPA and GPB. Both of them are programmable with additional interrupt features. Its two interrupt flag pins correspond to each ports. 

ATMega644P SPI and MCP23S17 GPIO Expansion Example
MCP23S17-E/SP

 The SPI version of this chip is very similar to the I2C version.

ATMega644P SPI and MCP23S17 GPIO Expansion Example

Package Types

Its SPI interface is bi-directional since it's a general purpose I/O chip. Its four SPI pins are Chip Select(CS), Serial Clock(SCK), Serial In(SI) and Serial Out(SO). Its three optional address pins are A2:A0. We can connect them to GND to set the SPI slave write address to 0x40 and 0x41 for read address.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
Functional Block Diagram

 The programmer must configure the following registers to get a proper operation.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
Register Addressed

 By default we don't set the IOCON register the default bank is BANK0 ranges from 0x00 to 0x15. For example the IODIRB locates at 0x01 and its output register OLATB locates at 0x15.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
SPI ADDRESSING REGISTERS

The idle state of CS pin is high. To complete a write or read operation the master MCU has to send at least 3 write operations.

ATMega644 and MCP23S17 SPI Programming

The write operations contain three data bytes, slave write address, register address and data. For example to make GPA as output we must do the following step:

  1. Set CS pin low
  2. Send its slave address 0x40 (A2:A0=0)
  3. Send IODIRA address 0x00
  4. Send data direction 0x00 that is output direction
  5. Clear CS pin 

Here the output ports GPA and GPB are output. They blink LED(s) every half second.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
GPA and GPB are output ports
ATMega644P SPI and MCP23S17 GPIO Expansion Example
SPI Waveform

 C source Code "main.c":

  1. /*
  2. * 12-spi_mcp23s17.c
  3. *
  4. * Created: 2/15/2026 9:55:53 PM
  5. * Author : Admin
  6. */

  7. #include <avr/io.h>
  8. #include <util/delay.h>
  9. #define F_CPU 16000000UL

  10. const char MCP23017_W=0x40;
  11. const char MCP23017_R=0x41;

  12. //IOCON.BANK=0
  13. enum BANK0{
  14. IODIRA=0,IODIRB,IPOLA,IPOLB,GPINTENA,GPINTENB,DEFVALA,
  15. DEFVALB,INTCONA,INTCONB,IOCON1,IOCON2,GPPUA,GPPUB,
  16. INTFA,INTFB,INTCAPA,INTCAPB,GPIOA,GPIOB,OLATA,OLATB
  17. };

  18. #define DDR_SPI DDRB
  19. #define PRT_SPI PORTB
  20. #define DD_MOSI 5
  21. #define DD_MISO 6
  22. #define DD_SCK 7
  23. #define DD_SS 4

  24. void SPI_MasterInit(void)
  25. {
  26. /* Set MOSI and SCK output, all others input */
  27. DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
  28. /* Enable SPI, Master, set clock rate fck/16 */
  29. SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
  30. }

  31. void SPI_MasterTransmit(char cData)
  32. {
  33. /* Start transmission */
  34. SPDR = cData;
  35. /* Wait for transmission complete */
  36. while(!(SPSR & (1<<SPIF)))
  37. ;
  38. }

  39. void SPI_SlaveInit(void)
  40. {
  41. /* Set MISO output, all others input */
  42. DDR_SPI = (1<<DD_MISO);
  43. /* Enable SPI */
  44. SPCR = (1<<SPE);
  45. }
  46. char SPI_SlaveReceive(void)
  47. {
  48. /* Wait for reception complete */
  49. while(!(SPSR & (1<<SPIF)))
  50. ;
  51. /* Return Data Register */
  52. return SPDR;
  53. }

  54. void mcp23s17_transmit(char address, char data){
  55. PRT_SPI&=~(1<<DD_SS);
  56. SPI_MasterTransmit(MCP23017_W);
  57. SPI_MasterTransmit(address);
  58. SPI_MasterTransmit(data);
  59. PRT_SPI|=(1<<DD_SS);
  60. }


  61. int main(void)
  62. {
  63. /* Replace with your application code */
  64. SPI_MasterInit();
  65. PRT_SPI|=(1<<DD_SS); //Set CS Pin High
  66. // GPA Output
  67. mcp23s17_transmit(IODIRA,0x00);
  68. //GPB Output
  69. mcp23s17_transmit(IODIRB,0x00);
  70. while (1)
  71. {
  72. mcp23s17_transmit(OLATA,0x0F);
  73. mcp23s17_transmit(OLATB,0x00);
  74. _delay_ms(500);
  75. mcp23s17_transmit(OLATA,0xF0);
  76. mcp23s17_transmit(OLATB,0xFF);
  77. _delay_ms(500);
  78. }
  79. }


At this point the master MCU set a port as input.

This is an example of making GPB as input:

  1. Set CS pin low
  2. Send its slave address 0x40 (A2:A0=0)
  3. Send IODIRB address 0x01
  4. Send data direction 0xFF that is input direction
  5. Clear CS pin 

And then we enable the weak pull up resistors of GPB:

  1. Set CS pin low
  2. Send its slave address 0x40 (A2:A0=0)
  3. Send GPPUB address 0x0D
  4. Send 0xFF to turn on all its pull up resistors
  5. Clear CS pin 

The read operation also contains three data bytes, slave read address, register address, any data and read operation. For instance GPB is already an input port. So the MCU needs to read its input data:

  1. Set CS pin low
  2. Send its slave address 0x41 (A2:A0=0)
  3. Send GPIOB address 0x13
  4. Send one byte of data (eg:0x00) for shift data out from MCP23S17 SO pin
  5. Perform the SPI read operation 
  6. Clear CS pin 

This example GPB of MCP23S17 is an input port with weak pull resistors enabled. The master MCU read data from this port and send it back to GPA output port.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
GPB as Input and GPA as Output
ATMega644P SPI and MCP23S17 GPIO Expansion Example
Waveform on virtual DSO

C Source Code "main.c":

  1. /*
  2. * 12-spi_mcp23s17_io.c
  3. *
  4. * Created: 2/15/2026 10:56:48 PM
  5. * Author : Admin
  6. */

  7. #include <avr/io.h>
  8. #include <util/delay.h>
  9. #define F_CPU 16000000UL

  10. const char MCP23017_W=0x40;
  11. const char MCP23017_R=0x41;

  12. //IOCON.BANK=0
  13. enum BANK0{
  14. IODIRA=0,IODIRB,IPOLA,IPOLB,GPINTENA,GPINTENB,DEFVALA,
  15. DEFVALB,INTCONA,INTCONB,IOCON1,IOCON2,GPPUA,GPPUB,
  16. INTFA,INTFB,INTCAPA,INTCAPB,GPIOA,GPIOB,OLATA,OLATB
  17. };

  18. #define DDR_SPI DDRB
  19. #define PRT_SPI PORTB
  20. #define DD_MOSI 5
  21. #define DD_MISO 6
  22. #define DD_SCK 7
  23. #define DD_SS 4

  24. void SPI_MasterInit(void)
  25. {
  26. /* Set MOSI and SCK output, all others input */
  27. DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
  28. /* Enable SPI, Master, set clock rate fck/16 */
  29. SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
  30. }

  31. void SPI_MasterTransmit(char cData)
  32. {
  33. /* Start transmission */
  34. SPDR = cData;
  35. /* Wait for transmission complete */
  36. while(!(SPSR & (1<<SPIF)))
  37. ;
  38. }

  39. void SPI_SlaveInit(void)
  40. {
  41. /* Set MISO output, all others input */
  42. DDR_SPI = (1<<DD_MISO);
  43. /* Enable SPI */
  44. SPCR = (1<<SPE);
  45. }
  46. char SPI_SlaveReceive(void)
  47. {
  48. /* Wait for reception complete */
  49. while(!(SPSR & (1<<SPIF)))
  50. ;
  51. /* Return Data Register */
  52. return SPDR;
  53. }

  54. void mcp23s17_transmit(char address, char data){
  55. PRT_SPI&=~(1<<DD_SS);
  56. SPI_MasterTransmit(MCP23017_W);
  57. SPI_MasterTransmit(address);
  58. SPI_MasterTransmit(data);
  59. PRT_SPI|=(1<<DD_SS);
  60. }

  61. char mcp23s17_receive(char address){
  62. PRT_SPI&=~(1<<DD_SS);
  63. SPI_MasterTransmit(MCP23017_R);
  64. SPI_MasterTransmit(address);
  65. SPI_MasterTransmit(0x00);
  66. char data=SPI_SlaveReceive();
  67. PRT_SPI|=(1<<DD_SS);
  68. return data;
  69. }

  70. int main(void)
  71. {
  72. /* Replace with your application code */
  73. SPI_MasterInit();
  74. PRT_SPI|=(1<<DD_SS); //Set CS Pin High
  75. mcp23s17_transmit(IODIRA,0x00);
  76. mcp23s17_transmit(IODIRB,0xFF);
  77. mcp23s17_transmit(GPPUB,0xFF);
  78. while (1)
  79. {
  80. char data=mcp23s17_receive(GPIOB);
  81. mcp23s17_transmit(OLATA,data);
  82. _delay_ms(500);
  83. }
  84. }




For PIC micro-controller user this post shows how a PIC16F887 interfaces with the MCP23S17 using its dedicated SPI hardware.

 

 

 

 

 

No comments:

Post a Comment

320x50

Search This Blog

tyro-728x90