你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

FW_H7 V1.12.1-HAL库DMA双缓冲配置

[复制链接]
talk is cheap 提问时间:2025-8-14 19:39 / 已解决
硬件平台:H750-ART-Pi
软件平台:CubeMX+HAL库 V1.12.1版本


问题背景:需要使用定时器触发DMA-SPI向外部DAC发送数据,由于需要向外部发送的数据量较大且配置SPI总线的多通道,当前需要使用DMA双缓冲配置(从SD卡中读取BIN文件依次填充PingPong-FIFO,使用外扩SDRAM作为DMA-BUF)
遇到的问题:当前已经能够使用单缓冲+定时器触发的方式来向外发送数据,使用双缓冲时,无反应(定时器在工作,当前网络上的一些例程都是基于标准库的,参考作用有限),想求助下HAL库DMA双缓冲案例或者函数调用顺序。
详情参考gitee链接:GIt-CubeMX配置
启动定时器部分代码:HAL_DMAEx_MultiBufferStart_IT这个函数应该是DMA双缓冲的关键 但是开启后无效,即使单缓冲正常工作下也无法看到SPI_TXDR更新,很奇怪
  1. void BSP_DAC_SPI_DMA_Start()
  2. {
  3.         LookUP_BUF_INIT();
  4. //        HAL_DMAEx_MultiBufferStart(&hspi2.hdmatx, LookUP_PingPongBufer.activeBuffer, &hspi2.Instance->TXDR,LookUP_PingPongBufer.readyBuffer,BUFFER_SIZE/4);
  5.         HAL_DMAEx_MultiBufferStart_IT(&hdma_spi2_tx, LookUP_PingPongBufer.activeBuffer, &hspi2.Instance->TXDR, LookUP_PingPongBufer.readyBuffer,BUFFER_SIZE/4);

  6. //        if(HAL_SPI_Transmit_DMA(&hspi2, (uint8_t*)LookUP_PingPongBufer.activeBuffer, BUFFER_SIZE/4)!= HAL_OK)       
  7. //        {
  8. //                printf("BSP_DAC_SPI_DMA_Start  HAL_SPI_Transmit_DMA ERROR !!! \r\n");                  
  9. //        }

  10.         if(HAL_TIM_OC_Start(&htim12, TIM_CHANNEL_1) != HAL_OK)
  11.         {
  12.                 printf("BSP_DAC_SPI_DMA_Start HAL_TIM_OC_Start ERROR !!! \r\n");                  
  13.         }       
  14. }
  15. void LookUP_BUF_INIT(void)
  16. {
  17.         FRESULT result;
  18.         uint32_t bw;
  19.         char path[64];
  20.        
  21.         PingPong_Init(&LookUP_PingPongBufer,LOOKUP_BUF1_ADDR,LOOKUP_BUF2_ADDR);
  22.        

  23.         result = f_mount(&fs, DiskPath, 0);                        /* Mount a logical drive */
  24.         if (result != FR_OK)
  25.         {
  26.                 printf("mount fs failed !!\r\n", FR_Table[result]);
  27.         }

  28.         sprintf(path, "%sTable_hex.bin", DiskPath);
  29.         result = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
  30.         if (result !=  FR_OK)
  31.         {
  32.                 printf("Don't Find File : Table_hex\r\n");
  33.                 return;
  34.         }

  35.         result = f_read(&file, LookUP_PingPongBufer.activeBuffer , BUFFER_SIZE, &bw);
  36.         if (bw > 0)
  37.         {
  38.                 FsReadBuf[bw] = 0;
  39.         }
  40.        
  41.         result = f_read(&file, LookUP_PingPongBufer.readyBuffer , BUFFER_SIZE, &bw);
  42.         if (bw > 0)
  43.         {
  44.                 FsReadBuf[bw] = 0;
  45.         }


  46. //        f_close(&file);

  47. //        f_mount(NULL, DiskPath, 0);
  48. }
