This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/LAUNCHXL-F28027: How to establish I2C communication between LAUNCHXL-F28027 and MCP9808?

Part Number: LAUNCHXL-F28027


Tool/software: Code Composer Studio

Hi everyone,

I'm trying to establish I2C communication between  the LAUNCHXL-F28027 and Adafruit MCP9808 Precision I2C Temperature Sensor.
I didn't find the eeprom example that intuative so i based my code upon this: 

jaraidaniel.blogspot.com/.../i2c-on-c2000-launchpad.html

So basically i wanted to constently read temperature from sensor, here is my code:

#include "DSP28x_Project.h"     // Device Headerfile and Examples Include File


 Uint16 data[I2C_MAX_BUFFER_SIZE];


void main(void)
{

   uint16_t i;

// WARNING: Always ensure you call memcpy before running any functions from RAM
// InitSysCtrl includes a call to a RAM based function and without a call to
// memcpy first, the processor will go "into the weeds"
   #ifdef _FLASH
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
   #endif



// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the f2802x_SysCtrl.c file.
   InitSysCtrl();

// Step 2. Initialize GPIO:
// This example function is found in the f2802x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
//InitGpio();
// Setup only the GP I/O only for I2C functionality
   //InitI2CGpio();

            EALLOW;

            //   GpioCtrlRegs.GPAPUD.bit.GPIO28 = 0;    // Enable pull-up for GPIO28 (SDAA)
            //   GpioCtrlRegs.GPAPUD.bit.GPIO29 = 0;       // Enable pull-up for GPIO29 (SCLA)

            GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0;    // Enable pull-up for GPIO32 (SDAA)
            GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0;       // Enable pull-up for GPIO33 (SCLA)


            //   GpioCtrlRegs.GPAQSEL2.bit.GPIO28 = 3;  // Asynch input GPIO28 (SDAA)
            //   GpioCtrlRegs.GPAQSEL2.bit.GPIO29 = 3;  // Asynch input GPIO29 (SCLA)

            GpioCtrlRegs.GPBQSEL1.bit.GPIO32 = 3;  // Asynch input GPIO32 (SDAA)
            GpioCtrlRegs.GPBQSEL1.bit.GPIO33 = 3;  // Asynch input GPIO33 (SCLA)


            //   GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 2;   // Configure GPIO28 for SDAA operation
            //   GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 2;   // Configure GPIO29 for SCLA operation

            GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 1;   // Configure GPIO32 for SDAA operation
            GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 1;   // Configure GPIO33 for SCLA operation

            EDIS;

// Step 4. Initialize all the Device Peripherals:

// Step 5. User specific code

   //CLK_enableI2cClock(myClk);

//*************************** configuring the I2C module clocks.

   I2caRegs.I2CMDR.bit.IRS = 0; //put the I2C module to reset state to config clocks

   I2caRegs.I2CPSC.all = 5;//setting IPSC to 5 results in a module clock of 10 MHz (module clock = CPU clock / (IPSC + 1))

   //The master clock
   //Tmod * (ICCH + d) and Tmod * (ICCL + d) formulas respectively, where Tmod = 1/module clock, and d = 5
   //both high-time and low-time to be 12.5 us, resulting in 25 us periodic time, equivalent to 40 kHz I2C frequency
   I2caRegs.I2CCLKL = 120;
   I2caRegs.I2CCLKH = 120;

   I2caRegs.I2CMDR.bit.IRS = 1;//put the I2C module out of reset state

//****************************The bus is now configured and ready to operate.

   // load slave address
   I2caRegs.I2CSAR = 0x18;

   // wait for STOP condition
   while (I2caRegs.I2CMDR.bit.STP != 0); //bus is not occupied

   // master mode
   I2caRegs.I2CMDR.bit.MST = 1; //resets after each stop condition

   // generate START condition
   I2caRegs.I2CMDR.bit.STT = 1;// resets once the START condition is issued


//*****************************send (write) the register address from which we want to read from to the slave device


   // repeat mode
   I2caRegs.I2CMDR.bit.RM = 1;// enabling repeat mode, in which a data byte is transmitted each time the I2CDXR register is written to, until a STOP or REPEATED START condition is generated

   // transmit mode
   I2caRegs.I2CMDR.bit.TRX = 1;//set the TRX bit to switch into transmit mode

   // wait for XRDY flag to transmit data
   while (I2caRegs.I2CSTR.bit.XRDY != 1);//wait for the XRDY (transmit mode ready) bit of the status register, which means that the data transmit register is ready to accept new data.


           //load the byte we want to send, and wait for the transmission to complete by waiting either for an ACK or NACK (error) from the slave

           // load data into the transmit register
           I2caRegs.I2CDXR = 0x05; //Temperature register (TA)

           // wait for XRDY flag to transmit data or ARDY if we get NACKed
           while (I2caRegs.I2CSTR.bit.XRDY != 1 || I2caRegs.I2CSTR.bit.ARDY != 1);

           // if a NACK is received, clear the NACK bit and stop the transfer
           if (I2caRegs.I2CSTR.bit.NACK == 1) {
               I2caRegs.I2CMDR.bit.STP = 1;
               I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
               return I2C_NACK_ERROR;
           }

           //hopefully, the slave device responded with an ACK, and we can continue our read operation.


           //Now that the slave device knows what to send (what we want to read)
           // issue repeated start
           I2caRegs.I2CMDR.bit.STT = 1;

           // non-repeat mode for reading
           I2caRegs.I2CMDR.bit.RM = 0;

           // set data count
           I2caRegs.I2CCNT = 2;// I2C Data Count Register (I2CCNT) determines how many bytes to receive

           // generate STOP condition when the data counter counts down to 0
           I2caRegs.I2CMDR.bit.STP = 1;

           // receiver mode
           I2caRegs.I2CMDR.bit.TRX = 0;


//****************************read the bytes the slave device sent us
           for (i = 0; i < 2; ++i) {
               // wait until the data receive register is ready to be read
               while (I2caRegs.I2CSTR.bit.RRDY != 1);

               data[i] = I2caRegs.I2CDRR;
           }

}   // end of main

After runing the code, i checked the SCL pin with oscilloscope and i found nothing. i also checked the I2CDRR register but the value dosn't change.

Could please see what's wrong with the code? or suggest another one?

Thanks for your help,

Cheers,

Younes,

  • Hi Younes,

    This is an interesting blog, never knew it existed.

    You'll likely need to further debug your program within CCS if you're wanting to use this code. I have a feeling it might be getting stuck in one of the while loops. Try pausing the code after running to see if it's stuck somewhere.

    I have been working on some I2C example code lately that is more straightforward and not interrupt driven when compared with the i2c_eeprom example. I'll attach it below, but please note that it's still a work in progress! Just replace the 'Example_2802xI2C_eeprom.c' within the i2c_eeprom example with the below file after importing into CCS.

    //###########################################################################
    //
    // FILE:    2802xI2C_non_FIFO_main.c
    //
    // TITLE:   2802x_I2C_non_FIFO_example
    //
    // ASSUMPTIONS:
    //
    //    Add description here... TODO
    //
    //    $Boot_Table
    //    While an emulator is connected to your device, the TRSTn pin = 1,
    //    which sets the device into EMU_BOOT boot mode. In this mode, the
    //    peripheral boot modes are as follows:
    //
    //      Boot Mode:   EMU_KEY        EMU_BMODE
    //                   (0xD00)         (0xD01)
    //      ---------------------------------------
    //      Wait         !=0x55AA        X
    //      I/O          0x55AA          0x0000
    //      SCI          0x55AA          0x0001
    //      Wait         0x55AA          0x0002
    //      Get_Mode     0x55AA          0x0003
    //      SPI          0x55AA          0x0004
    //      I2C          0x55AA          0x0005
    //      OTP          0x55AA          0x0006
    //      Wait         0x55AA          0x0007
    //      Wait         0x55AA          0x0008
    //      SARAM        0x55AA          0x000A   <-- "Boot to SARAM"
    //      Flash        0x55AA          0x000B
    //      Wait         0x55AA          Other
    //
    //   Write EMU_KEY to 0xD00 and EMU_BMODE to 0xD01 via the debugger
    //   according to the Boot Mode Table above. Build/Load project,
    //   Reset the device, and Run example
    //
    //   $End_Boot_Table
    //
    // DESCRIPTION:
    //
    //    This program will write 1-14 words to EEPROM and read them back.
    //    The data written and the EEPROM address written to are contained
    //    in the message structure, I2cMsgOut1. The data read back will be
    //    contained in the message structure I2cMsgIn1.
    //
    //    This program will work with the on-board I2C EEPROM supplied on
    //    the F2802x eZdsp.
    //
    //
    //###########################################################################
    // $TI Release: F2802x Support Library v3.02.00.00 $
    // $Release Date: Tue Jun 26 03:12:17 CDT 2018 $
    // $Copyright:
    // Copyright (C) 2009-2018 Texas Instruments Incorporated - http://www.ti.com/
    //
    // Redistribution and use in source and binary forms, with or without 
    // modification, are permitted provided that the following conditions 
    // are met:
    // 
    //   Redistributions of source code must retain the above copyright 
    //   notice, this list of conditions and the following disclaimer.
    // 
    //   Redistributions in binary form must reproduce the above copyright
    //   notice, this list of conditions and the following disclaimer in the 
    //   documentation and/or other materials provided with the   
    //   distribution.
    // 
    //   Neither the name of Texas Instruments Incorporated nor the names of
    //   its contributors may be used to endorse or promote products derived
    //   from this software without specific prior written permission.
    // 
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // $
    //###########################################################################
    
    //
    // Included Files
    //
    #include "DSP28x_Project.h"     // Device Headerfile and Examples Include File
    
    //
    // I2C Function Prototypes
    //
    void   I2CA_Init(void);
    
    uint16_t I2CA_WriteToReg(uint16_t slave_addr, uint16_t reg,
                             uint16_t data[], uint16_t data_size);
    
    uint16_t I2CA_WriteToManyRegs(uint16_t slave_addr, uint16_t reg[],
                   uint16_t reg_size, uint16_t data[], uint16_t data_size);
    
    uint16_t I2CA_ReadFromReg(uint16_t slave_addr, uint16_t reg,
                     uint16_t data[], uint16_t data_size);
    
    uint16_t I2CA_ReadFromManyRegs(uint16_t slave_addr, uint16_t reg[],
                     uint16_t reg_size, uint16_t data[], uint16_t data_size);
    
    void InitI2CAGpio();
    
    //
    // Defines
    //
    #define I2C_SLAVE_ADDR        0x50
    #define NACK_CHECK            1
    
    //
    // Globals
    //
    
    
    //
    // Main
    //
    void main(void)
    {
        uint16_t Status, i;
    
        //
        // WARNING: Always ensure you call memcpy before running any functions from
        // RAM InitSysCtrl includes a call to a RAM based function and without a 
        // call to memcpy first, the processor will go "into the weeds"
    #ifdef _FLASH
        memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
    #endif
    
        //
        // Initialize System Control:
        // PLL, WatchDog, enable Peripheral Clocks
        // This example function is found in the f2802x_SysCtrl.c file.
        //
        InitSysCtrl();
        
        //
        // Setup only the GP I/O only for I2C functionality
        //
        InitI2CAGpio();
    
        //
        // Clear all interrupts and initialize PIE vector table:
        // Disable CPU interrupts
        //
        DINT;
    
        //
        // Initialize PIE control registers to their default state.
        // The default state is all PIE interrupts disabled and flags
        // are cleared.
        // This function is found in the f2802x_PieCtrl.c file.
        //
        InitPieCtrl();
    
        //
        // Disable CPU interrupts and clear all CPU interrupt flags
        //
        IER = 0x0000;
        IFR = 0x0000;
    
        //
        // Initialize the PIE vector table with pointers to the shell Interrupt
        // Service Routines (ISR).
        // This will populate the entire table, even if the interrupt
        // is not used in this example.  This is useful for debug purposes.
        // The shell ISR routines are found in f2802x_DefaultIsr.c.
        // This function is found in f2802x_PieVect.c.
        //
        InitPieVectTable();
    
        //
        // Initialize all the Device Peripherals
        //
        I2CA_Init();    // I2C-A only
    
        //
        // Define variables/buffers used for I2C comms
        //
        uint16_t reg[2]; // Register bytes buffer
        uint16_t tx_data_buff[64]; // Data bytes buffer
        uint16_t rx_data_buff[64]; // Data bytes buffer
    
    
        // Define register bytes
        reg[0] = 0x00;
        reg[1] = 0x00;
    
        // Define data bytes
        for(i = 0; i <= sizeof(tx_data_buff); i++)
        {
            tx_data_buff[i] = i;
            rx_data_buff[i] = 0;
        }
    
        // Transfer register and data bytes
        Status = I2CA_WriteToManyRegs(I2C_SLAVE_ADDR, reg, sizeof(reg),
                                      tx_data_buff, sizeof(tx_data_buff));
    
        if(Status == I2C_SUCCESS)
        {
    
            Status = I2CA_ReadFromManyRegs(I2C_SLAVE_ADDR, reg, sizeof(reg),
                                           rx_data_buff, sizeof(rx_data_buff));
            while(Status != I2C_SUCCESS)
            {
                Status = I2CA_ReadFromManyRegs(I2C_SLAVE_ADDR, reg, sizeof(reg),
                                               rx_data_buff, sizeof(rx_data_buff));
            }
        }
    
        __asm("   ESTOP0");
    
    }
    
    //
    // I2CA_Init -
    //
    void
    I2CA_Init(void)
    {
        //
        // I2CCLK = SYSCLK/(I2CPSC+1)
        //
    #if (CPU_FRQ_40MHZ||CPU_FRQ_50MHZ)
        I2caRegs.I2CPSC.all = 4;       // Prescaler - need 7-12 Mhz on module clk
    #endif
    
    #if (CPU_FRQ_60MHZ)
        I2caRegs.I2CPSC.all = 6;       // Prescaler - need 7-12 Mhz on module clk
    #endif
        // Set I2CCLKL/I2CCLKH for 100KHz SCL clock
        I2caRegs.I2CCLKL = 38;           // NOTE: must be non zero
        I2caRegs.I2CCLKH = 38;            // NOTE: must be non zero
        I2caRegs.I2CIER.all = 0x0;      // Disable interrupts
    
        //
        // Take I2C out of reset. Stop I2C when suspended
        //
        I2caRegs.I2CMDR.all = 0x0020;
    
        return;
    }
    
    //
    // InitI2CGpio - This function initializes GPIO pins to function as I2C pins
    //
    // Each GPIO pin can be configured as a GPIO pin or up to 3 different
    // peripheral functional pins. By default all pins come up as GPIO
    // inputs after reset.
    //
    // Caution:
    // Only one GPIO pin should be enabled for SDAA operation.
    // Only one GPIO pin shoudl be enabled for SCLA operation.
    // Comment out other unwanted lines.
    //
    void
    InitI2CAGpio()
    {
        EALLOW;
    
        //
        // Enable internal pull-up for the selected pins
        // Pull-ups can be enabled or disabled disabled by the user.
        // This will enable the pullups for the specified pins.
        // Comment out other unwanted lines.
        //
        GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0;   // Enable pull-up for GPIO32 (SDAA)
        GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0;   // Enable pull-up for GPIO33 (SCLA)
    
        //
        // Set qualification for selected pins to asynch only
        // This will select asynch (no qualification) for the selected pins.
        // Comment out other unwanted lines.
        //
        GpioCtrlRegs.GPBQSEL1.bit.GPIO32 = 3;  // Asynch input GPIO32 (SDAA)
        GpioCtrlRegs.GPBQSEL1.bit.GPIO33 = 3;  // Asynch input GPIO33 (SCLA)
    
        //
        // Configure I2C pins using GPIO regs
        // This specifies which of the possible GPIO pins will be I2C
        // functional pins. Comment out other unwanted lines.
        //
        GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 1;   // Configure GPIO32 for SDAA
        GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 1;   // Configure GPIO33 for SCLA
    
        EDIS;
    }
    
    //
    // I2CA_WriteToReg - This function writes data bytes to a slave
    //                   device's register.
    //
    // INPUTS:
    //      - slave_addr ==> Address of slave device being written to
    //      - reg[] ==> Slave device register being written to
    //      - data[] ==> data buffer of bytes to be written to the slave
    //                      device register
    //      - data_size ==> # of data bytes being written, size of data buffer
    //
    uint16_t I2CA_WriteToReg(uint16_t slave_addr, uint16_t reg,
                             uint16_t data[], uint16_t data_size)
    {
        uint16_t i, Status;
    
        Status = I2C_SUCCESS;
    
        //
        // Wait until the STP bit is cleared from any previous master communication
        // Clearing of this bit by the module is delayed until after the SCD bit is
        // set. If this bit is not checked prior to initiating a new message, the
        // I2C could get confused.
        //
        if (I2caRegs.I2CMDR.bit.STP == 1)
        {
            return I2C_STP_NOT_READY_ERROR;
        }
    
        //
        // Setup slave address
        //
        I2caRegs.I2CSAR = slave_addr;
    
        //
        // Check if bus busy
        //
        if (I2caRegs.I2CSTR.bit.BB == 1)
        {
            return I2C_BUS_BUSY_ERROR;
        }
    
        //
        // Set up as master transmitter
        // FREE + MST + TRX + IRS
        //
        I2caRegs.I2CMDR.all = 0x4620;
    
        //
        // Setup number of bytes to send
        // == register byte + (# of data[] buffer bytes)
        //
        I2caRegs.I2CCNT = 1 + data_size;
    
        I2caRegs.I2CMDR.bit.STT = 0x1; // Send START condition
        I2caRegs.I2CMDR.bit.STP = 0x1; // STOP condition will be
                                       // generated when I2CCNT is zero
    
        //
        // I2C module will send the following:
        // register byte ==> data bytes ==> STOP condition
        //
        while(!I2caRegs.I2CSTR.bit.XRDY){} // Make sure data
                                           // is ready to be written
        I2caRegs.I2CDXR = reg;
    
        #if NACK_CHECK // check if NACK was received
            if(I2caRegs.I2CSTR.bit.NACK == 1)
            {
                I2caRegs.I2CMDR.bit.STP = 1;
                I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                Status = I2C_ERROR;
                return Status;
            }
        #endif
    
        // Transmit Data Bytes followed by STOP condition
        for (i=0; i< data_size; i++)
        {
            while(!I2caRegs.I2CSTR.bit.XRDY){} // Make sure data
                                               // is ready to be written
            I2caRegs.I2CDXR = data[i];
    
            #if NACK_CHECK // check if NACK was received
                if(I2caRegs.I2CSTR.bit.NACK == 1)
                {
                    I2caRegs.I2CMDR.bit.STP = 1;
                    I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                    Status = I2C_ERROR;
                    break;
                }
            #endif
        }
    
    
        // Data successfully written
        return Status;
    }
    
    //
    // I2CA_WriteToReg - This function writes data bytes to a slave
    //                   device's register.
    //
    // INPUTS:
    //      - slave_addr ==> Address of slave device being written to
    //      - reg[] ==> Slave device registers being written to
    //      - reg_size ==> # of register bytes being written, size of
    //                          reg_size buffer
    //      - data[] ==> data buffer of bytes to be written to the slave
    //                      device register
    //      - data_size ==> # of data bytes being written, size of data buffer
    //
    uint16_t I2CA_WriteToManyRegs(uint16_t slave_addr, uint16_t reg[],
                   uint16_t reg_size, uint16_t data[], uint16_t data_size)
    {
        uint16_t i, Status;
    
        Status = I2C_SUCCESS;
    
        //
        // Wait until the STP bit is cleared from any previous master communication
        // Clearing of this bit by the module is delayed until after the SCD bit is
        // set. If this bit is not checked prior to initiating a new message, the
        // I2C could get confused.
        //
        if (I2caRegs.I2CMDR.bit.STP == 1)
        {
            return I2C_STP_NOT_READY_ERROR;
        }
    
        //
        // Setup slave address
        //
        I2caRegs.I2CSAR = slave_addr;
    
        //
        // Check if bus busy
        //
        if (I2caRegs.I2CSTR.bit.BB == 1)
        {
            return I2C_BUS_BUSY_ERROR;
        }
    
        //
        // Set up as master transmitter
        // FREE + MST + TRX + IRS
        //
        I2caRegs.I2CMDR.all = 0x4620;
    
        //
        // Setup number of bytes to send
        // == (# of register bytes) + (# of data[] buffer bytes)
        //
        I2caRegs.I2CCNT = reg_size + data_size;
    
        I2caRegs.I2CMDR.bit.STT = 0x1; // Send START condition
        I2caRegs.I2CMDR.bit.STP = 0x1; // STOP condition will be
                                       // generated when I2CCNT is zero
    
        //
        // I2C module will send the following:
        // register bytes ==> data bytes ==> STOP condition
        //
    
        // Transmit Register Bytes
        for (i=0; i< reg_size; i++)
        {
            while(!I2caRegs.I2CSTR.bit.XRDY){} // Make sure data
                                               // is ready to be written
            I2caRegs.I2CDXR = reg[i];
    
            #if NACK_CHECK // check if NACK was received
                if(I2caRegs.I2CSTR.bit.NACK == 1)
                {
                    I2caRegs.I2CMDR.bit.STP = 1;
                    I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                    Status = I2C_ERROR;
                    return Status;
                }
            #endif
        }
    
        // Transmit Data Bytes followed by STOP condition
        for (i=0; i< data_size; i++)
        {
            while(!I2caRegs.I2CSTR.bit.XRDY){} // Make sure data
                                               // is ready to be written
            I2caRegs.I2CDXR = data[i];
    
            #if NACK_CHECK // check if NACK was received
                if(I2caRegs.I2CSTR.bit.NACK == 1)
                {
                    I2caRegs.I2CMDR.bit.STP = 1;
                    I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                    Status = I2C_ERROR;
                    return Status;
                }
            #endif
        }
    
    
        // Data successfully written
        return Status;
    }
    
    //
    // I2CA_ReadFromReg - This function reads data bytes from a slave
    //                   device's register.
    //
    // INPUTS:
    //      - slave_addr ==> Address of slave device being read from
    //      - reg ==> Slave device register being read from
    //      - data[] ==> data buffer to store the bytes read from the
    //                      slave device register
    //      - data_size ==> # of data bytes being received, size of data buffer
    //
    uint16_t I2CA_ReadFromReg(uint16_t slave_addr, uint16_t reg,
                      uint16_t data[], uint16_t data_size)
    {
        uint16_t i, Status;
    
        Status = I2C_SUCCESS;
    
        //
        // Wait until the STP bit is cleared from any previous master communication
        // Clearing of this bit by the module is delayed until after the SCD bit is
        // set. If this bit is not checked prior to initiating a new message, the
        // I2C could get confused.
        //
        if (I2caRegs.I2CMDR.bit.STP == 1)
        {
            return I2C_STP_NOT_READY_ERROR;
        }
    
        //
        // Setup slave address
        //
        I2caRegs.I2CSAR = slave_addr;
    
        //
        // Check if bus busy
        //
        if (I2caRegs.I2CSTR.bit.BB == 1)
        {
            return I2C_BUS_BUSY_ERROR;
        }
    
        //
        // 1. Transmit the slave address followed by the register
        //      bytes being read from
        //
    
        //
        // Setup number of bytes to send
        // == # of register bytes
        //
        I2caRegs.I2CCNT = 1;
    
        //
        // Set up as master transmitter
        // FREE + MST + TRX + IRS
        //
        I2caRegs.I2CMDR.all = 0x4620;
    
        I2caRegs.I2CMDR.bit.STT = 0x1; // Send START condition
    
        while(!I2caRegs.I2CSTR.bit.ARDY) // Wait for slave address to be sent
        {
            if(I2caRegs.I2CSTR.bit.XSMT == 0)
            {
                break;
            }
        }
    
        #if NACK_CHECK // check if NACK was received
            if(I2caRegs.I2CSTR.bit.NACK == 1)
            {
                I2caRegs.I2CMDR.bit.STP = 1;
                I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                Status = I2C_ERROR;
                return Status;
            }
        #endif
    
    
    
        // Transmit Register Byte
        while(!I2caRegs.I2CSTR.bit.XRDY){} // Make sure data
                                           // is ready to be written
        I2caRegs.I2CDXR = reg;
    
        #if NACK_CHECK // check if NACK was received
            if(I2caRegs.I2CSTR.bit.NACK == 1)
            {
                I2caRegs.I2CMDR.bit.STP = 1;
                I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                Status = I2C_ERROR;
                return Status;
            }
        #endif
    
    
        // Wait for previous communication to complete
        while(!I2caRegs.I2CSTR.bit.ARDY){}
    
        //
        // 2. Receive data bytes from slave device
        //
    
        //
        // Set up as master receiver
        // FREE + MST + IRS ==> (Master Receiver)
        //
        I2caRegs.I2CMDR.all = 0x4420;
    
        //
        // Setup number of bytes to receive
        // == # of data bytes
        //
        I2caRegs.I2CCNT = data_size;
    
        I2caRegs.I2CMDR.bit.STT = 0x1; // Send repeated START condition & Slave Addr
        I2caRegs.I2CMDR.bit.STP = 0x1; // set STOP condition to be
                                       // generated when I2CCNT is zero
    
        #if NACK_CHECK // check if NACK was received
            if(I2caRegs.I2CSTR.bit.NACK == 1)
            {
                I2caRegs.I2CMDR.bit.STP = 1;
                I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                Status = I2C_ERROR;
                return Status;
            }
        #endif
    
        for (i=0; i< data_size; i++)
        {
            while(!I2caRegs.I2CSTR.bit.RRDY){} // Make sure data
                                               // is ready to be received
    
            data[i] = I2caRegs.I2CDRR; // Buffer the received byte
    
        }
    
        // Data successfully read
        return Status;
    }
    
    //
    // I2CA_ReadFromManyRegs - This function reads data bytes from a slave
    //                  device's registers. For transmitting more than one
    //                  register during the initial writes
    //
    // INPUTS:
    //      - slave_addr ==> Address of slave device being read from
    //      - reg[] ==> Slave device registers being read from
    //      - reg_size ==> # of register bytes being written, size of
    //                          reg_size buffer
    //      - data[] ==> data buffer to store the bytes read from the
    //                      slave device register
    //      - data_size ==> # of data bytes being received, size of data buffer
    //
    uint16_t I2CA_ReadFromManyRegs(uint16_t slave_addr, uint16_t reg[],
                      uint16_t reg_size, uint16_t data[], uint16_t data_size)
    {
        uint16_t i, Status;
    
        Status = I2C_SUCCESS;
    
        //
        // Wait until the STP bit is cleared from any previous master communication
        // Clearing of this bit by the module is delayed until after the SCD bit is
        // set. If this bit is not checked prior to initiating a new message, the
        // I2C could get confused.
        //
        if (I2caRegs.I2CMDR.bit.STP == 1)
        {
            return I2C_STP_NOT_READY_ERROR;
        }
    
        //
        // Setup slave address
        //
        I2caRegs.I2CSAR = slave_addr;
    
        //
        // Check if bus busy
        //
        if (I2caRegs.I2CSTR.bit.BB == 1)
        {
            return I2C_BUS_BUSY_ERROR;
        }
    
        //
        // 1. Transmit the slave address followed by the register
        //      bytes being read from
        //
    
        //
        // Setup number of bytes to send
        // == # of register bytes
        //
        I2caRegs.I2CCNT = reg_size;
    
        //
        // Set up as master transmitter
        // FREE + MST + TRX + IRS
        //
        I2caRegs.I2CMDR.all = 0x4620;
    
        I2caRegs.I2CMDR.bit.STT = 0x1; // Send START condition
    
        while(!I2caRegs.I2CSTR.bit.ARDY) // Wait for slave address to be sent
        {
            if(I2caRegs.I2CSTR.bit.XSMT == 0)
            {
                break; // ACK received on slave address
            }
        }
    
        #if NACK_CHECK // check if NACK was received
            if(I2caRegs.I2CSTR.bit.NACK == 1)
            {
                I2caRegs.I2CMDR.bit.STP = 1;
                I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                Status = I2C_ERROR;
                return Status;
            }
        #endif
    
    
    
        // Transmit Register Bytes
        for (i=0; i< reg_size; i++)
        {
            while(!I2caRegs.I2CSTR.bit.XRDY){} // Make sure data
                                               // is ready to be written
            I2caRegs.I2CDXR = reg[i];
    
            #if NACK_CHECK // check if NACK was received
                if(I2caRegs.I2CSTR.bit.NACK == 1)
                {
                    I2caRegs.I2CMDR.bit.STP = 1;
                    I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                    Status = I2C_ERROR;
                    return Status;
                }
            #endif
        }
    
        // Wait for previous communication to complete
        while(!I2caRegs.I2CSTR.bit.ARDY){}
    
        //
        // 2. Receive data bytes from slave device
        //
    
        //
        // Set up as master receiver
        // FREE + MST + IRS ==> (Master Receiver)
        //
        I2caRegs.I2CMDR.all = 0x4420;
    
        //
        // Setup number of bytes to receive
        // == # of data bytes
        //
        I2caRegs.I2CCNT = data_size;
    
        I2caRegs.I2CMDR.bit.STT = 0x1; // Send repeated START condition & Slave Addr
        I2caRegs.I2CMDR.bit.STP = 0x1; // set STOP condition to be
                                       // generated when I2CCNT is zero
    
        #if NACK_CHECK // check if NACK was received
            if(I2caRegs.I2CSTR.bit.NACK == 1)
            {
                I2caRegs.I2CMDR.bit.STP = 1;
                I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    
                Status = I2C_ERROR;
                return Status;
            }
        #endif
    
        for (i=0; i< data_size; i++)
        {
            while(!I2caRegs.I2CSTR.bit.RRDY){} // Make sure data
                                               // is ready to be received
    
            data[i] = I2caRegs.I2CDRR; // Buffer the received byte
    
        }
    
        // Data successfully read
        return Status;
    }
    
    //
    // End of File
    //
    
    

    This is an example for a master device and is configured to communicate with an eeprom by default, however the functions should be useful for your purpose. The functions do not utilize the I2C FIFO, but this will be a good starting point if you are wanting to use it.

    Best,

    Kevin