1. STM32F103的ADC与DMA基础原理
ADC(模数转换器)是嵌入式系统中常见的硬件模块,用于将模拟信号转换为数字信号。STM32F103系列芯片内置了12位精度的ADC模块,支持多通道采集。在实际项目中,如果直接通过轮询方式读取ADC数据,会占用大量CPU资源。这时候DMA(直接内存访问)就派上用场了——它能在不经过CPU干预的情况下,自动将ADC转换结果搬运到指定内存区域。
我曾在多个电池管理项目中验证过,使用DMA传输ADC数据可以降低约70%的CPU负载。具体实现时需要注意三点:一是DMA通道与ADC的对应关系(STM32F103的DMA1通道1对应ADC1),二是要配置好内存地址自增模式,三是建议使用循环缓冲模式。下面这段配置代码是我经过多次优化后的稳定版本:
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = CHANNEL_COUNT; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;2. 多通道ADC的硬件连接要点
使用NTC热敏电阻测量温度时,典型电路采用分压原理。以10K/3950型号为例,需要将NTC与固定电阻(通常也是10K)串联,连接在3.3V与GND之间。中间节点接入ADC通道,这个设计看似简单,但实际布线时有几个坑我踩过:
- 走线干扰:ADC输入线要尽量远离高频信号线,我在一个电机控制项目中发现PWM信号会导致ADC值跳变,最后通过加装0.1μF滤波电容解决
- 参考电压:建议单独用LDO给VDDA供电,曾遇到VDD波动导致ADC值异常的情况
- 端口配置:GPIO必须设置为模拟输入模式,漏配这个会导致采样值不准
对于多通道采集,STM32F103的规则组通道配置有个细节需要注意:通道的采样顺序编号和实际通道号是独立的。比如要采集通道5、通道1、通道3,可以这样配置:
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_239Cycles5);3. 温度查表算法的优化实现
NTC热敏电阻的非线性特性使得直接计算温度较复杂,查表法是更高效的选择。原始代码中使用结构体数组存储温度-阻值对应关系,这里分享三个优化技巧:
- 二分查找优化:当表格数据量大时,线性查找效率低。实测将200个点的查找时间从56μs降到了8μs
- 浮点运算优化:在无FPU的Cortex-M3上,用定点数运算替代浮点能提升30%速度
- 温度补偿:对于高精度需求,建议在表格中增加温度补偿项
改进后的查表函数核心逻辑如下:
int low = 0, high = TABLE_SIZE - 1; while (low <= high) { int mid = low + (high - low)/2; if (table[mid].resistance > measured_res) { low = mid + 1; } else { high = mid - 1; } } // 找到最近点后进行线性插值 float temp_range = table[high].temp - table[low].temp; float res_range = table[high].resistance - table[low].resistance; return table[low].temp + (measured_res - table[low].resistance) * (temp_range/res_range);4. 系统级设计经验分享
在实际项目中,单纯实现功能只是第一步,更重要的是保证系统稳定运行。这里分享几个实战经验:
- 采样时序控制:DMA传输完成后可以触发中断,但建议配合定时器使用。我在一个工业温控系统中采用10ms定时触发+DMA循环缓冲,既保证实时性又避免频繁中断
- 数据滤波:简单的移动平均滤波就能显著提升稳定性。对于NTC测温,通常取5-10个采样值做平均即可
- 异常处理:要增加ADC值范围检查,曾遇到传感器脱落导致ADC值饱和的情况
- 低功耗优化:间歇采样模式下,ADC的启动时间需要补偿。测试发现STM32F103从停止模式唤醒后,前3个采样值建议丢弃
一个完整的系统初始化流程应该是这样的顺序:GPIO时钟→DMA→ADC校准→外设初始化。这个顺序如果搞错,可能会导致奇怪的硬件异常。有次我调了整整一天才发现是因为先初始化了ADC后配置DMA,导致DMA无法正常工作。