KL26Z I2C getting stuck on data register writes?

cancel
Showing results for 
Search instead for 
Did you mean: 

KL26Z I2C getting stuck on data register writes?

223 Views
Contributor I

I'm able to write the slave address and get a response from the device, but get stuck waiting for a response after the second write. Upon further examination, I find that the second write does not change the value in I2C0->D from the value I had previously loaded into it. Any thought?

Attached: a snipped of my START signal and I2C write command

0 Kudos
12 Replies

7 Views
Contributor I

I was under the impression that the line :

      while((I2C0->S & I2C_S_IICIF_MASK) == 0) {} 

was waiting for the ACK.

In the code snippet you sent, you have the same wait loop, and then a function that checks for an ACK. Is this what you mean by wait ACK or am I misunderstanding?

EDIT: I've noticed something that may be crucial, when I clear the IICIF flag, it the BUSY bit goes to 0 as well. I think I am losing the bus for some reason when I write the flag.

0 Kudos

7 Views
NXP TechSupport
NXP TechSupport

Hi 

I didn't find your post code contains wait the ACK,


void i2c_give_ack(I2C_MemMapPtr p)
{
    p->C1 &= ~I2C_C1_TXAK_MASK;
}

Your code:

pastedImage_3.png

Each write, do you add the ACK waiting?

   Wish it helps you!

   If you still have questions about it, please kindly let me know.

   

Best Regards,

Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

7 Views
Contributor I

Yes, the ACK waiting happens after each write. I've noticed that when I clear the IICIF flag for the first time, the BUSY bit in the status register goes low and the MST bit in the C1 register go low as well. For whatever reason, I'm losing the bus after attempting to write the address, and that's why I can't perform another write. I do not know why this is happening.

0 Kudos

7 Views
NXP TechSupport
NXP TechSupport

Hi Cameron Ehlers

  It's better to find a logical analyzer to check the I2C bus wave, whether the slave really give ACK or not.

  Do you check the master I2C status register? 

pastedImage_1.png

Just BUSY is 1?

Bus Busy
Indicates the status of the bus regardless of slave or master mode. This bit is set when a START signal is
detected and cleared when a STOP signal is detected.
0 Bus is idle
1 Bus is busy

Best Regards,

Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

7 Views
Contributor I

Hi Kerry, here is the code snipped in question. The problem arises after the first I2CWrite call near the end of SensorInit. It sends the address and the write bit successfully (0xE0, the device is the SHTC3 dual sensor), but then hangs in the second call to I2CWrite when it tries to send the SLEEP_MSB. In the debugger, I have noted that after writing the SLEEP_MSB to I2C->D, the value does not change, and remains as 0xE0. Hence, it seems to me that my KL26 I2C module is not sending the second byte. Unfortunately I do not have access to a scope at the moment so I can not confirm this.

Link to SHTC3 datasheet: https://media.digikey.com/pdf/Data%20Sheets/Sensirion%20PDFs/HT_DS_SHTC3_D1.pdf 

pastedImage_1.png

pastedImage_2.png

0 Kudos

7 Views
Senior Contributor II

The for() wait could be optimized away because it has no side effects by some compilers.

Declare 'i' as volatile to prevent this.

I was under the impression that the line :

      while((I2C0->S & I2C_S_IICIF_MASK) == 0) {} 

was waiting for the ACK.

This loop could also be optimized away, again because it has no side effects depending on the compiler and its settings.  It also depends on how 'S' is declared in the headers.

Add a 'nop' market as volatile in the {} to prevent the loop from being removed.

/*
* NOP is not necessarily a time-consuming NOP. The processor might
* remove it from the pipeline before it reaches the execution stage.
* Use in combination with the Sync Barrier Instruction to consume time.

[This does not apply to the KL family.]

[For GCC:]
*/
static inline ATTR_NO_INSTRUMENT_FUNCTION void nop( void )
{
__asm__ __volatile__ ("nop");
}

Also do not use |= to clear Write One to Clear flags (W1C) use assignment.

0 Kudos

7 Views
Contributor I

Hi Bob, 

I implemented your suggestions. I am now suspecting a hardware issue with my slave device. I find that as soon as I write the address + write big into I2C0->D, the MST bit in I2C0->C1 goes low, which according to the datasheet issues a STOP bit. Could losing master mode after writing be the result of a hardware issue or is something else at play?

0 Kudos

7 Views
Senior Contributor II

A loss of arbitration perhaps.

What do the status registers show?

What are your pull-ups on SDA and SCL?
Should be in the 1k to 4k range typically.

Do not rely on the internal ones.

0 Kudos

7 Views
Contributor I

My external pullups are both about 1k. 

Upon checking the status register, you are correct about loss of arbitration, the instant after I send the address plus write bit to the data register, the ARBL bit is set, and the I2C goes into slave mode, hence the inability to continue transmiting.

Do you know of any common mistakes that can cause loss of arbitration?

0 Kudos

7 Views
NXP TechSupport
NXP TechSupport

Hello Cameron Ehlers

  You didn't wait the ACK.

   From your slave, you can know the I2C wave should be:

pastedImage_1.png

 But, you check your own code, you just write the buffer and clear the flag, then no wait for the ACK.

I share some code which I wrote for the other slave, just for your reference:

    void hal_dev_vz89_write_reg(I2C_MemMapPtr I2Cx_B, uint8 cmd, uint8 data, uint8 crc)
    {
        i2c_start(I2Cx_B);
    
        i2c_write_byte(I2Cx_B, VZ89_I2C_ADDRESS|I2C_WRITE);
        i2c_wait(I2Cx_B);
        i2c_get_ack(I2Cx_B);
    
        i2c_write_byte(I2Cx_B, cmd);
        i2c_wait(I2Cx_B);
        i2c_get_ack(I2Cx_B);
    
        i2c_write_byte(I2Cx_B, 0X00);
        i2c_wait(I2Cx_B);
        i2c_get_ack(I2Cx_B);
    
        i2c_write_byte(I2Cx_B, 0X00);
        i2c_wait(I2Cx_B);
        i2c_get_ack(I2Cx_B);
    
        i2c_write_byte(I2Cx_B, 0X00);
        i2c_wait(I2Cx_B);
        i2c_get_ack(I2Cx_B);
    
        i2c_write_byte(I2Cx_B, 0X00);
        i2c_wait(I2Cx_B);
        i2c_get_ack(I2Cx_B);
      
        i2c_write_byte(I2Cx_B, crc);
        i2c_wait(I2Cx_B);
        i2c_get_ack(I2Cx_B);
      
        i2c_stop(I2Cx_B);
        pause();
    }
    
    
    // i2c general
    
    void i2c_give_nack(I2C_MemMapPtr p)
    {
        p->C1 |= I2C_C1_TXAK_MASK;
    }
    void i2c_give_ack(I2C_MemMapPtr p)
    {
        p->C1 &= ~I2C_C1_TXAK_MASK;
    }
    void i2c_repeated_start(I2C_MemMapPtr p)
    {
        p->C1     |= 0x04;
    }
    void i2c_write_byte(I2C_MemMapPtr p, uint8 data)
    {
        p->D = data;
    }
    uint8 i2c_read_byte(I2C_MemMapPtr p)
    {
        return p->D;
    }
    void i2c_start(I2C_MemMapPtr p)
    {
        i2c_set_master_mode(p);
        i2c_set_tx_mode(p);
    }
    void i2c_stop(I2C_MemMapPtr p)
    {
        i2c_set_slave_mode(p);
        i2c_set_rx_mode(p);
    }
    void i2c_wait(I2C_MemMapPtr p)
    {
      i2c_wait_cnt=0;
      i2c_err_flag=0;
        // wait flag
        while((p->S & I2C_S_IICIF_MASK)==0)
        {
        i2c_wait_cnt++;
       if(i2c_wait_cnt>0x00001000) 
       {
        i2c_err_flag=1;
          break;
       }
      }
            ;
        // clear flag
        p->S |= I2C_S_IICIF_MASK;
    }
    uint16 i2c_get_ack(I2C_MemMapPtr p)
    {
        if((p->S & I2C_S_RXAK_MASK) == 0)
            return TRUE;
        else
            return FALSE;
    }
    
    

    Just add the wait ACK!

       Wish it helps you!

       If you still have questions about it, please kindly let me know.

       

    Best Regards,

    Kerry

     

    -------------------------------------------------------------------------------
    Note:
    - If this post answers your question, please click the "Mark Correct" button. Thank you!

     

    - We are following threads for 7 weeks after the last post, later replies are ignored
    Please open a new thread and refer to the closed one, if you have 

    0 Kudos

    7 Views
    Senior Contributor II
     // clear flag
        p->S |= I2C_S_IICIF_MASK;

    Do not use OR as in |= to clear Write One To Clear flags (W1C)

    despite NXPs numerous examples of making this error.

    Use assignment (=).
    Depending on how interrupt code is structured this can lead to obscure timing races that can accidentally clear things unintentionally.

    0 Kudos

    7 Views
    NXP TechSupport
    NXP TechSupport

    Hi Cameron Ehlers ,

        Please share some calling API code snippets.

        Do you also check the I2C bus, what's the details bus now, especially during the second write.

       Before your second write, do you add the stop?

       Or you need to check with your I2C slave, the second write wave requirement.

       You also can refer to the KL26 SDK I2C code:

    SDK_2.2.0_FRDM-KL26Z\boards\frdmkl26z\driver_examples\i2c

       Which can be downloaded from this link:

      https://mcuxpresso.nxp.com/en/select 

     

       Wish it helps you!

       If you still have questions about it, please kindly let me know.

       

    Best Regards,

    Kerry

     

    -------------------------------------------------------------------------------
    Note:
    - If this post answers your question, please click the "Mark Correct" button. Thank you!

     

    - We are following threads for 7 weeks after the last post, later replies are ignored
    Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
    -------------------------------------------------------------------------------

    0 Kudos