1. STM32F103C8T6 ADC开发概述
STM32F103C8T6作为STMicroelectronics推出的经典Cortex-M3内核微控制器,内置12位逐次逼近型ADC模块,在工业控制、传感器采集等领域应用广泛。这款被开发者亲切称为"蓝莓派"的核心板,其ADC特性包括:
- 3个独立ADC单元(ADC1/2/3)
- 12位分辨率下采样率可达1MHz
- 支持单次/连续/扫描/间断四种工作模式
- 16个外部通道+2个内部通道(温度传感器和VREFINT)
在实际项目中,ADC配置常遇到的痛点包括参考电压选择不当导致精度下降、采样周期设置不合理引发数据抖动、DMA传输配置复杂等。本文将基于标准外设库和HAL库两种方式,详解从基础配置到高级应用的完整实现方案。
2. 硬件设计要点
2.1 参考电压电路设计
开发板通常采用以下两种参考电压方案:
- 直接使用VDDA(通常3.3V)作为参考电压
- 优点:电路简单
- 缺点:电源波动会直接影响ADC精度
- 独立TL431基准源(2.5V)
- 优点:稳定性高(温漂50ppm/℃)
- 缺点:占用PCB空间,成本增加
实测对比:使用开关电源供电时,方案1的采样波动可达±5LSB,而方案2能控制在±1LSB内
2.2 信号调理电路
针对不同传感器信号需要设计前端调理:
circuit复制[信号源] --> [RC低通滤波] --> [电压跟随器] --> [ADC输入]
典型参数选择:
- 滤波截止频率:2倍于信号最高频率
- 运放选型:需满足输入范围包含GND(如LMV358)
3. 标准外设库实现
3.1 基础单通道配置
关键初始化步骤:
c复制void ADC1_Init(void) {
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 单次转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
3.2 多通道扫描模式
使用DMA传输的典型配置:
c复制uint16_t ADC_ConvertedValue[3]; // 存储3通道数据
void DMA_ADC_Init(void) {
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 3;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
// ADC配置增加扫描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 3;
ADC_Init(ADC1, &ADC_InitStructure);
// 设置通道采样顺序
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
4. HAL库实现方案
4.1 CubeMX配置流程
- 在Pinout界面启用ADC1
- 配置通道参数:
- Resolution: 12Bits
- Scan Conversion Mode: Enabled
- Continuous Conversion Mode: Enabled
- DMA Settings添加DMA流:
- Mode: Circular
- Data Width: Half Word
4.2 关键代码解析
中断方式读取示例:
c复制ADC_HandleTypeDef hadc1;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
uint16_t value = HAL_ADC_GetValue(hadc);
// 处理转换结果
}
void Start_ADC_Conversion(void) {
HAL_ADC_Start_IT(&hadc1); // 启动中断模式
}
5. 精度优化技巧
5.1 采样时间计算
总转换时间公式:
code复制Tconv = (采样周期 + 12.5) × ADC时钟周期
当ADC时钟为14MHz时:
- 239.5周期 ≈ 17.1μs
- 适合源阻抗>10kΩ的信号
- 低阻抗信号可选用7.5周期(1.43μs)
5.2 软件滤波算法
移动平均滤波实现:
c复制#define FILTER_LEN 8
uint16_t ADC_Filter(uint16_t raw) {
static uint16_t buf[FILTER_LEN] = {0};
static uint8_t idx = 0;
uint32_t sum = 0;
buf[idx++] = raw;
if(idx >= FILTER_LEN) idx = 0;
for(uint8_t i=0; i<FILTER_LEN; i++) {
sum += buf[i];
}
return (uint16_t)(sum / FILTER_LEN);
}
6. 典型问题排查
6.1 采样值异常排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 值固定为0 | 通道未正确配置 | 检查GPIO模拟输入模式 |
| 值跳变大 | 参考电压不稳 | 增加滤波电容或改用基准源 |
| 值偏小 | 采样时间不足 | 增大ADC_SampleTime参数 |
| 值反偏 | 信号负电压 | 前端增加电平移位电路 |
6.2 DMA传输常见故障
- 数据错位:
- 检查DMA_MemoryInc配置
- 确认缓冲区大小匹配通道数
- 传输中断:
- 验证DMA优先级设置
- 检查内存访问冲突
7. 进阶应用实例
7.1 内部温度传感器校准
计算公式:
code复制温度(℃) = {(V25 - VSENSE) / Avg_Slope} + 25
其中:
- V25 = 1.43V @25℃
- Avg_Slope = 4.3mV/℃
实现代码:
c复制float Read_Temperature(void) {
ADC_ChannelConfig(ADC1, ADC_Channel_16, ADC_SampleTime_239Cycles5);
ADC_StartConversion(ADC1);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
uint16_t temp = ADC_GetConversionValue(ADC1);
float vsense = temp * 3.3f / 4095;
return ((1.43f - vsense) / 0.0043f) + 25.0f;
}
7.2 电池电压监测
通过内部VREFINT校准:
c复制float Read_BatteryVoltage(void) {
// 先读取VREFINT校准值
uint16_t vref_cal = *(__IO uint16_t*)0x1FFFF7BA;
ADC_ChannelConfig(ADC1, ADC_Channel_17, ADC_SampleTime_239Cycles5);
ADC_StartConversion(ADC1);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
uint16_t vref_data = ADC_GetConversionValue(ADC1);
// 计算实际电压
float vdd = 3.3f * vref_cal / vref_data;
// 读取分压电池电压
ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239Cycles5);
ADC_StartConversion(ADC1);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
uint16_t bat_data = ADC_GetConversionValue(ADC1);
return bat_data * vdd / 4095 * (R1 + R2) / R2; // 分压比计算
}
在长期使用中发现,ADC性能会随环境温度变化产生漂移。建议关键应用场合每隔8小时读取一次VREFINT值进行在线校准。对于需要更高精度的场景,可外接24位ADC芯片如ADS1220,通过SPI接口与STM32通信,此时基准电压的选择将更为关键。
