ADC(Analog-to-Digital Converter)是STM32单片机最常用的外设之一,负责将模拟信号转换为数字信号。STM32全系列产品都内置了12位精度的ADC模块,部分高端型号甚至达到16位精度。在实际项目中,ADC常用于传感器数据采集、电池电压监测、音频信号处理等场景。
以STM32F103系列为例,其ADC主要特性包括:
STM32的ADC需要稳定的参考电压才能保证转换精度。开发板通常提供两种参考电压方案:
注意:当使用VDDA作为参考时,必须确保电源纹波小于10mV,否则会显著影响ADC精度。建议在VDDA引脚添加10μF+0.1μF的退耦电容组合。
典型的传感器信号输入电路应包含以下保护元件:
code复制[传感器] --[100Ω]--+--[ADC引脚]
|
[100nF]
|
GND
对于高阻抗信号源(如热电偶),建议增加电压跟随器电路,避免信号衰减。
使用STM32CubeMX配置ADC的典型步骤:
基础的单通道ADC采集代码示例:
c复制// 初始化代码
ADC_HandleTypeDef hadc1;
void ADC_Init(void) {
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
// 配置通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
// 采集函数
uint16_t ADC_GetValue(void) {
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 10);
return HAL_ADC_GetValue(&hadc1);
}
当需要采集多个通道时,应启用扫描模式并配合DMA:
c复制// DMA配置
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
HAL_DMA_Init(&hdma_adc1);
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
// ADC多通道配置
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.NbrOfConversion = 3;
// 通道0
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 通道1
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 启动DMA传输
uint16_t adcValues[3];
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcValues, 3);
STM32 ADC出厂时已经过校准,但温度变化会影响精度。建议上电后执行校准:
c复制HAL_ADCEx_Calibration_Start(&hadc1);
通过软件过采样可将有效分辨率提高到14-16位:
c复制#define OVERSAMPLE_TIMES 256
uint32_t ADC_GetOversampleValue(void) {
uint32_t sum = 0;
for(int i=0; i<OVERSAMPLE_TIMES; i++) {
sum += ADC_GetValue();
}
return sum >> 4; // 12bit + 8bit(256次) = 20bit,右移4位得16bit
}
常用滤波方法对比:
| 滤波算法 | 适用场景 | 实现复杂度 | 效果 |
|---|---|---|---|
| 滑动平均 | 平稳信号 | 低 | 一般 |
| 中值滤波 | 脉冲干扰 | 中 | 好 |
| 卡尔曼滤波 | 动态系统 | 高 | 优 |
滑动平均滤波示例:
c复制#define FILTER_LEN 10
uint16_t filterBuf[FILTER_LEN];
uint8_t filterIndex = 0;
uint16_t MovingAverageFilter(uint16_t newVal) {
filterBuf[filterIndex++] = newVal;
if(filterIndex >= FILTER_LEN) filterIndex = 0;
uint32_t sum = 0;
for(int i=0; i<FILTER_LEN; i++) {
sum += filterBuf[i];
}
return sum / FILTER_LEN;
}
可能原因及解决方案:
检查步骤:
调试方法:
典型电路设计:
code复制[电池+] --[100kΩ]--+--[ADC引脚]
|
[100kΩ]
|
GND
电压计算公式:
c复制float batteryVoltage = adcValue * 3.3f / 4095 * 2; // 电阻分压比1:1
采用10K NTC与10K电阻分压:
c复制// Steinhart-Hart方程计算温度
float TempFromADC(uint16_t adcVal) {
float R = 10000.0f * (4095.0f/adcVal - 1);
float steinhart = log(R/10000.0f)/3950.0f + 1.0f/(25.0f + 273.15f);
return (1.0f/steinhart) - 273.15f;
}
需要250Ω精密电阻转换为1-5V电压:
code复制[4-20mA] --[250Ω]--+--[ADC引脚]
|
GND
电流计算公式:
c复制float current = (adcValue * 3.3f / 4095) / 250.0f * 1000; // mA单位
配置TIM2触发ADC定期采样:
c复制// 定时器配置
htim2.Instance = TIM2;
htim2.Init.Prescaler = 8400-1; // 84MHz/8400=10kHz
htim2.Init.Period = 100-1; // 100ms周期
HAL_TIM_Base_Init(&htim2);
// ADC配置
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
HAL_ADC_Init(&hadc1);
// 启动定时器
HAL_TIM_Base_Start(&htim2);
设置电压监控范围:
c复制HAL_ADC_AnalogWDGConfig(&hadc1,
&(ADC_AnalogWDGConfTypeDef){
.WatchdogMode = ADC_ANALOGWATCHDOG_SINGLE_REG,
.Channel = ADC_CHANNEL_0,
.ITMode = ENABLE,
.HighThreshold = 3000, // 3.0V对应值
.LowThreshold = 1000 // 1.0V对应值
});
注入通道可中断常规转换序列:
c复制// 配置注入通道
ADC_InjectionConfTypeDef sConfigInjected;
sConfigInjected.InjectedChannel = ADC_CHANNEL_1;
sConfigInjected.InjectedRank = 1;
HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected);
// 触发注入转换
HAL_ADCEx_InjectedStart(&hadc1);
HAL_ADCEx_InjectedPollForConversion(&hadc1, 10);
uint16_t val = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_1);
在低功耗应用中,可配置间断模式减少功耗:
c复制hadc1.Init.DiscontinuousConvMode = ENABLE;
hadc1.Init.NbrOfDiscConversion = 1; // 每组转换1个通道
HAL_ADC_Init(&hadc1);
采样完成后自动关闭ADC:
c复制void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
HAL_ADC_Stop(&hadc1);
HAL_ADC_DeInit(&hadc1);
__HAL_RCC_ADC1_CLK_DISABLE();
}
配合停机模式使用:
c复制// 配置唤醒中断
HAL_ADCEx_EnableVREFINT();
HAL_ADC_Start_IT(&hadc1);
// 进入停机模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
在不同条件下的ADC实测误差:
| 条件 | 采样率 | 输入电压 | 实测误差 |
|---|---|---|---|
| VDDA参考,无滤波 | 1MHz | 1.000V | ±8LSB |
| 外部3.0V参考 | 500kHz | 1.000V | ±3LSB |
| 过采样256次 | 10kHz | 1.000V | ±0.5LSB |
| 带硬件滤波 | 100kHz | 1.000V | ±2LSB |
示波器检查:
软件调试技巧:
c复制// 在调试时添加校验代码
if(HAL_ADC_GetState(&hadc1) != HAL_ADC_STATE_READY) {
Error_Handler();
}
使用STM32CubeMonitor实时查看ADC数据:
临界条件测试: