Electronic – Problem with transmitting data from PIC18F45K20 (C18) to MCP23017

c18expanderi2cpic

I am currently testing the MCP23017 (slave device) 16 bit I/O Expander using a PIC18F45K20 with the C18 compiler to transmit data to the MCP23017. The address pins (A0, A1 and A2) of the MCP23017 are grounded. The serial clock (SCL) is running at 100kHz.

I've checked my program code from head to toe and I couldn't find any problem from my code even though it was compiled without error.

The problems I faced are:

  • The MCP23017 does not output the data transmitted from the PIC18F45K20 (Program code shown below).

  • The SCL waveform from PIC18F45K20 is not a square wave. It was measured using an oscilloscope

    #include <stdio.h>
    #include <stdlib.h>
    #include <delays.h>
    #include <i2c.h>
    #include <p18f45k20.h>
    #include <sw_i2c.h>
    
    #define MCP_IODIR_A 0x00
    #define MCP_IPOL_A 0x02
    #define MCP_OLAT_A 0x14
    #define MCP_GPIO_A 0x12
    #define MCP_IOCON_A 0x0A
    #define MCP_GPINTEN_A 0x04
    
    /*
    * 
    */
    //----------------------------Global variable----------------------------
    
    
    // PIC18F45K20 Configuration Bit Settings
    
    // CONFIG1H
    #pragma config FOSC = INTIO67   // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)
    #pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
    #pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)
    
    // CONFIG2L
    #pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
    #pragma config BOREN = SBORDIS  // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
    #pragma config BORV = 30        // Brown Out Reset Voltage bits (VBOR set to 3.0 V nominal)
    
    // CONFIG2H
    #pragma config WDTEN = OFF      // Watchdog Timer Enable bit (WDT is controlled by SWDTEN bit of the WDTCON register)
    #pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)
    
    // CONFIG3H
    #pragma config CCP2MX = PORTC   // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
    #pragma config PBADEN = ON      // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
    #pragma config LPT1OSC = OFF    // Low-Power Timer1 Oscillator Enable bit  (Timer1 configured for higher power operation)
    #pragma config HFOFST = ON      // HFINTOSC Fast Start-up (HFINTOSC starts clocking the CPU without waiting for the oscillator to stablize.)
    #pragma config MCLRE = OFF      // MCLR Pin Enable bit (RE3 input pin enabled; MCLR disabled)
    
    // CONFIG4L
    #pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
    #pragma config LVP = OFF        // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
    #pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))
    
    // CONFIG5L
    #pragma config CP0 = OFF        // Code Protection Block 0 (Block 0 (000800-001FFFh) not code-protected)
    #pragma config CP1 = OFF        // Code Protection Block 1 (Block 1 (002000-003FFFh) not code-protected)
    #pragma config CP2 = OFF        // Code Protection Block 2 (Block 2 (004000-005FFFh) not code-protected)
    #pragma config CP3 = OFF        // Code Protection Block 3 (Block 3 (006000-007FFFh) not code-protected)
    
    // CONFIG5H
    #pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
    #pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code-protected)
    
    // CONFIG6L
    #pragma config WRT0 = OFF       // Write Protection Block 0 (Block 0 (000800-001FFFh) not write-protected)
    #pragma config WRT1 = OFF       // Write Protection Block 1 (Block 1 (002000-003FFFh) not write-protected)
    #pragma config WRT2 = OFF       // Write Protection Block 2 (Block 2 (004000-005FFFh) not write-protected)
    #pragma config WRT3 = OFF       // Write Protection Block 3 (Block 3 (006000-007FFFh) not write-protected)
    
    // CONFIG6H
    #pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
    #pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)
    #pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)
    
    // CONFIG7L
    #pragma config EBTR0 = OFF      // Table Read Protection Block 0 (Block 0(000800-001FFFh) not protected from table reads executed in other blocks)
    #pragma config EBTR1 = OFF      // Table Read Protection Block 1 (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks)
    #pragma config EBTR2 = OFF      // Table Read Protection Block 2 (Block 2 (004000-005FFFh) not protected from table reads executed in other blocks)
    #pragma config EBTR3 = OFF      // Table Read Protection Block 3 (Block 3 (006000-007FFFh) not protected from table reads executed in other blocks)
    
    // CONFIG7H
    #pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks)
    
    void init_I2C()
    {
        DDRCbits.RC3 = 1; //Configure SCL as input
        DDRCbits.RC4 = 1; //Configure SDA as input
    
        // SSPSTAT Configuration (0x80)
        SSPSTATbits.SMP = 1;    //Slew rate control disabled
        SSPSTATbits.CKE = 0;    //SMBus disabled
        SSPSTATbits.D_A = 0;    //Reserved in master mode
        SSPSTATbits.P = 0;      //Stop bit was not detected last
        SSPSTATbits.S = 0;      //Start bit was not detected last
        SSPSTATbits.R_W = 0; 
        SSPSTATbits.UA = 0;     //Address doesnot need to be updated
        SSPSTATbits.BF = 0; 
    
        // SSPCON1 Configuration (0x28)
        SSPCON1bits.WCOL = 0;
        SSPCON1bits.SSPOV = 0;
        SSPCON1bits.SSPEN = 1;  //Serial port enabled
        SSPCON1bits.CKP = 0;    //Holds clock low
        SSPCON1bits.SSPM3 = 1;  //1000 = I2C Master mode
        SSPCON1bits.SSPM2 = 0;  //BitRate = FOSC/(4*(SPPADD+1))
        SSPCON1bits.SSPM1 = 0; 
        SSPCON1bits.SSPM0 = 0;                       
    
        // SSPADD Configuration
        SSPADD = 0x27;  //should be 0x27 for 100kHz
                        //SSPADD = [(FOSC/BitRate)/4]-1
    
        // SSPCON2 Configuration (0x00)
        SSPCON2bits.GCEN = 0;       //General call address disabled
        SSPCON2bits.ACKSTAT = 0;    //Acknowledge was received from slave
        SSPCON2bits.ACKDT = 0;      //Acknowledge
        SSPCON2bits.ACKEN = 0;      //Acknowledge sequence idle
        SSPCON2bits.RCEN = 0;       //Receive idle
        SSPCON2bits.PEN = 0;        //Stop condition idle
        SSPCON2bits.RSEN = 0;       //Repeated start condition idle
        SSPCON2bits.SEN = 0;
    }
    
    void MCP_write()
    {
         WriteI2C(0x40);
    }
    
    void MCP_read()
    {
         WriteI2C(0x41);
    }
    
    void MCP_addr()
    {
         StartI2C();
         MCP_write();
    }
    
    void init_MCP()
    {
         //IODIR A configuration
         MCP_addr();
         AckI2C();
         WriteI2C(MCP_IODIR_A);
         AckI2C();
         WriteI2C(0x00);
         StopI2C();
    
         //IPOL A configuration
         MCP_addr();
         AckI2C();
         WriteI2C(MCP_IPOL_A);
         AckI2C();
         WriteI2C(0x00);
         StopI2C();
    
         //OLAT A configuration
         MCP_addr();
         AckI2C();
         WriteI2C(MCP_OLAT_A);
         AckI2C();
         WriteI2C(0xFF);
         StopI2C();
    
         //IOCON A configuration
         MCP_addr();
         AckI2C();
         WriteI2C(MCP_IOCON_A);
         AckI2C();
         WriteI2C(0x00);
         StopI2C();
    
         //GPINTEN A configuration
         MCP_addr();
         AckI2C();
         WriteI2C(MCP_GPINTEN_A);
         AckI2C();
         WriteI2C(0x00);
         StopI2C();
    }
    
    
    //----------------------------Main program----------------------------
    void main(void)
    {
         OSCCON = 0x70;      //Set internal oscillation to 16 MHz
         TRISD=0;
         init_I2C();         //I2C initialization
         init_MCP();         //MCP23017 initialization
    
         while(1)
         {
             MCP_addr();
             AckI2C();
             WriteI2C(MCP_GPIO_A);
             AckI2C();
             WriteI2C(0x11);
             StopI2C();
          }
    }
    

