STM32 HAL库 + LM2904运算放大器 + ADC + VDO温度传感器:电路设计及代码实现

news2024/12/26 2:48:53

本文将详细介绍如何使用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

  1. 启动STM32CubeMX,新建STM32CubeMX项目:
  2. 选择MCU:在软件中选择你的STM32型号-STM32F407ZGT6。
  3. 选择时钟源:

  4. 配置时钟:
  5. ​使能Debug功能:Serial Wire
  6. ​HAL库时基选择:SysTick
  7. USART1配置:选择异步模式。
  8. 开启外部时钟:配置系统时钟,确保ADC和串口的外部时钟已开启。

  9. 配置ADC:

1)选择ADC1作为采集模块。

2)设置AD1的通道8(对应引脚PB0)为采集通道。

3)配置采样时间和分辨率。通常,采样时间越长,ADC的转换精度越高,但也会增加转换时间。

4)禁用扫描模式,因为我们只采集一个通道。

10.配置工程参数:在Project标签页中,配置项目名称和位置,选择工具链MDK-ARM。​ 11.生成代码:在Code Generator标签页中,配置工程外设文件与HAL库,勾选头文件.c和.h文件分开,然后点击Project > Generate Code生成代码。 

五、代码实现与部署

  1.  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;
    	 
    }
  2.  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;
    }
  3. 连接USART1:用USB转TTL工具连接当前硬件USART1的PA9、PA10,GND。​​
  4. 打开串口助手:​​
  5. 编译代码:Keil编译生成的代码。
  6. 烧录程序:将编译好的程序用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.如果没有打印电压值,按一下复位键,检查连接和电源是否正确,注意根据你所用的硬件来接线,不要接错线了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2252249.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python酷库之旅-第三方库Pandas(252)

目录 一、用法精讲 1191、pandas.tseries.offsets.BusinessMonthBegin.n属性 1191-1、语法 1191-2、参数 1191-3、功能 1191-4、返回值 1191-5、说明 1191-6、用法 1191-6-1、数据准备 1191-6-2、代码示例 1191-6-3、结果输出 1192、pandas.tseries.offsets.Busine…

【Linux服务器】CPU问题排查

概述 总体排查思路 总体观察&#xff0c;htop命令观察系统负载程度 检查CPU占用高的进程检查CPU硬件信息是否为问题根源从宏观到细节分析系统IO情况最后检查系统日志 排查思路 系统负载 通过htop命令查看系统负载&#xff0c;通过系统负载可以判断系统是否繁忙&#xff0c;主…

详解MyBatis之篇一

目录 MyBatis 定义 使用MyBatis操作数据库 创建项目 配置 演示 UserInfo.java UserInfoMapper UserInfoMapperTest 数据准备 自动生成测试类 运行结果 MyBatis 定义 MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避…

编译器优化技术

方法内联 逃逸分析 公共子表达式消除 数据边界检查消除

汽车IVI中控开发入门及进阶(三十五):架构QML App Architecture Best Practices

在Qt/QML工程的架构中,架构很重要,虽然本身它有分层,比如QML调用资源文件(图片等)显示GUI界面,后面的CPP文件实现界面逻辑,但是这个分类还有点粗。在实际开发中,界面逻辑也就是基于类cpp的实现,也开始使用各种面向对象的设计模式,实现更加优秀的开发架构,这点尤其在…

本地Docker部署个人在线音乐平台Melody结合内网穿透远程访问听音乐

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

异常--C++

文章目录 一、异常的概念及使用1、异常的概念2、异常的抛出和捕获3、栈展开4、查找匹配的处理代码5、异常重新抛出6、异常安全问题7、异常规范 二、标准库的异常 一、异常的概念及使用 1、异常的概念 异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并…

字符串p型编码

字符串p型编码 C 语言实现C 实现Java 实现Python 实现 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 给定一个完全由数字字符&#xff08;‘0’,‘1’,‘2’,…,‘9’&#xff09;构成的字符串str&#xff0c;请写出str的p型编码串。例如&…

UIlicious - 自动化端到端测试

在现代软件开发中&#xff0c;测试自动化已然成为产品交付质量的基石。而端到端测试&#xff08;E2E&#xff09;&#xff0c;作为验证整个应用流畅运行的关键&#xff0c;常常是测试工作中最具挑战性的一环。这时&#xff0c;一款简单高效的自动化测试工具——UIlicious&#…

