Stm32F103R6之ADC:从基础配置到高级应用全解析

张开发
2026/4/13 22:45:40 15 分钟阅读

分享文章

Stm32F103R6之ADC:从基础配置到高级应用全解析
1. 认识Stm32F103R6的ADC模块第一次接触Stm32F103R6的ADC功能时我完全被各种专业术语搞晕了。后来在实际项目中反复使用才发现它其实就像个电子秤能把模拟世界的电压信号转换成数字世界能理解的数值。这款芯片内置的12位ADC模数转换器最高采样率能达到1MHz对于大多数嵌入式应用来说完全够用。ADC模块最吸引人的是它的灵活性。你可以选择单次转换测量某个特定时刻的电压也可以开启连续转换模式实时监控信号变化。我做过一个锂电池电压监测项目就是用连续模式每秒钟采样100次配合简单的滤波算法就能得到稳定的电压读数。硬件连接上要注意几个关键点输入电压范围必须控制在0~3.3VVREF引脚电压对于高阻抗信号源建议加电压跟随器模拟地和数字地要单点连接电源引脚需要加0.1uF去耦电容// 最简单的ADC初始化代码 void ADC1_Init(void) { RCC-APB2ENR | 19; // 开启ADC1时钟 ADC1-CR2 0; // 先清零配置寄存器 ADC1-CR2 | 10; // 开启ADC ADC1-CR2 | 12; // 持续转换模式 ADC1-CR2 | 120; // 启用外部触发 }2. 基础配置五步走2.1 时钟与引脚配置刚开始用ADC时最容易忽略时钟配置。Stm32F103R6的ADC时钟最大不能超过14MHz我一般选择PCLK2的6分频72MHz/612MHz。GPIO引脚要设置为模拟输入模式这点很多人会误配成浮空输入。// 正确的GPIO配置 GPIOA-CRL ~(0xF(1*4)); // PA1模拟输入 RCC-APB2ENR | 12; // 开启GPIOA时钟 RCC-APB2ENR | 19; // 开启ADC1时钟 RCC-CFGR | (214); // ADC时钟APB2/62.2 校准流程校准是保证精度的关键步骤。我发现很多初学者会跳过这步结果采样值总是有几十个LSB的偏差。正确的做法是上电后等待电压稳定确保ADC处于关闭状态(ADON0)延时至少1ms启动校准(CAL1)等待校准完成(CAL自动清零)ADC1-CR2 ~(10); // 先关闭ADC delay_ms(1); ADC1-CR2 | 12; // 开启持续转换 ADC1-CR2 | 13; // 启动校准 while(ADC1-CR2 (13)); // 等待校准完成2.3 通道与采样时间采样时间设置直接影响转换精度。对于不同的信号源阻抗需要配置不同的采样周期。我总结的经验值是低阻抗信号10kΩ7.5个周期中阻抗信号10kΩ-50kΩ41.5个周期高阻抗信号50kΩ239.5个周期// 配置通道1采样时间为239.5周期 ADC1-SMPR2 | (73); // SMP11112.4 触发方式选择ADC支持软件触发和硬件触发两种方式。在电机控制项目中我使用定时器触发ADC采样确保采样时刻与PWM波形严格同步// 配置TIM1 TRGO事件触发ADC TIM1-CR2 | (24); // MMS010 ADC1-CR2 | 120; // 外部触发使能 ADC1-CR2 | (317); // EXTSEL011(TIM1_TRGO)2.5 数据对齐与读取12位转换结果可以左对齐或右对齐存储。我建议使用右对齐这样数值处理更直观uint16_t read_ADC(void) { ADC1-CR2 | 10; // 开启ADC ADC1-CR2 | 122; // 启动转换 while(!(ADC1-SR (11))); // 等待转换完成 return ADC1-DR; // 读取右对齐结果 }3. 多通道管理实战3.1 规则通道组配置规则通道组最多支持16个通道我常用它来循环采集多个传感器信号。关键是要正确设置SQR寄存器的转换序列// 配置通道1、2、3按顺序转换 ADC1-SQR1 0; // 1个转换 ADC1-SQR3 (10) | (25) | (310); // SQ11, SQ22, SQ33实际项目中遇到过一个问题修改SQR寄存器后采样值异常。后来发现必须在ADC停止状态下修改配置转换过程中修改会导致数据错误。3.2 注入通道高级用法注入通道就像ADC的中断系统它有这些特点最多4个通道可以打断规则通道转换有自己的数据寄存器支持自动注入功能在电源监控项目中我用注入通道实现过压保护// 配置注入通道4为紧急电压检测 ADC1-JSQR (10); // 1个注入转换 ADC1-JSQR | (415); // JSQ1通道4 ADC1-HTR 0x7FF; // 设置高阈值(3V) ADC1-CR1 | 15; // 开启模拟看门狗3.3 通道切换优化技巧快速切换通道时要注意采样保持电容的放电时间。我的经验是高精度测量时切换后延时10us常规应用至少延时5个ADC时钟周期可以使用间断模式减少切换时间// 使用间断模式快速切换 ADC1-SQR1 (220); // 每3个转换后间断 ADC1-SQR3 (10)|(25); // 先通道1后通道2 ADC1-CR1 | (112); // 开启间断模式4. 中断与DMA高效处理4.1 中断回调实现ADC中断有3种常用场景转换完成中断模拟看门狗中断注入转换完成中断我建议使用状态机模式处理中断void ADC1_IRQHandler(void) { if(ADC1-SR (11)) { // 规则通道完成 adc_values[adc_index] ADC1-DR; if(adc_index 8) adc_index 0; } if(ADC1-SR (12)) { // 注入通道完成 emergency_check(ADC1-JDR1); } ADC1-SR 0; // 清除所有标志位 }4.2 DMA传输配置DMA是高效处理多通道采样的利器。配置时要注意内存地址递增数据宽度匹配循环模式使能DMA中断合理使用// 配置DMA1通道1用于ADC1 DMA1_Channel1-CPAR (uint32_t)ADC1-DR; DMA1_Channel1-CMAR (uint32_t)adc_buffer; DMA1_Channel1-CNDTR 8; // 传输8个数据 DMA1_Channel1-CCR (15)|(17)|(110); // 循环模式,内存递增,16位 ADC1-CR2 | 18; // 启用DMA4.3 双缓冲技巧在音频采集项目中我使用双缓冲避免数据竞争uint16_t adc_buf1[256], adc_buf2[256]; volatile uint8_t current_buf 0; void DMA1_Channel1_IRQHandler(void) { if(DMA1-ISR (11)) { // 传输完成中断 if(current_buf 0) { process_data(adc_buf1); DMA1_Channel1-CMAR (uint32_t)adc_buf2; } else { process_data(adc_buf2); DMA1_Channel1-CMAR (uint32_t)adc_buf1; } current_buf ^ 1; DMA1-IFCR | (11); // 清除标志 } }5. 高级应用场景5.1 温度传感器校准内部温度传感器的精度虽然不高但用来监测芯片温度变化很实用。实测发现不同芯片的偏差可能达到±10°C所以必须校准float read_temperature(void) { ADC1-SQR3 16; // 温度传感器通道 ADC1-CR2 | 122; // 启动转换 while(!(ADC1-SR (11))); uint16_t temp ADC1-DR; return ((1.43 - temp*3.3/4096) / 0.0043) 25; }5.2 电池电压监测用ADC测量电池电压时要注意分压电阻的精度和温漂。我通常使用0.1%精度的电阻并软件补偿温度影响#define VDDA 3.3f #define R1 100.0f #define R2 100.0f float read_battery(void) { ADC1-SQR3 1; // 通道1 ADC1-CR2 | 122; // 启动转换 while(!(ADC1-SR (11))); float adc ADC1-DR; return (adc / 4096 * VDDA) * (R1 R2) / R2; }5.3 双ADC同步采样在电机三相电流采样等需要同步测量的场景双ADC模式非常有用// 配置ADC1为主,ADC2为从 ADC1-CR1 (113); // 同步注入模式 ADC2-CR1 (113); // 同步规则模式 ADC1-CR2 | 120; // 外部触发使能 ADC2-CR2 | 120; // 外部触发使能 TIM1-CR2 | (24); // 使用TIM1触发6. 常见问题排查6.1 采样值不稳定遇到ADC值跳动时可以按以下步骤排查检查电源稳定性纹波50mV确认参考电压干净加10uF0.1uF电容优化采样时间噪声大时延长采样时间检查信号源阻抗高阻抗源要加缓冲软件滤波移动平均或中值滤波6.2 转换时间异常转换时间计算公式为 Tconv 采样时间 12.5个周期如果发现转换比预期慢检查ADC时钟分频设置是否开启了间断模式触发信号频率是否过高6.3 DMA传输不触发DMA不工作的常见原因未使能DMA控制器时钟内存/外设地址未对齐数据量寄存器CNDTR为0ADC的DMA使能位未设置DMA通道优先级冲突7. 性能优化技巧7.1 降低功耗方案在电池供电设备中我采用这些方法降低ADC功耗仅在需要时开启ADCCR2的ADON位使用单次转换模式降低采样频率关闭未用通道的IO口时钟// 低功耗采样模式 void low_power_sample(void) { ADC1-CR2 | 10; // 开启ADC delay_us(10); // 等待稳定 ADC1-CR2 | 122; // 启动转换 while(!(ADC1-SR (11))); uint16_t val ADC1-DR; ADC1-CR2 ~(10); // 关闭ADC }7.2 提高采样精度要获得最佳精度需要注意避免IO口同时翻转产生噪声保持VDDA和VREF电压稳定适当oversampling过采样定期重新校准远离高频信号线7.3 软件滤波算法常用的软件滤波方法有移动平均滤波适合缓慢变化信号中值滤波适合去除突发干扰卡尔曼滤波动态系统最佳IIR低通滤波计算量小// 简单的移动平均滤波 #define FILTER_SIZE 8 uint16_t adc_filter(uint16_t new_val) { static uint16_t buf[FILTER_SIZE]; static uint8_t index 0; static uint32_t sum 0; sum - buf[index]; buf[index] new_val; sum new_val; index (index 1) % FILTER_SIZE; return sum / FILTER_SIZE; }在实际项目中ADC的稳定性和精度往往决定了整个系统的性能。经过多个项目的验证我发现Stm32F103R6的ADC模块虽然不如专业ADC芯片但通过合理的配置和软件处理完全能满足大多数工业应用的需求。特别是在使用DMA双缓冲的方案后即使在复杂系统中也能保证稳定的采样性能。

更多文章