复制代码
定时器初始化部分代码:
  1. void MX_SPI2_Init(void)
  2. {

  3.   /* USER CODE BEGIN SPI2_Init 0 */

  4.   /* USER CODE END SPI2_Init 0 */

  5.   /* USER CODE BEGIN SPI2_Init 1 */

  6.   /* USER CODE END SPI2_Init 1 */
  7.   hspi2.Instance = SPI2;
  8.   hspi2.Init.Mode = SPI_MODE_MASTER;
  9.   hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  10.   hspi2.Init.DataSize = SPI_DATASIZE_32BIT;
  11.   hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  12.   hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  13.   hspi2.Init.NSS = SPI_NSS_SOFT;
  14.   hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  15.   hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  16.   hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  17.   hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  18.   hspi2.Init.CRCPolynomial = 0x0;
  19.   hspi2.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  20.   hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  21.   hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  22.   hspi2.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  23.   hspi2.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  24.   hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  25.   hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  26.   hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  27.   hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  28.   hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  29.   if (HAL_SPI_Init(&hspi2) != HAL_OK)
  30.   {
  31.     Error_Handler();
  32.   }
  33.   /* USER CODE BEGIN SPI2_Init 2 */

  34.   /* USER CODE END SPI2_Init 2 */

  35. }

  36. void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
  37. {

  38.   GPIO_InitTypeDef GPIO_InitStruct = {0};
  39.   HAL_DMA_MuxSyncConfigTypeDef pSyncConfig= {0};
  40.   RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
  41.   if(spiHandle->Instance==SPI2)
  42.   {
  43.   /* USER CODE BEGIN SPI2_MspInit 0 */

  44.   /* USER CODE END SPI2_MspInit 0 */

  45.   /** Initializes the peripherals clock
  46.   */
  47.     PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI2;
  48.     PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_CLKP;
  49.     if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  50.     {
  51.       Error_Handler();
  52.     }

  53.     /* SPI2 clock enable */
  54.     __HAL_RCC_SPI2_CLK_ENABLE();

  55.     __HAL_RCC_GPIOI_CLK_ENABLE();
  56.     /**SPI2 GPIO Configuration
  57.     PI1     ------> SPI2_SCK
  58.     PI2     ------> SPI2_MISO
  59.     PI3     ------> SPI2_MOSI
  60.     */
  61.     GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  62.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  63.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  64.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  65.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
  66.     HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);

  67.     /* SPI2 DMA Init */
  68.     /* SPI2_TX Init */
  69.     hdma_spi2_tx.Instance = DMA1_Stream0;
  70.     hdma_spi2_tx.Init.Request = DMA_REQUEST_SPI2_TX;
  71.     hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  72.     hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  73.     hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;
  74.     hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  75.     hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  76.     hdma_spi2_tx.Init.Mode = DMA_CIRCULAR;
  77.     hdma_spi2_tx.Init.Priority = DMA_PRIORITY_LOW;
  78.     hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
  79.     hdma_spi2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  80.     hdma_spi2_tx.Init.MemBurst = DMA_MBURST_SINGLE;
  81.     hdma_spi2_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
  82.     if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
  83.     {
  84.       Error_Handler();
  85.     }

  86.     pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_TIM12_TRGO;
  87.     pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_RISING;
  88.     pSyncConfig.SyncEnable = ENABLE;
  89.     pSyncConfig.EventEnable = ENABLE;
  90.     pSyncConfig.RequestNumber = 1;
  91.     if (HAL_DMAEx_ConfigMuxSync(&hdma_spi2_tx, &pSyncConfig) != HAL_OK)
  92.     {
  93.       Error_Handler();
  94.     }

  95.     __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi2_tx);

  96.     /* SPI2 interrupt Init */
  97.     HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
  98.     HAL_NVIC_EnableIRQ(SPI2_IRQn);
  99.   /* USER CODE BEGIN SPI2_MspInit 1 */

  100. //                HAL_DMAEx_MultiBufferStart_IT(&hdma_spi2_tx, (uint8_t*)LookUP_PingPongBufer.activeBuffer, (uint32_t)hspi2.Instance->TXDR, (uint8_t*)LookUP_PingPongBufer.readyBuffer,1000);
  101.                
  102.   /* USER CODE END SPI2_MspInit 1 */
  103.   }
  104. }
复制代码

SPI初始化部分代码:

  1. /* TIM12 init function */
  2. void MX_TIM12_Init(void)
  3. {

  4.   /* USER CODE BEGIN TIM12_Init 0 */

  5.   /* USER CODE END TIM12_Init 0 */

  6.   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  7.   TIM_MasterConfigTypeDef sMasterConfig = {0};
  8.   TIM_OC_InitTypeDef sConfigOC = {0};

  9.   /* USER CODE BEGIN TIM12_Init 1 */

  10.   /* USER CODE END TIM12_Init 1 */
  11.   htim12.Instance = TIM12;
  12.   htim12.Init.Prescaler = 240-1;
  13.   htim12.Init.CounterMode = TIM_COUNTERMODE_UP;
  14.   htim12.Init.Period = 1000-1;
  15.   htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  16.   htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  17.   if (HAL_TIM_Base_Init(&htim12) != HAL_OK)
  18.   {
  19.     Error_Handler();
  20.   }
  21.   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  22.   if (HAL_TIM_ConfigClockSource(&htim12, &sClockSourceConfig) != HAL_OK)
  23.   {
  24.     Error_Handler();
  25.   }
  26.   if (HAL_TIM_PWM_Init(&htim12) != HAL_OK)
  27.   {
  28.     Error_Handler();
  29.   }
  30.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;
  31.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  32.   if (HAL_TIMEx_MasterConfigSynchronization(&htim12, &sMasterConfig) != HAL_OK)
  33.   {
  34.     Error_Handler();
  35.   }
  36.   sConfigOC.OCMode = TIM_OCMODE_PWM1;
  37.   sConfigOC.Pulse = (1000)/2-1;
  38.   sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  39.   sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  40.   if (HAL_TIM_PWM_ConfigChannel(&htim12, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  41.   {
  42.     Error_Handler();
  43.   }
  44.   /* USER CODE BEGIN TIM12_Init 2 */

  45.   /* USER CODE END TIM12_Init 2 */

  46. }

  47. void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
  48. {

  49.   if(tim_baseHandle->Instance==TIM12)
  50.   {
  51.   /* USER CODE BEGIN TIM12_MspInit 0 */

  52.   /* USER CODE END TIM12_MspInit 0 */
  53.     /* TIM12 clock enable */
  54.     __HAL_RCC_TIM12_CLK_ENABLE();

  55.     /* TIM12 interrupt Init */
  56.     HAL_NVIC_SetPriority(TIM8_BRK_TIM12_IRQn, 0, 0);
  57.     HAL_NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);
  58.   /* USER CODE BEGIN TIM12_MspInit 1 */

  59.   /* USER CODE END TIM12_MspInit 1 */
  60.   }
  61. }
复制代码

图为使用逻辑分析仪抓取单缓冲数据时刻,SPI总线数据
image.png
赞 收藏 评论10 发布时间:2025-8-14 19:39

举报

10个回答
talk is cheap 最优答案 回答时间:2025-8-24 10:40:03

xmshao 发表于 2025-8-22 16:01
你把邮件私信给我,我发配置及代码给你。

您好 谢谢您的回复 经过测试 我已经成功实现 定时器触发 DMA双缓冲的测试例程(串口/SPI) 经测试观察我之前的现象(定时器跑动 但是DMA计数器不进行递减的原因 是由于外设没正常启动配置 和DMAMUX和触发配置无关 )如果我不配置串口的ATOMIC_SET_BIT(huart3.Instance->CR3, USART_CR3_DMAT); DMA发送器就会出现这种情况 SPI同理 需要配置() 类似在定时触发 DMA搬运的过程中数据是串行阻塞的 外设的数据转移状态机 没启动 就是DMA不进行搬运,SPI部分配置代码如下

UART-gitee链接如下:https://gitee.com/qindongzhi/uart_-trgo.git SPI部分核心代码如下

HAL_DMAEx_MultiBufferStart_IT(&hdma_spi2_tx, LookUp_Buf1, &hspi2.Instance->TXDR, LookUp_Buf2,10);
__HAL_SPI_ENABLE(&hspi2);
SET_BIT(hspi2.Instance->CR1, SPI_CR1_CSTART);
ATOMIC_SET_BIT(hspi2.Instance->CFG1, SPI_CFG1_TXDMAEN);
talk is cheap 最优答案 回答时间:2025-8-26 11:01:47

talk is cheap 发表于 2025-8-24 10:40</p>
<p>您好  谢谢您的回复 经过测试 我已经成功实现 定时器触发 DMA双缓冲的测试例程(串口/SPI) 经测试观 ...

[md]补充下后续进展: 1.测试D1区域 通过FMC将数据从SDRAM乒乓搬运到SPI发送寄存器正常 2.双缓冲 需要进行PingPong读写操作 逻辑是通过DMA的CT判断当前使用的M0/M1基址 建议使用半满中断进行异步写操作(HAL库根据基址不同分别调用XferHalfCpltCallback和XferM1HalfCpltCallback半满中断),

hdma_spi2_tx.XferHalfCpltCallback = usr_DMA_SPI_HalfCplt; hdma_spi2_tx.XferM1HalfCpltCallback = usr_DMA_SPI_HalfCplt;

xmshao 回答时间:2025-8-22 16:01:47

talk is cheap 发表于 2025-8-21 16:32</p>
<p>[md]您好 我仔细看了下 微信公众号文章和参考手册 似乎没有区别
1.我测试了HAL_DMAEx_EnableMuxReques ...

[md]你把邮件私信给我,我发配置及代码给你。

xmshao 回答时间:2025-8-17 08:00:38
talk is cheap 回答时间:2025-8-21 16:32:53

xmshao 发表于 2025-8-17 08:00
你可以参考下这篇文章。</p>
<p>[基于STM32H7 DMA 双缓冲实现SPI逐个数据输出](<a href="https://mp.weixin.qq.com/s/u">https://mp.weixin.qq.com/s/u</a> ...

[md]您好 我仔细看了下 微信公众号文章和参考手册 似乎没有区别 1.我测试了HAL_DMAEx_EnableMuxRequestGenerator(&hdma_dma_generator0);函数对当前双缓冲无法启动的问题也没有改善 2.为避免变量RAM所属区域的影响,我还变更了变量存储区域到SRAM4(D3区域),也是没有作用 我还是无法理解为什么我的代码在单缓冲的情况下是能正常工作,双缓冲下异常😕 双缓冲情况下我只修改这个函数

image.png

image.png
Zzzzwq 回答时间:2025-8-23 11:40:09

xmshao 发表于 2025-8-22 16:01
你把邮件私信给我,我发配置及代码给你。

您好,我也遇到了类似问题,是SAI+DMA双缓冲接收,就是驱动不起来,是否可以帮助一下

Zzzzwq 回答时间:2025-8-23 11:42:20

talk is cheap 发表于 2025-8-21 16:32</p>
<p>[md]您好 我仔细看了下 微信公众号文章和参考手册 似乎没有区别
1.我测试了HAL_DMAEx_EnableMuxReques ...

[md]您好,我想请问您解决双缓存的问题了吗?

talk is cheap 回答时间:2025-8-24 11:00:31

Zzzzwq 发表于 2025-8-23 11:40
您好,我也遇到了类似问题,是SAI+DMA双缓冲接收,就是驱动不起来,是否可以帮助一下
...

我觉得 DMA配置双缓冲的过程 外设部分的启动部分代码是不一致的(具体分析具体分析的 就像串口是DMAT标志位 SPI是CStart和DMAEN)根据DMA的NDTR来判断是否正常搬运 (建议看下安富莱的H7手册 感觉还需要很多点 RAM的区域 D1/D2/D3 我之前使用D1区域的SDRAM是FMC总线 看好像是64位读取(不知道会不会涉及FIFO和压缩包/解压包的屋内)我后续减少变量没有测试SDRAM 使用内部的SRAM4区域存放变量 均没有使用外设的FIFO) 但是基本都类似这样的命名 感觉还是得看他的寄存器定义

Zzzzwq 回答时间:2025-8-24 23:21:49

talk is cheap 发表于 2025-8-24 11:00</p>
<p>我觉得 DMA配置双缓冲的过程 外设部分的启动部分代码是不一致的(具体分析具体分析的 就像串口是DMAT ...

[md]好的,感谢您的建议,但我使用的是HAL库进行开发,那么其应该会有开启双缓存的一定步骤,应该是相同的才对,至于寄存器这边的话,每个外设固然不一样,所以这个应该是对于寄存器来说的吧?

talk is cheap 回答时间:2025-8-25 09:18:27

Zzzzwq 发表于 2025-8-24 23:21
好的,感谢您的建议,但我使用的是HAL库进行开发,那么其应该会有开启双缓存的一定步骤,应该是相同 ...

[md]是的 没有搞过音频SAI接口 具体可以问下ST官网的FAE 实力强劲

所属标签

相似问题

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版