Best Answer

The problems had been solved. The source of this problem is that there were acknowledge bits in the wcode. It is not necessary for the master device to send an acknowledge bit to the slave device during the write operation. In fact, the master device should wait for the acknowledge bit from the slave device.

Below is the code for testing the I2C protocol of the PIC18F45K20 with MCP23017.

#include <stdio.h>
#include <stdlib.h>
#include <delays.h>
#include <i2c.h>
#include <p18f45k20.h>
#include <sw_i2c.h>

#define MCP_IODIR_A 0x00
#define MCP_IPOL_A 0x02
#define MCP_OLAT_A 0x14
#define MCP_GPIO_A 0x12
#define MCP_IOCON_A 0x0A
#define MCP_GPINTEN_A 0x04

/*
* 
*/
//----------------------------Global variable----------------------------


// PIC18F45K20 Configuration Bit Settings

// CONFIG1H
#pragma config FOSC = INTIO67   // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = SBORDIS  // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
#pragma config BORV = 30        // Brown Out Reset Voltage bits (VBOR set to 3.0 V nominal)

// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer Enable bit (WDT is controlled by SWDTEN bit of the WDTCON register)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = PORTC   // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = ON      // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer1 Oscillator Enable bit  (Timer1 configured for higher power operation)
#pragma config HFOFST = ON      // HFINTOSC Fast Start-up (HFINTOSC starts clocking the CPU without waiting for the oscillator to stablize.)
#pragma config MCLRE = OFF      // MCLR Pin Enable bit (RE3 input pin enabled; MCLR disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = OFF        // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection Block 0 (Block 0 (000800-001FFFh) not code-protected)
#pragma config CP1 = OFF        // Code Protection Block 1 (Block 1 (002000-003FFFh) not code-protected)
#pragma config CP2 = OFF        // Code Protection Block 2 (Block 2 (004000-005FFFh) not code-protected)
#pragma config CP3 = OFF        // Code Protection Block 3 (Block 3 (006000-007FFFh) not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection Block 0 (Block 0 (000800-001FFFh) not write-protected)
#pragma config WRT1 = OFF       // Write Protection Block 1 (Block 1 (002000-003FFFh) not write-protected)
#pragma config WRT2 = OFF       // Write Protection Block 2 (Block 2 (004000-005FFFh) not write-protected)
#pragma config WRT3 = OFF       // Write Protection Block 3 (Block 3 (006000-007FFFh) not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection Block 0 (Block 0(000800-001FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection Block 1 (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection Block 2 (Block 2 (004000-005FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection Block 3 (Block 3 (006000-007FFFh) not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks)

