一、推荐参数
先用这一组,比较稳:
-
Oversampling Ratio = 8x
-
Right Bit Shift = 3
这等价于:
-
每个通道内部采 8 次
-
累加后右移 3 位(即除以 8)
-
输出结果仍保持接近 12 位量程
也就是说,你拿到的就是“8 次平均值”。STM32L4 的硬件过采样支持把输出当作 averaging function 来用。
如果你更想抑制噪声,可改成:
-
16x + Shift 4
但总转换时间会进一步增加。
二、CubeMX 配置
下面按 “软件触发一次,采完 6 个通道(每个都做过采样),DMA 搬 6 个值” 来配。
1)ADC1 基本参数
在 ADC1 > Parameter Settings 里:
-
Scan Conversion Mode:Enable
-
Nbr Of Conversion:6
-
把你的 6 个通道分别设成 Rank 1 ~ Rank 6
-
Resolution:12 bits
-
Data Alignment:Right
-
Continuous Conversion Mode:Disable
-
Discontinuous Conversion Mode:Disable
-
External Trigger Conversion Source:Software Start
-
DMA Continuous Requests:Disable
-
End Of Conversion Selection:End of Sequence
说明:
-
你要的是“触发一次,拿一组”,所以 Continuous 要关
-
DMA 用 Normal 模式,一次搬完 6 个结果就停
-
EOC 选 End of Sequence,这样一整组 6 通道完成后再作为一次完整结果处理,更省心。STM32L4 的 ADC 支持 single-shot 或 continuous 的单通道/扫描模式,规则组数据也可通过 DMA 获取。
2)每个通道的 Sampling Time
在 6 个通道各自的 Rank 设置里,给一个相对稳妥的采样时间:
-
若信号源阻抗不高:24.5 cycles 或 47.5 cycles
-
若是分压电阻较大 / 传感器阻抗高:建议 92.5 cycles 或更长
STM32L4 允许每个通道单独设置采样时间;高阻信号需要更长采样时间。
3)打开 Oversampling
在 ADC1 的参数页里,找到 Oversampling / Regular Oversampling(不同 CubeMX 版本位置略有不同),设置:
-
Oversampling Mode:Enable
-
Oversampling Ratio:8x
-
Right Bit Shift:3 bits
-
Triggered Oversampling Mode / Regular Oversampling Mode:
-
若有这个选项,建议先用 Continued mode(连续过采样)
-
也就是一次规则转换内,把 8 次子采样连续完成,再给出一个结果
-
说明:
-
L4 的硬件过采样支持 2/4/8/16/32/64/128/256 倍。
-
8x + shift 3 是最像“硬件平均 8 次”的配置。
4)DMA 配置
在 ADC1 > DMA Settings 添加一个 DMA 通道:
-
Direction:Peripheral to Memory
-
Mode:Normal
-
Peripheral Increment:Disable
-
Memory Increment:Enable
-
Peripheral Data Width:Half Word
-
Memory Data Width:Half Word
-
Priority:Low / Medium 都可以
因为 ADC 数据寄存器是 16 位,DMA 用 Half Word 最合适。STM32 的 ADC 结果寄存器是 16 位,数据可由 DMA 取走。
三、CubeMX 生成代码关键检查
不同 HAL 版本字段名略有差异,但核心会类似下面这样。
MX_ADC1_Init() 里关键部分(示例)
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_SEQ_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 6;
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_OVERWRITTEN;
hadc1.Init.OversamplingMode = ENABLE;
然后还有 Oversampling 结构体(字段名按你当前 HAL 为准):
hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_3;
hadc1.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
hadc1.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE;
这里最后两个枚举名在不同 HAL 版本中可能略有差异;CubeMX 生成什么就以什么为准。
你只需要保证逻辑上是:一次启动后,每个规则转换会连续完成 8 次子采样并输出平均值。
四、6 通道配置示例(Rank 1~6)
这个你前面已经配好了,只确认保留:
sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
/* Rank 1 */
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
/* Rank 2 */
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_REGULAR_RANK_2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
/* … 一直到 Rank 6 */
STM32L4 的 sequencer 可按任意顺序转换多达 16 个通道;你这里只用 6 个没问题。
五、应用代码模板(一次触发,DMA 收 6 个“已平均值”)
1)全局变量
extern ADC_HandleTypeDef hadc1;
#define ADC_CH_NUM 6
uint16_t adc_avg_buf[ADC_CH_NUM];
volatile uint8_t adc_done = 0;
2)初始化时先做一次校准
STM32L4 官方建议在应用中做 ADC 校准;当参考电压变化超过 10%(如复位、某些低功耗恢复)时建议重新校准。
{
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
}
3)启动一次采样
{
adc_done = 0;
return HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_avg_buf, ADC_CH_NUM);
}
这里的 adc_avg_buf[0..5] 不是“原始单次值”,而是:
-
Rank1 通道:内部 8 次平均后的值
-
Rank2 通道:内部 8 次平均后的值
-
…
-
Rank6 通道:内部 8 次平均后的值
4)DMA 完成回调
{
if (hadc->Instance == ADC1)
{
adc_done = 1;
HAL_ADC_Stop_DMA(&hadc1);
}
}
5)使用方法
{
// 等待 adc_done 或在主循环里异步判断
}
主循环里:
{
adc_done = 0;
// adc_avg_buf[0] -> Rank1 通道的平均值
// adc_avg_buf[1] -> Rank2
// …
// adc_avg_buf[5] -> Rank6
}
六、这时“单次采样时间”会怎么变
这是你最容易忽略的点。
如果你原来单通道一次转换耗时近似是:
Tone=Tsample+TconvertT_{one} = T_{sample} + T_{convert}
那打开 8x oversampling 后,每个通道大致会变成:
Tone_os≈8×(Tsample+Tconvert)T_{one\_os} \approx 8 \times (T_{sample} + T_{convert})
所以 整组 6 通道 的总时间也大约乘以 8。STM32L4 的硬件过采样就是先做多次采样并累加,再输出给 DMA。
举例(粗略):
-
若原来 6 通道一轮约 20µs
-
开 8x 后,可能接近 160µs
所以如果你有“50µs 内完成”的硬限制,8x 可能就太慢了;那就改成:
-
2x + shift1
-
或 4x + shift2
七、最常见的坑
1)开了 Oversampling,却还按“原采样速度”估算
这是最常见误判。过采样会显著拉长转换时间。
2)DMA 长度还写成 6*N
模板 B 下,DMA 长度就是 6,不是 6×8。
因为多次采样在 ADC 内部已经处理完了。
3)采样时间太短
如果前端源阻抗偏大(比如几十 kΩ 分压),哪怕开了过采样,采样电容本身还没充稳,结果照样漂。
这时应优先增加 Sampling Time,而不是盲目加大 Oversampling Ratio。
4)误以为分辨率一定“真实提高”
过采样确实能改善随机噪声、实现平均/基本滤波,L4 在一定条件下可把 12 位输出扩展到 16 位表现;但这依赖噪声特性和输入信号条件,不是“无条件白捡 4 位有效精度”。