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.

TM4C123GH6PM: I2C Dual Slave Address Operation

Part Number: TM4C123GH6PM

Hello All,

I have been trying to get dual slave address operation on TM4C123GH6PM without success. I can read and write multiple bytes to main address, but not to slave address.

For slave address, it will not return a ACK,  resulting in an error on my master side, which is an RPI. That being said, I have been able to transfer multiple bytes from the RaspberryPI side for the main address.

Here is my simplified code:

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_gpio.h"
#include "inc/hw_i2c.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"

#define SLAVE_ADDRESS 0x3C
#define SECONDARY_SLAVE_ADDRESS 0x3D

volatile uint8_t rxRegister = 0x00;
volatile uint8_t txRegister = 0x00;

volatile uint8_t rxBuffer[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
volatile uint8_t txBuffer[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};

volatile uint8_t rxBufferIdx = 0;
volatile uint8_t txBufferIdx = 0;

volatile bool g_i2c_ready = false;


void I2C0SlaveIntHandler(void) {

    // clear data interrupt
    HWREG(I2C0_BASE + I2C_O_SICR) = I2C_SICR_DATAIC;

    switch(I2CSlaveStatus(I2C0_BASE)) {
        case I2C_SLAVE_ACT_NONE:
            break;
        case I2C_SLAVE_ACT_RREQ_FBR:
            rxRegister = I2CSlaveDataGet(I2C0_BASE);
            break;
        case I2C_SLAVE_ACT_RREQ:
            rxBuffer[rxBufferIdx++] = I2CSlaveDataGet(I2C0_BASE);
            break;
        case I2C_SLAVE_ACT_TREQ:
            I2CSlaveDataPut(I2C0_BASE, txBuffer[txBufferIdx++]);
            break;
        case I2C_SLAVE_ACT_OWN2SEL:
            break;
        default:
            break;
    }

    g_i2c_ready = true;

}

int main(void) {

    // init device
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);

    // pin config for i2c0
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
    HWREG(GPIO_PORTB_BASE + GPIO_O_PUR) |= (GPIO_PIN_3 | GPIO_PIN_2);

    IntEnable(INT_I2C0);

    I2CSlaveIntEnableEx(I2C0_BASE, I2C_SLAVE_INT_DATA);
    HWREG(I2C0_BASE + I2C_O_SICR) = I2C_SICR_DATAIC;

    I2CSlaveEnable(I2C0_BASE);
    I2CSlaveInit(I2C0_BASE, SLAVE_ADDRESS);
    I2CSlaveAddressSet(I2C0_BASE, 1, SECONDARY_SLAVE_ADDRESS);

    while(1){
        if(g_i2c_ready) {
            g_i2c_ready = false;
        }
    }

}


I am also able to detect start and stop conditions, where I reset my rxBufferIdx, and txBufferIdx, but these parts are removed for readability.

To enable slave mode, basically I call the following functions:

    I2CSlaveEnable(I2C0_BASE);
    I2CSlaveInit(I2C0_BASE, SLAVE_ADDRESS);
    I2CSlaveAddressSet(I2C0_BASE, 1, SECONDARY_SLAVE_ADDRESS);

Another peculiar observation is that if the secondary slave address is set, the interrupt handler will not work for the first request, but the second. if it is commented out, it will work as expected.

I have tried various debug methods to detect I2C_SLAVE_ACT_OWN2SEL inside the interrupt handler, without any success.

Any ideas, help, recommendations greatly appreciated.

Best Regards,

Can

  • Hi Can,

    I think I see what's going on with your code. It took me a bit to figure it out but the issue with how you have written your case statements.

    So the OAR2SEL bit will be set along with the cause of the interrupt. So the way you have your case statement setup right now is:

    case I2C_SLAVE_ACT_NONE: This compiles to be case 0x00 
          break;
    case I2C_SLAVE_ACT_RREQ_FBR: This compiles to be case 0x05
          rxRegister = I2CSlaveDataGet(I2C0_BASE);
          break;
    case I2C_SLAVE_ACT_RREQ: This compiles to be case 0x01
          rxBuffer[rxBufferIdx++] = I2CSlaveDataGet(I2C0_BASE);
          break;
    case I2C_SLAVE_ACT_TREQ: This compiles to be case 0x02
          I2CSlaveDataPut(I2C0_BASE, txBuffer[txBufferIdx++]);
          break;
    case I2C_SLAVE_ACT_OWN2SEL: This compiles to be case 0x08
          break;
    default:
          break;

    Since you are just using the raw Slave Status without masking any bits, when the master sends the first byte to the secondary address, both the OWN2SEL and the RREQ_FBR bits would be set which means you are actually going to receive a 0x0D. That isn't covered by your case statements now, and so you are landing in Default and doing nothing. The same will occur for any RREQ/TREQ's sent to the second slave address, so you'll need to expand your case statement to account for those or use the OWN2SEL bit to select between two case statements with an if-else.

    Best Regards,

    Ralph Jacobi