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/TM4C123GH6PM: I2C Master only works once after receiving data from Slave

Part Number: TM4C123GH6PM
Other Parts Discussed in Thread: EK-TM4C123GXL

Tool/software: Code Composer Studio

Hello everyone, I'm trying to program I2C between 2 TM4C123GH6PM, I used module I2C0 and 1K pull-up resistors. The idea is UART received data, then trigger the I2C Master send character 'R' to I2C Slave, the Slave after receive and detect 'R' will send back data for Master . The data has been transferred correctly. But there are 2 problems I cannot figure it out by myself. 

  1. When Master send data to Slave, there is a character 'y' (121) go after. I wonder whether it's the frame ACK or not because the data receive is correct and ACK's value in ASCII is different. 
  2. I can only read data once. After that, the Master's program is stuck in while(!I2CMasterBusy()). I don't know how to change it.

Master:

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

#define SLAVE_ADDRESS 0x3C

unsigned char B[10];
unsigned char data[21];
unsigned char *ptr;
unsigned char a,b,c,d;
uint8_t i = 0;
uint8_t j = 0;
float x;

static void I2C0MasterIntHandler(void)
{

    // Clear the I2C0 interrupt flag.
    I2CMasterIntClear(I2C0_BASE);
    // Read the data from the slave.
    data[j] = I2CMasterDataGet(I2C0_BASE);
    I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, true);
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
    j++;
    if(j > 21)
    {
        j = 0;
    }
}

static void UARTISR(void)
{
    UARTIntClear(UART0_BASE,UARTIntStatus(UART0_BASE,true));
    while (UARTCharsAvail(UART0_BASE))
    {
        B[i]=UARTCharGetNonBlocking(UART0_BASE);
        i++;
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
        SysCtlDelay(1000000);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0);
        SysCtlDelay(1000000);
    };
    i = 0;
    if(B[i] == 'R')
    {
        I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);
        I2CMasterDataPut(I2C0_BASE, B[i]);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
        while(I2CMasterBusy(I2C0_BASE));
        I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, true);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
//        while(I2CMasterBusBusy(I2C0_BASE)); // check MasterBusBusy vs MasterBusy
    }
}

int main(void)
{
    x = 11.7;
    ptr = (unsigned char *)&x;
    a = *ptr;
    b = *(ptr + 1);
    c = *(ptr + 2);
    d = *(ptr + 3);
    //System clock set
    SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    //Enable UART and GPIO peripherals
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);

    //Enable the appropriate GPIO port for the UART0 or UART1 Tx and Rx pins
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);  //Configure the UART0 or UART1 GPIO pins for UART operation
    //Set the pins to be peripheral controlled
    UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 9600,(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_EVEN));
    UARTIntEnable(UART0_BASE, UART_INT_RX|UART_INT_RT);
    UARTEnable(UART0_BASE);
    UARTIntRegister(UART0_BASE,&UARTISR);

    //enable GPIO peripheral that contains I2C 0
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB));

    //enable I2C module 0
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    //reset module
    SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);

    // Configure the pin muxing for I2C0 functions on port B2 and B3.
    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);

    // Select the I2C function for these pins.
    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);

    // Enable and initialize the I2C0 master module.  Use the system clock for
    // the I2C0 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.
    I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
    I2CMasterIntEnableEx(I2C0_BASE, I2C_MASTER_INT_DATA);
    I2CIntRegister(I2C0_BASE, &I2C0MasterIntHandler);

    while(1)
    {


    }
}

Slave:

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

#define SLAVE_ADDRESS 0x3C
unsigned char  result = 'V';
volatile unsigned char   data[21];


void I2C0_Slave_Init(void)
{
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);

    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);

    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);

    I2CSlaveEnable(I2C0_BASE);
    I2CSlaveInit(I2C0_BASE, SLAVE_ADDRESS);
}

void I2C0SlaveIntHandler(void)
{
    uint8_t i;
    // Clear the I2C0 interrupt flag.
    I2CSlaveIntClear(I2C0_BASE);
    // Read the data from the slave.
    result = I2CSlaveDataGet(I2C0_BASE);
    if(result == 'R')
    {
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0x02);
        SysCtlDelay(SysCtlClockGet()/50);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0);
        SysCtlDelay(SysCtlClockGet()/50);
        for(i = 0; i < 21; i++)
        {
            I2CSlaveDataPut(I2C0_BASE, data[i]);
            SysCtlDelay(1200);
        }
    }
}

int main(void)
{
    data[0] = 'a';
    uint8_t i;
    for(i = 0; i < 21; i++)
    {
        data[i] = data[0] + i;
    }

    SysCtlClockSet(SYSCTL_SYSDIV_1| SYSCTL_USE_OSC| SYSCTL_OSC_MAIN| SYSCTL_XTAL_16MHZ);

    I2C0_Slave_Init();

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);

    I2CSlaveIntEnableEx(I2C0_BASE, I2C_SLAVE_INT_DATA);
    I2CIntRegister(I2C0_BASE,&I2C0SlaveIntHandler);

    while(1)
    {
    }
}

