STM32H753 迁移到 STM32U599 后,ADC 的 DMA 传输为何不工作?
应先修改 ADC1 的 ConversionDataManagement。贴出的 U5 代码将它设为 ADC_CONVERSIONDATA_DR,该模式只把转换结果保存在 DR 中。对于当前“启动一次序列、处理结果、再启动下一次”的流程,应先使用 ADC_CONVERSIONDATA_DMA_ONESHOT。
这处配置可以解释“ADC_DR 有数据,但 DMA 数组不更新”的现象。不过,材料没有给出完整的 DMA 通道初始化和 MSP 代码,因此还不能断定它是唯一问题,也没有实际硬件验证结果。
为什么调用 HAL_ADC_Start_DMA 仍然没有传输?
STM32U5 HAL 区分 ADC_CONVERSIONDATA_DR、ADC_CONVERSIONDATA_DMA_ONESHOT 和 ADC_CONVERSIONDATA_DMA_CIRCULAR。当前配置选择的是 DR 模式。ST 官方 ADC 头文件
在核对的官方驱动实现中,ADC1/ADC2 的数据管理模式由初始化设置。HAL_ADC_Start_DMA() 会配置 DMA 回调、启动 DMA 和 ADC 转换,但不会自动把 ADC1 的 DR 模式切换成 DMA 模式。ADC4 的处理分支有所不同,不能套用到这里的 ADC1。ST 官方 ADC 驱动源码
启用 EOSIE、EOCIE,或手动设置 ADC、GPDMA 的使能位,都不能代替正确的数据管理模式。ADC 转换完成和 DMA 完成传输是两个环节;没有传输完成事件,就不会产生预期的 DMA 完成中断。
先修改 ADC 数据管理模式
在 MX_ADC1_Init() 中、调用 HAL_ADC_Init() 之前设置:
hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_ONESHOT;
hadc1.Init.ContinuousConvMode = DISABLE;
如果使用 CubeMX 管理配置,也要修改对应的 IOC 设置,避免下次生成代码时恢复为 DR 模式。修改后从复位状态重新运行,以排除之前手动修改寄存器留下的状态影响。
当前流程可以先用普通 DMA 单次传输实现,排查时不必立即改成 circular linked-list。GPDMA 的传统循环传输由循环链表配置实现,应与本次单次扫描问题分别处理。ST 官方 DMA 驱动说明
检查真正的 GPDMA 通道配置
贴出的 MX_GPDMA1_Init() 只包含时钟和 NVIC 配置,无法据此判断 DMA 通道是否正确初始化。相关代码可能位于 HAL_ADC_MspInit(),也可能在其他初始化函数中。
以 ADC1 使用 GPDMA1_Channel0 为例,应逐项确认:
| 项目 | 检查内容 |
|---|---|
| 通道实例 | DMA handle 是否指向实际使用的 Channel0 |
| Request | 是否选择 GPDMA1_REQUEST_ADC1 |
| Direction | 是否为 DMA_PERIPH_TO_MEMORY |
| 源地址递增 | ADC_DR 地址固定 |
| 目标地址递增 | 每次传输后移动到下一个数组元素 |
| 数据宽度 | 源宽度、目标宽度和缓冲区布局相符 |
| handle 关联 | hadc1.DMA_Handle 是否关联到该 DMA handle |
| 初始化结果 | HAL_DMA_Init() 是否返回 HAL_OK |
这些参数需要在 GPDMA 中独立配置,单纯切换通道或 port 不能代替请求源、方向和宽度的配置。ST 官方 DMA 配置定义
例如,假设项目中 ADC1 的 DMA handle 名为 hdma_adc1,初始化完成后应有相应关联:
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
这里的名称只是示例,应使用工程中的实际 handle。没有看到这段代码,不代表工程中一定缺少它,需要到 MSP 和生成代码中确认。
用独立的 32 位缓冲区验证传输
材料提到尝试过 32 位数组,但贴出的版本仍是:
uint16_t reg_scan_data[ADC_SCAN_ARRAY_SIZE];
下面的强制转换只改变传入指针的类型,不会改变数组元素大小,也不会设置 DMA 数据宽度:
(uint32_t*)scan->reg_scan_data
排查时可以先让 ADC1 使用独立的 uint32_t 缓冲区,并将 DMA 的 SrcDataWidth 和 DestDataWidth 都配置为 WORD,避开原有结构体和计算函数的影响。最终允许使用哪些宽度,仍需按 STM32U599 对该 ADC 实例和总线访问的要求确定,不能仅凭“GPDMA”就推导出所有目标数组必须使用同一种类型。
假设保留两个 rank,并已完成时钟、ADC、DMA 初始化和校准,可使用以下诊断片段。它用于替换相应的启动与回调逻辑,不应与原有同名 callback 重复定义:
static uint32_t adc1_raw[2];
static volatile uint8_t adc1_done;
static volatile uint8_t adc1_failed;
static volatile HAL_StatusTypeDef adc1_start_status;
void start_adc1_probe(void)
{
adc1_done = 0;
adc1_failed = 0;
adc1_start_status = HAL_ADC_Start_DMA(&hadc1, adc1_raw, 2U);
if (adc1_start_status != HAL_OK)
{
adc1_failed = 1;
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1)
{
adc1_done = 1;
}
}
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1)
{
adc1_failed = 1;
}
}
如果缩减为一个 rank,缓冲区和启动长度也应同步改为一个结果。原代码忽略了 HAL_ADC_Start_DMA() 的返回值,容易把启动失败误判为中断问题;还应记录 HAL_ADC_GetError() 和关联 DMA 的错误码。
还要区分两个 API 的长度单位:在核对的 U5 HAL 实现中,HAL_ADC_Start_DMA() 接收结果数量,再按源宽度换算成字节数。不要直接把 sizeof(adc1_raw) 当作这里的结果数量。两个 WORD 结果对应 8 字节。ST 官方 HAL_ADC_Start_DMA 实现
如果 DMA 以 WORD 写入两个结果,需要 8 字节;贴出的 U5 结构中,uint16_t[3] 只有 6 字节,不能承接这种写法。后续若将整个模块改为 uint32_t 缓冲区,还要同步修改读取函数的指针类型和索引逻辑,不能继续通过 uint16_t * 直接解释同一片数据。
确认 DMA 中断能进入 HAL
NVIC 使能之后,中断处理函数还必须调用对应 handle 的 HAL_DMA_IRQHandler()。假设 ADC1 确实使用 Channel0、handle 名为 hdma_adc1:
void GPDMA1_Channel0_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_adc1);
}
DMA 的 HAL 中断入口负责处理传输事件并调用已安装的回调。应检查启动文件中的向量、函数名称,以及传入的 handle 是否属于同一个通道。ST 官方 DMA 中断处理源码
调试时可以按以下现象区分故障位置:
- DMA 剩余字节数始终不变:先检查 ADC 数据管理模式、请求源及通道启动结果。
- 目标数组已更新且传输完成标志置位,但 ISR 不进入:检查 NVIC、向量表、中断屏蔽和目标中断域。
- ISR 已进入,但 ADC callback 不执行:检查 DMA handle、HAL 中断调用及回调配置。
- DMA 出现传输错误:记录错误状态,再检查地址、宽度、对齐和访问权限;若工程启用了 TrustZone,也要核对相关安全属性。
传输恢复后再校验测量计算
先确认原始数组更新和完成回调正常,再恢复 oversampling、ADC2 和自动重启流程,避免一次改变太多配置。
原代码还有几处需要独立处理:
is_scan_done由 ISR 写入、主循环读取,至少应按这种共享方式声明为volatile;这不能代替更复杂并发场景所需的同步机制。- 关闭 oversampling 后,不能继续沿用原来累加模式对应的
0xFFF0和OVERSAMPLING_FACTOR = 16。 calculate_vref_uV_from_vrefint()应在除法前处理vref_adc == 0,避免无效采样触发除零。- U5 的电池电压计算使用了
m->select_power_button_sense_mV,而前一行计算的是m->battery_sense_mV,应核实这里是否误用了变量。
这些问题会影响数据读取或测量结果,但不能解释最初没有 DMA 传输完成中断的全部现象。应先修正 ConversionDataManagement,再检查通道配置、缓冲区和 IRQ 链路,逐步确定剩余故障。材料没有给出项目使用的 STM32CubeU5/HAL 版本,涉及驱动行为的判断还需与工程内的实际源码对照。
备注:内容仅供参考。
