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.

TM4C129ENCPDT: I2C5 on PB0 and PB1

Part Number: TM4C129ENCPDT

Hi all,

I'm trying to run a simple example for I2C5 on Tiva. I want to read one byte from SI7005. In datasheet, I have this description to read the register.

I started from loopback example in Tivaware and it works fine. Also, after change the source code I can see the SCL and SDA signals on oscilloscope as expected. 

I'm using 400kbps for I2C clock and I have external pull-up resistors in SDA and SCL.

I can see only 0 in Watch Expressions when I try to read this register.

There is something wrong with the code?

I'm not understanding the configuration of this peripheral?

Or, is it more likely to be a hardware issue?

The code is attached.

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"

#define SLAVE_ADDRESS 0x40
#define ID_ADDRESS    0x11

uint32_t readI2C5(uint32_t device_address, uint32_t device_register)
{
    //specify that we want to communicate to device address with an intended write to bus
    I2CMasterSlaveAddrSet(I2C5_BASE, device_address, false);

    //the register to be read
    I2CMasterDataPut(I2C5_BASE, device_register);

    //send control byte and register address byte to slave device
    I2CMasterControl(I2C5_BASE, I2C_MASTER_CMD_SINGLE_SEND);

    //wait for MCU to complete send transaction
    while(I2CMasterBusy(I2C5_BASE));

    //read from the specified slave device
    I2CMasterSlaveAddrSet(I2C5_BASE, device_address, true);

    //send control byte and read from the register from the MCU
    I2CMasterControl(I2C5_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);

    //wait while checking for MCU to complete the transaction
    while(I2CMasterBusy(I2C5_BASE));

    //Get the data from the MCU register and return to caller
    return( I2CMasterDataGet(I2C5_BASE));
}

//*****************************************************************************
//
// Configure the I2C5 master.
//
//*****************************************************************************
uint32_t test = 0;

void main(void)
{
    uint32_t ui32SysClock;

    // I don't have a crystal
    ui32SysClock = SysCtlClockFreqSet((SYSCTL_OSC_INT | SYSCTL_USE_PLL |
                                        SYSCTL_CFG_VCO_480), 120000000);

    //
    // The I2C5 peripheral must be enabled before use.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C5);

    //
    // For this example I2C5 is used with PortB[0:1].  The actual port and
    // pins used may be different on your part, consult the data sheet for
    // more information.  GPIO port B needs to be enabled so these pins can
    // be used.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    //
    // Select the I2C function for these pins.  This function will also
    // configure the GPIO pins pins for I2C operation, setting them to
    // open-drain operation with weak pull-ups.  Consult the data sheet
    // to see which functions are allocated per pin.
    //
    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_0);
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_1);

    //
    // Configure the pin muxing for I2C5 functions on port B0 and B1.
    // This step is not necessary if your part does not support pin muxing.
    //
    GPIOPinConfigure(GPIO_PB0_I2C5SCL);
    GPIOPinConfigure(GPIO_PB1_I2C5SDA);


    //
    // Enable and initialize the I2C5 master module.  Use the system clock for
    // the I2C5 module.  The last parameter sets the I2C data transfer rate.
    // If false the data rate is set to 100kbps and if true the data rate will
    // be set to 400kbps.  For this example we will use a data rate of 400kbps.
    //
    I2CMasterInitExpClk(I2C5_BASE, ui32SysClock, true);


    GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_2);
    GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_2, 0); //SI7005 CS
    SysCtlDelay(1200000);

    for(;;) {
        //GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_2, 0);
        //SysCtlDelay(1200000); //10 ms
        test = readI2C5(SLAVE_ADDRESS, ID_ADDRESS);
        //GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_2, ~0);
        SysCtlDelay(12000000); //100 ms
    }

}

  • Hello Allef,

    Looking over your code, I don't think you need to do a I2CMasterSlaveAddrSet(I2C5_BASE, device_address, true); after you send the address/register data.

    You also should get data with the I2CMasterDataGet API such as: pui32DataRx[ui32Index] = I2CMasterDataGet(I2C5_BASE);

    There is likely more that needs to be done to figure out the issue, but that stood out initially.

    For debugging I would suggest since you are able to see the SCL/SDA signals on the oscilloscope that you look at the communication being sent to the SI7005 device and compare it to what is suggested in the device datasheet.

    Make sure that you are getting the 0x40 and 0x11 being sent, and then see if any bytes are being received and what values they are. If address and data byte appears on the I2C line, but not in the TM4C, then you can debug what is occurring with your receive. If the data isn't being transmitted correctly, then part of your transmission process got messed up, and if the data is being transmitted but no reply is coming, then it could be a hardware, packet framing, or timing issue.

  • May I suggest that your "initial attempt" to communicate w/a "remote device" has a far higher probability of success if you employ "KISS!"     "KISS" dictates that you "start" your remote device interaction with the simplest possible I2C Slave.    (which (usually) is a small capacity I2C EEprom)

    The more advanced device you've chosen (instead) is likely to:

    • prove more demanding in terms of I2C set-up & config.
    • require more time to test/troubleshoot
    • and be "outside" (both) the interest & usage/experience of your "helper corps!"     Minus this group - you are "alone at sea" - can that be good?

    None of the above "works in your favor!"     The "KISS" approach - (via small capacity, simple EEProm) will surely be useful to (many) other of your projects - AND is much more likely to be within the "wheel-house" of your helpers, here.   (device you've chosen is far too specialized/little known - thus (commands) focused, read/review (from your helper crüe) - who may not choose to make that sacrifice.)

    As a general guide to "remote I2C device success:"

    • have you measured & confirmed that the remote device is fully/properly powered - during the I2C transaction?
    • unspecified is the separation distance between MCU and remote I2C slave 
    • are noise, power, HF switching, and/or RF sources in near proximity to the I2C slave and/or cabling?
    • starting at 400KHz violates "KISS" - which always seeks the simplest means to speed & ease "success."   (100KHz at this early stage is quite sufficient!)
    • if a "user built bread-board" - has it (really) been checked for signal & power continuity?     Have the I2C signals been "scoped" directly at the Slave device's pins? 

    Recall that "KISS" seeks to "achieve design success in the simplest & quickest fashion."     Later is "time enough" to, "Proceed by Refinement."     (refinement early (i.e. another's "Premature Optimization" is the downfall of SO many here - who (due to vendor's "sheltering them from "KISS") have not obtained the Many KNOWN BENEFITS offered by "KISS!"

  • Hi all,

    there is an errata about I2C5. Apparently, I can't use polling for this.

    I'm performing the suggested tests and I will report the results soon.

    I can't find an example with i2c interrupts on Tivaware. Do you know any?

    Thanks.

  • Hello Allef,

    For initial development to get I2C working at a basic level, it is unlikely you'd hit that case. Polling tends to be easier for 'getting started' and I don't see anything in your code that makes me worry you'd run into that item yet.

    That said, yes you will want to use interrupt-driven I2C for your 'final product' - though making sure you can get to talk to your slave device at all first is likely easier done with polling.

    As far as lack of I2C master interrupt examples... I wish I could say why. I had no role in example selection. As a software-heavy individual though I certainly agree the examples offered do sometimes leave one desiring more. There is one for slave mode though which is slave_receive_int.c under the 'examples/peripherals/i2c' folder.
  • Hi, Ralph!

    There is an old group in Google Groups from TI in Brazil. I have the response from a member in this group who has had problems with I2C in Tiva before.

    Just add a delay before:

    while(I2CMasterBusy(I2C5_BASE));

    And change this line:
    I2CMasterControl(I2C5_BASE, I2C_MASTER_CMD_SINGLE_SEND);

    For this line:
    I2CMasterControl(I2C5_BASE, I2C_MASTER_CMD_BURST_SEND_START);

    And now I can get data from this register.

    Thank you very much!
  • Hello Allef,

    Thanks for posting back the solution. The mention of a delay does trigger a bit of memory that I have read to use

    while(!I2CMasterBusy(ui2cBase));
    while(I2CMasterBusy(ui2cBase));

    before from another TI'er. Not sure what delay you tried, but you may find the above the most succinct solution ultimately.
  • Hi, Ralph!

    I'm using a dummy delay, "SysCtlDelay(80000000);"

    I will try this other solution too!