STM32 HAL:如何在真正的非阻滞模式下读取I2C内存?
这是我所拥有的:具有输入的PCA9555芯片,如果输入上的信号状态更改,则发送中断信号。然后,我可以通过I2C阅读芯片以检查输入。
我需要的东西 - 当PIN改变状态时,我需要阅读芯片,检查哪个PIN更改状态并通知我的应用程序。
因此,我有一个中断,中断处理程序一定不能阻止MCU。
我明显的选择是使用hal_i2c_mem_read_it(),对吗?
我制作了整个代码,对其进行了测试。好像它起作用了...一段时间了。
直到我添加像每100毫秒一样读取芯片。
代码仍然有效,但是我看到闪烁的东西,停止眨眼,超过一秒钟甚至2。
我检查了HAL来源并找到了此处:
static HAL_StatusTypeDef I2C_RequestMemoryRead(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout,
uint32_t Tickstart)
{
I2C_TransferConfig(hi2c, DevAddress, (uint8_t)MemAddSize, I2C_SOFTEND_MODE, I2C_GENERATE_START_WRITE);
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* If Memory address size is 8Bit */
if (MemAddSize == I2C_MEMADD_SIZE_8BIT)
{
/* Send Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
}
/* If Memory address size is 16Bit */
else
{
/* Send MSB of Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_MSB(MemAddress);
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Send LSB of Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
}
/* Wait until TC flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TC, RESET, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_OK;
}
作为名称i2c_waitontxisflaguntiltimeout()
建议 - 等待。是的,这是一个,而
循环会阻止执行线程直到设置标志:
static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status,
uint32_t Timeout, uint32_t Tickstart)
{
while (__HAL_I2C_GET_FLAG(hi2c, Flag) == Status)
{
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U))
{
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
}
}
}
return HAL_OK;
}
其中3个滞后函数。
对于我的应用程序,这是一个表演塞子。它只是行不通的,因为它取决于实时处理事件。当中断处理程序块时,它还具有冻结的GUI。
有很快的解决方法吗?这是HAL驱动程序中的错误吗?
我必须实现自己的非阻滞功能吗?似乎很多小时的编码,因为该函数不是微不足道的,并且与该模块的其余部分紧密结合。
我的想法是将其重写并替换 循环使用我的非阻止延迟功能,该功能使用计时器中断在一段时间后继续工作。为了使其更加平底,每个回调都必须接收必要的状态数据才能继续。然后,状态计算机可以弄清楚我们的i2c_requestmemoryread _
进程的位置。最后,我只是致电注册回调并完成。它应该真正的非阻滞作用...
但是我有截止日期。可以更快地完成吗?当循环时,甚至如何使用的hal“ _it”功能阻止线程?这完全是错的!它破坏了“中断模式函数”的全部目的。如果它阻止,则已经有一个更简单的封锁版本。
Here's what I have: PCA9555 chip that has inputs, if a signal state on the input changes, the interrupt signal is sent. Then I can read the chip via I2C to check the inputs.
What I need - when a pin changes state, I need to read the chip, check which pin changed state and notify my app about it.
So I have an interrupt and the interrupt handler MUST NOT block the MCU.
My obvious choice is using HAL_I2C_Mem_Read_IT(), right?
I made the whole code, tested it. It seemed like it worked... For a while.
Until I added reading the chip like every 100ms.
The code still works, but I see the blinking things stutter, stop blinking for more than a second or even 2. So - it became obvious that HAL_I2C_Mem_Read_IT() BLOCKS my interrupt that causes the MCU to freeze.
I checked the HAL sources and found this:
static HAL_StatusTypeDef I2C_RequestMemoryRead(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout,
uint32_t Tickstart)
{
I2C_TransferConfig(hi2c, DevAddress, (uint8_t)MemAddSize, I2C_SOFTEND_MODE, I2C_GENERATE_START_WRITE);
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* If Memory address size is 8Bit */
if (MemAddSize == I2C_MEMADD_SIZE_8BIT)
{
/* Send Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
}
/* If Memory address size is 16Bit */
else
{
/* Send MSB of Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_MSB(MemAddress);
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Send LSB of Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
}
/* Wait until TC flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TC, RESET, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_OK;
}
As the name I2C_WaitOnTXISFlagUntilTimeout()
suggests - it WAITS. Yes, it's a while
loop that blocks the executing thread until a flag is set:
static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status,
uint32_t Timeout, uint32_t Tickstart)
{
while (__HAL_I2C_GET_FLAG(hi2c, Flag) == Status)
{
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U))
{
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
}
}
}
return HAL_OK;
}
There are 3 of those lagging functions.
For my application this is a show stopper. It just doesn't work, since it depends on handling the events in real time. Also it has a GUI that freezes when the interrupt handler blocks.
Is there a quick workaround for this? Is it a bug in HAL driver?
Do I have to implement my own non-blocking function? It seems like many, many hours of coding, since the function is non trivial and tightly coupled with the rest of the module.
My idea is to rewrite it and replace while
loops with my non-blocking delay function that uses a timer interrupt to continue work after some time passes. To make it more non-trivial, each callback would have to receive the necessary state data to continue. Then the state machine to figure out where we are with my I2C_RequestMemoryRead_
process. At the end I just call the registered callback and done. It should work truly non-blocking...
But I have deadlines. Can it be done faster? How is it even possible the HAL "_IT" function BLOCKS the thread with some while
loops? It's just wrong! It defeats the entire purpose of an "interrupt mode function". If it blocks, there already IS a blocking version that is simpler.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我解决了黑客入侵原始HAL驱动程序的问题。
https://gist.github.com/htd/e36fb68488742f27a7a7a7a7a7a7a5d096170623
在将文件添加到项目中后,原始HAL驱动程序需要在STM32H7XXX_HAL_HAL_HAL_HAL_HAL_I2C_NB.HHALB.HHB.HHB.HHB.HHB.HHB.HHB.HHB.HHAL中进行修改。
为了与STM32Cubeide一起使用,最好将修改后的驱动程序文件移至其他位置,以防止IDE覆盖它。
我留下了其他 *它的功能不变,但是在所有其余功能中添加类似的修改非常容易。
在我的STM32H747I-DISCO板上,使用PCA9555 16位I/O扩展器对现实世界应用进行了测试。
在测试中,我用刚崩溃了原始驱动程序的随机噪声信号向输入发送了垃圾邮件。在这里它可以正常工作,不会阻止其他线程,GUI全速工作。
I solved the problem with hacking the original HAL driver.
https://gist.github.com/HTD/e36fb68488742f27a737a5d096170623
After adding the files to the project the original HAL driver needs to be modified as described in the stm32h7xx_hal_i2c_nb.h comment.
For use with STM32CubeIDE it's best to move the modified driver file to a different location to prevent the IDE from overwriting it.
I left other *IT functions unchanged, but it's very easy to add similar modifications to all remaining functions.
The hacked driver was tested in real world application on my STM32H747I-DISCO board with PCA9555 16-bit I/O expander.
For the test I spammed the input with random noise signals that just crashed the original driver. Here it works, doesn't block other threads, the GUI works with full speed.