728x90

Friday, February 9, 2024

PIC16F887 SPI MCP23S17 and Character LCD XC8 Example

In previous post I use the SPI interface of PIC16F887 to communicate with the MCP23S17 to read and write data from its I/O ports. The I/O port of MCP23S17 could be used for many input output purposes, relays driving, input sensor reading, etc.

PIC16F887 SPI MCP23S17 and Character LCD XC8 Example
Program Simulation in Proteus

In this example, I use GPIOA of MCP23S17 to control an HD44780 based character LCD module using its 4-bit data transfer mode. The MCP23S17 just need to write the 8-bit command or data to the LCD without concerning about reading the data back.

PIC16F887 SPI MCP23S17 and Character LCD XC8 Example
MCP23S17 DIP-28

The 8-bit LCD data will transfer the LCD controller twice. The higher nibble is sent first and then the lower nibble of the data byte. So the data byte must be latched into the LCD controller twice. Data latching is activated at falling edge of Enable (E) pin of the LCD.

I use the internal RC oscillator of PIC16F887 of 8MHz. The SPI clock frequency is Fosc/4. So the transfer rate is slower compare to direct I/O port driving. However we can use the external 20MHz crystal to get a higher frequency.

  1. /*
  2.  * File: main.c
  3.  * Author: Admin
  4.  *
  5.  * Created on February 9, 2024, 3:47 PM
  6.  */
  7.  
  8. #include <xc.h>
  9. #include "config.h"
  10. #include "mcp23S17.h"
  11.  
  12. #define _XTAL_FREQ 8000000UL
  13.  
  14. #define RS 0
  15. #define EN 2
  16.  
  17. void lcd_command(uint8_t command){
  18. uint8_t data;
  19. data=command&0xF0;
  20. mcp23S17_send(OLATA, data|(1<<EN));
  21. __delay_us(50);
  22. mcp23S17_send(OLATA, data);
  23. __delay_us(50);
  24.  
  25. data=command<<4;
  26. mcp23S17_send(OLATA, data|(1<<EN));
  27. __delay_us(50);
  28. mcp23S17_send(OLATA, data);
  29. __delay_us(50);
  30. }
  31.  
  32. void lcd_data(uint8_t myChar){
  33. uint8_t data;
  34. data=myChar&0xF0;
  35. mcp23S17_send(OLATA, data|(1<<RS)|(1<<EN));
  36. __delay_us(50);
  37. mcp23S17_send(OLATA, data);
  38. __delay_us(50);
  39.  
  40. data=myChar<<4;
  41. mcp23S17_send(OLATA, data|(1<<RS)|(1<<EN));
  42. __delay_us(50);
  43. mcp23S17_send(OLATA, data|(1<<RS));
  44. __delay_us(50);
  45. }
  46.  
  47. void lcd_xy(uint8_t x, uint8_t y){
  48. /*20x4 Character LCD*/
  49. uint8_t tbe[]={0x80,0xC0,0x94,0xD4};
  50. lcd_command(tbe[y-1]+x-1);
  51. }
  52.  
  53. void lcd_text(uint8_t *txt){
  54. while(*txt) lcd_data(*txt++);
  55. }
  56.  
  57. void lcd_init(void){
  58. spi_init();
  59. nCS=1;
  60. mcp23S17_send(IODIRA,0x00);
  61. mcp23S17_send(OLATA,0x00);
  62.  
  63. lcd_command(0x33);
  64. lcd_command(0x32);
  65. lcd_command(0x28);
  66. lcd_command(0x0F);
  67. lcd_command(0x01);
  68. __delay_ms(5);
  69. lcd_command(0x06);
  70. }
  71.  
  72. void main(void) {
  73. OSCCONbits.IRCF=7;
  74. lcd_init();
  75. lcd_xy(2,1);
  76. lcd_text("PIC16F887 MCP23S17");
  77. lcd_xy(2,2);
  78. lcd_text("SPI GPIO Extender");
  79. lcd_xy(2,3);
  80. lcd_text("Example With MPLABX");
  81. lcd_xy(1,4);
  82. lcd_text("And XC8 C Compiler..");
  83.  
  84. while(1){
  85.  
  86. }
  87. return;
  88. }
  89.  

