簡體   English   中英

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

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

這是我所擁有的:具有輸入的PCA9555芯片,如果輸入上的信號state發生變化,則發送中斷信號。 然后我可以通過 I2C 讀取芯片以檢查輸入。

我需要什么 - 當引腳更改 state 時,我需要讀取芯片,檢查哪個引腳更改了 state 並通知我的應用程序。

所以我有一個中斷,中斷處理程序絕不能阻塞 MCU。

我明顯的選擇是使用 HAL_I2C_Mem_Read_IT(),對吧?

我制作了整個代碼,並對其進行了測試。 似乎它奏效了……有一段時間了。

直到我添加了每 100 毫秒讀取一次芯片。

代碼仍然有效,但我看到閃爍的東西斷斷續續,停止閃爍超過一秒甚至 2 秒。所以 - 很明顯,HAL_I2C_Mem_Read_IT() 阻止了導致 MCU 凍結的中斷。

我檢查了 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()所暗示的那樣 - 它等待。 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;
}

其中有 3 個滯后函數。

對於我的應用程序,這是一個展示塞子。 它只是行不通,因為它依賴於實時處理事件。 它還具有一個 GUI,當中斷處理程序阻塞時會凍結。

有沒有快速解決方法? 它是 HAL 驅動程序中的錯誤嗎?

我必須實現自己的非阻塞 function 嗎? 這似乎是很多很多小時的編碼,因為 function 非常重要並且與模塊的 rest 緊密耦合。

我的想法是重寫它並用我的非阻塞延遲 function 替換while循環,它使用定時器中斷在一段時間后繼續工作。 為了讓它更重要,每個回調都必須接收必要的 state 數據才能繼續。 然后 state 機器用我的I2C_RequestMemoryRead_進程找出我們在哪里。 最后我只調用注冊的回調並完成。 它應該真正無阻塞地工作......

但我有最后期限。 可以做得更快嗎? HAL“_IT”function 怎么可能用一些while循環阻塞線程? 這是錯誤的。 如果它阻塞,它就破壞了“中斷模式功能”的全部目的。 已經有一個更簡單的阻塞版本。

我通過破解原始 HAL 驅動程序解決了這個問題。

https://gist.github.com/HTD/e36fb68488742f27a737a5d096170623

將文件添加到項目后,需要按照 stm32h7xx_hal_i2c_nb.h 注釋中的描述修改原始 HAL 驅動程序。

為了與 STM32CubeIDE 一起使用,最好將修改后的驅動程序文件移動到不同的位置,以防止 IDE 覆蓋它。

我保留了其他 *IT 功能不變,但很容易對所有剩余功能添加類似的修改。

被黑的驅動程序在我的帶有 PCA9555 16 位 I/O 擴展器的 STM32H747I-DISCO 板上的實際應用中進行了測試。

為了進行測試,我用隨機噪聲信號向輸入發送垃圾郵件,這些信號剛剛使原始驅動程序崩潰。 它在這里工作,不阻塞其他線程,GUI 全速工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM