ADC相关算法以及热敏电阻测温

目录

前言

一、平均值滤波算法

二、快速排序算法的使用

三、中位值滤波算法

四、二分查找法

4.1 二分查找法查找某个元素是否存在

 4.2 二分查找法查找接近目标数值的元素的下标

五、NTC热敏电阻实现测温

5.1 分层设计

5.2 软件流程图

​编辑 5.3 API接口及数据结构

5.4 热敏电阻模块原理图

5.5 热敏电阻工作原理


前言

        本文章介绍在使用STM32F103片上外设ADC时,可能会用到的一些算法,平均值滤波算法、中位值滤波算法、快速排序算法、二分查找法。并且实现了使用热敏电阻测量温度,串口打印显示。使用到的资源如下:

PA0用于热敏电阻的ADC,PB10和PB11用于串口。

        相关的代码和资料请点击下方链接查看:

https://jcnwdt8hb184.feishu.cn/wiki/AUXQwtZ6AipW16kUAn0cIi0anMe?form_wx_login=1

一、平均值滤波算法

        ADC转换的数据是在动态变化的,这个范围可能不是很大,比如采集温度数据,ADC转换的值每次都可能浮动零点几,24.2、24.5、24.1、24.2。每次转换数字都在变化,这样的数字如果显示在LCD等显示屏幕上,会造成非常不好的体验,数字一会儿跳一会跳。所以就需要采用ADC滤波算法。这里介绍常用的平均值滤波算法,在我提供的资料里,有一份文档,上面写了近10种滤波算法,具体使用哪一种还要根据自己的项目确定。

        平均值滤波算法十分简单,就是把转换的数据放到数组里面,最终显示到屏幕上的数字是数组内的元素的平均值,这样显示的数据就会稳定。

static uint16_t ArithAvgFlter(uint16_t* arr, uint32_t len)
{
	uint32_t sum = 0;
	for (uint32_t i = 0; i < len; i++)
	{
		sum += arr[i];
	}
	return (uint16_t)(sum / len);
}

        代码在调试的时候,遇到了一个问题,当代码里的波特率设置为9600(串口助手也为9600)时,打印显示的信息会错行,而当都设置为115200的时候,就不会错行,如下图所示。

        后来经过PN学堂各位老师和同学的指点,我才知道是串口助手里“超时时间”的问题。在波特率9600的时候把超时时间设置大一点就不会出错了。那么什么是超时时间呢?

在串口助手中,“超时时间”是指在接收串口数据时等待的最大时间。
比如说,设置超时时间为1000毫秒。当串口助手开始等待接收数据时,它会在1000毫秒内一直处于等待接收状态。如果在这1000毫秒内有数据到来,就正常接收处理;要是超过1000毫秒还没有接收到数据,就会停止等待,这种机制可以避免程序一直处于无期限的等待状态,从而提高通信效率和程序运行的稳定性。

判断串口助手超时时间设置是否合理,可以考虑以下几点:
通信速率
- 如果串口通信波特率高、数据传输快,超时时间可以设置得较短。例如,波特率为115200bps时,传输一个字节所需时间很短,超时时间设几十毫秒可能就足够了。
- 对于较低波特率(如9600bps),传输一个字节时间较长,超时时间可能需要设置到几百毫秒,以确保能完整接收数据。
 
数据量和数据发送频率
- 当每次传输的数据量较小且发送频率低时,较短超时时间(如50 - 100毫秒)或许就能满足需求。
- 若数据量较大或者数据发送很频繁,为避免接收不完整,需要适当延长超时时间,确保接收缓冲区能接收完整的数据块。
 
系统响应要求
- 如果系统对数据的实时性要求高,希望快速处理接收到的数据,超时时间不宜过长。比如工业控制的实时监控系统,超时时间过长可能会导致系统响应延迟。
- 对于对实时性要求不高的系统,可适当放宽超时时间设置。
 
设备性能
- 设备处理速度快、资源充足时,能快速处理接收的数据,可以设置较短超时时间。
- 对于性能较差的设备,适当延长超时时间可避免因处理不及时而丢失数据。
 
可以通过实际测试来验证设置是否合理。在不同的超时时间设置下,发送和接收一系列已知的数据,观察数据接收的完整性、准确性和系统响应情况。如果数据接收完整、系统响应及时,就说明当前超时时间设置比较合理。 

二、快速排序算法的使用

        在使用ADC的时候,不可避免的需要对数据进行排序,以前学过冒泡排序,但是在工作中,常使用的是快速排序,接下来就介绍快速排序的使用方法,具体如何实现的不做介绍,如果读者想要了解,可以去查看“数据结构与算法”相关的书籍,里面是有介绍的。

        在C标准库中提供了快速排序的接口函数,需要包含头文件stdlib.h。这个排序函数不仅可以排序普通数组,还可以排结构体数组。

         第一个参数是“数组首地址”,第二个参数是“数组元素个数”,第三个参数是“数组元素所占用内存空间大小”,第四个参数是“用户自己定义的回调函数的地址,即函数指针变量”。

        前三个参数都好理解,关键是第四个参数,这个参数需要我们自己定义一个回调函数,传入这个回调函数的地址的作用是告诉qsort排序规则是升序还是降序,函数中的两个参数是数组元素的地址,所以画红圈的部分表示,这两个参数的类型必须与你要排序的数组的类型一致,比如你要对uint32_t buf[10]排序,那么红圈部分就是uint32_t*。下图是升序规则,如果把if和else if里的内容互换就是降序规则。

        示例代码参考文章开头的链接。

三、中位值滤波算法

        所谓的中位值算法就是去掉数据当中的最大值和最小值,再对剩余数据取平均值,显而易见的,该算法需要联合使用平均值滤波算法和快速排序算法。

#include "stm32f10x.h"                  // Device header
#include "stdio.h"
#include "stdlib.h"
#include "uart_drv.h"


static int32_t g_dataBuf[6] = {-1, -4, 0, 12, 23, 4};

static int32_t CompareCallBack(const void* _a, const void* _b)//升序规则
{
	int32_t* a = (int32_t*) _a;
	int32_t* b = (int32_t*) _b;
	int32_t val = 0;
	
	if (*a > *b)
	{
		val = 1;
	}
	else if(*a < *b)
	{
		val = -1;
	}
	else
	{
		val = 0;
	}
	return val;
}

static int32_t ArithAvgFlter(int32_t* arr, uint32_t len)//平均值滤波算法
{
	int32_t sum = 0;
	for (uint32_t i = 0; i < len; i++)
	{
		sum += arr[i];
	}
	return (int32_t)(sum / len);
}

static int32_t MedianAvgFltr(int32_t* arr, uint32_t len)//中位值滤波算法
{
	qsort(arr, len, sizeof(arr[0]), CompareCallBack);//数据排序
	/*排序后的数据显示*/
	printf("after qsort :\n");
	for (uint8_t i = 0; i < len; i++)
	{
		printf ("g_dataBuf[%d] = %d\n", i, arr[i]);
	}
	
	putchar ('\n');
	
	/*返回中位值滤波后的结果*/
	return ArithAvgFlter(&arr[1], len - 2);
	
}

int main(void)
{	
	UartDrvInit();
	
	int32_t result = 0;
	result = MedianAvgFltr(g_dataBuf, 6);
	printf("result =  %d\n", result);
	while(1)
	{
		
	}
}

四、二分查找法

        二分查找法用于查找一个有序数组中某个目标值是否存在,或者查找到接近目标值的元素(下标);相比把整个数组遍历一次的O(n)复杂度,二分查找法可以把复杂度降低到O(log2^n),假如数组元素个数为1024,那么遍历法最多遍历1024次,而二分查找法最多10次搞定,2^10 = 1024嘛。

