cancel
Showing results for 
Search instead for 
Did you mean: 

I2C IT RX Fails with a stopped SCL, but the Rx interrupt still fires.

Jcoll.1
Associate II

I have an STM32WB design using I2C to get 6 bytes from a BMI0888 every 50ms (triggered by an external interrupt).  The interrupt sets a flag in my main loop to go get the data.  The devices first requires you to write the desired register, then read the 6 bytes.  If I use back to back calls to HAL_I2C_Master_Transmit followed by HAL_I2C_Master_Receive, it works great.  See figure 1 below.  That will happen every 50ms.

 

I am now trying to do this using the interrupt versions of the functions.  If I try and use HAL_I2C_Master_Transmit_IT and HAL_I2C_Master_Transmit_IT.  I again use flags to inform my main loop what to do.  The external interrupt flags the loop to perform the transmit, then the HAL_I2C_MasterTxCpltCallback informs the main loop to perform the receive, and then HAL_I2C_MasterRxCpltCallback tells the main loop the data is ready.  I get all of these interrupts, however the data isn't actually read.  See figure 2 below where SCL stops high.

 

 

Blocking:

20240203_142329.jpg

Interrupt:

20240203_142152.jpg

3 REPLIES 3
Jcoll.1
Associate II

I traced the execution of this and found where it's failing, but I don't know why.

 

First I transfer a register I want to read.  I'm intentionally doing this as multiple steps rather than use the MemRead functions:

 

 

status = HAL_I2C_Master_Transmit_IT (
  &hi2c1, ACC_ADDR,
  &regAddr, 1);

 

 

 
 
After I get the complete-interrupt from that, and observe it on a scope, I then two a 6-byte read.  I follow it with a while(1) so that I know nothing else will happen with the i2c line.

 

 

status = HAL_I2C_Master_Receive_IT(
&hi2c1,
ACC_ADDR,
buffer,
6);
  while(1)
  {  }

 

 

  
 That function calls 

 

 

I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, xfermode, I2C_GENERATE_START_READ);​

 

 

 
 When this is called the device will transfer the slave address and then receive the first 2-bytes but NOT the last ack.  At which point the clock goes low.  It stays low forever.
 
The I2C_Master_ISR_IT interrupt is triggered which does this:

 

 

     /* Remove RXNE flag on temporary variable as read done */
    tmpITFlags &= ~I2C_FLAG_RXNE;

    /* Read data from RXDR */
    *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->RXDR;

    /* Increment Buffer pointer */
    hi2c->pBuffPtr++;

    hi2c->XferSize--;
    hi2c->XferCount--;

 

 

 
This interrupt will fire 6 times, each time running the code above, but scl stays low.  On the 6-th call the I2C_ITMasterCplt function is called because it thinks the transfer is done.
 
At this point the registers are.  I don't see any errors here, and I see a stopf.  Again, scl is still low and the last 4 bytes are never actually clocked:

Jcoll1_0-1707264959549.png

 

Jcoll.1
Associate II

Another example.  Every blocking read here works fine, but the IT version hangs SCL low.

 

  uint8_t regAddr = BMI_ACC_DATA;
  uint8_t status = 1;
  status = HAL_I2C_Master_Transmit (
      &hi2c1, ACC_ADDR,
      &regAddr, 1, 1000);
  status = HAL_I2C_Master_Receive(
      &hi2c1,
      ACC_ADDR,
      buffer,
      6, 1000);
  status = HAL_I2C_Master_Receive(
      &hi2c1,
      ACC_ADDR,
      buffer,
      6, 1000);
  status = HAL_I2C_Master_Receive(
      &hi2c1,
      ACC_ADDR,
      buffer,
      6, 1000);
  status = HAL_I2C_Master_Receive_IT(
      &hi2c1,
      ACC_ADDR,
      buffer,
      6);
  while(1)
  {

  }
Jcoll.1
Associate II

The manual describes this exact behavior is RXDR isn't read.  Specifically that SCL will stretch between and 8th and 9th bit.  However I am seeing the callback in the hal read from RXDR yet nothing happens on the line:

 

Jcoll1_0-1707434240770.png