zhuan:串口成帧协议

串口成帧协议
串口接收中的问题

在电子系统中,最简单、最广泛的通信方式无疑是串口了,几乎所有与模块相关的产品,差不多都有串口的操作方式,如串口蓝牙模块、串口WIFI模块、串口ZigBee模块、串口语音模块等。不仅是这样,在嵌入式开发中,很多时侯调试离不开串口,固件升级离不开串口,可见搞电子的人如果不能很好的操作串口,那就真是说不过去了。
串口的应用场合非常多,但是有一个残酷的现实摆在我们面前,那就是通信问题。我们知道串口发送数据时是面向字节的,接收方接收数据时也是面向字节的,而在实际应用中,收发数据往往都是面向帧的,这样往往会对接收方接收数据造成一定的困难。举个例子吧,一个单片机系统通过串口往PC机发送一帧100字节数据,但是PC机在调用系统API来接收数据时,往往不能一次性接收完100字节,可能第一次接收5字节,第二接收10字节……,虽然最后都能收到100字节。如果单片机发送两帧数据,并且这两帧数据之间的时间间隔非常短,那PC就无法区别出两帧数据的边界了。初入行的人或许会想到一些诸如增加标志位、判断时间等方法来解决这个问题,但不够严谨的。所以我们有必要设计一个相对可靠一点的串口成帧的方法来解决接收难题。
也许搞技术的人,或多或少都听说过网络通信的OSI模型,标准的OSI模型有7层之多,像串口这种简单的通信,并不需要这么多层的实现,但是至少要实现链路层,才能解决数据组帧问题。很多人之所以在接收串口数据时出现困难就是因为从物理层接收数据之后,直接提交给应用层,这样应用层必然是面向字节的,通信方式是如下形式:
物理层 –>> 应用层
在这里,我们要做的是在物理层与应用层之间插入一个链路层,也即OSI模型的第二层,本文要描述的就是这个链路层的协议,通信方式会变成如下形式:
物理层 –>> 链路层 –>> 应用层
由于大部分人都习惯于直接操作串口,所以链路层只需要实现最基本的组帧功能即可,没有必要搞的那么复杂。这里链路层的功能负责将物理层的字节数据,按照一定规则封装成帧,然后提交给应用层,这样应用层接收的便是完整的一帧数据,而不是零零散散的字节数据。
那么链路层使用什么样的方法来进行封装会比较好呢,这里可以效仿网络通信协议中一些知名协议的做法,比如SLIP、PPP协议等,这些协议的链路层都是通过转义的方法来组帧。因此,我们的串口成帧协议也是使用转义的方法,但是要说明一点,这里只提供一种组帧的方法,任何可靠性的问题如检测、纠错等必须由上层来实现。

串口成帧链路层组帧规则如下:

1、首先我们要定义三个特殊字符:帧起始标志SOF,用0x7d表示;帧结束标志EOF,用0x7e表示、转义标志ESC表示0x7f。数据帧封装格式如下表所示:

串口链路层帧格式
域名称 描述
SOF 帧起始标志,固定为0x7d,SOF是Start of Frame的英文编写
DATA 要传输的数据,串口数据建议在512字节以内为宜。
EOF 帧结束标志,固定为0x7e,EOF是End of Frame的英文缩写

2、由数据帧的封装格式中可以看出,0x7d和0x7e是帧的起始和末尾标志,那要是DATA中要传输0x7d和0x7e怎么办呢,这就需要进行转义了,我们定义了一个转义字符ESC,用0x7f 表示。当发送数据时,需要将数据域中的0x7d用0x7f 、0x00两字符替代、这就是所谓的转义,同样的,发送方将数据中的0x7e转义为0x7f、0x01,将0x7f转义成0x7f、0x02。对于接收方可以按照这样的规则对数据进行还原。转义字符表总结如下:
0x7d –>> 0x7f、0x00
0x7e –>> 0x7f、0x01
0x7f –>> 0x7f、0x02

以上两点就是链路层的规则,规则虽然简单,但是对于组帧却非常有用,而且不会产生歧义。

样例一:
应用层发送数据:0x55 0x66 0x88 0x99
数据经过链路层之后,实际发送数据:0x7d 0x55 0x66 0x88 0x99 0x7e
从这里可以看出,接收方根据起始标志0x7d和末尾标志0x7e很容易提取出实际数据0x55 0x66 0x88 0x99
样例二
应用层发送数据:0x55 0x7d 0x88 0x99
数据链路层发送数据:0x7d 0x55 0x7f 0x00 0x88 0x99 0x7e
这里可以看出,应用层发送数据中是包含了起始标志0x7d,但是在链路层就被转义成了0x7f、0x00,接收方连续收到0x7f、0x00就能还原为0x7d。
样例三
应用层发送数据:0x55 0x7f 0x00 0x99
数据链路层发送数据:0x7d 0x55 0x7f 0x02 0x00 0x99 0x7e