4.1 二分查找法查找某个元素是否存在

        假如现在有一个升序数组为{1,3,7,10,14,20},现要求查找元素20的下标,如果没有这个元素,返回下标为-1。

        我理解的二分查找法,一堆砝码随意的摆成一行,我需要找这其中是否有一个1KG的砝码a,首先我假设这行砝码里有我要的那个砝码a,那我把这行砝码分成两份,这个1KG的砝码a是不是不在这半边就在那半边?如果我能用一种简单的方法判断它究竟在那边,是不是直接就可以排除掉一半的砝码,大大减轻了筛选的工作量?二分查找法就是这样的思路。

        那么关键就在于我如何用一种较为简单的方法判断我要找的数字究竟在数据中的那一半呢?那就要思考数据的本质特征,是的,不管是砝码的质量还是数组中的数字,只要涉及数,都有一个最本质的特点——大小!!(这就是为什么二分查找法要求数据是有序排列的!

        有了上面的思路,我们就可以想,如果能把数据按照升序或降序排列,那么我只需要把我要找的那个数字,和数据排序好之后--下标位于数据中间的那个数字作比较,就可以轻而易举的知道,我要找的数字究竟在数据中的那一边,我不断的二分,不断的二分,最终就能找到那个数字,或者知道数据中并没有这个数字!!

        现在回到最开始提出的问题,结合下图进行讲解:首先要明确的一点就是,left, right, mid都是保存的元素的下标,现在left = 0, right = 5, 那么要找出中间数字的下标,很简单,mid = (right + left )/2!噫,可是图中并不是用的这种方法求取中间数字的下标,这是为什么呢?

        作为嵌入式工程师,做数值运算处理的时候,时刻都要问自己,是否有溢出的风险?如果left和right都是uint8_t数据类型,因为随着不断的二分、二分....,显而易见,left和right的值是会增大或减小的,假如现在left增大到100,而right又为234,那么234 + 100 = 334显然是超出了uint8_t类型的数值范围。而采用图中的方法就不会有溢出的风险。

        言归正传,mid = 0 + (5 - 0)/2 = 2,下标为2的数字是7,7是小于要找的数字20的,由于数据是升序排列,那么很显然要找的20一定处于右半部分,所以左半部分可以直接不找了。怎么个不找法呢?现在20处于下标3~5之间,因此把left = mid + 1 = 3;right = 5不变,再一次二分,mid = left +(right - left)/2 = 4; 那么下标为4的数字为14,仍然小于20,继续二分。。。。略

        那么如何用代码实现上述的逻辑呢?代码如下,只要C语言基本功扎实的朋友应该都可以看明白,我就不过多赘述了,只提一些比较容易忽略的、重要的点。

        函数IncBinarySearch和函数DecBinarySearch分别为查找升序数组和查找降序数组的二分查找法代码,在这两个函数里面,我把left, right, mid这三个代表数组下标的变量类型都设为了int32_t,这是为什么呢?明明数组的下标是不可能会有负数的,设置为无符号数据类型不就好了吗,为什么要设为有符号类型呢?

        如果想要知道答案的话,不妨可以自己试着这样思考一下,如果我把数据类型设为无符号的,会出错吗?然后去实验,脑子里预想各种数据情况,过一遍代码。如果把数据类型设为无符号,假如我要查找的数字并不在数组当中,就会出现这样一种情况,left会大于right然后退出循环,可是如果我要查找的数字不在数组里面,并且它小于数组中的任何一个数,那么就会出现right < left = 0的情况,也就是说right里面存的负数,可是无符号数据类型从0减1会直接溢出为数据有效范围的最大值,所以不会出现left > right退出循环的情况,代码就死在这个地方了。

#include "stm32f10x.h"                  // Device header
#include "stdio.h"
#include "stdlib.h"
#include "uart_drv.h"


static uint16_t g_incDataBuf[6] = {0, 5, 7, 9, 11, 23};
static uint16_t g_decDataBuf[6] = {43, 32, 21, 13, 6, 3};

static int32_t IncBinarySearch(uint16_t* arr, uint32_t size, uint16_t key)//查找升序数组
{
	int32_t left = 0;
	int32_t right = size - 1;
	int32_t mid = 0;
	
	while (left <= right)
	{
		mid = left + (right - left) / 2;
		if (arr[mid] > key)
		{
			right = mid - 1;
		}
		else if(arr[mid] < key)
		{
			left = mid + 1;
		}
		else
		{
			return mid;
		}
	}
	printf ("error index!!!");
	return -1;
}

static int32_t DecBinarySearch(uint16_t* arr, uint32_t size, uint16_t key)//查找降序数组
{
	int32_t left = 0;
	int32_t right = size - 1;
	int32_t mid = 0;
	
	while (left <= right)
	{
		mid = left + (right - left) / 2;
		if (arr[mid] > key)
		{
			left = mid + 1;
		}
		else if(arr[mid] < key)
		{
			right = mid - 1;
		}
		else
		{
			return mid;
		}
	}
	printf ("error index!!!");
	return -1;
}
int main(void)
{	
	UartDrvInit();
	
	/*查找9在升序数组g_incDataBuf中的下标*/
	printf("find location of number 9 in the g_incDataBuf\n");
	int32_t incIndex = IncBinarySearch(g_incDataBuf, 6, 9);
	printf ("g_incDataBuf[%d] = %d\n", incIndex, 9);
	incIndex = IncBinarySearch(g_incDataBuf, 6, 88);
	printf ("g_incDataBuf[%d] = %d\n\n", incIndex, 88);
	
	/*查找21在降序数组g_decDataBuf中的下标*/
	printf("find location of number 9 in the g_decDataBuf\n");
	int32_t deccIndex = DecBinarySearch(g_decDataBuf, 6, 21);
	printf ("g_decDataBuf[%d] = %d\n", deccIndex, 21);
	deccIndex = DecBinarySearch(g_decDataBuf, 6, 99);
	printf ("g_decDataBuf[%d] = %d\n", deccIndex, 99);
	while(1)
	{
		
	}
}

 4.2 二分查找法查找接近目标数值的元素的下标

        什么意思,也就是一堆砝码里面,我要找10KG的砝码,如果没有10KG的砝码,那就把最接近10KG质量的砝码找出来,嘿,找到一个9.9KG的砝码!!

        二分查找法查找接近目标的值有下图所示的两种情况,为什么这样我还没有想明白。

        升序查找代码图如下,图片资源来自郭天祥老师的PN学堂,ARM32位单片机教学课。

         降序代码如下:

五、NTC热敏电阻实现测温

5.1 分层设计

        软件架构框图如下:

5.2 软件流程图

 5.3 API接口及数据结构

        根据自己要实现的功能,先把各个.h文件中的API结构函数声明写好,再逐一实现。

sensor_app.h

/**
***********************************************************
* @brief    传感器任务处理函数
* @param
* @return 
***********************************************************
*/
void SensorTask(void);

sensor_drv.h

typedef struct 
{
    float temp;
    uint8_t humi;
}SensorDataInfo_t;//传感器数据类型成员

/**
***********************************************************
* @brief    传感器驱动初始化
* @param
* @return 
***********************************************************
*/
void SensorDrvInit(void);

/**
***********************************************************
* @brief    触发传感器转换数据
* @param
* @return 
***********************************************************
*/
void SensorDrvProc(void);

/**
***********************************************************
* @brief       获取温度数据
* @param    sensorData:结构体指针,带出数值
* @return 
***********************************************************
*/
void GetSensorData(SensorDataInfo_t* sensorData);

hmi_app.h

/**
***********************************************************
* @brief 人机交互任务处理函数
* @param 
* @return 
***********************************************************
*/
void HmiTask(void); 

5.4 热敏电阻模块原理图

         C1是芯片的退藕电容,C2应该是起到滤波作用;

        R1和热敏电阻N1分压,热敏电阻温度越高阻值越低,即采集到的电压越低;

        LM393是电压比较器,同相端INA+大于反向端INA-则OUTA输出高电平,反之输出低电平,调节R2电阻的值可以设置低于多少温度时D2亮;

        D1是电源指示灯,AC输出热敏电阻电压模拟量;

5.5 热敏电阻工作原理

        数据手册中给出了对应温度下的NTC电阻值,温度范围-55~125℃,电阻的精度是百1,温度精度是1℃。

         

        由于STM32的ADC是12位的,直接读取ADC得到的是电压的数字量0~4095,所以,我们可以根据手册中提供的温度对应的电阻值,自己计算把温度对应的热敏电阻电压数字量计算出来,列成表格。

        比如说0摄氏度下NTC的中心电阻值是32.116KΩ,那么计算公式就是:

                                32.116/(10+32.116)*4095 = 3122.685

        这样,ADC采集到一个数字量之后,可以利用二分查找法(注意由于热敏阻值随温度升高而降低,所以得到的电压表格天然降序排列)找到最接近这个数值的温度对应值,从而确定温度。 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机的错误计算(一百九十四)

摘要 用两个大模型计算 其中&#xff0c;一个大模型通过化简&#xff0c;得出正确结果 0&#xff1b;而另外一个在化简过程中出现错误&#xff0c;得出了错误结果。 例1. 计算 下面是一个大模型的推导化简过程。 以上为一个大模型的回答。 下面是另外一个大模型的回复。 点评…

任意文件包含漏洞原理解析及演示

原理 文件包含漏洞&#xff1a;即file inclusion&#xff0c;意思是文件包含&#xff0c;是指当服务器开启allow_url_include选项时&#xff0c;就可以通过PHP的某些特性函数&#xff08;include()&#xff0c;require()和include_once()&#xff0c;requir_once()&#xff09;…

Linux系统之tree命令的基本使用

Linux系统之tree命令的基本使用 一、tree命令介绍二、tree工具安装三、tree命令帮助3.1 查询帮助信息3.2 tree命令帮助解释 四、tree命令的基本使用4.1 直接使用4.2 *限制显示的层级4.3 仅显示目录4.4 不显示隐藏文件4.5 显示文件大小4.6 彩色输出4.7 输出到文件4.8 输出不同格…

微信小程序性能优化、分包

性能优化是任何应用开发中的重要组成部分&#xff0c;尤其是在移动环境中。对于微信小程序而言&#xff0c;随着用户量的增加和应用功能的丰富&#xff0c;性能优化显得尤为关键。良好的性能不仅提升用户体验&#xff0c;还能增加用户留存率和应用的使用频率。我们将探讨如何在…

【星海随笔】删除ceph

cephadm shell ceph osd set noout ceph osd set norecover ceph osd set norebalance ceph osd set nobackfill ceph osd set nodown ceph osd set pause参考文献&#xff1a; https://blog.csdn.net/lyf0327/article/details/90294011 systemctl stop ceph-osd.targetyum re…

Unity游戏环境交互系统

概述 交互功能使用同一个按钮或按钮列表&#xff0c;在不同情况下显示不同的内容&#xff0c;按下执行不同的操作。 按选项个数分类 环境交互系统可分为两种&#xff0c;单选项交互&#xff0c;一般使用射线检测&#xff1b;多选项交互&#xff0c;一般使用范围检测。第一人…

DVWA安装

这里讲的很清楚&#xff0c;如果是win10的话可能会出现端口80占用的情况&#xff0c; 使用管理员身份运行 cmdnet stop http //停止系统http服务sc config http start disabled //禁用服务的自动启动&#xff0c;此处注意等号后面的空格不可少

Oracle考试多少分算通过?

OCP和OCM认证的考试及格分数并不是固定的&#xff0c;而是根据考试的难度和考生的整体表现来确定。对于OCP认证&#xff0c;考生需要全面掌握考试要求的知识和技能&#xff0c;并在考试中表现出色才有可能通过。而对于OCM认证&#xff0c;考生则需要在每个模块中都达到一定的水…

18_HTML5 Web IndexedDB 数据库 --[HTML5 API 学习之旅]

HTML5 Web IndexedDB API 是一种在用户浏览器中存储大量结构化数据的机制&#xff0c;它允许存储和检索键值对&#xff0c;其中键可以是任何有效的JavaScript对象。IndexedDB 主要用于需要复杂查询的数据密集型Web应用。 IndexedDB 的特点&#xff1a; HTML5 Web IndexedDB A…

【Seata】分布式事务问题和理论基础

目录 1.分布式事务问题 1.1本地事务 1.2分布式事务 2.理论基础 2.1CAP定理 2.1.1一致性 2.1.2可用性 2.1.3分区容错 2.1.4矛盾 2.2BASE理论 2.3解决分布式事务的思路 1.分布式事务问题 1.1本地事务 本地事务&#xff0c;也就是传统的单机事务。在传统数据库事务中…

【数据库初阶】Linux中库的基础操作

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; 数据库初阶 &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 亲爱的小伙伴们&#xff0c;大家好&#xff01;在这篇文章中&#xff0c;我们将深入浅出地为大家讲解 Linux…

数据结构与算法Python版 图

文章目录 一、图二、抽象数据类型图三、图的实现-邻接列表法 一、图 表示图的英文单词 painting&#xff1a;用画刷画的油画drawing&#xff1a;用硬笔画的素描/线条画picture&#xff1a;真实形象所反映的画&#xff0c;如照片等&#xff0c;如take pictureimage&#xff1a…

Word表格另起一页解决办法

Word表格另起一页解决办法 表格设置根据内容自动调整&#xff0c;取消指定高度第1步 第2步

Python数据可视化案例——折线图

目录 json介绍&#xff1a; Pyecharts介绍 安装pyecharts包? 构建一个基础的折线图 配置全局配置项 综合案例&#xff1a; 使用工具对数据进行查看?&#xff1a; 数据处理 json介绍&#xff1a; json是一种轻量级的数据交互格式&#xff0c;采用完全独立于编程语言的…

【Seata】seata的部署和集成

一、部署Seata的tc-server 1.下载 首先我们要下载seata-server包&#xff0c;地址在http://seata.io/zh-cn/blog/download.html 当然&#xff0c;课前资料也准备好了&#xff1a; 2.解压 在非中文目录解压缩这个zip包&#xff0c;其目录结构如下&#xff1a; 3.修改配置 修…

链表 之 无头结点【哨兵位】单向非循环链表【单链表】增删改查 等方法

系列文章目录 &#x1f388; &#x1f388; 我的CSDN主页:OTWOL的主页&#xff0c;欢迎&#xff01;&#xff01;&#xff01;&#x1f44b;&#x1f3fc;&#x1f44b;&#x1f3fc; &#x1f389;&#x1f389;我的C语言初阶合集&#xff1a;C语言初阶合集&#xff0c;希望能…

GCP Cloud Architect exam - PASS

备考指南 推荐视频课程 https://www.udemy.com/course/google-cloud-architect-certifications/?couponCodeKEEPLEARNING 推荐题库 https://www.udemy.com/course/gcp-professional-cloud-architect-exam-practice-tests-2024​/?couponCodeKEEPLEARNING 错题集 http…

CCF-GESP 等级考试 2023年12月认证C++二级真题解析

2023年12月真题 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 正确答案&#xff1a;C 考察知识点&#xff1a;变量的定义与使用 解析&#xff1a;变量命名规则&#xff1a;1、只能包括数字、字母和下划线&#xff1b;2、不能以数字开头&#xff1b;3、不能和…

5.学习webpack配置 babel基本配置

babel是一个javascript编译工具&#xff0c;其主要功能是将新版本的JavaScript代码&#xff08;如es6&#xff09;转换为旧版本的代码&#xff08;如es5&#xff09;&#xff0c;以便能够在旧版本的浏览器或环境中运行。一般配合webpack使用。 使用npm i -D babel/core babel/p…

配置搜索无人机

升级ubuntu内核 https://www.bilibili.com/video/BV11X4y1h7qN/?spm_id_from333.337.search-card.all.click 进入四个内核文件并安装 sudo dpkg -i *.deb安装ROS&#xff0c;PX4&#xff0c;XTDrone&#xff0c;QGC https://blog.csdn.net/qq_45493236/article/details/13…