平台和网站有什么区别,苏州十大软件公司招聘,网站建设 投标书,怎么用vs2008做网站KEIL开发 51 单片机程序 算法处理过程中遇到的问题 ...... by 矜辰所致前言
因为产品的更新换代#xff0c; 把所有温湿度传感器都换成 SHT40 #xff0c;替换以前的 SHT21。在 STM32 系列产品上的替换都正常#xff0c;但是在一块 51 内核的无线产品上面#xff0c;数据…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 Temperature0;float Humidity0;Readthstruct sensordata;I2C_Start();I2C_Send_Byte(addr1 | 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(addr1 | 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)(Temperature80)(Humidity0)(Humidity100))//过滤错误数据{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);hum0;tem0;if(sensordata.tem_1004000) sensordata.tem_1004000; //A5-04-01 0~40du else if(sensordata.tem_1000) sensordata.tem_1000;if(sensordata.hum_10010000) sensordata.hum_10010000; //prevent temperature over-/underflowelse if(sensordata.hum_1000) sensordata.hum_1000;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 Temperature0;float Humidity0;time_wait(20);i2c_start();u8Ack i2c_write(0x94); //SHT40_SOFT_RESETi2c_stop();time_wait(10); i2c_start();u8Ack i2c_write(0X441); //I2C_Send_Byte(0x441 | 0);u8Ack i2c_write(0xFD);i2c_stop();time_wait(20); //HAL_Delay(20);i2c_start();u8Ack i2c_write((0X441) 1);if(u8AckI2C_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)(Temperature80)(Humidity0)(Humidity100))//过滤错误数据{aTemperature.value (u16)(Temperature*100);aHumidity.value (u16)(Humidity*100);}else{//数据出错再来一遍i2c_start();u8Ack i2c_write(0x94); //SHT40_SOFT_RESETi2c_stop();// 这里就省略了就是重复一遍}if(aTemperature.value4000) aTemperature.value4000; //prevent temperature over-/underflowelse if(aTemperature.value0) aTemperature.value0;if(aHumidity.value10000) aHumidity.value10000; //prevent temperature over-/underflowelse if(aHumidity.value0) aHumidity.value0;}上面代码其实也不复杂因为本来就很简单。 但是在测试的时候总是会出现温湿度数据异常的情况比如湿度为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 编译器自己的问题导致的。
结语
其实问题虽然是解决了但是我还是不知道是因为什么原因如果有小伙伴知道还望多多指教记得留言哦 。
当然出了这个问题也给我们提了个醒算法最好是封装成函数虽然说函数调用会有一定的开销但是这样做不仅可读性和维护性强 在重用调试等方面也更有优势 最重要的可以避免编译器对我们的程序进行不理想的优化导致的一些错误 。
那么本文就到这里谢谢大家