ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

关于C语言连续强制类型转换,有符号数据位移,以及温度传感器int16有符号数据重组处理问题

关于C语言连续强制类型转换,有符号数据位移,以及温度传感器int16有符号数据重组处理问题 关于C语言连续强制类型转换是从温度传感器中数据处理拓展讨论的一个问题。一. 温度传感器datasheet1. 图一温度结果和限制2. 图二 温度寄存器IIC从这里读取。二. IIC接收数据到温度处理计算代码怎么写问题的重点在于通过IIC传输数据时是一个一个Bytes传输的但温度数据是int16_t如何把两个byts重组处理成int16_t1. 方式一这是来自朋友的开源代码基本写法和逻辑没有太大问题但是比较绕哈。uint8_t TEMP_ARRAY[2];//通过IIC从温度寄存器读取的两个BYTE温度数据。if(TEMP_ARRAY[0]0x80){//高八位最高位是1也即符号位为1说明是负数。zero_flag1;//高八位。tempTEMP_ARRAY[0];//在有符号整数扩展到更大宽度后只要左移位数不超过原本数值位的剩余空间左移不会改变数据也不会影响符号位一旦左移触及符号位符号位可能改变。现在安全。temp8;// 得到完整的数据。tempTEMP_ARRAY[1];// 取反补码处理temp~temp_ct7117;//得到负数绝对值。temp1;high_byte(temp0xff00)8;low_byte(temp0x00ff);//高八位。在15位温度数据模式下是整数。刚好对应整数部分。dot_valuehigh_byte;//低八位是小数部分。0.0078125是分辨率。右移一位是因为现在是15位温度数据的模式原始最低位无意义。dot_value(low_byte1)*0.0078125;//最终结果取负dot_value-dot_value;}else//说明书温度数据是正数或者0。{//高八位。在15位温度数据模式下是整数。刚好对应整数部分。dot_valueTEMP_ARRAY[0];//低八位是小数部分。0.0078125是分辨率。右移一位是因为现在是15位温度数据的模式原始最低位无意义。dot_value((TEMP_ARRAY[1])1)*0.0078125;//用于保留一位小数。TEMP_Display_Vlauedot_value*10;zero_flag0;}其实上面的代码写法冗余且多此一举。不推荐。2. 方式二涉及C语言连续强制类型转换。写法看起来会简单非常多。但是要理解C语言连续强制转换逻辑。uint8_t rxBuf[2];//数组用于存储温度寄存器数据。int32_t raw_temperature;//用于存储寄存器16bit的值这个值是rxBuf合并计算出来的。double temperature;//温度数据。raw_temperature (((int32_t)(int8_t)rxBuf[0]) 8) | rxBuf[1]; temperature raw_temperature * (78125e-7);//计算摄氏度温度保留小数。2.1. 解析接收到的数据被存储在无符号的数组中首先高八位在rxBuf[0]中通过datasheet以及C语言int16_t的数据表示可以知道当rxBuf[0]大于等于0x80时也可以把0x80当作无符号数据看待温度是负数。int8_t的十六进制0x80~0xFF表示的都是负数。int16_t数据的正负数由最高位决定。所以第一个高八位直接先转换成int8_t数据接着再转换成int32_t。2.1.1. 有符号数据的位移操作符号位是否可能变化条件说明备注左移是符号位被移除时如负数左移若原符号位最高位为1负数左移后可能变为0正数右移否算术右移保留原符号位高位补原符号位负数补1正数补0符号位本身不变PS如果位移位数超过数据位数将可能发生未知行为依赖编译器和CPU。接着进行左移8位的操作。左移不是会移除符号位吗注意上面说的是可能因为数据是补码的方式存储并且被转换成了int32_t,一个int8_t的负数被转换成了int32_t它的bit8~bit31会全为1所以这时候左移不会有任何影响在有符号整数扩展到更大宽度后只要左移位数不超过原本数值位的剩余空间左移不会改变数据也不会影响符号位一旦左移触及符号位符号位可能改变。2.2. 连续强制转换逻辑从右到左依次转换编译器并不会直接忽略中间的转换类型。比如如果温度是负数时下面第二行代码将输出错误的数值。⚠️⚠️⚠️⚠️//正确示例。 raw_temperature (((int32_t)(int8_t)rxBuf[0]) 8) | rxBuf[1]; //错误示例。 raw_temperature (((int32_t)rxBuf[0]) 8) | rxBuf[1];ok这个方式比较适合老道的程序员新手要谨慎千万别依葫芦画瓢。3. 方式三类型只是告诉编译器、CPU应该怎么去解释处理这段内存。0x81赋值给有符号和无符号数据是怎么识别的甚至给double会怎么样只要了解数据类型其数据的内存布局或者说表示方法我们就可以利用这个方法直接处理。uint8_t rxBuf[2];//从温度传感器寄存器读取的数值。 int16_t raw_temp 0;//rxBuf温度数据重组重组成int16_t uint16_t raw (rxBuf[0]8)|rxBuf[1];//rxBuf温度数据重组重组成uint16_t raw_temp | raw;//按位操作。 int32_t temp raw_temp * 0.0078125*10;//乘10是把数据放大10倍以保留一位小数。 cout temp endl;重点看第四行按位操作。为什么可以这么做。类型只是告诉编译器、CPU应该怎么去解释这段内存。利用这个规则直接按位操作而不是进行强制类型。数据的整个处理过程都是按位处理和uint8_t左移处理(左移时会被隐式转换成更高的类型所以数据无影响)所以重组前后的数据在内存中没有发生任何变化。如果处理器支持你甚至可以直接raw_temp (*(int16_t * )rxBuf)。处理器需要两个满足①内存对齐②字节序匹配IIC是低地址存高八位也即大端。MCU一般是小段低地址低八位。所以rxBuf[0]存储IIC低八位时则满足条件。。3.1. 方式三的简化效率版本。// IIC大端序传感器 → 小端序处理器。 // 以下是简化版本执行效率最高。 //方式三简化版本1 raw_temp (*(int16_t * )rxBuf) //方式三简化版本2 int16_t raw_temp (int16_t)( (rxBuf[0] 8) | rxBuf[1] );4. 方式四方式三的安全版本。// 使用显式字节序的联合体 int32_t decode_temperature(const uint8_t* buf) { union { int16_t val; struct { uint8_t lsb; //低八位无符号。 int8_t msb; // 高八位有符号存储 }; } raw; raw.lsb buf[1]; raw.msb (int8_t)buf[0]; // 强制符号转换 // 定点运算保留3位小数 return (raw.val * 78125LL) / 10000000; }方式四方案是最优解①字节大小端序的更加安全。②符号位安全无隐患。③可移植性和可读性高。方案三则是方式二的简化版本也可以使用执行效率是最高的。推荐方式四和二既安全又可靠。Disclaimer / 免责声明:本文仅代表作者在撰写和修改时的个人观点不代表当前或未来立场。文中观点和内容未经学术机构或标准组织验证作者不对其准确性、完整性或可靠性作任何保证。请读者仅供参考并自行核实相关信息。本文旨在探索与经验分享因篇幅及作者水平所限难免存在疏漏欢迎批评指正。如有问题或交流建议请联系flourishinggardenoutlook.com。copyright / 版权声明:本文为作者原创。引用或转载需注明“转自或引用自 flourishinggardenoutlook.com”。 未经注明来源的擅自使用、抄袭或传播行为均被禁止。
返回列表