
1. 引言在嵌入式开发中ADC模数转换器是连接模拟世界与数字世界的桥梁。STM32 系列微控制器内置了高性能的 ADC 外设能够将温度、电压、电流等模拟信号转换为数字量供处理器进一步处理。本文将带你从原理到实战系统掌握 STM32 ADC 数据采集的完整流程。2. ADC 基础概念2.1 什么是 ADCADCAnalog-to-Digital Converter模数转换器将连续的模拟电压信号转换为离散的数字值。STM32 的 ADC 分辨率通常为 12 位即转换结果范围为 0~4095。2.2 关键参数分辨率12 位可配置为 6/8/10/12 位转换模式单次转换、连续转换、扫描模式触发方式软件触发、定时器触发、外部触发采样时间可配置影响转换速度与精度参考电压通常为 VDDA3.3V2.3 转换公式数字量 模拟电压 / 参考电压 × 4095例如参考电压 3.3V输入 1.65V 时转换结果为 2047。3. STM32 ADC 硬件结构模拟输入引脚采样保持电路逐次逼近比较器12位数据寄存器DMA传输内存缓冲区STM32 的 ADC 采用逐次逼近型架构支持多通道扫描配合 DMA 可实现多通道连续采样而无需 CPU 干预。4. 开发环境准备本文以 STM32F103C8T6蓝色药丸板为例开发环境如下IDESTM32CubeIDE 或 Keil MDK固件库STM32Cube HAL 库调试工具ST-Link / USB-TTL硬件电位器或分压电路作为模拟信号源5. CubeMX 配置步骤打开 STM32CubeMX选择芯片 STM32F103C8T6在Pinout Configuration中使能 ADC1选择通道如ADC1_IN0对应 PA0 引脚配置参数分辨率12 位扫描模式Disabled单通道连续转换模式Enabled采样时间55.5 Cycles生成初始化代码6. HAL 库代码实现6.1 单次阻塞式采集#includeadc.h#includemain.huint32_tadc_read_value(void){uint32_tvalue0;HAL_ADC_Start(hadc1);HAL_ADC_PollForConversion(hadc1,100);valueHAL_ADC_GetValue(hadc1);HAL_ADC_Stop(hadc1);returnvalue;}6.2 连续转换 DMA 采集#defineADC_BUF_SIZE100uint32_tadc_buffer[ADC_BUF_SIZE];voidadc_dma_init(void){HAL_ADC_Start_DMA(hadc1,(uint32_t*)adc_buffer,ADC_BUF_SIZE);}voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc-InstanceADC1){// 转换完成处理 adc_buffer 中的数据process_adc_data(adc_buffer,ADC_BUF_SIZE);}}6.3 电压换算floatadc_to_voltage(uint32_tadc_value){return(float)adc_value*3.3f/4095.0f;}7. 多通道扫描采集当需要采集多个模拟信号时使用扫描模式voidadc_multi_channel_init(void){// 配置 ADC1 扫描模式依次采集 IN0、IN1、IN2sConfig.ChannelADC_CHANNEL_0;HAL_ADC_ConfigChannel(hadc1,sConfig);sConfig.ChannelADC_CHANNEL_1;HAL_ADC_ConfigChannel(hadc1,sConfig);sConfig.ChannelADC_CHANNEL_2;HAL_ADC_ConfigChannel(hadc1,sConfig);}voidadc_multi_channel_read(uint32_t*values){HAL_ADC_Start_DMA(hadc1,values,3);// 等待转换完成values[0]~values[2] 对应三个通道}下面给出一个完整的 DMA 多通道高速采集实战示例以 ADC1 的 IN0、IN1、IN2 三个通道为例配合 DMA 循环模式实现不间断采集。CubeMX 配置要点在Pinout Configuration中使能 ADC1依次添加通道ADC1_IN0PA0、ADC1_IN1PA1、ADC1_IN2PA2配置 ADC1 参数分辨率12 位扫描模式Scan Conversion ModeEnabled连续转换模式Continuous Conversion ModeEnabled采样时间55.5 CyclesDMA Continuous RequestsEnabled在 DMA Settings 中为 ADC1 添加 DMA 通道并配置ModeCircular循环模式Data Width数据宽度Word32 位优先级High生成初始化代码注意DMA 数据宽度必须设为 Word与 ADC 的 12 位结果寄存器对齐HAL 库以uint32_t读取。若设为 Half Word 会导致数据错位。HAL 初始化代码#defineADC_CHANNEL_NUM3#defineADC_SAMPLE_NUM100// 缓冲区3 通道 × 100 次采样uint32_tadc_buffer[ADC_CHANNEL_NUM*ADC_SAMPLE_NUM];voidadc_multi_dma_init(void){// 启动 DMA 循环采集缓冲区填满后自动从头开始HAL_ADC_Start_DMA(hadc1,(uint32_t*)adc_buffer,ADC_CHANNEL_NUM*ADC_SAMPLE_NUM);}DMA 传输完成回调voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc-InstanceADC1){// 缓冲区已填满一轮处理 3 通道 × 100 次采样数据process_multi_channel_data(adc_buffer,ADC_CHANNEL_NUM,ADC_SAMPLE_NUM);}}voidprocess_multi_channel_data(uint32_t*buf,uint8_tch_num,uint16_tsample_num){uint32_tch0_sum0,ch1_sum0,ch2_sum0;for(uint16_ti0;isample_num;i){// 按通道交错排列buf[i*30]IN0, buf[i*31]IN1, buf[i*32]IN2ch0_sumbuf[i*ch_num0];ch1_sumbuf[i*ch_num1];ch2_sumbuf[i*ch_num2];}// 求各通道平均值降低噪声floatv0adc_to_voltage(ch0_sum/sample_num);floatv1adc_to_voltage(ch1_sum/sample_num);floatv2adc_to_voltage(ch2_sum/sample_num);// 输出或存储三个通道的电压值report_voltages(v0,v1,v2);}缓冲区中数据的排列顺序在扫描模式下DMA 按通道配置的顺序依次搬运转换结果数据在缓冲区中是交错排列的缓冲区下标012345…对应通道IN0IN1IN2IN0IN1IN2…即buf[i * 3 0]对应 IN0buf[i * 3 1]对应 IN1buf[i * 3 2]对应 IN2。处理数据时务必按此规律拆分否则会得到错误的通道对应关系。8. 数据滤波与处理实际采样中常伴随噪声可采用以下滤波方法8.1 中值滤波uint32_tmedian_filter(uint32_t*data,uint8_tlen){// 冒泡排序for(uint8_ti0;ilen-1;i){for(uint8_tj0;jlen-1-i;j){if(data[j]data[j1]){uint32_ttempdata[j];data[j]data[j1];data[j1]temp;}}}returndata[len/2];}8.2 滑动平均滤波#defineFILTER_N10uint32_tmoving_average(uint32_tnew_value){staticuint32_tbuffer[FILTER_N]{0};staticuint8_tindex0;staticuint32_tsum0;sum-buffer[index];buffer[index]new_value;sumnew_value;index(index1)%FILTER_N;returnsum/FILTER_N;}9. 实战读取电位器电压并串口输出9.1 硬件连接电位器中间抽头 → PA0ADC1_IN0电位器两端 → 3.3V 和 GND9.2 主程序intmain(void){HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_ADC1_Init();MX_USART1_UART_Init();uint32_tadc_value;floatvoltage;charmsg[64];while(1){adc_valueadc_read_value();voltageadc_to_voltage(adc_value);sprintf(msg,ADC: %lu, Voltage: %.2f V\r\n,(unsignedlong)adc_value,voltage);HAL_UART_Transmit(huart1,(uint8_t*)msg,strlen(msg),100);HAL_Delay(500);}}10. 常见问题与优化建议10.1 采样值跳动增加采样时间降低等效内阻影响添加硬件 RC 滤波电路软件层面使用滤波算法10.2 参考电压不稳定确保 VDDA 供电稳定可加去耦电容使用内部参考电压VREFINT校准10.3 多通道串扰适当延长采样时间通道间切换后丢弃前几次采样值11. 定时器触发采样前面介绍的软件触发方式是由 CPU 主动调用HAL_ADC_Start()启动转换。但在需要精确控制采样时刻如电机控制、电力监测、音频采集的场景下软件触发难以保证时序的确定性。此时可以借助定时器产生精确的触发脉冲让 ADC 在固定的时间点自动启动转换实现与 PWM 周期、电网频率等外部事件严格同步的采样。11.1 定时器触发与软件触发的区别对比项软件触发定时器触发DMA 触发启动方式CPU 调用 HAL 函数启动定时器事件自动启动DMA 循环模式自动搬运时序精度受中断、任务调度影响由硬件定时器保证精确到微秒级由 DMA 与 ADC 硬件协同保证CPU 占用每次转换都需 CPU 参与转换过程无需 CPU 干预转换与搬运均无需 CPU 干预适用场景低速、对时序不敏感的数据采集电机控制、电网同步、音频采样等高速连续波形采集简单来说软件触发适合“想采就采”的场合而定时器触发适合“到点必须采”的场合。DMA 触发通常与定时器触发配合使用定时器负责决定采样时刻DMA 负责搬运转换结果。定时器按固定频率产生触发脉冲ADC 在每次触发时完成一次转换转换完成后 DMA 立即将结果从数据寄存器搬运到内存缓冲区整个过程无需 CPU 干预。这种组合既保证了采样时刻的精确性又避免了 CPU 频繁读取数据非常适合高速连续波形采集。在场景选择上如果只是低速、偶发地读取一两个通道软件触发最简单直接如果需要精确控制采样时刻如电机控制、电网同步应使用定时器触发如果采样率较高且需要连续采集大量数据如振动分析、音频采集则应在定时器触发的基础上叠加 DMA实现“定时采样 自动搬运”的高效方案。11.2 CubeMX 配置步骤在Pinout Configuration中使能 ADC1选择通道如ADC1_IN0对应 PA0配置 ADC1 参数分辨率12 位扫描模式Disabled单通道连续转换模式Disabled由定时器触发单次转换触发源Trigger SourceTimer 2 Trigger Out event使能 TIM2配置预分频与计数周期得到所需的采样频率在 TIM2 配置中将Trigger Event Selection设为Update Event使能触发输出生成初始化代码例如需要 10 kHz 采样率系统时钟 72 MHz可设置 TIM2 预分频为 72-1计数周期为 100-1则触发频率为 72 MHz / 72 / 100 10 kHz。11.3 HAL 库代码实现voidadc_timer_trigger_init(void){// 启动定时器开始周期性触发 ADC 转换HAL_TIM_Base_Start(htim2);// 启动 ADC等待定时器触发HAL_ADC_Start(hadc1);}voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc-InstanceADC1){uint32_tadc_valueHAL_ADC_GetValue(hadc1);// 每次定时器触发转换完成后在此处理采样值process_adc_sample(adc_value);}}注意使用定时器触发时ADC 的连续转换模式应关闭否则一次触发会连续转换多次导致采样时序混乱。每次转换完成后ADC 会自动等待下一次触发无需 CPU 反复调用启动函数。11.4 定时器触发 DMA 高速采集当采样率较高时可配合 DMA 将转换结果直接搬运到内存进一步降低 CPU 负担#defineADC_BUF_SIZE512uint32_tadc_buffer[ADC_BUF_SIZE];voidadc_timer_dma_init(void){HAL_TIM_Base_Start(htim2);HAL_ADC_Start_DMA(hadc1,(uint32_t*)adc_buffer,ADC_BUF_SIZE);}voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc-InstanceADC1){// 缓冲区已填满处理 512 个采样点process_adc_buffer(adc_buffer,ADC_BUF_SIZE);}}这种方式非常适合需要连续波形采集的应用例如振动分析、电流波形监测等。11. 总结本文从 ADC 基础原理出发详细介绍了 STM32 ADC 的配置方法、HAL 库编程、多通道采集、数据滤波以及实战应用。掌握 ADC 数据采集是嵌入式开发的重要技能希望读者能够动手实践将理论知识转化为实际能力。后续可以进一步探索 DMA 多通道高速采集、定时器触发采样等高级应用。最后大家也可以关注给我私信看看想了解或者学习什么期待大家的留言