2025-03-11 4:57 PM
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
Solved! Go to Solution.
2025-03-11 5:21 PM
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.)
2025-03-11 5:21 PM
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.)
2025-03-11 5:34 PM
So if I run the I2C at 100 kHz then my interrupts would have 80 us to complete?
2025-03-12 2:50 AM
Hello @JGaby.1
It is recommended to switch to I2C IT mode or I2C DMA mode to avoid this issue.
2025-03-12 6:02 AM
Yes, approximately.
2025-03-12 7:45 AM
Thanks for your help. You are not the first to suggest that I use a more recent chip. If I were to change, what device would you suggest?