简体   繁体   English

STM32 HAL:如何以真正的非阻塞模式读取I2C memory?

[英]STM32 HAL: How to read I2C memory in truly non-blocking mode?

Here's what I have: PCA9555 chip that has inputs, if a signal state on the input changes, the interrupt signal is sent.这是我所拥有的:具有输入的PCA9555芯片,如果输入上的信号state发生变化,则发送中断信号。 Then I can read the chip via I2C to check the inputs.然后我可以通过 I2C 读取芯片以检查输入。

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.我需要什么 - 当引脚更改 state 时,我需要读取芯片,检查哪个引脚更改了 state 并通知我的应用程序。

So I have an interrupt and the interrupt handler MUST NOT block the MCU.所以我有一个中断,中断处理程序绝不能阻塞 MCU。

My obvious choice is using HAL_I2C_Mem_Read_IT(), right?我明显的选择是使用 HAL_I2C_Mem_Read_IT(),对吧?

I made the whole code, tested it.我制作了整个代码,并对其进行了测试。 It seemed like it worked... For a while.似乎它奏效了……有一段时间了。

Until I added reading the chip like every 100ms.直到我添加了每 100 毫秒读取一次芯片。

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.代码仍然有效,但我看到闪烁的东西断断续续,停止闪烁超过一秒甚至 2 秒。所以 - 很明显,HAL_I2C_Mem_Read_IT() 阻止了导致 MCU 冻结的中断。

I checked the HAL sources and found this:我检查了 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;
}

As the name I2C_WaitOnTXISFlagUntilTimeout() suggests - it WAITS.正如名称I2C_WaitOnTXISFlagUntilTimeout()所暗示的那样 - 它等待。 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.其中有 3 个滞后函数。

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.它还具有一个 GUI,当中断处理程序阻塞时会冻结。

Is there a quick workaround for this?有没有快速解决方法? Is it a bug in HAL driver?它是 HAL 驱动程序中的错误吗?

Do I have to implement my own non-blocking function?我必须实现自己的非阻塞 function 吗? It seems like many, many hours of coding, since the function is non trivial and tightly coupled with the rest of the module.这似乎是很多很多小时的编码,因为 function 非常重要并且与模块的 rest 紧密耦合。

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.我的想法是重写它并用我的非阻塞延迟 function 替换while循环,它使用定时器中断在一段时间后继续工作。 To make it more non-trivial, each callback would have to receive the necessary state data to continue.为了让它更重要,每个回调都必须接收必要的 state 数据才能继续。 Then the state machine to figure out where we are with my I2C_RequestMemoryRead_ process.然后 state 机器用我的I2C_RequestMemoryRead_进程找出我们在哪里。 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? HAL“_IT”function 怎么可能用一些while循环阻塞线程? 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.已经有一个更简单的阻塞版本。

I solved the problem with hacking the original HAL driver.我通过破解原始 HAL 驱动程序解决了这个问题。

https://gist.github.com/HTD/e36fb68488742f27a737a5d096170623https://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.将文件添加到项目后,需要按照 stm32h7xx_hal_i2c_nb.h 注释中的描述修改原始 HAL 驱动程序。

For use with STM32CubeIDE it's best to move the modified driver file to a different location to prevent the IDE from overwriting it.为了与 STM32CubeIDE 一起使用,最好将修改后的驱动程序文件移动到不同的位置,以防止 IDE 覆盖它。

I left other *IT functions unchanged, but it's very easy to add similar modifications to all remaining functions.我保留了其他 *IT 功能不变,但很容易对所有剩余功能添加类似的修改。

The hacked driver was tested in real world application on my STM32H747I-DISCO board with PCA9555 16-bit I/O expander.被黑的驱动程序在我的带有 PCA9555 16 位 I/O 扩展器的 STM32H747I-DISCO 板上的实际应用中进行了测试。

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.它在这里工作,不阻塞其他线程,GUI 全速工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM