最近在Nucleo-H7S3L8开发板上调试HPDMA配合ADC采集,遇到一个让人挠头的问题。把ADC配成连续转换模式,DMA也选了循环模式,结果程序直接卡在HAL_ADC_Start_DMA这一行出不来了。更奇怪的是,关了循环模式就能正常工作,但采样跑一次就停,完全没法用。
翻了一堆资料才明白,STM32H7系列的HPDMA和传统GPDMA在循环模式的实现上根本不是一回事。有个关键信息藏在官方文档里,DMA的传统循环传输方式,已经被循环链表配置取代了。也就是说,你直接在DMA句柄里设个循环模式位就指望它无限跑,硬件根本不认这个配置。
先看一组典型的配置代码,很多人都是这么写的
c
DmaHandle.Init.Mode = DMA_CIRCULAR; // 直接开循环模式
DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
if (HAL_DMA_Init(&DmaHandle) != HAL_OK) {
// 初始化可能成功,但跑起来就卡
}
这代码在GPDMA上可能没问题,但HPDMA需要额外配置链表才能实现循环。你想实现无限循环,得用HAL_DMAEx_List_SetCircularMode函数把链表头尾接起来,或者用HAL_DMAEx_List_SetCircularModeConfig指定从哪个节点开始循环。直接在Init结构体里开循环标志,底层根本识别不了,卡死是必然的。
ADC这边还有一个隐藏坑,ConversionDataManagement这个参数配错了也会导致卡死。ADC_CFGR寄存器里的DMNGT位控制数据管理模式,要使用DMA循环模式,必须把这项设成ADC_CONVERSIONDATA_DMA_CIRCULAR。如果设成了普通DMA模式或者DR寄存器模式,ADC的DMA请求生成逻辑和DMA的循环配置对不上,传输就卡住了。
另外一个容易忽略的问题是ADC溢出处理。H7的ADC在连续转换模式下,如果DMA没来得及把数据搬走,ADC的DR寄存器会被新数据覆盖,此时会置起OVR标志位。关键点是一旦OVR标志置位,DMA传输请求会被硬件直接阻塞,直到软件把OVR位清掉才能恢复。如果DMA中断处理不及时或者中断优先级太低,OVR标志一直挂着,DMA就***停在那里。
还有内存地址选择的问题。H7的DMA1和DMA2无法访问TCM内存区,把ADC缓存放在DTCM或者ITCM里,DMA根本搬不了数据,表面看就是卡住了。AXI SRAM(地址0x24000000开始)是通用DMA可以正常访问的区域,优先把缓冲区放在那里。
如果你用的是HPDMA而不是通用DMA,还得确认AHB端口选择是否正确。官方有明确提示,当HPDMA配合ADC1或ADC2使用时,外设侧的AHB端口必须选择端口1。端口配错了,总线握手***不成功,HAL_ADC_Start_DMA也就一直等在那里。
遇到卡死问题,第一步是查ADC状态寄存器里的OVR位,如果有溢出先处理溢出逻辑。第二步检查ConversionDataManagement是不是设成了循环模式,第三步确认DMA句柄是否通过__HAL_LINKDMA正确关联到了ADC句柄。这三项都对了,再考虑链表配置的问题。 |
|