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

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 最优答案 回答时间:3 天前

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 最优答案 回答时间:昨天 11:01

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 回答时间:5 天前

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 回答时间:6 天前

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 回答时间:4 天前

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

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

Zzzzwq 回答时间:4 天前

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

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

talk is cheap 回答时间:3 天前

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 回答时间:3 天前

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

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

talk is cheap 回答时间:前天 09:18

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

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

所属标签

相似问题

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版