在谈通讯相关编程的时候,我们需要对存储格式和机制加以留意。
1、由来
1980年,Danny Cohen在其著名的论文"On Holy Wars and a Plea for Peace"中为了平息一场关于在消息中字节该以什么样的顺序进行传送的争论而引用了该词。该文中,Cohen非常形象贴切地把支持从一个消息序列的最高位开始传送的那伙人叫做Big-Endians,支持从最低位开始传送的相对应地叫做Little-Endians。此后Endian这个词便随着这篇论文而被广为采用。(摘自百度百科Endian词条)
2、术语
小端 = Little-Endians = Intel Mode:高字节保存在高地址中,低字节保存在低地址中。按我们书写的方向,低字节在右,高字节在左。
大端 = Big-Endians = Motorola Mode:高字节保存在低地址中,低字节保存在高地址中。按我们书写的方向,高字节在右,低字节在左。
3、说明
在一个8位的字节中,二进制数以高位在左,低位在右排列,如同我们书写十进制数的格式。
在表达一个多字节的整数时,就出现大端和小端的分歧,这主要与CPU的架构有关。Intel系列和ARM系列均采用小端,KEIL C51系列均采用大端。
以CAN标准的Data数据排列为例:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 |
将上述数据以流的方式读入计算机内存的字节数组对应:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 |
不过,需要说明的这只是CAN的一帧数据的Data部分,包含了多个信号数据,这些信号数据的起始位,位长和格式(采用小端或大端)都可以单独定义。
4、应用协议
在进行计算机通讯编程时,行走于网络中的数据来自不同的设备,根据通讯协议,我们需要对其进行转换。
比较多的工业传输协议,例如串口协议,CANOPEN和MODBUS协议等。广泛用于汽车,传感器和控制的数据传输方面。
在CAN数据结构中,CANID是固定的小端结构,而8个字节的Data部分则是自定义,协议记录在BDC文件中。
5、转换方法
以下为Delphi代码,但是使用的函数均为Windows的API函数。
方法一,使用Windows.unit的makeword和makelong函数
type Tbyte4: array [0..3] of byte; function Convert(Value: longword): longword; implementation function Convert(Value: longword): longword; var myData: ^Tbyte4; begin myData1 := @Value; Result := makelong(makeword(myData^[3], myData^[2]), makeword(myData^[1], myData^[0])); end;
方法二,使用Winsock.unit的htons和htonl函数,这是最经典的代码
function Convert(Value: longword): longword; begin Result := htonl(Value); end;
function Convert(Value: word): word; begin Result := htons(Value); end;
这里,htons()函数是将一个无符号的短整形数(16位)值转换成网络字节顺序,其实就是将小端转成大端。同理htonl()用于无符号长整形(32位)。
需要注意的是,htons()用于将主机短整数转换成网络字节顺序,反过来,则使用ntohs()函数。htonl()则对应ntohl()。
方法三,了解上述原理后,不妨自己用指针写个更加高效的程序。