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

news2024/9/22 21:22:34
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 只需要一个 0xFD
        I2C_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-/underflow
		else 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_RESET
	i2c_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_RESET
		i2c_stop();
		// 这里就省略了,就是重复一遍
    }
	
	if(aTemperature.value>4000) aTemperature.value=4000;              //prevent temperature over-/underflow
	else if(aTemperature.value<0) aTemperature.value=0;
	if(aHumidity.value>10000) aHumidity.value=10000;              //prevent temperature over-/underflow
	else 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 result

  u16RH &= ~0x0003;          // clear bits [1..0] (status bits)
  //-- calculate relative humidity [%RH] --

  humidityRH = (sint16)(-600 + (12500*(sint32)u16RH)/65536 ); // RH = -6 + 125 * SRH/2^16
  return humidityRH;                                          // Return RH*100
}

// -------------------------------------------------------------------
sint16 sht21_calcTemperature(uint16 u16T)
{
  sint16 temperature;            // variable for result

  u16T &= ~0x0003;           // clear bits [1..0] (status bits)

  //-- calculate temperature [癈] --
  temperature= (sint16)(-4685 + (17572*(sint32)u16T)/65536); //T = -46.85 + 175.72 * ST/2^16
  return 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 编译器自己的问题导致的。

结语

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

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

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

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

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

相关文章

两个月冲刺软考——逻辑地址与物理地址的转换(例题+讲解);文件类型的考点

1.已知计算机系统页面大小和进程的逻辑地址&#xff0c;根据页面变换表(页号-物理块号)&#xff0c;求变换后的物理地址。 首先介绍几个公式&#xff1a; 逻辑地址 页号 页内地址 (默认为32机位) 物理地址 物理块号 物理地址的页内地址 其中&#xff1a;页内地址 物理地址…

Kubernetes--服务发布(Service、Ingress)

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 出自B站博主教程笔记&#xff1a; 完整版Kubernetes&#xff08;K8S&#xff09;全套入门微服务实战项目&#xff0c;带你一站式深入掌握K8S核心能…

算法_栈专题---持续更新

文章目录 前言删除字符中的所有相邻重复项题目要求题目解析代码如下 比较含退格的字符串题目要求题目解析代码如下 基本计算器II题目要求题目解析 字符串解码题目要求题目解析代码如下 验证栈序列题目要求题目解析代码如下 前言 本文将会向你介绍有关栈的相关题目&#xff1a;…

matter中的Fabric(网络结构)

什么是Fabric&#xff1f; Fabric可以被理解为一组相互信任的设备和控制器&#xff0c;它们共享一个共同的信任域。这意味着在同一个Fabric中的设备和控制器之间可以进行安全的通信&#xff0c;而无需额外的身份验证或安全检查。每个Fabric有一个唯一的标识&#xff0c;确保Fab…

迁移替换AD域时,有几个关键点需要注意

在当今的数字化时代&#xff0c;企业对于身份管理和访问控制的需求日益增长。然而&#xff0c;传统的AD域控方案在面对国产化替代和业务上云的趋势时&#xff0c;逐渐显露出一些局限性。宁盾国产化身份域管作为一种迁移替换AD域控的创新解决方案&#xff0c;正逐渐崭露头角&…

文心一言 VS 讯飞星火 VS chatgpt (341)-- 算法导论23.1 10题

十、给定图 G G G和 G G G的一棵最小生成树 T T T&#xff0c;假设减小了 T T T中一条边的权重。证明&#xff1a; T T T仍然是 G G G的一棵最小生成树。更形式化地&#xff0c;设 T T T为 G G G的一棵最小生成树&#xff0c; G G G的边权重由权重函数 w w w给出。选择一条边 (…

职称评审中,论文发表要求?

无论是医生、教师或其他等职业&#xff0c;职称评审无疑是一个非常重要的环节。而职称评审中的论文发表则是评定我们专业能力的重要一环&#xff0c;可如何才能让自己辛苦撰写的的论文被发表&#xff0c;达到论文发表都有哪些要求呢&#xff1f; 一、选题要新颖 编辑和审稿人…

VMware的三种网络模式及应用场景

在VMware中&#xff0c;虚拟机网络连接的方式主要有三种模式&#xff1a;桥接模式&#xff08;Bridged Mode&#xff09;、NAT模式&#xff08;Network Address Translation Mode&#xff09;、仅主机模式&#xff08;Host-Only Mode&#xff09;。每种模式都有其独特的用途和配…

SSM+Ajax实现广告系统

文章目录 1.案例需求2.编程思路3.案例源码(这里只给出新增部分的Handler和ajax部分&#xff0c;需要详情的可以私信我)4.小结 1.案例需求 使用SSMAjax实现广告系统&#xff0c;包括登录、查询所有、搜索、新增、删除、修改等功能&#xff0c;具体实现的效果图如下&#xff1a;…

『功能项目』状态模式转换场景【30】

本章项目成果展示 打开上一篇29Unity本地数据库读取进入游戏的项目&#xff0c; 本章要做的事情是通过状态者模式转换场景&#xff0c;在进入账号登陆界面前闪烁显示Logo 首先创建一个新的场景命名为StartUI 修改游戏场景名字 重命名为FightGame01 首先创建一个脚本文件夹&…

gazebo可能打不开的问题

如果经常遇到gazebo只能断网才能运行的时候&#xff0c;主要就是因为无法联网访问gazebo的在线模型库&#xff0c;此时我们一般无法在联网的情况下打开gazebo。 这个时候就直接将下载好的模型先放到~/.gazebo/models/文件夹下面即可&#xff1a; https://github.com/osrf/gazeb…

RTOS Sensor Framework对比

1.背景 传感器(Sensor)是物联网(IOT)的重要组成部分&#xff0c;用于感知和采集环境中的各种数据&#xff0c;大部分智能硬件都需要。 为使传感器能正常⼯作&#xff0c;驱动开发者需要开发不同的驱动程序&#xff0c;驱动程序要实现芯片寄存器\IO设置&#xff0c;又要响应使用…

搭建 canal 监控mysql数据到 elasticsearch 中(本机到远端sql)

搭建 canal 监控mysql数据到 elasticsearch 中&#xff08;本机到远端sql&#xff09; 需求: 要将 MySQL 数据库 info 中的 notice 和 result 表的增、删、改操作同步到 Elasticsearch 的 notice 和 result 索引&#xff0c;您需要正确配置 MySQL、Canal 、Canal Adapter 、 …

3--Web前端开发-前端工程化,vue项目

目录 端口配置 element 快速入门 table表格组件 分页组件 Dialog对话框组件 表单组件 端口配置 在vue.config.js中更改 源代码为 const { defineConfig } require(vue/cli-service) module.exports defineConfig({transpileDependencies: true })更改为 const { def…

Linux——redis主从复制、哨兵模式

一、redis 的安全加固&#xff1a; 对redis数据库访问的角度 auth // 验证登录redis 数据库的用户acl // 设置redis用户的权限将配置完成的ACL策略写入配置文件 config rewrite //目前redis生效的配置全部写入到默认配置文件的尾部写入到acl文件中&#xff0c;在加载配置文件时…

《论软件设计模式及其应用》通关范文,软考高级系统架构设计师

论文真题 设计模式(Design Pattern)是一套被反复使用的代码设计经验总结,代表了软件开发人员在软件开发过程中面临的一般问题的解决方案和最佳实践。使用设计模式的目的是提高代码的可重用性,让代码更容易被他人理解,并保证代码可靠性。现有的设计模式已经在前人的系统中…

每日一练:和为K的子数组

一、题目要求 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2示例 2&#xff1a; 输入&#xff1a;n…

python深度学习:从注意力机制到生成模型,全面解析现代AI技术

近年来&#xff0c;伴随着以卷积神经网络&#xff08;CNN&#xff09;为代表的深度学习的快速发展&#xff0c;人工智能迈入了第三次发展浪潮&#xff0c;AI技术在各个领域中的应用越来越广泛。注意力机制、Transformer模型&#xff08;BERT、GPT-1/2/3/3.5/4、DETR、ViT、Swin…

OpenCV结构分析与形状描述符(10)检测并提取轮廓函数findContours()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在二值图像中查找轮廓。 该函数使用算法 253从二值图像中检索轮廓。轮廓是有用的工具&#xff0c;可用于形状分析和对象检测与识别。参见 OpenC…

SDN架构详解

目录 1&#xff09;经典的IP网络-分布式网络 2&#xff09;经典网络面临的问题 3&#xff09;SDN起源 4&#xff09;OpenFlow基本概念 5&#xff09;Flow Table简介 6&#xff09;SDN的网络架构 7&#xff09;华为SDN网络架构 8&#xff09;传统网络 vs SDN 9&#xf…