从上面三个例子中可以看出,发送的数据帧的数据域中是不可能出现0x7d和0x7e这两个字符的,因为链路层已经对它进行了转义,所以接收方一旦接收到0x7d就可以断定这是一帧的开始,接收到0x7e就表示一帧的结束,而0x7d与0x7e之间的数据就是发送方的数据内容,接收方需要在链路层将被转义的字符还原,然后再提交给应用层。
这么一来,就解决了数据成帧的问题。串口之间的通讯由面向字节通讯变成了面向帧的通讯,也就是说应用层数据不再是以字节为单位,而是以帧为单位。好比单片机发送一帧100字节的数据给PC,PC应用程序接收的便是一帧100字节的数据了。

<think>好的,我现在要解决用户的问题:“java Long zhuan Interger”。看起来用户可能想了解如何将Java中的Long类型转换为Integer类型,但可能存在拼写错误。首先,我需要确认用户的需求是否正确,即是否真的是将Long转为Integer,而不是其他类型,比如int。 接下来,我应该回顾Java中基本数据类型和包装类的转换方法。Long和Integer都是包装类,分别对应基本类型long和int。由于它们都是Number的子类,但直接转换可能会遇到问题,因为它们之间没有继承关系,所以需要显式转换。 可能的转换步骤包括:首先从Long对象中获取long值,然后将其转换为int,再使用Integer的valueOf方法或者直接构造Integer对象。但需要注意,如果Long的值超出了Integer的范围(-2^31到2^31-1),转换会导致数据丢失或错误,因此需要检查范围。 另外,还要考虑自动拆箱和装箱的问题,以及Java 8之后的一些方法,比如Math.toIntExact,这个方法在转换时如果超出范围会抛出异常,增加安全性。 还需要提醒用户,从Java 9开始,Integer的构造函数被标记为过时,推荐使用静态工厂方法valueOf,以提高性能,因为会利用缓存。 可能的示例代码包括:使用intValue()方法,或者显式转换,以及处理可能的异常情况。同时,应该比较不同方法的优缺点,比如直接转换和检查范围的方法。 最后,总结步骤和注意事项,确保用户理解转换过程中可能的风险和最佳实践。</think>在Java中,将`Long`类型转换为`Integer`类型需要分步处理。以下是详细的步骤和注意事项: --- ### 一、基本转换步骤 1. **获取`Long`的基本类型值** 通过`longValue()`方法提取`Long`对象的原始`long`类型值: ```java Long longObj = 12345L; long primitiveLong = longObj.longValue(); ``` 2. **将`long`强制转换为`int`** 通过强制类型转换将`long`转为`int`: ```java int primitiveInt = (int) primitiveLong; ``` 3. **将`int`封装为`Integer`** 使用`Integer.valueOf()`或自动装箱生`Integer`对象: ```java Integer integerObj = Integer.valueOf(primitiveInt); // 或直接自动装箱 Integer integerObj2 = primitiveInt; ``` --- ### 二、完整代码示例 ```java Long longValue = 12345L; // 方法1:分步显式转换 int intValue = longValue.intValue(); // 直接调用intValue()方法 Integer integerValue1 = Integer.valueOf(intValue); // 方法2:一步完自动拆箱+强制转换) Integer integerValue2 = (int) (long) longValue; // 方法3:使用Java 8的Math.toIntExact()(推荐) try { Integer integerValue3 = Math.toIntExact(longValue); } catch (ArithmeticException e) { System.out.println("超出Integer范围!"); } ``` --- ### 三、关键注意事项 1. **范围检查** `Integer`的范围是$-2^{31}$到$2^{31}-1$(即`-2147483648`到`2147483647`)。 如果`Long`值超出此范围,强制转换会导致数据错误: ```java Long largeLong = 3000000000L; // 超出Integer最大值 int badValue = (int) largeLong.longValue(); // 结果为-1294967296(错误) ``` 2. **推荐使用`Math.toIntExact()`** Java 8新增的`Math.toIntExact()`方法会在转换时检查范围,若溢出则抛出`ArithmeticException`: ```java try { int safeValue = Math.toIntExact(largeLong); } catch (ArithmeticException e) { System.out.println("转换失败:" + e.getMessage()); } ``` 3. **避免直接调用构造函数** Java 9后,`new Integer(int)`已过时,建议使用`Integer.valueOf(int)`,该方法会复用缓存对象(范围`-128`到`127`),提高性能。 --- ### 四、总结 - **安全转换流程**: $$ \text{Long对象} \xrightarrow{\text{.longValue()}} \text{long} \xrightarrow{\text{强制转换}} \text{int} \xrightarrow{\text{Integer.valueOf()}} \text{Integer对象} $$ - **优先选择范围检查方法**(如`Math.toIntExact()`),避免数据丢失。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值