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.

RTOS/EK-TM4C129EXL: I2C Slave with RTOS: Interrupts missing, no acks

Part Number: EK-TM4C129EXL

Tool/software: TI-RTOS

Using TIRTOS 2.16.01.04 and CCS v8.

I'm trying to implement an I2C Slave on I2C7, but I'm not getting the expected interrupts, and no ACK is generated for data bytes.

I'm stuck and don't find the cause. I know mixing RTOS and Driverlib is not the best way to do but I haven't found anything suitable in the RTOS abstraction layer.

Best Regards

Frank-Christian Kruegel


My Code:

/*
 * I2C_Slaves.c
 */

#include <stdint.h>
#include <stdbool.h>

#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/family/arm/m3/Hwi.h>

#include <inc/hw_ints.h>
#include <inc/hw_memmap.h>
#include <inc/hw_types.h>
#include <inc/hw_gpio.h>

#include <driverlib/gpio.h>
#include <driverlib/i2c.h>
#include <driverlib/pin_map.h>
#include <driverlib/sysctl.h>

#include "I2C_Slaves.h"

// I2C Address PCA9536
#define SLAVE_ADDRESS 0x41
#define NUM_REGISTERS 4

/*
 * Emulates an PCA9536/9538 Port Expander
 * Register 0 contains Boot Data and is read only.
 * Register 1...3 are read-write, but the content
 * is ignored (different from real chip)
 */

// Registers
static volatile unsigned char PCA_Regs[NUM_REGISTERS];
static volatile unsigned char PCA_RegPtr;
static volatile _Bool PCA_RegFlag;

Error_Block eb;
Hwi_Handle hInt;
Hwi_Params IntParams;

static void CommonI2CSlaveIntHandler(UArg m)
{
    uint32_t IntStatus = I2CSlaveIntStatusEx(I2C7_BASE, true);
    uint32_t UiStatus = I2CSlaveStatus(I2C7_BASE);
    I2CSlaveIntClear(I2C7_BASE);

    if (IntStatus & I2C_SLAVE_INT_START)
    {
        PCA_RegFlag = true;
    }
    if (IntStatus & I2C_SLAVE_INT_DATA)
    {
        if (UiStatus & I2C_SLAVE_ACT_TREQ)
        {
            // reads register. PCA_RegFlag doesn't care here
            I2CSlaveDataPut(I2C7_BASE, PCA_Regs[PCA_RegPtr]);
            PCA_RegPtr++;
            if (PCA_RegPtr >= NUM_REGISTERS)
            {
                PCA_RegPtr = 0;
            }
        }
        if (UiStatus & I2C_SLAVE_ACT_RREQ)
        {
            unsigned char c = I2CSlaveDataGet(I2C7_BASE);
            if (PCA_RegFlag) // Set Register Number
            {
                PCA_RegFlag = false;
                PCA_RegPtr = c;
                if (PCA_RegPtr >= NUM_REGISTERS)
                {
                    PCA_RegPtr = 0;
                }
            }
            else    // write register data and autoincrement register number
            {
                if (PCA_RegPtr)
                {
                    PCA_Regs[PCA_RegPtr] = c;
                }
                PCA_RegPtr++;
                if (PCA_RegPtr >= NUM_REGISTERS)
                {
                    PCA_RegPtr = 0;
                }
            }
        }
    }
}

void Init_I2C_Slaves(void)
{
    PCA_RegPtr = 0;
    PCA_RegFlag = true;
    for (int j = 1; j < NUM_REGISTERS; j++)
    {
        PCA_Regs[j] = 0;
    }

    Error_init(&eb);
    Hwi_Params_init(&IntParams);
    IntParams.arg = 0;
    IntParams.enableInt = true;
    IntParams.eventId = -1;
    IntParams.priority = -1;
    hInt = Hwi_create(INT_I2C7, CommonI2CSlaveIntHandler,
                    &IntParams, &eb);

    /* I2C7 Init */
    /* Enable the peripheral */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C7);

    /* Configure the appropriate pins to be I2C instead of GPIO. */
    GPIOPinConfigure(GPIO_PD0_I2C7SCL);
    GPIOPinConfigure(GPIO_PD1_I2C7SDA);
    GPIOPinTypeI2CSCL(GPIO_PORTD_BASE, GPIO_PIN_0);
    GPIOPinTypeI2C(GPIO_PORTD_BASE, GPIO_PIN_1);

    I2CSlaveIntEnableEx(I2C7_BASE, I2C_SLAVE_INT_DATA | I2C_SLAVE_INT_START);
    I2CSlaveEnable(I2C7_BASE);
    I2CSlaveACKOverride(I2C7_BASE, true);
    I2CSlaveACKValueSet(I2C7_BASE, true);
    I2CSlaveInit(I2C7_BASE, SLAVE_ADDRESS);

    Hwi_enableInterrupt(INT_I2C7);
}

Logic Analyzer Output:

  • Saleae has been a popular lately.

    I doubt the problem is mixing RTOS and driverlib, but to be sure you can make a separate experimental non-RTOS project to test your configuration. If one thing is wrong in configuration (my hunch), the whole setup is a failure. This would simplify code by reducing the HWI code to a two functions, I2CIntRegister() and I2CSlaveIntEnable().

    Its interesting that the there is GPIOPinTypeI2CSCL() but no GPIOPinTypeI2CSDA()

    I believe IntParams.enableInt = true; and Hwi_enableInterrupt(INT_I2C7); are redundant, but that shouldn't cause a problem.

  • Hi,
    I agree with Peter to first try without TI-RTOS. It will be easier to debug your code if you want to mix TivaWare with TI-RTOS's Hwi managing the interrupts. Please note that TI-RTOS has its own drivers to support I2C as well if you want to use it. With that much said, I notice in your capture the slave address sent by the master is 0x82 while you set your slave address to 0x41 in the code.

    Please also make sure two things in your code. First, please make sure INT_I2C7 is the correct interrupt channel number for I2C7. Second, please make sure the handle returned by Hwi_create is not NULL. You should test if the handle is NULL or not before proceeding.
  • 0x41 is the 7 bit I2C address, 0x82 is the 8 bit address with a 0 write bit appended. This seems to work correctly, otherwise I wouldn't get an ACK after the first byte.

    The hwi handle is nonzero. I've added a check, but this doesn't seem to be a problem.

    I'd be happy to use the TI-RTOS I2C drivers (I actually do), but these seem to be for master operation only.

    Going without RTOS is not an option for me, since the rest of the application relies on it. I'd rather loose the I2C Slave feature then.

  • Hi,
    You are correct. The 0x82 is the 8bit address. So the address is fine. What Peter and I suggested was not to abandon the RTOS. We were suggesting to isolate the problem for easier debug if you can start with the I2C communication in a non RTOS environment. Once proven working then you can integrate into the TI-RTOS.

    This I2C app note may be helpful to you as well. www.ti.com/.../spma073.pdf
  • You could try replicating the situation on the launchpad dev kits if you ware worried about a temporary project damaging your device.

    I am confused by your hwi handle statement. This field is text that becomes a symbolic and instance name. I believe you can arbitrarily set this field to "asdf" and the code would work correctly. You may be referring to the interrupt number which should be 24 for an I2C interrupt. If my hunch is true about the config being wrong, the hwi settings wouldn't matter...
  • Problem solved.

    You must do I2CSlaveEnable() before I2CSlaveIntEnableEx(), otherwise the Interrupt Enable bits wouldn't be set.

    Thanks.