程序数据存储中的大端小端问题

发布时间 2023-11-29 11:26:33作者: HAOstudio

程序数据存储中的大端小端问题

事情是这样的,有一次我使用TPC116S8 DAC芯片,该芯片是使用SPI方式进行数据传输,通讯协议如下图所示:
SPI通讯协议

可以看出来,一次传输需要传输24个bit,而且是高位在前(MSB)。
所以我就想着将数据存放到一个32位的存储空间里,然后取其低3个Byte进行传输即可。然后发现通讯不正常,通过示波器测量波形发现,数据位并没有按照预想的那样进行传输。突然想到有可能是数据大小端存储方式的问题。

MSB:Most Significant Bit,最高有效位。
LSB:Last Significant Bit,最低有效位。

大端和小端

大端模式(Big-Endian):数据的低位Byte存放在高位地址中,高位Byte存放在低位地址中。
小端模式(Little-Endian):数据的低位Byte存放在低位地址中,高位Byte存放在高位地址中。

举个例子:
对于一个无符号32位整数0x12345678来说,大端、小端的存储模式分别如下图所示:
大端模式与小端模式

这就回到了最开始的那个问题,我想传输0x12345678的低3个Byte(低24bit)0x345678

uint32_t mdata = 0x12345678;
SPI_Transmit((uint8_t *)&mdata+1, 3);//传输指定地址开始的三个字节

如果是小端存储模式,则传输的数据为0x56、0x34、0x12
如果是大端存储模式,则传输的数据为0x34、0x56、0x78

可以看出来,如果是大端存储,传输的数据则是我想要的。

判断计算机是大端存储还是小端存储

将以下C程序自己的电脑上运行一下:

#include <stdio.h>
#include <stdint.h>

//联合体
union mdata{
    uint32_t a;
    uint8_t b[4];
}tmp;

int main()
{
    tmp.a = 0x12345678;
    printf("b1:%x, b2:%x, b3:%x, b4:%x\n",tmp.b[0],tmp.b[1],tmp.b[2],tmp.b[3]);
    return 0;
}

小端模式的运行结果为:b1:78, b2:56, b3:34, b4:12
大端模式的运行结果为:b1:12, b2:34, b3:56, b4:78

一般来说,Windows系统为小端存储,Linux系统为大端存储。

将其封装成一个函数,用来判断是大端还是小端。

int IsLittleEndian()  
{  
    int a = 0x1234;  
    char b =  *(char *)&a;  //通过将int强制类型转换成char单字节,通过判断起始存储位置。即等于取b等于a的低地址部分  
    if( b == 0x34)  
    {  
        return 1;//little endian
    }  
    return 0;//big endian
}

大端小端转换

定义2个函数EndianSwap16()EndianSwap32(),分别为16位和32位的字节序转换函数。

//16位大小端转换函数
#define EndianSwap16(A) ((((uint16_t)(A) & 0xff00) >> 8) | \
                         (((uint16_t)(A) & 0x00ff) << 8))
//32位大小端转换函数
#define EndianSwap32(A) ((((uint32_t)(A) & 0xff000000) >> 24)  | \
                         (((uint32_t)(A) & 0x00ff0000) >> 8)   | \
                         (((uint32_t)(A) & 0x0000ff00) << 8)   | \
                         (((uint32_t)(A) & 0x000000ff) << 24))  

参考

博客园:https://www.cnblogs.com/little-white/p/3236548.html