机器学习:机器学习项目的完整周期

建立一个有价值的机器学习系统时&#xff0c;需要考虑和计划哪些步骤&#xff1f; 以语音识别为例演示机器学习项目的全周期&#xff1a;机器学习项目的第一步是对项目进行范围划分&#xff0c;即决定什么是项目和你想做什么&#xff0c;然后是收集数据&#xff0c;所以决定需…

浪潮X86服务器NF5280、8480、5468、5270使用inter VROC Raid key给NVME磁盘做阵列

Inter VROC技术简介 Intel Virtual RAID on CPU (Intel VROC) 简单来说就是用CPU的PCIE通道给NVME硬盘做Raid 更多信息可以访问官方支持页面 Raid Key 授权&#xff0c;即VROC SKU 授权主要有用的有2个标准和高级&#xff0c;仅Raid1的授权我暂时没见过。 标准 VROCSTANMOD …

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本)

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) code review! 参考笔记 1.ROS基本框架1——编写简单的发布者和订阅者(C++和Python版本) 2.ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) 文章目录 ROS基本框架2——在ROS开发中创建并使用自定义…

鸿蒙征文|鸿蒙技术分享:使用到的开发框架和技术概览

目录 每日一句正能量前言正文1. 开发环境搭建关键技术&#xff1a;2. 用户界面开发关键技术&#xff1a;3. 应用逻辑开发关键技术&#xff1a;4. 应用测试关键技术&#xff1a;5. 应用签名和打包关键技术&#xff1a;6. 上架流程关键技术&#xff1a;7. 后续维护和更新关键技术…

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验二----网络分析(超超超详细!!!)

相信实验一大家已经完成了&#xff0c;对Arcgis已进一步熟悉了&#xff0c;现在开启第二个实验 ArcMap实验--网络分析 目录 ArcMap实验--网络分析 1.1 网络分析介绍 1.2 实验内容及目的 1.2.1 实验内容 1.2.2 实验目的 2.2 实验方案 2.3 实验流程 2.3.1 实验准备 2.3.2 空间校正…

go语言 Pool实现资源池管理数据库连接资源或其他常用需要共享的资源

go Pool Pool用于展示如何使用有缓冲的通道实现资源池&#xff0c;来管理可以在任意数量的goroutine之间共享及独立使用的资源。这种模式在需要共享一组静态资源的情况&#xff08;如共享数据库连接或者内存缓冲区&#xff09;下非 常有用。如果goroutine需要从池里得到这些资…

马铃薯病害识别(VGG-16复现)

VGG16-Pytorch实现马铃薯病害识别 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 电脑系统&#xff1a;Windows11 显卡型号&#xff1a;NVIDIA Quadro P620 语言环境&#xff1a;python 3.9.7 编译器&am…

HCSIF: 中国区域2000-2022年高时空分辨率(500m)SIF数据集

日光诱导叶绿素荧光&#xff08;Solar-induced chlorophyll fluorescence, SIF&#xff09;被誉为“植被光合作用的探针”。2017年&#xff0c;搭载在Sentinel-5P卫星上的 TROPOMI (TROPOspheric Monitoring Instrument&#xff09;传感器成功发射&#xff0c;该卫星同时具有高…

STL:相同Size大小的vector和list哪个占用空间多?

在C中&#xff0c;vector和list是两种不同的序列容器。vector底层是连续的内存&#xff0c;而list是非连续的&#xff0c;分散存储的。因此&#xff0c;vector占用的空间更多&#xff0c;因为它需要为存储的元素分配连续的内存空间。 具体占用多少空间&#xff0c;取决于它们分…

蓝牙设备驱动开发

文章目录 一、蓝牙协议架构二、蓝牙协议的HCI传输层三、编程框架 一、蓝牙协议架构 蓝牙是无线数据和语音传输的开放式标准&#xff0c;它将各种通信设备、计算机及其终端设备、各种数字数据系统、甚至家用电器采用无线方式联接起来。它的传输距离为10cm&#xff5e;10m&#…

【计算机网络】实验7:默认路由和特定主机路由以及路由环路问题

实验 7&#xff1a;默认路由和特定主机路由以及路由环路问题 一、 实验目的 了解默认路由以及特定主机路由。 了解静态路由配置错误导致的路由环路问题。 二、 实验环境 • Cisco Packet Tracer 模拟器 三、 实验内容 1、默认路由以及特定主机路由 (1) 第一步&#xff…