簡體   English   中英

STM32 cubeMX:使用中斷觸發SPI DMA中斷

[英]STM32 cubeMX: triggering SPI DMA interrupt using interrupt

我目前正在練習使用 SPI+DMA 將數據發送到 SPI 顯示器。 顯示的數據順序如下:

[pull CS low]->[pull D/C low]->[1 SPI byte of CMD]->[pull D/C high]->[n SPI byte of data]->[pull CS high]。 其中 D/C 引腳是 GPIO 引腳。

我的想法是先把CS和D/C拉低,然后通過HAL_SPI_Transmit_IT(); 並將 D/C 引腳拉高並在 SPI 中斷例程中啟動 DMA 傳輸。 CS 引腳將在 DMA TxComplete 中斷中拉高。

我的SPI設置為8位數據長度,DMA設置為內存到外設和遞增模式。

我正在使用 cubeMX 來生成代碼,這里大致是我的代碼:

uint8_t displayData[DIS_DATA_BUFF_SIZE];

int main(void)
{
    ...init stuff from cubeMX
    cmdBuffer[0].cmd = 0xAA;
    cmdBuffer[0].amountOfData = 10;
    cmdBuffer[0].pDataStart = displayData;

    while (1)
    {
        HAL_Delay(500);

        cmdBuffer[0].status = IN_USE;
        pExecuteCmd = &cmdBuffer[0];

        SPI_START();
        DIS_CMD_MODE_ON();
        HAL_SPI_Transmit_IT(&hspi2, &pExecuteCmd->cmd, 1);
    }
}

這是我的 SPI 中斷例程

void SPI2_IRQHandler(void)
{
  /* USER CODE BEGIN SPI2_IRQn 0 */
    uint8_t startDMA = 0;
    if(__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE)){
        if(pExecuteCmd->status == EXE_CMD){
            DIS_CMD_MODE_OFF();
            if(pExecuteCmd->amountOfData == 0){
                SPI_END();
                pExecuteCmd->status = EMPTY;
            }else{
                pExecuteCmd->status = EXE_DATA;
                startDMA = 1;
            }
        }
        else if(pExecuteCmd->status == IN_USE){
             pExecuteCmd->status = EXE_CMD;
        }
    }

  /* USER CODE END SPI2_IRQn 0 */
    HAL_SPI_IRQHandler(&hspi2);
    if(startDMA)
    {
    
        HAL_SPI_Transmit_DMA(&hspi2, pExecuteCmd->pDataStart,
                    pExecuteCmd->amountOfData);
    }
  /* USER CODE BEGIN SPI2_IRQn 1 */

  /* USER CODE END SPI2_IRQn 1 */
}

這是我的 DMA 中斷例程的最后一部分

void DMA1_Channel5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel5_IRQn 0 */
    if(__HAL_DMA_GET_FLAG(&hdma_spi2_tx, DMA_FLAG_TC5)){
        SPI_END();
        pExecuteCmd->status = EMPTY;
    }


  /* USER CODE END DMA1_Channel5_IRQn 0 */
    HAL_DMA_IRQHandler(&hdma_spi2_tx);
  /* USER CODE BEGIN DMA1_Channel5_IRQn 1 */

  /* USER CODE END DMA1_Channel5_IRQn 1 */
}

在我目前的嘗試中,主要啟動 spi CMD 傳輸,我希望 DMA 傳輸將由HAL_SPI_Transmit_DMA()觸發。 但是DMA只能啟動一次,即第一次發送。 然后HAL_SPI_Transmit_DMA()似乎返回HAL_BUSY由於hspi->State != HAL_SPI_STATE_READY

我不確定我哪里做錯了。 任何人都可以提供任何提示,驅動基於中斷的 DMA 傳輸的正確方法是什么?

謝謝。

更新1

調查后我得到了一個奇怪的結果。 由於我只有一個邏輯分析儀作為我的調試工具,所以我將引腳切換作為調試信息。 我將一個放在 SPI_IRQHandler 中,如下所示:

void SPI2_IRQHandler(void)
{
/* USER CODE BEGIN SPI2_IRQn 0 */
    uint8_t startDMA = 0;
    if(__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE)){
        if(pExecuteCmd->status == EXE_CMD){
            DIS_CMD_MODE_OFF();
            if(pExecuteCmd->amountOfData == 0){
                SPI_END();
                pExecuteCmd->status = EMPTY;
            }else{
                pExecuteCmd->status = EXE_DATA;
                startDMA = 1;
            }
        }
        else if(pExecuteCmd->status == IN_USE){
            pExecuteCmd->status = EXE_CMD;
        }
    }
    /* USER CODE END SPI2_IRQn 0 */
    HAL_SPI_IRQHandler(&hspi2);

    if(startDMA)
    {
        if(hspi2.State == HAL_SPI_STATE_READY){

            HAL_GPIO_TogglePin(DIS_NRST_GPIO_Port, DIS_NRST_Pin);
            HAL_GPIO_TogglePin(DIS_NRST_GPIO_Port, DIS_NRST_Pin);
            //^^^^^^^toggle pin showing the state is READY^^^^^//
            HAL_SPI_Transmit_DMA(&hspi2, pExecuteCmd->pDataStart,
                            pExecuteCmd->amountOfData);
        }
    }
}

