[STM32L4] 多通道扫描、单次转换 ADC 问题

[复制链接]
37|15
地瓜patch 发表于 2026-7-2 09:32 | 显示全部楼层 |阅读模式
我需要实现软件触发多通道 ADC 扫描,由 10ms 定时器中断回调驱动采集;DMA 时序无法精准控制,因此不能用 DMA。
以前纯 CMSIS 寄存器写法从没出过问题,但 HAL 库调试出现问题。
硬件初始化逻辑理论上没问题:调度器启动前有一段 while 循环测试,连续跑 10 次采样数值全部正常。一旦放到定时器中断回调里执行,程序直接卡死、触发硬件异常 Fault。
void MX_ADC1_Init(void)
{
ADC_MultiModeTypeDef multimode = {0};

ADC_ChannelConfTypeDef sConfig = {0};

/** Common config */

hadc1.Instance = ADC1;

hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;

hadc1.Init.Resolution = ADC_RESOLUTION_12B;

hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;

hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

hadc1.Init.LowPowerAutoWait = DISABLE;

hadc1.Init.ContinuousConvMode = DISABLE;

hadc1.Init.NbrOfConversion = 2;

hadc1.Init.DiscontinuousConvMode = DISABLE;

hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;

hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;

hadc1.Init.DMAContinuousRequests = DISABLE;

hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;

hadc1.Init.OversamplingMode = DISABLE;

if (HAL_ADC_Init(&hadc1) != HAL_OK)

{

   Error_Handler();

}

/** Configure the ADC multi-mode */

multimode.Mode = ADC_MODE_INDEPENDENT;

if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)

{

   Error_Handler();

}

/** Configure Regular Channel */

sConfig.Channel = ADC_CHANNEL_13;

sConfig.Rank = ADC_REGULAR_RANK_1;

sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5;

sConfig.SingleDiff = ADC_SINGLE_ENDED;

sConfig.OffsetNumber = ADC_OFFSET_NONE;

sConfig.Offset = 0;

if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

{

   Error_Handler();

}

/** Configure Regular Channel */

sConfig.Channel = ADC_CHANNEL_14;

sConfig.Rank = ADC_REGULAR_RANK_2;

if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

{

   Error_Handler();

}

}
启动前初始化
MX_ADC1_Init();
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
10ms TIM2 中断回调内错误采集代码
{
      HAL_ADC_Start(&hadc1);
      HAL_ADC_PollForConversion(&hadc1, 50);
      accumulator[TEMP] += HAL_ADC_GetValue(&hadc1);
      HAL_ADC_PollForConversion(&hadc1, 50);
      accumulator[VBAT] += HAL_ADC_GetValue(&hadc1);      
      HAL_ADC_Stop(&hadc1);      

}
这套代码无法正常工作。请教故障在哪里?如果无法解决,我计划把板子 L4 芯片全部换成 F4,换回原生 CMSIS 寄存器开发。

内政奇才 发表于 2026-7-2 16:40 | 显示全部楼层
你的配置里EOCSelection设成了ADC_EOC_SINGLE_CONV,这意味着每个通道转换完才置位标志,但HAL库在扫描模式下通常需要配合ADC_EOC_SEQ_CONV使用,否则状态机逻辑会乱。
七毛钱 发表于 2026-7-2 17:11 | 显示全部楼层
在中断里调用HAL_ADC_PollForConversion是极其危险的操作,如果ADC因为任何原因没完成转换,这个函数会死等直到超时,极易导致看门狗复位或系统卡死。
又见江南雨 发表于 2026-7-2 17:34 | 显示全部楼层
多通道扫描模式下,HAL_ADC_GetValue只能读取最后转换完成的那个通道的数据,你连续调两次GetValue拿不到两个不同通道的值,必须用HAL_ADCEx_MultiModeGetValue或者等待序列结束。
在海边聆听 发表于 2026-7-2 18:07 | 显示全部楼层
扫描模式开启后,必须等待整个序列转换结束,而不是单个通道结束,你的代码逻辑是把它当成单次转换在轮询,完全误解了扫描模式的工作机制。
又见江南雨 发表于 2026-7-2 18:42 | 显示全部楼层
HAL库的中断回调里严禁使用阻塞式延时或轮询函数,这会阻塞其他低优先级中断,建议改用HAL_ADC_Start_IT开启中断模式,在转换完成回调里处理数据。
才没有脸红 发表于 2026-7-2 19:14 | 显示全部楼层
检查NVIC中ADC中断和TIM2中断的优先级,如果ADC中断优先级低于或等于TIM2,且在TIM2中断里等待ADC,可能会发生优先级冲突导致死锁。
故里说长安 发表于 2026-7-2 19:46 | 显示全部楼层
你的代码里没有清除ADC的状态标志,连续快速触发可能导致DR寄存器数据未读取就被覆盖,产生Overrun错误,进而触发硬件Fault异常。
没有太阳的晴天 发表于 2026-7-2 20:17 | 显示全部楼层
STM32L4的ADC校准后需要一定的稳定时间,虽然你在初始化时校准了,但在高频中断触发下,建议确认是否每次都需要重新校准,通常不需要,但需确保ADC处于就绪状态。
甜心puppy 发表于 2026-7-2 20:48 | 显示全部楼层
把HAL_ADC_Stop放在中断最后是不必要的,甚至可能干扰下一次启动,扫描模式单次触发后会自动停止,除非你开了连续模式,而你配置的是DISABLE。
海滨消消 发表于 2026-7-2 21:20 | 显示全部楼层
故障卡死很可能是因为HAL_ADC_Start返回了HAL_BUSY,因为上一次转换还没彻底结束你就又启动了,在中断里加一个HAL_ADC_GetState判断是否为READY再启动。
甜心puppy 发表于 2026-7-2 21:51 | 显示全部楼层
不要用HAL库的轮询方式做高速多通道采集,效率极低且不可靠,既然你熟悉CMSIS,直接操作ADC->ISR和ADC->DR寄存器反而更可控,避免HAL层层封装的开销。
等凌晨日出 发表于 2026-7-2 22:22 | 显示全部楼层
检查堆栈大小,HAL库函数调用层级深,局部变量多,如果中断堆栈设得太小,递归或深层调用会导致栈溢出,直接触发HardFault。
豌豆爹 发表于 2026-7-2 22:55 | 显示全部楼层
ADC时钟分频系数ADC_CLOCK_ASYNC_DIV2是否合适?如果ADC时钟太快超过30MHz,L4系列可能会工作不稳定,导致转换错误或内部总线挂起。
狄克爱老虎油 发表于 2026-7-3 12:21 | 显示全部楼层
检查NVIC优先级设置,确保高于定时器中断。同时,在中断回调中用HAL_ADC_GetState()判断状态为READY后再启动ADC,避免HAL_BUSY问题。
进入猫次元 发表于 2026-7-3 14:47 | 显示全部楼层
别急着换F4芯片,L4的ADC性能足够,问题出在HAL库的使用上,改用DMA+循环模式或者中断+序列结束标志处理,能完美解决时序和卡死问题。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:出一块TI-PLABS-AMP-EVM

2806

主题

17569

帖子

30

粉丝
快速回复 在线客服 返回列表 返回顶部
0