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

     2023-02-19     335

关键词:

【中文标题】STM32 cubeMX:使用中断触发SPI DMA中断【英文标题】:STM32 cubeMX: triggering SPI DMA interrupt using interrupt 【发布时间】:2019-05-28 16:52:33 【问题描述】:

我目前正在练习使用 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 个字节的 CMD 并将 D/C 引脚拉高并在 SPI 中断程序中启动 DMA 传输。并且在 DMA TxComplete 中断中 CS 引脚会被拉高。

我的 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 */

在我当前的尝试中,main 启动了 spi CMD 传输,我希望 DMA 传输将由HAL_SPI_Transmit_DMA() 触发。但是 DMA 只能启动一次,这是第一次传输。然后HAL_SPI_Transmit_DMA() 似乎因为hspi->State != HAL_SPI_STATE_READY 而返回HAL_BUSY

我不确定我在哪里做错了。任何人都可以提供任何提示,驱动基于中断的 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()???

screen shot of logic analyzer

怎么会这样?

【问题讨论】:

调用HAL_SPI_Transmit_IT后你的SPI有输出数据吗?我希望您使用 HAL_Delay 的解决方案只是暂时的。使用中断然后希望它们在固定延迟后完成不是很有效。 您好,此代码仅用于测试机制,并非最终实现。是的,HAL_SPI_Transmit_IT() 确实按预期传输了 0xAA。但是对于 HAL_SPI_Transmit_DMA(),它只在启动后的第一个中断时起作用,然后 HAL_SPI_Transmit_DMA 不再做任何事情,它返回 HAL_BUSY。 顺便说一下,我在main中单独尝试了HAL_SPI_Transmit_DMA(),该函数本身起作用,它发出分配的数据量。但是当我按照我在帖子中描述的那样将它放入中断例程时它不起作用。 您是否尝试过调试 HAL_SPI_IRQHandler() 函数?该函数负责将 HAL 状态设置为例如HAL_OK 或 HAL_BUSY。如果此功能后的状态不正常,您的发送 dma 功能将无法正常工作。 谢谢你的意见,我做了更多的调试,我在原帖里有更新,你能看看吗? 【参考方案1】:

我在希望 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 时,这对我来说似乎有点“合理”......

我没有进一步调查,所以我无法提供复杂的答案,幕后发生了什么......

【讨论】:

stm32cubemx使用之dma使用spi

目录一、概述二、Pinout&Configuration2.1选择端口2.2开启DMA中断三、测试SPI一、概述这篇文章简单记录下SPI的使用。二、Pinout&Configuration2.1选择端口选择要使用的SPI,对应的PIN脚会自动初始化为SPI模式:查看原理图,... 查看详情

stm32g070rbt6基于stm32cubemx创建exti外部中断工程(代码片段)

STM32G070RBT6基于STM32CubeMX创建外部中断工程相关篇《【硬件开源电路】STM32G070RBT6开发板》🌻STM32CubeMX配置EXTI外部中断过程演示📝工程概要📜使用STM32CubeMX配置一个外部中断工程。通过外部两个按键分别接到PC0和PC1引脚... 查看详情

stm32cubemx外部中断(代码片段)

stm32cubemx外部中断一、软件配置将KEY0(PE4)和KEY1引脚配置为外部中断引脚:然后配置其模式及其上下拉。模式共有六种,如下:这里配置为带下降沿触发检测的外部中断模式,并上拉。配置NVIC设置中断优先级&#... 查看详情

stm32cubemx外部中断(代码片段)

stm32cubemx外部中断一、软件配置将KEY0(PE4)和KEY1引脚配置为外部中断引脚:然后配置其模式及其上下拉。模式共有六种,如下:这里配置为带下降沿触发检测的外部中断模式,并上拉。配置NVIC设置中断优先级&#... 查看详情

stm32cubemx-配置spi驱动max31865读取铂电阻温度(代码片段)