並且還在 HAL_SPI_Transmit_DMA() 的末尾放置了另一個引腳切換。 我把它放在函數的末尾。

HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;

  /* Check Direction parameter */
  assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));

  /* Process Locked */
  __HAL_LOCK(hspi);

  if(hspi->State != HAL_SPI_STATE_READY)
  {
    errorcode = HAL_BUSY;
    goto error;
  }

  if((pData == NULL) || (Size == 0U))
  {

    errorcode = HAL_ERROR;
    goto error;
  }

  /* Set the transaction information */
  hspi->State       = HAL_SPI_STATE_BUSY_TX;
  hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
  hspi->pTxBuffPtr  = (uint8_t *)pData;
  hspi->TxXferSize  = Size;
  hspi->TxXferCount = Size;

  /* Init field not used in handle to zero */
  hspi->pRxBuffPtr  = (uint8_t *)NULL;
  hspi->TxISR       = NULL;
  hspi->RxISR       = NULL;
  hspi->RxXferSize  = 0U;
  hspi->RxXferCount = 0U;

  /* Configure communication direction : 1Line */
  if(hspi->Init.Direction == SPI_DIRECTION_1LINE)
  {
    SPI_1LINE_TX(hspi);
  }

#if (USE_SPI_CRC != 0U)
  /* Reset CRC Calculation */
  if(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SPI_RESET_CRC(hspi);
  }
#endif /* USE_SPI_CRC */

  /* Set the SPI TxDMA Half transfer complete callback */
  hspi->hdmatx->XferHalfCpltCallback = SPI_DMAHalfTransmitCplt;

  /* Set the SPI TxDMA transfer complete callback */
  hspi->hdmatx->XferCpltCallback = SPI_DMATransmitCplt;

  /* Set the DMA error callback */
  hspi->hdmatx->XferErrorCallback = SPI_DMAError;

  /* Set the DMA AbortCpltCallback */
  hspi->hdmatx->XferAbortCallback = NULL;

  /* Enable the Tx DMA Stream */
  HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR, hspi->TxXferCount);

  /* Check if the SPI is already enabled */
  if((hspi->Instance->CR1 &SPI_CR1_SPE) != SPI_CR1_SPE)
  {
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
  }

  /* Enable the SPI Error Interrupt Bit */
  SET_BIT(hspi->Instance->CR2, SPI_CR2_ERRIE);

  /* Enable Tx DMA Request */
  SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);

error :
  /* Process Unlocked */
  __HAL_UNLOCK(hspi);
  HAL_GPIO_WritePin(DIS_DC_GPIO_Port, DIS_DC_Pin, RESET);
  HAL_GPIO_WritePin(DIS_DC_GPIO_Port, DIS_DC_Pin, SET);
  return errorcode;
}

結果:DMA 傳輸只在第一次工作,然后沒有數據通過 DMA 傳輸。 而且我只切換了一次 DIS_DC_Pin,多次切換 DIS_NRST_Pin。 這意味着,該進程確實在中斷例程中進入了if(hspi2.State == HAL_SPI_STATE_READY) ,但沒有進入HAL_SPI_Transmit_DMA() ???

邏輯分析儀的屏幕截圖

這怎么會發生?

我在狀態機中遇到了同樣的問題,我希望 SPI 傳輸“在后台”運行。 我有/有一個 SPI Tx-Rx 狀態機,其中下一個 Tx/Rx 由一次最后一次傳輸的 DMA Rx/Tx 完成回調中斷觸發。

症狀是相同的:在第一次調用 SPI_Tx_DMA、SPI_Rx_DMA 序列之后——正好由一個 Tx 和一個 Rx 組成——當我嘗試下一個 Tx 時,SPI 似乎卡住了並報告了“HAL_BUSY”錯誤。

解決方案是檢查 CubeMX 中的 SPI DMA 設置並將 RX DMA 通道的 DMA 模式從循環更改為正常。 我不知道為什么我最初選擇了“循環”——主要是因為當我第一次在 CubeMX 中設置 SPI 時,這對我來說似乎有點“合理”……

我沒有進一步調查,所以我無法提供復雜的答案,幕后發生了什么......

暫無
暫無

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

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