在嵌入式开发中,模数转换器(ADC)是连接现实世界模拟信号与数字处理系统的核心桥梁。无论是读取传感器数据、采集电压信号,还是进行电池电量监测,都离不开ADC的精确工作。本文将深入解析ADC转换的完整流程,并结合STM32微控制器的实际应用进行说明。
ADC基本概念与原理
ADC,全称为Analog-to-Digital Converter(模数转换器),其核心功能是将连续变化的模拟信号(如电压、电流)转换为离散的数字编码,以便微控制器进行计算和处理。
例如,一个温度传感器可能输出一个在0V至3.3V间连续变化的电压。而单片机只能理解如0、1、2、3这样的离散数字。ADC则负责将2.5V这样的模拟电压,依据其分辨率(如12位)和参考电压(如3.3V),转换为3100这样的数字值。

ADC转换的核心五步流程
一个完整的ADC转换周期,通常包含采样、保持、量化、编码和输出五个关键阶段。
1. 采样阶段
采样是流程的第一步,其目的是在特定时刻“捕获”输入模拟信号的瞬时值。ADC内部通过一个采样开关和保持电容实现此功能。当开关闭合时,输入信号对电容充电,直至电容电压与输入电压相等。这个过程所需的时间即为“采样时间”。
采样时间是关键参数。时间过短,电容未充分充电会导致采样值偏低;时间过长,则会影响整体转换速率。在实际应用中,需要根据信号源内阻和精度要求来权衡设置。
在STM32 HAL库中,可以通过配置通道参数来设定采样时间:
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 设置为239.5个ADC时钟周期
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}

2. 保持阶段
采样完成后,采样开关断开,电容与输入信号隔离。此时,电容上的电压被“保持”住,并在整个后续转换期间维持不变。这确保了即便外部信号在转换过程中发生变化,ADC转换的仍然是采样时刻的“定格”电压值,避免了转换误差。
3. 量化阶段
量化是ADC的核心,它将连续的模拟电压值映射到有限个离散的数字等级上。这是一个存在固有误差的“近似”过程。
分辨率决定了量化精度。对于一个12位ADC,其输出范围为0至4095(2^12 - 1)。若参考电压Vref为3.3V,则每个数字等级(1 LSB)代表的电压为:
Vref / 4096 ≈ 0.8mV
量化误差最大为±0.5 LSB,这是由ADC原理决定的,无法消除,只能通过选用更高分辨率的ADC来减小。
4. 编码阶段
编码是将量化后的十进制数字值转换为二进制代码的过程。STM32的ADC通常采用直接二进制编码,即数字0对应0V,数字4095对应参考电压Vref。转换结果以二进制形式准备好,等待输出。
5. 输出阶段
编码完成后,数字结果被存入数据寄存器(如STM32的ADC_DR寄存器)。同时,ADC会置位标志位(如EOC)或产生中断,通知主控制器数据已就绪。在STM32中,可通过轮询、中断或DMA方式读取结果。

STM32 ADC应用实例:单次转换模式
以下代码展示了在STM32上使用HAL库完成一次ADC转换的完整流程,涵盖了初始化、启动、等待和读取结果。
ADC初始化配置:
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
// 配置ADC基本参数
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.ContinuousConvMode = DISABLE; // 单次模式
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Error_Handler();
}
// 配置通道0
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error_Handler();
}
}
ADC读取与转换函数:
// 启动一次转换并读取原始值
uint32_t ADC_Read(void)
{
uint32_t adc_value = 0;
HAL_ADC_Start(&hadc1); // 启动转换(开始采样)
if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) { // 等待转换完成(保持、量化、编码)
adc_value = HAL_ADC_GetValue(&hadc1); // 读取结果(输出阶段)
}
HAL_ADC_Stop(&hadc1);
return adc_value;
}
// 将原始ADC值转换为电压值(单位:mV,假设Vref=3300mV)
uint32_t ADC_ToVoltage(uint32_t adc_value)
{
return (adc_value * 3300) / 4096; // 12位ADC
}
// 主循环中的应用示例
int main(void)
{
// 初始化代码...
while (1)
{
uint32_t raw_value = ADC_Read();
uint32_t voltage_mv = ADC_ToVoltage(raw_value);
printf("ADC Value: %lu, Voltage: %lu mV\r\n", raw_value, voltage_mv);
HAL_Delay(1000);
}
}
当调用HAL_ADC_Start()时,ADC开启采样阶段;HAL_ADC_PollForConversion()等待期间,ADC依次完成保持、量化与编码;最后由HAL_ADC_GetValue()读取输出结果。
提升ADC转换精度的关键因素
在实际工程中,需从硬件和软件两方面着手,以获得稳定可靠的转换结果。
- 参考电压稳定性:ADC结果是输入电压与参考电压的比值。建议对精度要求高的场合使用独立、低噪声的参考电压芯片,而非直接采用电源电压。
- 信号源阻抗与采样时间:信号源内阻过大会延长采样电容充电时间。若采样时间配置不足,会导致采样不完整。通常建议源阻抗小于10kΩ,或在ADC前端添加电压跟随器(运放缓冲)。
- PCB布局与接地:
- 采用单点接地,分离模拟地与数字地。
- ADC输入走线尽量短,远离数字信号线、时钟线等噪声源。
- 在ADC输入端并联一个容值较小的去耦电容(如10nF~100nF),以滤除高频干扰。
- 软件滤波算法:硬件优化后,软件滤波可进一步平滑数据。常用的方法包括多次采样取平均值、中值滤波、滑动平均滤波等。
// 简单平均滤波函数示例
uint32_t ADC_Read_Average(uint8_t times)
{
uint32_t sum = 0;
for(uint8_t i=0; i<times; i++)
{
sum += ADC_Read();
HAL_Delay(5); // 每次采样间隔少量时间
}
return sum / times;
}
总结
ADC转换是一个包含采样、保持、量化、编码和输出的系统化过程。理解每个阶段的作用与影响因素,是进行精确模拟信号采集的基础。在基于STM32等微控制器的开发中,需结合具体需求合理配置分辨率、采样时间和触发模式,并通过稳定的参考源、良好的PCB布局及有效的软件滤波来综合保障转换精度。掌握ADC的完整工作流程,将使开发者在处理各类传感器数据与模拟信号时更加得心应手。