KEIL中编译51程序 算法计算异常的疑问

2024-09-06 20:12

本文主要是介绍KEIL中编译51程序 算法计算异常的疑问,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

KEIL开发 51 单片机程序 算法处理过程中遇到的问题  ...... by 矜辰所致

前言

因为产品的更新换代, 把所有温湿度传感器都换成 SHT40 ,替换以前的 SHT21。在 STM32 系列产品上的替换都正常,但是在一块 51 内核的无线产品上面,数据莫名其妙的总会遇到异常的情况,弯弯绕绕了好一阵子,最后才发现是程序在执行一个不算复杂的算法的时候会出错。

那么本文的目的就是说明这个问题,以及如何解决这个问题,同时也想向大家请教这个问题出现的原因。 因为到最后,没有花时间去过多的研究到底是怎么出的问题。

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

目录

  • 前言
  • 一、 SHT40 温湿度读取
  • 二、51 上的数据异常
    • 2.1 程序移植
    • 2.2 问题分析
    • 2.3 问题解决
  • 结语

一、 SHT40 温湿度读取

本来 SHT40 其实特别简单, 简单的 I2C 通讯,简单的计算公式,在 SHT40 数据手册上面,只要看一个地方基本就能把 SHT40 用起来了,如下图:

在这里插入图片描述

硬件电路,和算法手册都给出来了,实际上在使用 STM32 的时候确实是真简单就可以正确的读取到数据,代码如下:

/*
SHT40
地址和 SHT30 一样 0x44
*/
Readthstruct SHT40_read_result(u8 addr)
{u16 tem,hum;u16 buff[6] = {0};float Temperature=0;float Humidity=0;Readthstruct sensordata;I2C_Start();I2C_Send_Byte(addr<<1 | write);//写7位I2C设备地址加0作为写取位,1为读取位I2C_Wait_Ack();I2C_Send_Byte(0xFD);    //  SHT 40 只需要一个 0xFDI2C_Wait_Ack();// I2C_Send_Byte(0x06);// I2C_Wait_Ack();I2C_Stop();HAL_Delay(20);I2C_Start();I2C_Send_Byte(addr<<1 | read);//写7位I2C设备地址加0作为写取位,1为读取位if(I2C_Wait_Ack()==0){buff[0]=I2C_Read_Byte(1);buff[1]=I2C_Read_Byte(1);        buff[2]=I2C_Read_Byte(1);        buff[3]=I2C_Read_Byte(1);buff[4]=I2C_Read_Byte(1);buff[5]=I2C_Read_Byte(0);I2C_Stop();}tem = ((buff[0]<<8) | buff[1]);//温度拼接hum = ((buff[3]<<8) | buff[4]);//湿度拼接/*转换实际温度*/Temperature= (175.0*(float)tem/65535.0-45.0) ;// T = -45 + 175 * tem / (2^16-1)/**humi = (1.0 * 125 * (readData[3] * 256 + readData[4])) / 65535.0 - 6.0;rh_pRH = -6 + 125 * rh_ticks/65535*/Humidity= (125.0*(float)hum/65535.0-6.0); // sprintf(humiture_buff1,"%6.2f*C %6.2f%%",Temperature,Humidity);//111.01*C 100.01%(保留2位小数)// printf("温湿度:%s\n",humiture_buff1);if((Temperature>=-20)&&(Temperature<=80)&&(Humidity>=0)&&(Humidity<=100))//过滤错误数据{sensordata.tem_100 = (u16)(Temperature*100);sensordata.hum_100 = (u16)(Humidity*100);}else{//重新通讯读取一遍}// printf("温度100倍:%d\n湿度100倍:%d\n",sensordata.tem_100,sensordata.hum_100);hum=0;tem=0;if(sensordata.tem_100>4000) sensordata.tem_100=4000;              //A5-04-01 0~40du else if(sensordata.tem_100<0) sensordata.tem_100=0;if(sensordata.hum_100>10000) sensordata.hum_100=10000;              //prevent temperature over-/underflowelse if(sensordata.hum_100<0) sensordata.hum_100=0;return sensordata;
}

