用STM32内部温度传感器玩点不一样的:ADC双通道采集实战(含代码优化技巧)
STM32内部温度传感器与ADC双通道采集实战:从原理到代码优化
在嵌入式开发中,ADC(模数转换器)是最基础也最核心的外设之一。大多数开发者对ADC的基本使用已经驾轻就熟——配置单通道、读取电压值、简单计算后输出结果。但当我们把目光投向STM32内部集成的温度传感器,结合双通道ADC采集技术时,会发现一片全新的应用天地。本文将带你深入探索这一组合的独特价值,从硬件原理到代码优化,提供一套完整的实战方案。
1. STM32内部温度传感器的工作原理与特性
STM32系列微控制器内部集成了一个温度传感器,这个看似简单的功能模块实则蕴含着精妙的设计。与外部温度传感器相比,它不需要额外占用GPIO引脚,也不受外部电路干扰,特别适合对成本敏感且要求可靠性的应用场景。
温度传感器的关键参数:
- 典型测量范围:-40°C到+125°C(具体范围因型号而异)
- 精度:±1.5°C(在25°C时),整体精度约±3°C
- 输出特性:输出电压随温度线性变化,斜率约为2.5mV/°C
- 校准值:出厂时在30°C和110°C两点校准,校准值存储在芯片特定地址
温度传感器连接在ADC的专用通道上,不同型号STM32的具体通道号可能不同。以STM32F103系列为例:
// STM32F103内部温度传感器通道定义
#define TEMP_SENSOR_ADC_CHANNEL ADC_Channel_16
需要注意的是,内部温度传感器在默认情况下是关闭的,需要通过设置ADC_CCR寄存器的TSVREFE位来启用:
ADC_TempSensorVrefintCmd(ENABLE); // 启用温度传感器和内部参考电压
2. 双通道ADC采集的硬件架构设计
实现温度传感器与外部电压同步采集的关键在于合理配置ADC的多通道扫描模式。STM32的ADC模块支持多种工作模式,我们需要特别关注以下两种:
- 扫描模式:按预定顺序自动切换多个通道进行转换
- 连续模式:完成一轮转换后自动开始下一轮转换
双通道采集的硬件连接示例:
| 信号类型 | GPIO引脚 | ADC通道 | 备注 |
|---|---|---|---|
| 外部电压输入 | PA1 | ADC1_IN1 | 需确保输入电压≤VDDA |
| 内部温度传感器 | - | ADC1_IN16 | 内部连接,无需外部电路 |
| 参考电压 | - | - | 通常使用内部VDDA作为参考 |
在时钟配置方面,ADC的输入时钟不能超过14MHz。对于72MHz的系统时钟,推荐的分频设置:
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 72MHz/6=12MHz
3. 代码实现与DMA优化
传统的单通道ADC采集代码难以满足双通道应用的需求,我们需要重构整个采集流程。下面是一个完整的实现方案:
3.1 初始化配置
void ADC1_DualChannel_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;
DMA_InitTypeDef DMA_InitStruct;
// 1. 启用相关外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 2. 配置外部电压输入引脚(PA1)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. 启用内部温度传感器
ADC_TempSensorVrefintCmd(ENABLE);
// 4. DMA配置
DMA_DeInit(DMA1_Channel1);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&adc_values;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_BufferSize = 2;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel1, ENABLE);
// 5. ADC基本配置
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_InitStruct.ADC_ScanConvMode = ENABLE; // 启用扫描模式
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; // 连续转换
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfChannel = 2; // 两个通道
ADC_Init(ADC1, &ADC_InitStruct);
// 6. 配置通道及其采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5); // PA1
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5); // 温度传感器
// 7. 启用ADC DMA
ADC_DMACmd(ADC1, ENABLE);
// 8. 校准并启用ADC
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
// 9. 开始转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
3.2 数据读取与处理
使用DMA后,ADC转换结果会自动存储到指定数组中,我们只需定期读取即可:
// 定义全局变量存储ADC结果
uint16_t adc_values[2]; // index0:外部电压, index1:温度传感器
// 温度计算函数
float Get_Temperature(void) {
float vsense = (float)adc_values[1] * 3.3f / 4095.0f;
float temperature = (vsense - 0.76f) / 0.0025f + 25.0f;
return temperature;
}
// 外部电压读取函数
float Get_ExternalVoltage(void) {
return (float)adc_values[0] * 3.3f / 4095.0f;
}
4. 性能优化与实用技巧
4.1 采样时间优化
不同信号源需要不同的采样时间:
- 外部电压:通常信号阻抗较低,可使用较短的采样时间(如55.5周期)
- 温度传感器:输出阻抗较高,需要更长的采样时间(如239.5周期)
采样时间计算公式:
总转换时间 = (采样周期 + 12.5) × ADC时钟周期
以12MHz ADC时钟为例:
- 55.5周期 → 5.67μs
- 239.5周期 → 21μs
4.2 软件滤波算法
针对温度传感器的特性,推荐采用以下滤波组合:
- 移动平均滤波:减少随机噪声
#define FILTER_SIZE 8
float temp_history[FILTER_SIZE];
uint8_t index = 0;
float Moving_Average_Filter(float new_value) {
temp_history[index] = new_value;
index = (index + 1) % FILTER_SIZE;
float sum = 0;
for(int i=0; i<FILTER_SIZE; i++) {
sum += temp_history[i];
}
return sum / FILTER_SIZE;
}
- 中值滤波:消除偶发干扰脉冲
4.3 温度校准提升精度
虽然STM32内部温度传感器精度有限,但通过两点校准可显著改善:
- 在已知温度T1下读取ADC值V1
- 在已知温度T2下读取ADC值V2
- 计算实际斜率:slope = (T2 - T1)/(V2 - V1)
- 应用校准公式:T_actual = T1 + slope × (V_current - V1)
4.4 低功耗优化策略
对于电池供电设备:
void Enter_LowPowerMode(void) {
// 1. 降低ADC时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 9MHz
// 2. 增加采样时间,允许使用更高阻抗的输入电路
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_480Cycles);
// 3. 间歇采样模式
ADC_ContinuousConvModeCmd(ADC1, DISABLE);
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_ExternalTrigConfig(ADC1, ADC_ExternalTrigConv_T3_TRGO);
}
5. 创新应用场景
结合双通道采集能力,可以构建多种实用应用:
-
温度补偿电压测量系统:
- 通道1测量外部电压
- 通道2监测芯片温度
- 根据温度变化补偿电压测量值
-
设备健康监测:
- 长期记录芯片温度变化
- 分析温度与工作电压的关联性
- 提前发现散热问题或电源异常
-
环境感知节点:
- 温度作为环境参数
- 外部通道连接光敏电阻
- 实现低成本多参数传感
// 温度补偿示例
float Get_CompensatedVoltage(void) {
float raw_voltage = Get_ExternalVoltage();
float temperature = Get_Temperature();
float compensation = 0.001f * (temperature - 25.0f); // 0.1%/°C补偿
return raw_voltage * (1.0f + compensation);
}
在实际项目中,我发现内部温度传感器虽然精度有限,但其稳定性和重复性相当出色。通过适当的软件校准和滤波,完全能够满足大多数工业应用的需求。特别是在空间受限或成本敏感的设计中,这种方案比外接温度传感器更具优势。
更多推荐


所有评论(0)