Thank you.

  • Hello Nam,

    For issue #1, that is not really possible to determine without the use of an oscilloscope, please and get access to an oscilloscope (or a logic state analyzer) and observe the data on the I2C lines. This the only way to determine why incorrect data is received.

    For issue #2, I think the problem is the structure of your ISR's. You are doing way way too much inside of them. You want ISR's to be very quick and very little code and handle as much as possible outside of ISR's. Especially avoid long delays and such.

    I would suggest you re-write your program flow to have a lot of the ISR code in your while(1) loop and then shrink down the ISR's to just handle sending/receiving data based on interrupts. You may even want to start by polling instead of using ISR's to get the program functional, and then implement the ISR's afterwards, this would be easier for you to debug as when you get to the ISR implementation you don't need to also solve issues like issue #1.

    If you decide to do polling method, see this E2E post where I provided I2C C code files that handle polling I2C to send data between two EK-TM4C123GXL LaunchPad's: e2e.ti.com/.../2817634
  • Thank you for your detail reply, Ralph.

    I have been searching through the similar problem and saw that you guys're using some kind of oscilloscope. To be honest, I have no idea what is that thing and how to use it. Right now I don't have that device so yet, cannot provide you a picture about the data transferred on the I2C line (I'm really sorry for lacking of knowledge and equipment).

    After considering your suggestion, I think you might be correct. I did try to write my program and put the data handler into the while(1) loop but I will re-write it using both data handler in major loop and ISR to send/receive data on I2C line. (I will try and tell you whether it works as desired or not.

    But there is a problem I want to ask for your opinion. That I want to put an array of data through I2C line. In the past, I tried the command BURST send/receive but nothing works so I'm really confused and for the alternative method, I have to send and receive every single byte. Do you have any suggestion for this?

  • Hello Nam,

    To send large amounts of data you need to use I2C_MASTER_CMD_BURST_SEND_START to send the first byte, then I2C_MASTER_CMD_BURST_SEND_CONT for subsequent bytes until you get to the last byte at which point you use I2C_MASTER_CMD_BURST_SEND_FINISH.
  • Hello Nam,

    You may want to read our I2C app note as well, I think you may find some good pointers in it: www.ti.com/.../spma073.pdf
  • Hi Ralph,

    I really appreciate your help. And I'm sorry for the late reply., right now I'm trying to work it out as soon as possible. 

    Sincerely,

  • Hi Ralph,

    I really appreciate your helps. I've done my code for Master and Slave.

    For Master:

    #define SLAVE_ADDRESS 0x3C
    
    unsigned char B[10];
    unsigned char data[21];
    unsigned char pData[21];
    unsigned char *ptr;
    unsigned char a,b,c,d;
    uint8_t i = 0;
    uint8_t fl = 0;
    float x;
    
    //static void I2C0MasterIntHandler(void)
    //{
    //
    //    // Clear the I2C0 interrupt flag.
    //    I2CMasterIntClear(I2C0_BASE);
    //    // Read the data from the slave.
    //    data[j] = I2CMasterDataGet(I2C0_BASE);
    //    I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, true);
    //    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
    //    j++;
    //    if(j > 21)
    //    {
    //        j = 0;
    //    }
    //}
    
    static void UARTISR(void)
    {
        UARTIntClear(UART0_BASE,UARTIntStatus(UART0_BASE,true));
        while (UARTCharsAvail(UART0_BASE))
        {
            B[i]=UARTCharGetNonBlocking(UART0_BASE);
            i++;
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
            SysCtlDelay(1000000);
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0);
            SysCtlDelay(1000000);
            fl = 1;
        };
        i = 0;
    }
    
    int main(void)
    {
        uint8_t j = 0;
        pData[0] = '0';
        for(j = 1; j < 20; j++)
        {
            pData[j] = pData[0] + j;
        }
        x = 11.7;
        ptr = (unsigned char *)&x;
        a = *ptr;
        b = *(ptr + 1);
        c = *(ptr + 2);
        d = *(ptr + 3);
        //System clock set
        SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
        //Enable UART and GPIO peripherals
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
        GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);
    
        //Enable the appropriate GPIO port for the UART0 or UART1 Tx and Rx pins
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);  //Configure the UART0 or UART1 GPIO pins for UART operation
        //Set the pins to be peripheral controlled
        UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 9600,(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_EVEN));
        UARTIntEnable(UART0_BASE, UART_INT_RX|UART_INT_RT);
        UARTEnable(UART0_BASE);
        UARTIntRegister(UART0_BASE,&UARTISR);
    
        //enable GPIO peripheral that contains I2C 0
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB));
    
        //enable I2C module 0
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
        //reset module
        SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
    
        // Configure the pin muxing for I2C0 functions on port B2 and B3.
        GPIOPinConfigure(GPIO_PB2_I2C0SCL);
        GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    
        // Select the I2C function for these pins.
        GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
        GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
    
        // Enable and initialize the I2C0 master module.  Use the system clock for
        // the I2C0 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.
        I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
        I2CMasterIntEnableEx(I2C0_BASE, I2C_MASTER_INT_DATA);
    //    I2CIntRegister(I2C0_BASE, &I2C0MasterIntHandler);
    
        while(1)
        {
            if(fl == 1)
            {
                if(B[i] == 'R')
                {
                    I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);
                    I2CMasterDataPut(I2C0_BASE, B[i]);
                    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
                    while(I2CMasterBusy(I2C0_BASE));
                    I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, true);
                    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
                    while(I2CMasterBusy(I2C0_BASE));
                    data[0] = I2CMasterDataGet(I2C0_BASE);
                    uint8_t index=1;
                    for(index=1;index<20;index++)
                    {
                        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
                        while(I2CMasterBusy(I2C0_BASE));
                        data[index]=I2CMasterDataGet(I2C0_BASE);
                    }
                    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
                    while(I2CMasterBusy(I2C0_BASE));
                    data[index]=I2CMasterDataGet(I2C0_BASE);
                    fl = 0;
                }
                if(B[i] == 'W')
                {
                    I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);
                    I2CMasterDataPut(I2C0_BASE, B[i]);
                    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
                    while(I2CMasterBusy(I2C0_BASE));
    
                    I2CMasterDataPut(I2C0_BASE, pData[0]);
                    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
                    while(I2CMasterBusy(I2C0_BASE));
    
                    uint8_t index=0;
                    for(index=1;index<20;index++)
                    {
                        I2CMasterDataPut(I2C0_BASE, pData[index]);
                        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
                        while(I2CMasterBusy(I2C0_BASE));
                    }
    
                    I2CMasterDataPut(I2C0_BASE, pData[20]);
                    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
                    while(I2CMasterBusy(I2C0_BASE));
                    fl = 0;
                }
            }
        }
    }
    

    For Slave:

    #define SLAVE_ADDRESS 0x3C
    unsigned char  result = 'V';
    volatile unsigned char   data[21];
    volatile unsigned char   pData[64];
    volatile unsigned char   receive[21];
    uint8_t fl = 0;
    uint8_t i = 0;
    
    void I2C0_Slave_Init(void)
    {
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
    
        GPIOPinConfigure(GPIO_PB2_I2C0SCL);
        GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    
        GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
        GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
    
        I2CSlaveEnable(I2C0_BASE);
        I2CSlaveInit(I2C0_BASE, SLAVE_ADDRESS);
    }
    
    static void I2C0SlaveIntHandler(void)
    {
        // Clear the I2C0 interrupt flag.
        I2CSlaveIntClear(I2C0_BASE);
        // Read the data from the slave.
        while(!(I2CSlaveStatus(I2C0_BASE))) {}
        pData[i] = I2CSlaveDataGet(I2C0_BASE);
        i++;
        fl = 1;
    }
    
    int main(void)
    {
        data[0] = 'a';
        uint8_t j;
        for(j = 0; j < 21; j++)
        {
            data[j] = data[0] + j;
        }
    
        SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    
        I2C0_Slave_Init();
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
        GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
    
        I2CSlaveIntEnableEx(I2C0_BASE, I2C_SLAVE_INT_DATA);
        I2CIntRegister(I2C0_BASE,&I2C0SlaveIntHandler);
    
    
        while(1)
        {
            if(fl == 1)
            {
                if(pData[0] == 'R')
                {
                    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0x02);
                    SysCtlDelay(SysCtlClockGet()/50);
                    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0);
                    for(j = 0; j < 21; j++)
                    {
                        while(!(I2CSlaveStatus(I2C0_BASE)));
                        I2CSlaveDataPut(I2C0_BASE, data[j]);
                    }
                    fl = 0;
                }
                if((pData[0] == 'W')&&(i > 21))
                {
                    for(j = 0; j < 21; j++)
                    {
                        receive[j] = pData[j+1];
                    }
                    fl = 0;
                }
                if(fl == 0)
                {
                    i = 0;
                }
            }
        }
    }
    

    I'm successful in transfer data back and forth between Master and Slave. But then, there is an problem here that I would want to listen to your opinion. In further development, the Slave will handle some PWM-related works, which may cause a delay in the I2C data communication. For more detail, my assumption is that, when the Master send the request to the Slave, the Slave still receive data, but only process the data when all the PWM-related codes are executed. Is there any solution for this kind of problem?

    Regards,

  • Hello Nam,

    As it sounds like you are making a closed loop system that should be no issue because you don't have to reply to the master immediately if you don't want to. As long as the master knows it can expect data later on. You will still probably want to understand the time frames involved so you can have a timeout on the master end to retry communication though, and may also be smart to send a short acknowledgement package from the slave when data is received to tell the master that the transmission was successful. Then on the master end you can activate the timer for the timeout tracking.
  • Dear Ralph, 

    I really appreciate your help, it's really helpful. The I2C problem now is solved even though I'm still struggling with the project due to the time asynchronous. I will try to figure it out myself, but if I'm stuck again, I really hope that you can help me on another topic.

    Once again, thank you for helping me solving my I2C problem.

    Best wishes,

    Nam.