I putted the SPI driver in separated files. 

  • The spi.h header file

  1. /*
  2.  * File: spi.h
  3.  * Author: Admin
  4.  *
  5.  * Created on February 9, 2024, 9:13 AM
  6.  */
  7.  
  8. #include <xc.h>
  9.  
  10. #define nCS RC2
  11.  
  12. void spi_init(void);
  13. void spi_send(uint8_t data);
  14. uint8_t spi_receive(void);
  15.  
  16.  
  • The spi.c source file
  1.  
  2. #include "spi.h"
  3.  
  4. void spi_init(void){
  5. /*SPI Mode Clock Low To High*/
  6. SSPCONbits.CKP=0;
  7. SSPSTATbits.CKE=1;
  8. SSPSTATbits.SMP=0;
  9. /*SPI Master Mode Clock = Fosc/4*/
  10. SSPCONbits.SSPM=0;
  11. /*Turn On The Module*/
  12. SSPCONbits.SSPEN=1;
  13. SSPSTATbits.BF=1;
  14. PORTC=0;
  15. TRISC=0;
  16. /*SPI SDI Pin Input*/
  17. TRISC4=1;
  18. }
  19.  
  20. void spi_send(uint8_t data){
  21. SSPSTATbits.BF==1;
  22. SSPBUF=data;
  23. while(SSPSTATbits.BF==0);
  24. SSPSTATbits.BF==1;
  25. }
  26.  
  27. uint8_t spi_receive(void){
  28. uint8_t data;
  29. spi_send(0x00);
  30. data=SSPBUF;
  31. return data;
  32. }

I wrote a driver for the MCP23S17 to send and received the data from this chip. It uses the SPI driver above.

  • The MCP23S17.h header file
  1. /*
  2.  * File: mcp23S17.h
  3.  * Author: Admin
  4.  *
  5.  * Created on February 9, 2024, 3:54 PM
  6.  */
  7.  
  8. #include <xc.h>
  9. #include "spi.h"
  10.  
  11. /*MCP23S17 Registers Definition*/
  12. #define IODIRA 0x00
  13. #define IODIRB 0x01
  14. #define IPOLA 0x02
  15. #define IPOLB 0x03
  16. #define GPINTENA 0x04
  17. #define GPINTENB 0x05
  18. #define DEFVALA 0x06
  19. #define DEFVALB 0x07
  20. #define INTCONA 0x08
  21. #define INTCONB 0x09
  22. #define IOCONA 0x0A
  23. #define IOCONB 0x0B
  24. #define GPPUA 0x0C
  25. #define GPPUB 0x0D
  26. #define INTFA 0x0E
  27. #define INTFB 0x0F
  28. #define INTCAPA 0x10
  29. #define INTCAPB 0x11
  30. #define GPIOA 0x12
  31. #define GPIOB 0x13
  32. #define OLATA 0x14
  33. #define OLATB 0x15
  34.  
  35. /*MCP23S17 Address*/
  36. #define WRITE_ADDR 0x40
  37. #define READ_ADDR 0x41
  38.  
  39. /*MCP23S17 Read and Write Functions*/
  40. void mcp23S17_send(uint8_t address, uint8_t data);
  41. uint8_t mcp23S17_receive(uint8_t address);
  42.  
  43.  
  • The mcp23S17.c source file
  1.  
  2. #include "mcp23S17.h"
  3.  
  4. void mcp23S17_send(uint8_t address, uint8_t data){
  5. nCS=0;
  6. spi_send(WRITE_ADDR);
  7. spi_send(address);
  8. spi_send(data);
  9. nCS=1;
  10. }
  11.  
  12. uint8_t mcp23S17_receive(uint8_t address){
  13. uint8_t data;
  14. nCS=0;
  15. spi_send(READ_ADDR);
  16. spi_send(address);
  17. data=spi_receive();
  18. nCS=1;
  19. return data;
  20. }

The MPLABX IDE version is v6.15 and the XC8 C compiler version is v2.36. You can use the latest version of C compiler as you prefer. Click here to download its source file.






Thursday, February 8, 2024

PIC16F887 SPI and MCP23S17 XC8 Example

Overview

In previous post, I showed about the MSSP module of PIC16F887 with an SPI shift registers example. There are many SPI slave devices, a EEPROM memory, a GPIO extender chip, Flash memory, etc. 

