本文将详细介绍如何使用STM32F407的HAL库,实现通过单通道ADC采集VDO温度传感器的信号,并通过串口将采集到的温度值打印输出。具体流程包括:通过分压电阻将获得VDO温度传感器的分压电压,再利用运算放大器LM2904对信号进行放大,随后将信号输入STM32的ADC1模块读取通道8(对应引脚PB0)。最终,处理后的温度数据通过串口1发送至串口助手进行实时显示。
一、开发环境
硬件:自己搭建采样电路
单片机:STM32F407ZGT6
Keil版本:5.32
STM32CubeMX版本:6.9.2
STM32Cube MCU Packges版本:STM32F4 V1.27.1
二、VDO温度传感器
1.VDO温度传感器:是一种常见的汽车部件,常用于检测发动机或冷却液的温度。它通过热敏电阻的特性,利用温度与电阻之间的非线性关系输出数据。
1000 | 508 | 327 | 217 | 106 | 77 | 56 | 32 | 19 |
10 | 20 | 40 | 60 | 80 | 100 | 110 | 130 | 180 |
2.参考曲线
三、硬件电路
1.设计思路
VDO温度传感器的热敏电阻值来感知环境温度,经分压电路生成电压信号,信号经LM2904放大后输入MCU的ADC口。
LM2904内部包括有两个独立的、高增益、内部频率补偿的双运算放大器。不经过LM2904运算放大器直接输入STM32的ADC可能会导致信号幅度不足、信号质量下降、ADC输入阻抗不匹配以及系统稳定性和可靠性降低等问题。因此,在大多数情况下,建议使用运算放大器LM2904来放大和调理传感器信号,以确保输入到ADC的信号具有足够的幅度、清晰度和稳定性。
2.电阻分压与增益计算:
通过电阻分压比和LM2904增益计算电压。S+5v的电压是V0,VDO温度传感器电阻记为Rot,Rot和R83、R89并联后的总电阻记为Rzot,V1,V2,V3,V4所在点的电压值.
1)基本公式与定义
1>根据并联电阻计算公式1/R总=1/R1+1/R2推导出,R2=(R总*R1)/(R1-R总),对应到电路图的标识如下
1/Rzot=1/Rot+1/(R89_6K8+R83_10K0)Rot=(R89_6K8+R83_10K0)*Rzot/((R89_6K8+R83_10K0 )-Rzot)
2>根据串联电阻分压原理R1/R2=U1/U2=(U-U2)/U2,推导出R2=R1*U2/(U-U2),对应到电路图的标识如下
Rzot=R80_4K99*V1/(V0-V1)
3>V1=V0*Rot/(R80_4K99+Rot)=V0*Rzot/(R80_4K99+Rzot)
4>V2=V1*R89_6K8/(R89_6K8+R83_10K0)
通过计算电阻分压比和LM2904的增益,可以准确计算出ADC引脚口的电压。
5>V3=V4=V2*(1+R77_6K8/R81_10K0)
6>V4=V1*R89_6K8/(R89_6K8+R83_10K0)*(1+R77_6K8/R81_10K0)=V1*0.68
2)不接传感器(开路)时的电压计算
V0=5.3V,
V1=V0*(R89_6K8+R83_10K0)/(R80_4K99+R89_6K8+R83_10K0)=4.078V
V2=V0*(R89)/(R80_4K99+R89_6K8+R83_10K0)=1.65V
V3=V2*(1+R77/R81)=1.65*(1+6800/10000)=2.771V
V4=2.771V
3) 温度传感器在16℃下的测试
如果用温度传感器在16℃下测试时,此时的电阻约687Ω。对于单片机来说,只能知道ADC引脚口的电压,知道电压再反推温度传感器的电阻,再通过查温度电阻曲线表来确定此时的温度对应的电阻电流电压数据如下:
V0=5.3V,
V1=V0*(R89_6K8+R83_10K0)/(R80_4K99+R89_6K8+R83_10K0)=0.62V
V1=V0*Rot/(R80_4K99+Rot)=5.285*664/(4990+664)=0.62V
V2=V0*(R89)/(R80_4K99+R89_6K8+R83_10K0)=0.252V
V3=V2*(1+R77/R81)=1.65*(1+6800/10000)=0.422V
V4=0.422V
Rzot=R80_4K99*V1/(V0-V1)=4990*0.62/(5.3-0.62)=661Ω;
Rot=(R89_6K8+R83_10K0)*Rzot/(R89_6K8+R83_10K0-Rzot)=688Ω;反算出传感器的电阻值(并联10K+6.8K电阻)
增益
V1=V4/0.68=1.470588235*V4=0.62V
通过上面计算,得到温度传感器的电阻,下面通过查温度电阻曲线表来确定此时的温度。
通过两个温度和电阻点得到斜率,k=(y2−y1)/(x2−x1)
代入电阻,得到温度,y = kx + b 是直线方程
四、配置STM32CubeMX
- 启动STM32CubeMX,新建STM32CubeMX项目:
- 选择MCU:在软件中选择你的STM32型号-STM32F407ZGT6。
-
选择时钟源:
- 配置时钟:
- 使能Debug功能:Serial Wire
- HAL库时基选择:SysTick
- USART1配置:选择异步模式。
-
开启外部时钟:配置系统时钟,确保ADC和串口的外部时钟已开启。
-
配置ADC:
1)选择ADC1作为采集模块。
2)设置AD1的通道8(对应引脚PB0)为采集通道。
3)配置采样时间和分辨率。通常,采样时间越长,ADC的转换精度越高,但也会增加转换时间。
4)禁用扫描模式,因为我们只采集一个通道。
10.配置工程参数:在Project标签页中,配置项目名称和位置,选择工具链MDK-ARM。 11.生成代码:在Code Generator标签页中,配置工程外设文件与HAL库,勾选头文件.c和.h文件分开,然后点击Project > Generate Code生成代码。
五、代码实现与部署
-
main.c增加代码:main.c的第1行添加printf的头文件#include<stdio.h>,在 while(1)里增加数行代码。ADC电压换算时用到公式:待测电压=(ADC的返回值/4095)∗3.3V.具体参考附件的完整代码。
/* USER CODE BEGIN 0 */ uint16_t adc_value = 0; float adc_voltage_value = 0; float input_voltage_value = 0; char buffer[30]; #define R_4K99 4990 //分压电阻值4.99K #define R_6K80 6800 //分压电阻值6.80K #define R_10K0 10000 //并联电阻值10.0K #define SENSOR_VDD 5303 //分压输入源 //温度传感器温度电阻曲线, int16_t VT_T_SGH_Tsensor_tab[15][2] = { //(阻值R,温度℃) {19, 180}, {32, 130}, {56, 110}, {77, 100}, {106, 80}, {217, 60}, {327, 40}, {508, 20}, {1000, 10}, // 剩余元素填充为示例,实际使用中应确保所有元素都已正确初始化, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }; float getCheckTabValue(int16_t tab[15][2], float x); float RzOT; //总电阻值 Rot和R83(R_10K0)、R89(R_6K80)并联后的总电阻记为Rzot float ROT; //VDO温度传感器电阻记为Rot, float sensor_VOT; //反算出VDO温度传感器电阻Rot的分压 float temperature; /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); MX_ADC1_Init(); MX_ADC3_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_ADC_Start(&hadc1); // 启动ADC采集 // 轮询ADC转换状态 if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) == HAL_OK) { adc_value = HAL_ADC_GetValue(&hadc1); // 获取转换后的数字值 adc_voltage_value = adc_value*1000/ 4095.0f* 3.3; // 获取转换后的adc电压值mv sensor_VOT = adc_voltage_value *1.470588235f; //反算出温度传感器的分压 RzOT = (R_4K99*sensor_VOT) /(SENSOR_VDD-sensor_VOT); //总电阻值 Rot和R83(R_10K0)、R89(R_6K80)并联后的总电阻记为Rzot ROT = ((R_10K0+R_6K80)*RzOT) /((R_10K0+R_6K80)-RzOT); //换算VDO温度传感器电阻Rot,(并联 10K+6.8K电阻) temperature=getCheckTabValue(VT_T_SGH_Tsensor_tab,ROT); //查表获得温度 // 将温度值格式化为字符串 sprintf(buffer, "temperature: %.1fV\r\n",temperature); // 发送温度值到串口 printf("%s",buffer); } HAL_Delay(500); // 添加延时 } /* USER CODE END 3 */ } /* USER CODE BEGIN 4 */ float getCheckTabValue(int16_t tab[][2], float x) { // 定义斜率k和截距b float k, b, t; int i,index; // 处理x小于最小tab[0][0]的情况 if (x < tab[0][0]) { return tab[0][1]; } // 找到x所在的区间 [tab[i][0], tab[i+1][0]] for (i = 0; i < 14; i++) { if (x >= tab[i][0] && x <= tab[i + 1][0]) { index = i; break; } } if (i == 14 && x > tab[14][0]) { index = 14; } if(i<2) return 65535; // 如果x大于tab中最大的x值,使用最后一个点 // 计算斜率k和截距b k = (float)(tab[i + 1][1] - tab[i][1]) / (tab[i + 1][0] - tab[i][0]); //k=(y2-y1)/(x2-x1) b = tab[i][1] - k * tab[i][0]; // 计算并返回y值 t = k * x + b; //y = kx + b 是直线方程 return t; }
- usart.c增加代码:usart.c的第1行添加头文件#include <stdio.h>
#include <string.h>,在末尾用户代码区增加如下代码。printf调用“fputc()”,fgetc(),该函数会使用HAL_UART_Transmit发送数据。/* * 添加如下代码,可不在工程设置中勾选Use MicroLIB */ #pragma import(__use_no_semihosting) struct __FILE { int a; }; FILE __stdout; FILE __stdin; void _sys_exit(int x) { } /***************************************************** *function: 写字符文件函数 *param1: 输出的字符 *param2: 文件指针 *return: 输出字符的ASCII码 ******************************************************/ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 10); return ch; } /***************************************************** *function: 读字符文件函数 *param1: 文件指针 *return: 读取字符的ASCII码 ******************************************************/ int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, (uint8_t*)&ch, 1, 10); return (int)ch; }
- 连接USART1:用USB转TTL工具连接当前硬件USART1的PA9、PA10,GND。
- 打开串口助手:
- 编译代码:Keil编译生成的代码。
-
烧录程序:将编译好的程序用ST-LINK烧录到STM32微控制器中。
六、运行结果
1.温度传感器在16℃下的测试
在16℃下测试时,获取转换后的adc电压值418mv ,换算VDO温度传感器电阻Rot=681欧姆,查表获得温度16.4℃,与上面电阻分压与增益计算和实际温度接近.
2.温度传感器在90℃下的测试
在90℃下测试时,获取转换后的adc电压值63mv ,换算VDO温度传感器电阻Rot=90欧姆,查表获得温度90.9℃,与上面电阻分压与增益计算和实际温度接近.
七、总结
通过本文的实践,我们成功实现了基于STM32F407的HAL库进行VDO温度传感器信号采集与处理的完整流程。从分压电阻电路设计到运算放大器LM2904的应用,再到STM32 ADC模块的配置与串口数据传输,每一步都为实现精准温度测量提供了关键支持。本项目不仅展示了模拟信号与数字处理的结合应用,还为其他传感器信号采集与处理方案提供了参考。希望本文能为你的嵌入式开发项目带来启发,欢迎留言交流更多开发思路!
八、注意事项
1.确保你的开发环境和工具已经正确安装和配置。
2.确保自己搭建采样电路连接没错,接到响应的ADC口。
3.如果没有打印电压值,按一下复位键,检查连接和电源是否正确,注意根据你所用的硬件来接线,不要接错线了。