STM32的大小端模式——什么是大小端模式?

1. 什么是大小端模式

大端模式Big-Endian:高字节存于内存低地址,低字节存于内存高地址。
小端模式Little-Endian:低字节存于内存低地址,高字节存于内存高地址。

网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

首先我们来看下数据在大小端两种模式下存储图,假设有一个整型数0x12345678,从图中可以看出明显的区别:
在这里插入图片描述
再给一张图便于理解:
在这里插入图片描述

2. 为什么会有大小端模式之分

这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于 8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于 大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

关于为什么会有大小端模式之分还可以从大小端两种模式各自的优缺点来分析:
Big-Endian优点:靠首先提取高位字节,你总是可以由看看在偏移位置为0的字节来确定这个数字是 正数还是负数。你不必知道这个数值有多长,或者你也不必过一些字节来看这个数值是否含有符号位。这个数值是以它们被打印出来的顺序存放的,所以从二进制到 十进制的函数特别有效。
因而,对于不同要求的机器,在设计存取方式时就会不同。

Little-Endian优点:提取一个,两个,四个或者更长字节数据的汇编指令以与其他所有格式相同的方式进行:首先在偏移地址为0的地方提取最低位的字节,因为地址偏移和字节数是一对一的关系,多重精度的数学函数就相对地容易写了。
如果你增加数字的值,你可能在左边增加数字(高位非指数函数需要更多的数字)。 因此, 经常需要增加两位数字并移动存储器里所有Big-endian顺序的数字,把所有数向右移,这会增加计算机的工作量。不过,使用Little- Endian的存储器中不重要的字节可以存在它原来的位置,新的数可以存在它的右边的高位地址里。
这就意味着计算机中的某些计算可以变得更加简单和快速。

3. 什么情况需要考虑大小端模式

相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。

原因如下:网络协议规定接收到得第一个字节是高字节,存放到低地址,所以发送时会首先去低地址取数据的高字节。小端模式的多字节数据在存放时,低地址存放的是低字节,而被发送方网络协议函数发送时会首先去低地址取数据(想要取高字节,真正取得是低字节),接收方网络协议函数接收时会将接收到的第一个字节存放到低地址(想要接收高字节,真正接收的是低字节),所以最后双方都正确的收发了数据。

而相同平台进行通信时,如果双方都进行转换最后虽然能够正确收发数据,但是所做的转换是没有意义的,造成资源的浪费。而不同平台进行通信时必须进行转换,不转换会造成错误的收发数据,字节序转换函数会根据当前平台的存储模式做出相应正确的转换,如果当前平台是大端,则直接返回不进行转换,如果当前平台是小端,会将接收到得网络字节序进行转换。

4. 常见的设备的大小端模式

STM32单片机:小端模式
STM8:大端
KEIL C51:大端
x86:小端
ARM既可以工作在大端模式,也可以工作在小端模式

注意:大小端模式是和硬件有关的,即和芯片本身有关,IDE不能进行设置。

5. 测试大小端模式例程

//如果字节序为big-endian,返回true;
//反之为   little-endian,返回false

bool IsBig_Endian()
{
    
    
    unsigned short test = 0x1234;
    if(*( (unsigned char*) &test ) == 0x12)
       return TRUE;
   else
       return FALSE;
}//IsBig_Endian()

其他例程:

int checkCPUendian()
{
    
    
      union{
    
    
             unsigned long int i;
             unsigned char s[4];
       }c;
       c.i = 0x12345678;
       return (0x12 == c.s[0]); 
}

6. 大小端转化例程

typedef unsigned short int uint16;
typedef unsigned long int uint32;
// 短整型大小端互换
#define BigLittleSwap16(A)  ((((uint16)(A) & 0xff00) >> 8) | \
 
                             (((uint16)(A) & 0x00ff) << 8))
 
 // 长整型大小端互换
#define BigLittleSwap32(A)  ((((uint32)(A) & 0xff000000) >> 24) | \

                             (((uint32)(A) & 0x00ff0000) >> 8) | \
 
                            (((uint32)(A) & 0x0000ff00) << 8) | \
 
                            (((uint32)(A) & 0x000000ff) << 24))

参考:
https://blog.csdn.net/weixin_34088583/article/details/94191143
https://blog.csdn.net/dongfangjing/article/details/50822143
https://strongerhuang.blog.csdn.net/article/details/99669954

猜你喜欢

转载自blog.csdn.net/weixin_44788542/article/details/112245179