cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_I2C_Mem_Read disables interrupts

JGaby.1
Associate III

I have set up a STM32F103 as a I2C master. I have found that calling the function HAL_I2C_Mem_Read sometimes disables all interrupts for more than 140us. Needless to say this causes significant problems with the timing of other external interrupts. The section of code that does this is:

 

 

          __disable_irq();

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

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

          /* Update counter */
          hi2c->XferSize--;
          hi2c->XferCount--;

          /* Wait until BTF flag is set */
          count = I2C_TIMEOUT_FLAG * (SystemCoreClock / 25U / 1000U);
          do
          {
            count--;
            if (count == 0U)
            {
              hi2c->PreviousState       = I2C_STATE_NONE;
              hi2c->State               = HAL_I2C_STATE_READY;
              hi2c->Mode                = HAL_I2C_MODE_NONE;
              hi2c->ErrorCode           |= HAL_I2C_ERROR_TIMEOUT;

              /* Re-enable IRQs */
              __enable_irq();

              /* Process Unlocked */
              __HAL_UNLOCK(hi2c);

              return HAL_ERROR;
            }
          }
          while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == RESET);

          /* Generate Stop */
          SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);

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

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

          /* Update counter */
          hi2c->XferSize--;
          hi2c->XferCount--;

          /* Re-enable IRQs */
          __enable_irq();

 

 

 Why is it necessary to disable the interrupts for so long. What happens if I remove the code which disables the interrupts? Is there some other way to work around this problem?

Thanks

4 REPLIES 4
TDK
Guru

The source code states the reason for doing this:

 

          /* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
             software sequence must complete before the current byte end of transfer */
          __disable_irq();

 

At a 400 kHz bus speed, this is a ~20us window. Probably they did this to prevent people from saying the library doesn't work due to interrupts happening. Ultimately, it's a limitation of the hardware. The peripheral is a mess with different behavior required for 1, 2, and 3+ byte transfers and some race-like types of conditions like this. It is much better on newer families.

 

> What happens if I remove the code which disables the interrupts?

If your other interrupts complete quickly enough that the event is not missed, it will work.

 

> Is there some other way to work around this problem?

Use HAL_I2C_Mem_Read_IT, doesn't appear to be any interrupts disabled in that code.

Write your own driver.

Move to a more recent chip.

(No magic solutions, naturally, or they would have done that in HAL.)

If you feel a post has answered your question, please click "Accept as Solution".
JGaby.1
Associate III

So if I run the I2C at 100 kHz then my interrupts would have 80 us to complete?

Saket_Om
ST Employee

Hello @JGaby.1 

It is recommended to switch to I2C IT mode or I2C DMA mode to avoid this issue. 

If your question is answered, please close this topic by clicking "Accept as Solution".

Thanks
Omar

Yes, approximately.

If you feel a post has answered your question, please click "Accept as Solution".