1. 为什么会有大端模式和小端模式

在计算机中,我们知道数据是按照字节存储的,如果数据都是单字节存储,就不涉及存储顺序的问题。但是,大多数情况下,数据不是按照单字节的方式存储的,例如会有类似于int,double等数据类型,这就涉及到存储顺序的问题了,于是也就出现了两种存储方:大端模式(big endian)和小端模式(little endian)

我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。

2. 大端模式与小端模式的存储方式

  • 大端模式(顺序):高字节在前(高字节存放在地址的低端),低字节在后
  • 小端模式:高字节在后,低字节在前
  • 大端模式的优点:符号位的判定固定为第一个字节,容易判断正负
  • 小端模式的优点:强制转换数据不需要调整字节内容

2.1 举例说明一

以0x1234为例进行说明。

地址

0x4000(低地址)

0x4001(高地址)

大端存储

0x12

0x34

小端存储

0x34

0x12

2.2 举例说明二

下面来看看数组在大端模式和小端模式的存储区别,以unsigned int value = 0x12345678进行说明,假设有unsigned char buf[4]的一个数组。

  • 大端模式

buf[3] (0x78) – 高地址,存放低位(字节)
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) – 低地址,存放高位(字节)

低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78

  • 小端模式

buf[3] (0x12) – 高地址,存放高位(字节)
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) – 低地址,存放低位(字节)

低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12

可见,大端模式和字符串的存储模式类似。

3. 用代码判断大端和小端

3.1 union判断

使用union类型共享内存的判断方法。联合体union的存放顺序是所有成员都从低地址开始存放

bool IsBigEndian()
{
    union
    {
        unsigned short a ;//2个字节
        char b ;//高地址
    } c;

    c.a =0x0102 ;

    if(c.b ==1)//如果高地址存放低字节
        return true ;
    else
        return false ;
}

3.2 int判断法

直接取int类型的高地址

bool IsBigEndian()
{
    int a =1 ; 
    if(((char*)&a)[3] ==1)//直接取高地址
        return true ;
    else
        return false ;
}

4. Intel格式与Motorola格式

在进行CAN总线通信设计或者测试过程中,经常看到CAN总线信号的编码格式有两种定义:Intel格式与Motorola格式。Motorola是大端字节序,Intel是小端字节序

4.1 CAN报文

CAN总线上有4种报文:数据帧、远程帧、错误帧、超载帧。其中只有数据帧真正承载数据。假设报文中含有8各字节的数据,共8*8 = 64 bit

  • 如果位编号从右至左(R2L),那么:
7   6   5   4   3   2   1   0
--------------------------------
                                | 0
                                | 1
                                | 2
                                | 3
                                | 4
                                | 5
                                | 6
                                | 7
  • 如果位编号从左至右(L2R),那么:
0   1   2   3   4   5   6   7
--------------------------------
                                | 0
                                | 1
                                | 2
                                | 3
                                | 4
                                | 5
                                | 6
                                | 7

这两种编号方式中,字节的编号是一样的,位的编号不同。CAN报文是串行发送的,CAN节点在发送报文时,不论如何编号,总是从表的左上第一位开始发送。从左至右,从上至下

4.2 Intel格式与Motorola格式排列

使用<或者>表示信号的衔接点。

  • Intel格式

Intel格式的信号的每位,从MSB(高字节)到LSB(低字节),按照从右至左,从上至下的顺序排列(向右上角塞)

x   x   x   x   x   x   x   x
--------------------------------
>.. ... ... ... ... ... ... MSB | 0
                LSB ... ... ..> | 1
                                | 2
                                | 3
                                | 4
                                | 5
                                | 6
                                | 7
  • Motorola格式

而Motorola格式的信号,从MSB(高字节)到LSB(低字节),按照每位从左至右,从上至下排列(向左上角塞)

x   x   x   x   x   x   x   x
--------------------------------
MSB ... ... ... ... ... ... ..< | 0
<.. ... ... LSB                 | 1
                                | 2
                                | 3
                                | 4
                                | 5
                                | 6
                                | 7

从上面可以看出,Motorola格式对can报文的解析更加友好。

5. 大端数据解析示例

小端模式Python 小端模式的优点_小端模式


解析方法:

小端模式Python 小端模式的优点_big endian_02


以速度为例,如果速度为负值,需要对speed或0xF800,因为要取反+1,对不需要的数据补上1,防止对要解析的数据造成影响。如果是满字节,不管正数还是负数,也就无所谓补1了

//目标信息结构体
typedef struct
{
    uint64_t snr      :  8;  //SNR
    uint64_t dynProp  :  3;  //运动状态
    uint64_t resv     :  2;
    uint64_t latVel   :  9;  //横向速度
    uint64_t angle    : 10;  //角度
    uint64_t velocity : 11;  //速度
    uint64_t range    : 13;  //距离
    uint64_t objId    :  8;  //目标ID
}stObjInfoMsg;

//buf为待解析的8个字节
uint8_t * ptr = (uint8_t *)(buf);
  uint64_t msgdata = ((uint64_t)ptr[0] << 56) | ((uint64_t)ptr[1]   << 48)
                    | ((uint64_t)ptr[2] << 40) | ((uint64_t)ptr[3]   << 32)
                    | ((uint64_t)ptr[4] << 24) | ((uint64_t)ptr[5]   << 16)
                    | ((uint64_t)ptr[6] << 8)  | ((uint64_t)ptr[7]   << 0);
  stObjInfoMsg *pMsg = (stObjInfoMsg *)&msgdata;

  m_ID = pMsg->objId;
  m_Range = (float)pMsg->range / 5;
  m_Speed = pMsg->velocity & 0x400 ? (float)((int16_t)(pMsg->velocity | 0xF800)) / 5
                                              : (float)((int16_t)(pMsg->velocity & 0x3FF)) / 5;
  m_Angle = pMsg->angle & 0x200 ? (float)((int16_t)(pMsg->angle | 0xFC00)) / 4
                                          : (float)((int16_t)(pMsg->angle & 0x1FF)) / 4;