void init_I2C()
{
    DDRCbits.RC3 = 1; //Configure SCL as input
    DDRCbits.RC4 = 1; //Configure SDA as input

    // SSPSTAT Configuration (0x80)
    SSPSTATbits.SMP = 1;    //Slew rate control disabled
    SSPSTATbits.CKE = 0;    //SMBus disabled
    SSPSTATbits.D_A = 0;    //Reserved in master mode
    SSPSTATbits.P = 0;      //Stop bit was not detected last
    SSPSTATbits.S = 0;      //Start bit was not detected last
    SSPSTATbits.R_W = 0; 
    SSPSTATbits.UA = 0;     //Address doesnot need to be updated
    SSPSTATbits.BF = 0; 

    // SSPCON1 Configuration (0x28)
    SSPCON1bits.WCOL = 0;
    SSPCON1bits.SSPOV = 0;
    SSPCON1bits.SSPEN = 1;  //Serial port enabled
    SSPCON1bits.CKP = 0;    //Holds clock low
    SSPCON1bits.SSPM3 = 1;  //1000 = I2C Master mode
    SSPCON1bits.SSPM2 = 0;  //BitRate = FOSC/(4*(SPPADD+1))
    SSPCON1bits.SSPM1 = 0; 
    SSPCON1bits.SSPM0 = 0;                       

    // SSPADD Configuration
    SSPADD = 0x27;  //should be 0x27 for 100kHz
                    //SSPADD = [(FOSC/BitRate)/4]-1

    // SSPCON2 Configuration (0x00)
    SSPCON2bits.GCEN = 0;       //General call address disabled
    SSPCON2bits.ACKSTAT = 0;    //Acknowledge was received from slave
    SSPCON2bits.ACKDT = 0;      //Acknowledge
    SSPCON2bits.ACKEN = 0;      //Acknowledge sequence idle
    SSPCON2bits.RCEN = 0;       //Receive idle
    SSPCON2bits.PEN = 0;        //Stop condition idle
    SSPCON2bits.RSEN = 0;       //Repeated start condition idle
    SSPCON2bits.SEN = 0;
}

void MCP_write()
{
     WriteI2C(0x40);
}

void MCP_read()
{
     WriteI2C(0x41);
}

void MCP_addr()
{
     StartI2C();
     MCP_write();
}

void init_MCP()
{
     //IODIR A configuration
     MCP_addr();
     WriteI2C(MCP_IODIR_A);
     WriteI2C(0x00);
     StopI2C();

     //IPOL A configuration
     MCP_addr();
     WriteI2C(MCP_IPOL_A);
     WriteI2C(0x00);
     StopI2C();

     //OLAT A configuration
     MCP_addr();
     WriteI2C(MCP_OLAT_A);
     WriteI2C(0xFF);
     StopI2C();

     //IOCON A configuration
     MCP_addr();
     WriteI2C(MCP_IOCON_A);
     WriteI2C(0x00);
     StopI2C();

     //GPINTEN A configuration
     MCP_addr();
     WriteI2C(MCP_GPINTEN_A);
     WriteI2C(0x00);
     StopI2C();
} 


//----------------------------Main program----------------------------
void main(void)
{
     OSCCON = 0x70;      //Set internal oscillation to 16 MHz
     TRISD=0;
     init_I2C();         //I2C initialization
     init_MCP();         //MCP23017 initialization

     while(1)
     {
         MCP_addr();
         WriteI2C(MCP_GPIO_A);
         WriteI2C(0x11);
         StopI2C();
     }
}