上面的代码是为了得到 温湿度实际数据的100倍的结果, 上面时候因为测试,中途需要打印浮点数,所以中途按照浮点数计算了结果,
其实可以在Temperature= (175.0*(float)tem/65535.0-45.0) 这地方直接得到 100 倍的数值,
而且还可以省去浮点数的计算。

反正到这里一切都是正常的,于情于理挺简单的应用当然不会有问题。

二、51 上的数据异常

2.1 程序移植

在 STM32 上替换很顺利,那么还有一款 51 内核的无线芯片也需要替换,那么其实也就是做了简单的移植,通讯逻辑基本和上面一样:

void SHT40THMeasure()
{sint16 tem,hum;uint16 buff[6] = {0};float Temperature=0;float Humidity=0;time_wait(20);i2c_start();u8Ack = i2c_write(0x94); //SHT40_SOFT_RESETi2c_stop();time_wait(10);	i2c_start();u8Ack = i2c_write(0X44<<1); //I2C_Send_Byte(0x44<<1 | 0);u8Ack = i2c_write(0xFD);i2c_stop();time_wait(20); //HAL_Delay(20);i2c_start();u8Ack = i2c_write((0X44<<1) + 1);if(u8Ack==I2C_ACK){buff[0]= i2c_read(I2C_ACK);	buff[1]= i2c_read(I2C_ACK);	buff[2]= i2c_read(I2C_ACK);	buff[3]= i2c_read(I2C_ACK);buff[4]= i2c_read(I2C_ACK);	buff[5]= i2c_read(I2C_NACK);i2c_stop();} tem = ((buff[0]<<8) | buff[1]);//温度拼接hum = ((buff[3]<<8) | buff[4]);//湿度拼接Temperature= (175.0*(float)tem/65535.0-45.0) ;// T = -45 + 175 * tem / (2^16-1)Humidity= (125.0*(float)hum/65535.0-6.0); if((Temperature>=-20)&&(Temperature<=80)&&(Humidity>=0)&&(Humidity<=100))//过滤错误数据{aTemperature.value = (u16)(Temperature*100);aHumidity.value = (u16)(Humidity*100);}else{//数据出错,再来一遍i2c_start();u8Ack = i2c_write(0x94); //SHT40_SOFT_RESETi2c_stop();// 这里就省略了,就是重复一遍}if(aTemperature.value>4000) aTemperature.value=4000;              //prevent temperature over-/underflowelse if(aTemperature.value<0) aTemperature.value=0;if(aHumidity.value>10000) aHumidity.value=10000;              //prevent temperature over-/underflowelse if(aHumidity.value<0) aHumidity.value=0;}

上面代码其实也不复杂,因为本来就很简单。 但是在测试的时候,总是会出现温湿度数据异常的情况,比如湿度为0 ,温度最最大值等等 。

2.2 问题分析

在最开始的时候,连硬件问题,然后还有因为低功耗需要给传感器断电问题都统统想到过,都先给一一排除了。

最终还是回到程序上来,也尝试过校验,软件复位,多次读取等等方式,发现依然存在问题。

各种可能的不可能的问题估计都想过,最后考虑了一下以前是 32 位的 ARM 内核,现在是 8 位 51 内核,是不是算法有点问题,然后看了下代码,主要集中在算法那两行代码 :

	Temperature= (175.0*(float)tem/65535.0-45.0) ;// T = -45 + 175 * tem / (2^16-1)Humidity= (125.0*(float)hum/65535.0-6.0); 

想着在 51 单片机上进行浮点数运算比较 “困难” 的,如果可以尽量减少浮点数的运算 。

所以为了防止因为浮点数计算导致的问题,直接把浮点数运算也去掉,因为以前 SHT21 在 51 上数据也是正常的,所以参考了一下 以前 SHT21 的算法书写:

sint16 sht21_calcRH(uint16 u16RH)
{sint16 humidityRH;              // variable for resultu16RH &= ~0x0003;          // clear bits [1..0] (status bits)//-- calculate relative humidity [%RH] --humidityRH = (sint16)(-600 + (12500*(sint32)u16RH)/65536 ); // RH = -6 + 125 * SRH/2^16return humidityRH;                                          // Return RH*100
}// -------------------------------------------------------------------
sint16 sht21_calcTemperature(uint16 u16T)
{sint16 temperature;            // variable for resultu16T &= ~0x0003;           // clear bits [1..0] (status bits)//-- calculate temperature [癈] --temperature= (sint16)(-4685 + (17572*(sint32)u16T)/65536); //T = -46.85 + 175.72 * ST/2^16return temperature;                                        //return T*100
}

把算法变成如下:

tem = ((buff[0]<<8) | buff[1]);//温度拼接hum = ((buff[3]<<8) | buff[4]);//湿度拼接aTemperature.value =   (sint16)	(17500*(sint32)tem/65535 - 4500) ;aHumidity.value = (sint16) (12500*(sint32)hum/65535 - 600); ...

上面已经没有了浮点数运算,数据类型也注意到了,感觉应该没什么问题。

但是实际测试下来,结果还是和以前一样。

期间还测试发现当湿度大于接近 40% 的时候,湿度 aHumidity.value 就会变成 0 。

为了排除不是传感器通讯的问题,期间还读取过 tem 和 hum 的值观察,然后自己通过算法计算都能得到准确的数据。

然后自己给一个合理的 tem hum 的值,如下图:

tem = ((buff[0]<<8) | buff[1]);//温度拼接hum = ((buff[3]<<8) | buff[4]);//湿度拼接tem = 0x 78;//写文章的测试例子,当时是多少来着忘记了tem = 0x 78;aTemperature.value =   (sint16)	(17500*(sint32)tem/65535 - 4500) ;aHumidity.value = (sint16) (12500*(sint32)hum/65535 - 600); ...

确实发现只要 aHumidity.value 正确计算结果在接近或者大于 4000 的时候,这个算是结果在程序中就会变成 0 。

对比了一下算法的书写,是在看不出哪里会溢出什么的。

再反复看了以前 SHT21 的算法,明明也是这样的算法,怎么就会不行了呢?

在这里插入图片描述

2.3 问题解决

最后实在是觉得还是不应该出问题,但是明明以前 SHT21 也是在同样的环境,同样的平台下,就是这么写的算式,不应该啊。

最后才看来看去,再看了下以前 SHT21 怎么处理的:

在这里插入图片描述

在文章上面其实我也给出了 SHT21 的这两个计算结果的函数,在以前 I2C 通讯完毕,是通过调用了计算结果的函数得到的数值没有问题。

于是乎,我把 SHT40 也尝试封装成了同样的函数,算法直接复制进去,如下图:

在这里插入图片描述

然后忽然就发现…… 好了…… 数据怎么样都正常了……

这真的是沙比问题,我确实有点懵了,反正最后自己也不知道是什么根本原因。

网上也没有明确的此类问题的,到处查阅了一些资料,有的说是因为编译器的代码优化问题,或者说是因为 51 编译器自己的问题导致的。

结语

其实问题虽然是解决了,但是我还是不知道是因为什么原因,如果有小伙伴知道,还望多多指教,记得留言哦 。

当然出了这个问题,也给我们提了个醒,算法最好是封装成函数,虽然说,函数调用会有一定的开销,但是这样做不仅可读性和维护性强, 在重用,调试等方面也更有优势, 最重要的可以避免编译器对我们的程序进行不理想的优化导致的一些错误 。

那么本文就到这里,谢谢大家!

这篇关于KEIL中编译51程序 算法计算异常的疑问的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1143003

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费