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.

I2C0 sending byte doesnt work TM4C123GH6PM

Other Parts Discussed in Thread: TM4C123GH6PM, EK-TM4C123GXL, TM4C123GH6PZ

Hi!
ive already searched through various topics, API manuals (i2c.h) and so on, but still i cant achieve my goal (at least send write byte as master).

You can see my code below. After calling those two functions i can not see any activity on SDA or SCL pins via scope.

any help would be really appreciated!

cheers Jan


EDIT1: Weird is, that after ive add while(I2CMasterBusy(I2C0_BASE)); code stuck there (like master would be busy all the time?) But still nothing at SDA SCL pins :(

- using i2c.h library (TI driverlib)

- working with ek-tm4c123gxl launchpad (TM4C123GH6PM chip)
 

void I2CInit(void)
{
  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);
       
  I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);  
 
  I2CMasterEnable(I2C0_BASE);
}


void test(void)
{
  I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);
  I2CMasterDataPut(I2C0_BASE, 0xAC);
  I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
  while(I2CMasterBusy(I2C0_BASE));
}

ek-tm4c123gxl
  • Hello Jan,

    Do you have an external Pull Up on the SCL and SDA?

    Regards

    Amit

  • Hello!
    Thanks for your fast response!

    I admit i dont have external Pull Up on those pins. So iam supposed to enable internal Pull Up by my own?

    Is it enough to use "weak pullup" on those pins?


    ROM_GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_3, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);


    ROM_GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);

  • Hi,

      I was going thru the same issue and Amit has helped me a lot. I have figured out the right code after some alterations, this code worked for me. I have attached it here for your reference:

    This is the code that has worked for me:

    MY SLAVE ADDR = 0x50

    THE CODE WAS ORIGINALLY GIVEN IN SEPARATE FORUMS HERE... I HAVE JUST MODIFIED A BIT ACCORDING TO MY USE

    I2C MODULE USED = I2C1

    HAVE SAVED SOME DATA AND RECALLED IT BACK USING REPEATED START TECHNIQUE.

    THIS CODE I HAVE TESTED WITH PA6 = SCL & PA7 = SDA AND WORKD FINE WITH 24C02

    #include <stdint.h> // ift
    #include <stdbool.h>
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/i2c.h"
    #include "driverlib/rom.h"
    #include "driverlib/pin_map.h"
    #include "inc/tm4c123gh6pm.h"
    #include "inc/hw_i2c.h"

    int i = 0;
    uint8_t lcd[5]={0x99,0x88,0x77,0x66,0x54}; // i tried saving these uchars at eeprom mem locs 10,11,12 and so on


    int main(void)
    {
    unsigned long data;
    //
    // Set the clocking to run directly from the external crystal/oscillator.
    // TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
    // crystal on your board.
    //
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |SYSCTL_XTAL_16MHZ);

    //
    // The I2C1 peripheral must be enabled before use.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C1);

    //
    // For this example I2C0 is used with PortB[3:2]. 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.
    // TODO: change this to whichever GPIO port you are using.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // 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.
    // TODO: change this to select the port/pin you are using.
    //
    //GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_2 | GPIO_PIN_3);
    GPIOPinTypeI2CSCL(GPIO_PORTA_BASE, GPIO_PIN_6); // I2CSCL
    GPIOPinTypeI2C(GPIO_PORTA_BASE, GPIO_PIN_7);

    //
    // Configure the pin muxing for I2C0 functions on port B2 and B3.
    // This step is not necessary if your part does not support pin muxing.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinConfigure(GPIO_PA6_I2C1SCL);
    GPIOPinConfigure(GPIO_PA7_I2C1SDA);

    //
    // 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. For this example we will use a data rate of 100kbps.
    //
    I2CMasterInitExpClk(I2C1_BASE, SysCtlClockGet(), false);

    //
    // Tell the master module what address it will place on the bus when
    // communicating with the slave. Set the address to SLAVE_ADDRESS
    // (as set in the slave module). The receive parameter is set to false
    // which indicates the I2C Master is initiating a writes to the slave. If
    // true, that would indicate that the I2C Master is initiating reads from
    // the slave.
    //

    while(1)
    {
    I2CMasterSlaveAddrSet(I2C1_BASE, 0x50, false); // The address of the Slave is 0x50, PUT YOURS

    I2CMasterDataPut(I2C1_BASE, 0x10); // Addr 0x10

    I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_SEND_START);

    while(I2CMasterBusy(I2C1_BASE));

    for (i=0;i<5;i++)
    {
    I2CMasterDataPut(I2C1_BASE, lcd[i]); // data to be saved at Addr 0x11 under autoincrement mode

    I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);

    while(I2CMasterBusy(I2C1_BASE));

    }

    //I2CMasterDataPut(I2C1_BASE, 0xee); // data to be saved at Addr 0x12 under autoincrement mode

    I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);

    while(I2CMasterBusy(I2C1_BASE));
    //////// write complete/////////////////////////////////////////////////////////////////////////////////
    SysCtlDelay(5500000); // writing time needed in EEPROM datasheet of 24C02 atleast 5ms

    ///////////////////////////////////////////////////////////////
    // Read at address 0x11
    I2CMasterSlaveAddrSet(I2C1_BASE, 0x50, false);

    I2CMasterDataPut(I2C1_BASE, 0x10); // Addr 0x10 written is being read back

    I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_SINGLE_SEND);

    while(I2CMasterBusy(I2C1_BASE));


    I2CMasterSlaveAddrSet(I2C1_BASE, 0x50, true);

    data = I2CMasterDataGet(I2C1_BASE); //Receive

    //I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);

    I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
    //
    while(I2CMasterBusy(I2C1_BASE));

    data = I2CMasterDataGet(I2C1_BASE); //test

    for (i=0;i<4;i++)
    {
    I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
    //
    while(I2CMasterBusy(I2C1_BASE));

    data = I2CMasterDataGet(I2C1_BASE); //Rest locations are being read back
    }

    I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
    //
    while(I2CMasterBusy(I2C1_BASE));

    }

    }

    please let me know if you have any issues. I have tried my best to help you. ALSO MY SLAVE WAS 24C02 WITH ONLY ONE MASTER TM4C123H6PM. Whats your slave and how many. If youre using RTC, let me know

  • Hello Jan

    The internal pull up are weak pull up. Based on how long the trace is and how much load it is going to see (+ current drain when the line is driven low as spec'ed in I2C specification limit for current) you would need to determine the external pull.

    For the short term you can use the internal pull if the load is not less, but if you want a stable system across operating temp and devices, I would "strongly" recommend using an external Pull Up

    Regards

    Amit

  • Thanks for your time guys!


    so ive tried the internal pullups(WPU, 2mA). i am directly scoping SDA and SCL lines (high Z load on the lines). This is what i can see:

    testing code is still:


     void test(void)
    {
      I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);
      I2CMasterDataPut(I2C0_BASE, 0xAC);
      I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
      while(I2CMasterBusy(I2C0_BASE));
    }


  • Hello Jan,

    The SCL lines is fine. The SDA line must at least show the Slave Address that you are sending which is not happening. You may want to check the line that is being scoped is the SDA (PB3)

    Regards

    Amit

  • So ive applied external 10k Pull Ups and now its working properly!
    Thanks a lot for advices Amit!

    here is my final code, that works for me (simple write without interrupts):

    // Notice that external Pull Ups are applied!
    void I2CInit(void)
    {
      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);
     
      I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
    }

    void test(void)
    {
      I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);
      I2CMasterDataPut(I2C0_BASE, 0xAC);
      I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
      while(I2CMasterBusy(I2C0_BASE));

    }



  • Hello Jan,

    Why was one of the line pulled up to 3.3V but not changing? Can you explain that behavior as it may be of interest in the forum (just in case someone else run's into it)

    Regards

    Amit

  • Hi Amit,


    Honestly, iam comfused about that a little. Only thing ive changed was using external pullups and disabling the internal ones in the code. So i think thats it. Ive posted my final code, so hopefully others can benefit from that when solving similar issue.

    best regards
    Jan

  • Hello!
    I continued my work on I2C and now iam solving another thing.
    Ive wired my launch pad via I2C to EEPROM. When i write a more data at once to EEPROM (like ona page is 64B), after that write, it takes some time to EEPROM gets data from input buffer to actual inner memory.
    After that EEPROM doesnt take further commands and there should be used technique called "ACK polling" to find when EEPROM transfered date and is ready for another commands.
    That means trying in loop if the final ACK was given by slave (that means that slave transferred data from buffer.
    How this could be achieved ?

    cheers
    Jan

  • Hello Jan,

    As per the data sheet of the EEPROM, after writing the number of words, the uC has to send a new Start condition with the Slave Address. if the EEPROM sends a NACK then it means it is processing the data. In this case the uC will put a Stop condition on the bus. This process has to be done till it does not get an ACK

    Regards

    Amit

  • Hi Jan

     As Amit said. After working with I2C EEPROM what i have learned is that, after you have written the entire data in page write mode, the EEPROM needs around 5 ms time to write its data from internal buffer. Please note that during this time the slave will NOT answer to any start. So after you have completed the writing and issued the STOP (SDA goes 0 > 1, SCL remains  1), you wait for the time mentioned in the datasheet. 

    If you want to poll the ACK being denied by slave, then you may use the bit ADRACK bit in the MCS register of the particular I2C register you are using. This bit will be '0' if your slave has responded to the sent address (whether you are using a START or a RESTART) with an ACK command. This will give you your desired ACK Polling.

  • Is it possible to force I2C master send only address + write?
    I2CMasterSlaveAddrSet(I2C0_BASE, slave_addr, false);

    after that i should be able to know if ACK was present or not right? Information about that should be stored as Error in some master register? I2CMasterErr(I2C0_BASE) do that ? i should loop and check if I2C_MASTER_ERR_ADDR_ACK comes ?


    EDIT: ive enclosed image describing that from EEPROM datasheet

  • I donot know if specific Tivaware code dealing with the specific API libraries allow you to do that. But I gues if you are writing your own driver files , then you can follow the steps given in the I2C section of the Datasheet of TM4C123H6PM, where you may find the details in algorithm form.

    You can write the Slave addr in the Slave addr register and try setting the Run+Start condition from the I2C_MCS register to see if the ADRACK bit has gone down with some breakpoint in your code 

  • ACK polling is only possible if you send address or data. In your red circle, it is somewhat same like a restart condition. As I have stated in the previous post, you can use the ADRACK to check if it is going low to see that acknowledge is sent or not

  • Hello Jan,

    Yes you are correct about the usage of TivaWare API's. Just make sure that when you read I2CMCS register you read it to a variable on do the processing on the variable. The reason why I say so is because I2CMCS is self clearing, so you may end up losing status

    Regards

    Amit

  • Thanks for advices, ive achieved what i needed and result is this function:

    bool I2CSlaveRespond(uint8_t slave_addr)
    {
      uint32_t status = 0;
      I2CMasterSlaveAddrSet(I2C0_BASE, slave_addr, false);
      I2CMasterDataPut(I2C0_BASE, 0x00);
      I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
      while(I2CMasterBusy(I2C0_BASE));
     
      status = I2CMasterErr(I2C0_BASE);
      if(status & I2C_MASTER_ERR_ADDR_ACK)
        return 0;   //slave doesnt respond ack
      else
        return 1;   //slave respond ack
    }

    regards

    Jan

  • Hello Jan,

    Good work and thanks for contributing your code back to the forum

    Regards

    Amit

  • Hi!

    ive new I2C challange in front of me. We are trying to simulate i2c slave device (EEPROM) with our TM4C123GH6PZ uP.

    weve implemented complete state machine simulating behavior of EEPROM, but i dont know how to implement waiting time of EEPROM, when it is writing data from buffer to actual EEPROM (its about 3 - 4 ms). Exactly problem is how to force slave NOT to send ACK after last byte written to it. I guess i need something like "disable" that particular slave for that period of time. Is there any function in i2c.h? The slave is on I2C0 primar.


    For better understanding i enclose scope screen displaying behaviour of real EEPROM we need to simulate.

    Cheers

    Jan

    EDIT: I can achieve that with I2CSlaveDisable(uint32_t ui32Base); and after period of time again I2CSlaveEnable(uint32_t ui32Base); ? But this will cause enable/disable whole I2C device. Both primar and secundar slave right? Thats undesirable ... i need to disable only one of them so the second can run.

  • Hello Jan,

    if you are using I2C of TM4C device as a slave then you have to use the I2CSACKCTL register which can be used by the Slave to control response of received frames from a Master.

    Regards

    Amit

  • Hi Amit,

    i am wondering, if that particular I2CSACKCTL settings is applied (using I2CSlaveACKOverride(I2C0_BASE, false);) will works for both slaves on I2C0 bus (we are using primary and secondary slave). For us would be the best, if we could disable only one of those (primar or secundar) at the same time (so the other slave could further generate ACK).
    I hope you understand what i mean.


    Jan

  • Hello Jan,

    I think you are referring to SOAR and SOAR2 registers as primary and secondary slave's. If that is the case then I believe the control will assert for both the addresses. However by making sure that after the affected slave operation is done, the I2CSACKCTL register be returned to the default value

    Regards

    Amit

  • Hello,


    so iam still trying to control ACK/NACK of my slave, but still no results. Non of the combinations of I2CSlaveACKOverride(I2C0_BASE, 1/0); and I2CSlaveACKValueSet(I2C0_BASE, 1/0); is making desired result. 

    I thought that i should call I2CSlaveACKOverride(I2C0_BASE, 0); in initialization and then at palce i need to make NACK answer call I2CSlaveACKValueSet(I2C0_BASE, 1); and after i want again send ACKs i should call I2CSlaveACKValueSet(I2C0_BASE, 1); ?

    That approach unfortunately doesnt work ... Communication just stuck after calling I2CSlaveACKValueSet(I2C0_BASE, 1);, but properly it should send NACK after each byte send from master.

    Best regards

    Jan

  • Hello Jan,

    Are you trying to NACK during the address phase of the I2C Transaction or the Data Phase of the Transaction?

    Regards

    Amit

  • My goal is to NACK after series of bytes are transmitted to my slave, so when master will try to communicate after that long frame is sent, he will get NACK.


    So i guess iam setting NACK when communication is IDLE (after that frame sent, after stop). Look at the picture (thats how actual EEPROM is behaving after write), after that long frame is sent, master is polling by sending 50W 00, if no ACK is present, he just asks over and over again till there is ACK and he continues transmission.

    Pitty that i cant find any examples describing that Slave ACK handling ...

  • Hello Jan,

    So in this case you want to NAK from Slave after receiving 0x08 data. If my interpretation is correct then you need to first call the function I2CSlaveACKOverride(I2C0_BASE,true). After getting the byte I2CSlaveACKValueSet(I2C0_BASE,false) must be called. On getting the Stop condition (based on the I2CSRIS Status bit)

    I2CSlaveACKOverride(I2C0_BASE,false);

    I2CSlaveACKValueSet(I2C0_BASE,true);

    must be called.

    If you attach the code you have or Slave, we can have a look into why it is not working: or what would happen

    Regards

    Amit

  • So i think ive found solution in disabling whole I2C channel. That results in desired behavior - so this way we will be able to simulate EEPROM behavior (EEPROM write delay).

    Iam going to take a vacasion for rest of the week. When i come back and ill solve that, ill put there final solution for others

    cheers

    Jan

  • you solve my a big problem about i2c.
  • Hello user3752433,

    What was the problem that got solved? Please elaborate.

    Regards
    Amit