当前位置: 首页 > news >正文

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

关于C语言连续强制类型转换,是从温度传感器中数据处理拓展讨论的一个问题。

一. 温度传感器datasheet

1. 图一温度结果和限制

2. 图二 温度寄存器,IIC从这里读取。

二. IIC接收数据到温度处理计算代码怎么写?

问题的重点在于通过IIC传输数据时,是一个一个Bytes传输的,但温度数据是int16_t,如何把两个byts重组处理成int16_t?

1. 方式一

这是来自朋友的开源代码,基本写法和逻辑没有太大问题,但是比较绕哈。
uint8_t TEMP_ARRAY[2];//通过IIC从温度寄存器读取的两个BYTE温度数据。

    if(TEMP_ARRAY[0]&0x80)

        {

        zero_flag=1;

        temp=TEMP_ARRAY[0];

        temp<<=8;

        temp+=TEMP_ARRAY[1];

        temp=~temp_ct7117;

        temp+=1;

        high_byte=(temp&0xff00)>>8;

        low_byte=(temp&0x00ff);

        dot_value=high_byte;

        dot_value+=(low_byte>1)*0.0078125;

        dot_value=-dot_value;

        }

    else

        {

       

        dot_value=TEMP_ARRAY[0];

        dot_value+=((TEMP_ARRAY[1])>>1)*0.0078125;

        TEMP_Display_Vlaue=dot_value*10;

        zero_flag=0;

        }

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;
}

这个方案是最优解,①字节大小端序的更加安全。②符号位安全无隐患。③可移植性和可读性高。

方案三的简化版本也可以使用,执行效率是最高的。

http://www.dtcms.com/a/317362.html

相关文章:

  • C++之vector类的代码及其逻辑详解 (下)
  • SELinux加固Linux安全2
  • 【数据结构初阶】--排序(四):归并排序
  • 软考软件设计师考点总结
  • [linux] Linux系统中断机制详解及用户空间中断使用方法
  • Linux部署tp5.1,nginx服务器不管访问那个方法,一直访问index/index问题解决方法
  • 阶段二:1-信息技术概述
  • helm下载tiller失败
  • 【数字图像处理系列笔记】Ch04:灰度变换与空间域图像增强(2)
  • 蚊子咬人问题何时休:深度学习引领智能灭蚊新时代
  • qt窗口--02
  • 无人设备遥控器之跳频技术篇
  • 鹧鸪云:光伏电站的“智慧中枢”,精准调控逆变器
  • 使用 Helm 在 Kubernetes 中安装 Milvus
  • 企业知识库:RAG技术实现流程总览(一)
  • 【motion】标签体系设计与检索 1:HumanML3D 和 KIT Motion-Language(KITML)
  • 河南萌新联赛2025第(四)场【补题】
  • 键帽(dp)
  • 分布式光伏气象站:安装与维护
  • 【运维进阶】DHCP服务配置和DNS域名解析
  • 最长公共子序列-动态规划
  • 如何在linux中使用Makefile构建一个C++工程?
  • 中科米堆CASAIM机加工件来料尺寸自动化三维测量方案
  • 第十八天:C++进制之间的转换
  • 机器学习算法篇(六)贝叶斯算法
  • 基于php的个人健康管理系统设计与实现/vue/php开发
  • Leetcode题解:739每日温度,用单调栈解决问题!
  • LeetCode 91~110题解
  • Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备故障预测与智能运维中的应用(384)
  • 自动驾驶系统的网络安全风险分析