文章目录STM32CubeMX-配置SPI驱动MAX31865读取铂电阻温度一、初始准备1.硬件平台2.软件平台3.原理图接线二、操作步骤1.CubeMX生成初始化代码1.1建立工程(通用步骤)1.2开启串口1.3配置SPI1.4配置GPIO1.5生成代码(通用步骤... 查看详情

stm32cubemx定时器中断(代码片段)

stm32cubemx定时器中断一、基础知识stm32f4:二、软件配置定时器时钟源:APB2负责AD,I/O,高级TIM,串口1。APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM。所支持速度不同:APB2支持高速状态... 查看详情

stm32cubemx定时器中断(代码片段)

stm32cubemx定时器中断一、基础知识stm32f4:二、软件配置定时器时钟源:APB2负责AD,I/O,高级TIM,串口1。APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM。所支持速度不同:APB2支持高速状态... 查看详情

stm32cubemx笔记--外部中断,使用按键key检测(代码片段)

外部中断,使用按键KEY检测1、STM32CubeMX引脚设置和代码生成2、编写相关中断的C文件1.相关中断函数:2.相关中断函数的编写:3.相关中断的优先级顺序:3、编译工程文件,使用ST-Link烧录,测试按键的外部... 查看详情

使用stm32cubemx生成rtc工程[闹钟中断2]

在上次使用STM32CubeMX生成RTC工程[闹钟中断]基础上实现周期间隔的闹钟一些场合需要周期性的闹钟现在为了方便设置每十秒来一次。备注:当然可以直接修改HAL库staticHAL_StatusTypeDefRTC_WriteAlarmCounter(RTC_HandleTypeDef*hrtc,uint32_tAlarmCounter)... 查看详情

stm32cubemx——硬件spi驱动七针0.96寸oled(代码片段)

1、使用工具STM32Cubemx版本6.0.1Keil版本5.31ST-LinkSTM32F407VE核心板0.96寸七针OLED接线方式如下SPI_MOSO不需要连接2.STM32Cubemx配置1.配置时钟源时钟树等2.打开任一SPI,并开启DMA将SPI的SCK与OLED的D0连接,MOSI与OLED的D1连接2.配置普通IO... 查看详情

STM32 HAL SPI 中断处理

...ndling【发布时间】:2018-01-0917:35:48【问题描述】:我正在使用STM32F4xx并想学习使用ST-HAL进行编程。目前我尝试通过中断发送/接收SPI。我使用以下函数通过INT接收数据:初始化函数:voidHAL_MspInit(void)/*USERCODEBEGINMspInit0*//*USERCODEENDMsp... 查看详情

stm32cubemx-spi+dma驱动2812灯带(代码片段)

文章目录STM32CubeMX-SPI+DMA驱动2812灯带一、初始准备1.硬件平台二、操作步骤1.CubeMX生成初始化代码1.1建立工程(通用步骤)1.2配置SPI和DMA外设1.3生成代码(通用步骤)2.编写代码3.程序下载,观察现象(通... 查看详情

stm32cubemx之外部中断

STM32CubeMX之外部中断1.中断简介​​   中断,是指处理机处理程序运行中出现的紧急事件的整个过程。程序运行过程中,系统外部、系统内部或者现行程序本身若出现紧急事件,处理机立即中止现行程序的运行,自动... 查看详情

stm32f103vet6基于stm32cubemx创建串口中断+dma不定长数据接收(代码片段)

STM32F103VET6基于STM32CubeMX创建串口中断+DMA不定长数据接收✨STM32CubeMX工程配置过程演示:📍此工程参考《STM32CubeMX|STM32使用HAL库DMA+空闲中断实现串口不定长数据接收》📓DMA参数介绍🎉DMA传输方式🌿传输... 查看详情

stm32cubemx之fatfs+spi驱动w25qxx(代码片段)

文章目录1W25Q128简介2STM32CubeMX配置SPI2.1配置SPI2.2配置CS片选引脚3添加W25Q128驱动4将W25Q128挂载到FATFS4.1STM32CubeMX配置FATFS4.2在工程中为FATFS适配W25Q1284.2.1DSTATUSUSER_initialize(BYTEpdrv)4.2.2DSTATUSUSER_status(BYTEpdrv)4.2.3USE 查看详情

stm32cubemx(04)串口中断实验(代码片段)

文章目录前言一、STM32CubeMX配置1.1.芯片选择1.2.配置SYS,RCC1.3.配置时钟树1.4.工程文件配置二、串口配置2.1.STM32CubeMX配置2.2.USART中断2.3.USART1引脚2.4.生成代码三、添加代码3.1.添加回调函数3.2main函数中初始化3.3while中编写代码4.... 查看详情

stm32cubemx之硬件spi驱动w25q64

STM32CubeMx之硬件SPI驱动W25Q641.SPI简介   SPI是串行外设接口(SerialPeripheralInterface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,... 查看详情

lvgl移植stm32f1基于stm32cubemx配置硬件spi驱动1.8寸tftst7735s跑lvgl图形demo(代码片段)

【LVGL移植】STM32F1基于STM32CubeMX配置硬件SPI驱动1.8寸TFTST7735S屏幕跑LVGL图形demo🎬运行LVGL按键组件demo✨基于STM32CubeMX配置工程是因为方便移植,只要是STM32芯片,拿到我的这个工程源码就可以根据自己的stm32芯片,自... 查看详情