首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >STM32 cubeMX:使用中断触发SPI DMA中断

STM32 cubeMX:使用中断触发SPI DMA中断
EN

Stack Overflow用户
提问于 2019-01-02 06:59:36
回答 1查看 2.4K关注 0票数 0

我目前正在练习使用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();发送1字节的命令,拉高D/C引脚,并在SPI中断例程中启动DMA传输。在DMA TxComplete中断中,CS引脚将被拉高。

我的SPI设置为数据长度为8位,DMA设置为存储器到外设和增量模式。

我正在使用cubeMX生成代码,下面是我的大致代码:

代码语言:javascript
运行
复制
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中断例程

代码语言:javascript
运行
复制
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中断例程的最后一部分

代码语言:javascript
运行
复制
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 */
}

在我目前的尝试中,main启动了spi CMD传输,我预计DMA传输将由HAL_SPI_Transmit_DMA()触发。但是DMA只能启动一次,这是第一次传输。然后,由于hspi->State != HAL_SPI_STATE_READYHAL_SPI_Transmit_DMA()看起来像是返回HAL_BUSY

我不确定我哪里做错了。谁能提供任何提示,什么是驱动基于中断的DMA传输的正确方法?

谢谢。

Update1

我在调查之后得到了一个奇怪的结果。因为我只有一个逻辑分析仪作为我的调试工具,所以我把引脚切换作为调试消息。我将其中一个放入SPI_IRQHandler,如下所示:

代码语言:javascript
运行
复制
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()的末尾放置了另一个引脚进行切换。我把它放在函数的末尾。

代码语言:javascript
运行
复制
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()

screen shot of logic analyzer

这怎么会发生呢?

EN

回答 1

Stack Overflow用户

发布于 2021-01-25 01:10:12

我在一个状态机中遇到了同样的问题,我想让SPI传输“在后台”运行。我有一个SPI Tx-Rx状态机,其中下一个Tx/Rx由上一次传输的DMA Rx/Tx完成回调中断触发。

症状是相同的:在第一次调用SPI_Tx_DMA,SPI_Rx_DMA序列-恰好由一个Tx和一个Rx组成-之后,当我尝试下一个Tx时,SPI卡住了,并报告了一个"HAL_BUSY“错误。

解决方案是检查CubeMX中的SPI设置,并将RX DMA通道的DMA模式从循环更改为正常。我不知道为什么我最初选择“循环”--主要是因为当我第一次在CubeMX中设置SPI时,它似乎是“合理的”……

我没有进一步调查,所以我不能提供一个复杂的答案,幕后发生了什么……

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53999597

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档