PIC16F887 SPI and MCP23S17 XC8 Example
Program Simulation in Proteus

Here I use a MCP23S17 SPI GPIO expanding chip. The Microchip MCP23X17 series has two options of communication interfaces, the MCP23017 (I2C) and the MCP23S17(SPI). Both of them are bi-directional in data data transaction. 

PIC16F887 SPI and MCP23S17 XC8 Example
MCP23X17 Packaging Information


This chip has many package types including the Dual In Line Package (DIP-28) easing the prototyping for electronics hobbyists. I don't have the SPI communication interface of this chip. I have only one MCP23017 I2C one's.

PIC16F887 SPI and MCP23S17 XC8 Example
MCP23017 I2C Type

This chip can be used for LEDs or relays driving, keypad scanning, LCD driving, etc.

PIC16F887 SPI and MCP23S17 XC8 Example

Package Types

The SPI addressing mode is similar to the I2C mode. It uses a two-byte data package, device address, additional 3-bit addresses, and a read/write bit.

PIC16F887 SPI and MCP23S17 XC8 Example
Functional Block Diagram

For SPI mode (MCP23S17) has the following I/O pins.

  • Chip Select (CS) - Active low input signal latching data to its internal registers
  • Serial Clock (SCK) - input synchronous clock signal up to 10MHz
  • Serial Input (SI) - SPI serial data input (8-bit)
  • Serial Output (SO) - SPI serial data output (8-bit)

It has an additional 3-bit addresses (A2:A0). These address could be ignored or enable by software setting. By setting bit 3 of IOCON register we can enable this feature. By default this feature is ignored (A2:A0=0x00) and the memory addressing locates at BANK0 of the registers map.

Reset input (RESET) is active low. This pin can be shared with the micro-controller reset circuit.

GPIOA Interrupt (INTA) and GPIOB Interrupt (INTB) are the output signal notifying the master SPI whenever the input interrupt at GPIOA and GPIOB occurs. However this feature must be enable in its interrupt registers setting.

GPIOA (GPA) and GPIOB (GPB) are digital bi-directional readable/writable ports. Its data direction registers can be selected using the IODIRA and IODIRB registers. These registers will be shown in its registers map.

In SPI mode the clock rate has a maximum frequency of 10MHz. 

PIC16F887 SPI and MCP23S17 XC8 Example
SPI Addressing Registers

Its registers are divide into two banks, BANK0 and BANK1. By default BANK0 is active. So the programmer doesn't need to access BANK1 as it's not necessary. 

PIC16F887 SPI and MCP23S17 XC8 Example
Register Addressed

For a full details of these registers you can see its device datasheet. Here I will use only some registers to work with.

  • IODIRA - I/O DIRECTION REGISTER A (ADDR 0x00) : Clearing its bits for output direction, setting its bits for input direction.
  • IODIRB - I/O DIRECTION REGISTER B (ADDR 0x01) : Clearing its bits for output direction, setting its bits for input direction.
  • IOCONA - I/O EXPANDER CONFIGURATION REGISTER (ADDR 0x05)
  • IOCONB - I/O EXPANDER CONFIGURATION REGISTER (ADDR 0x06)
  • GPPUA -  GPIO PULL-UP RESISTOR REGISTER A(ADDR 0x0C)
  • GPPUB -  GPIO PULL-UP RESISTOR REGISTER B(ADDR 0x0D)
  • GPIOA -  GENERAL PURPOSE I/O PORT REGISTER A (ADDR 0x12)
  • GPIOB -  GENERAL PURPOSE I/O PORT REGISTER B (ADDR 0x13)
  • OLATA -  OUTPUT LATCH REGISTER A (ADDR 0x14)
  • OLATB -  OUTPUT LATCH REGISTER B (ADDR 0x15)

In this example we use use these registers in the MPLABX IDE and XC8 compiler. 

PIC16F887 MPLABX IDE and XC8 Programming

I use the high speed SPI communication interface of the MSSP module to interface with this chip. The MCP23S17 SPI slave chip is writable and readable.

GPIOA is configured as digital input reading input data from a DIP switch. GPIOB is configured as digital output writing data to the output LED. I turned on the pull up resistors of GPIOA to raise it high by default.

  1. /*
  2.  * File: main.c
  3.  * Author: Admin
  4.  *
  5.  * Created on February 9, 2024, 9:10 AM
  6.  */
  7.  
  8. #include <xc.h>
  9. #include "config.h"
  10. #include "spi.h"
  11.  
  12. #define _XTAL_FREQ 8000000UL
  13.  
  14. void mcp23S17_send(uint8_t address, uint8_t data){
  15. nCS=0;
  16. spi_send(WRITE_ADDR);
  17. spi_send(address);
  18. spi_send(data);
  19. nCS=1;
  20. }
  21.  
  22. uint8_t mcp23S17_receive(uint8_t address){
  23. uint8_t data;
  24. nCS=0;
  25. spi_send(READ_ADDR);
  26. spi_send(address);
  27. data=spi_receive();
  28. nCS=1;
  29. return data;
  30. }
  31.  
  32. void mcp23S17_init(void){
  33. nCS=1;
  34. mcp23S17_send(0x0A,0x08); // Enable Address Select
  35. mcp23S17_send(0x0B,0x08); // Enable Address Select
  36. mcp23S17_send(0x00,0xFF); // GPIOA AS INPUT
  37. mcp23S17_send(0x0C,0xFF); // Enable All GPPUA
  38. mcp23S17_send(0x01,0x00); // GPIOB AS OUTPUT
  39. }
  40.  
  41. void main(void) {
  42. uint8_t data;
  43. OSCCONbits.IRCF=7;
  44. spi_init();
  45. mcp23S17_init();
  46. while(1){
  47. data=mcp23S17_receive(0x12);
  48. mcp23S17_send(0x15,data);
  49. __delay_ms(150);
  50. }
  51. return;
  52. }
  53.  

I created a separate SPI driver in this project.

The SPI.h file:

  1. /*
  2.  * File: spi.h
  3.  * Author: Admin
  4.  *
  5.  * Created on February 9, 2024, 9:13 AM
  6.  */
  7.  
  8. #include <xc.h>
  9.  
  10. #define WRITE_ADDR 0x40
  11. #define READ_ADDR 0x41
  12.  
  13. #define nCS RC2
  14.  
  15. void spi_init(void);
  16. void spi_send(uint8_t data);
  17. uint8_t spi_receive(void);
  18.  
  19.  

 The SPI.c file:

  1.  
  2. #include "spi.h"
  3.  
  4. void spi_init(void){
  5. /*SPI Mode Clock Low To High*/
  6. SSPCONbits.CKP=0;
  7. SSPSTATbits.CKE=1;
  8. SSPSTATbits.SMP=0;
  9. /*SPI Master Mode Clock = Fosc/64*/
  10. SSPCONbits.SSPM=2;
  11. /*Turn On The Module*/
  12. SSPCONbits.SSPEN=1;
  13. SSPSTATbits.BF=1;
  14. PORTC=0;
  15. TRISC=0;
  16. /*SPI SDI Pin Input*/
  17. TRISC4=1;
  18. }
  19.  
  20. void spi_send(uint8_t data){
  21. SSPSTATbits.BF==1;
  22. SSPBUF=data;
  23. while(SSPSTATbits.BF==0);
  24. SSPSTATbits.BF==1;
  25. }
  26.  
  27. uint8_t spi_receive(void){
  28. uint8_t data;
  29. spi_send(0x00);
  30. data=SSPBUF;
  31. return data;
  32. }

Click here to download its source file.

PIC16F887 SPI and MCP23S17 XC8 Example
SPI Waveform

I use a virtual digital oscilloscope to get its waveform.




PIC16F887 Serial Peripheral Interface Example

Overview

Serial Peripheral Interface (SPI) is a wired synchronous serial communication interface, commonly used in embedded electronics control system used for communication between controller and its peripheral devices. 

PIC16F887 Serial Peripheral Interface Example
SPI Example with the SN74HC595N Shift Registers Chip

Typical applications include interfacing micro-controllers with peripheral chips for Secure Digital cards, liquid crystal displays, analog-to-digital and digital-to-analog converters, flash and EEPROM memory, and various communication chips.

PIC16F887 Serial Peripheral Interface Example
SPI Master Multiple Slave Devices Diagram
One SPI master device able to connect to many slave devices. Additional slave device requires one chip select (CS) pin. Conventionally these SPI  defined as follow,

  • SCK - Serial Clock
  • MOSI - Master Out Slave In
  • MISO - Master In Slave Out
  • CS - Chip Select

These symbol commonly used for the AVR micro-controller from Microchip Technology. However the Microchip PIC micro-controllers use different symbols.

PIC16F887 Serial Peripheral Interface (SPI)

The PIC16F887 has a Master Synchronous Serial Port (MSSP) Module that contains the SPI and I2C module. The communication configuration depends on software setting in program.

The SPI pins locate at,

  • RC3 - Serial Clock (SCK)
  • RC4 - Serial Data In (SDI)
  • RC5 - Serial Data Out (SDO)
  • nSS - Slave Select (RA5)

The SPI operation could be configured as master or slave mode.

PIC16F887 Serial Peripheral Interface Example
MSSP Block Diagram (SPI Mode)
 

To operate in master mode, the SCK, SDO and SS pin must configured as digital output pins. The SDI pin must be configured as digital input pin. The SPI operation relates to these SFR registers.

PIC16F887 Serial Peripheral Interface Example
MSSP Registers
For more details about these registers you can read the datasheet of this device.

The SSPBUF is an 8-bit readable/writable register for data transmitting and receiving. The programmer must set the following configuration to operate the SPI master mode.

  • Set serial clock polarity and data sample mode
  • Set the SPI clock frequency
  • Set the SPI I/O pins direction
  • Enable the SPI module

In master mode we can select various SPI wave forms.

PIC16F887 Serial Peripheral Interface Example
SPI Master Mode Wave Form
The commonly used wave form is a low to high clock transition. The slave select pin used for latching the data into the slave device. It could be a high to low transition, or vice versa depending on the target slave device specification. 

The SN74HC595N Shift Registers Interfacing Example

In this example, I use the SN74HC595N serial in parallel out shift registers to interface with the master SPI module of PIC1F887. The SN74HC595N operate as a SPI slave device receiving the data from the master.

PIC16F887 Serial Peripheral Interface Example
SN74HC595N DIP-16

The clock transition is positive edge (low to high). The latch (slave select) pin is a high to low transition.

PIC16F887 Serial Peripheral Interface Example
SPI Wave Form

We can serially connect these chips as much as possible. This chip is very popular in driving seven segment display or dot matrix display.

In this example, the PIC16F887 master SPI will send an 8-bit data to the SN74HC595N periodically. 

  1. /*
  2.  * File: main.c
  3.  * Author: Admin
  4.  *
  5.  * Created on February 6, 2024, 9:16 PM
  6.  */
  7.  
  8. #include <xc.h>
  9. #include "config.h"
  10.  
  11. #define _XTAL_FREQ 8000000UL
  12.  
  13. void spi_init(void){
  14. /*SPI Mode Clock Low To High*/
  15. SSPCONbits.CKP=0;
  16. SSPSTATbits.CKE=1;
  17. SSPSTATbits.SMP=0;
  18. /*SPI Master Mode Clock = Fosc/64*/
  19. SSPCONbits.SSPM=2;
  20. /*Turn On The Module*/
  21. SSPCONbits.SSPEN=1;
  22. SSPSTATbits.BF=1;
  23. }
  24.  
  25. void main(void) {
  26. OSCCONbits.IRCF=7;
  27. spi_init();
  28. PORTC=0;
  29. TRISC=0;
  30. TRISC4=1;
  31. while(1){
  32. SSPBUF=0xF0;
  33. __delay_us(80);
  34. RC2=1;
  35. __delay_us(10);
  36. RC2=0;
  37. __delay_ms(1000);
  38.  
  39. SSPBUF=0x0F;
  40. __delay_us(80);
  41. RC2=1;
  42. __delay_us(10);
  43. RC2=0;
  44. __delay_ms(1000);
  45.  
  46. SSPBUF=0xAA;
  47. __delay_us(80);
  48. RC2=1;
  49. __delay_us(10);
  50. RC2=0;
  51. __delay_ms(1000);
  52. }
  53. return;
  54. }
  55.  

Click here to download this example.


Search This Blog