大端模式、小端模式、高字节序、低字节序、MSB、LSB

本文涉及的产品
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据同步 1个月
简介: 你知道内存是怎么读取数据的吗?知道数据是怎么一个一个字节发送的吗?是低字节先发还是高字节先发?是bit0先发还是bit7先发?是从低地址开始读还是从高地址开始读?看完本篇比应该就明白了~

摘要:你知道内存是怎么读取数据的吗?知道数据是怎么一个一个字节发送的吗?是低字节先发还是高字节先发?是bit0先发还是bit7先发?是从低地址开始读还是从高地址开始读?看完本篇比应该就明白了~

内存的读写永远从低地址开始读/写,从低到高!从低到高!从低到高!重要的话说三遍

大端模式和小端模式

大端模式和小端是实际的字节顺序和存储的地址顺序对应关系的两种模式。

百度百科

大端模式:高位字节存放在低地址中,低位字节存放在高地址中。最直观的字节序

小端模式:高位字节存放在高地址中,低位字节存放在低地址中。最符合人的思维的字节序,x86、ARM都这么搞(KEIL C51中,变量都是大端模式的;KEIL MDK中,变量是小端模式的。)。

用图表示更加容易理解。以unsigned int value = 0x12345678为例,分别按照大端模式和小端模式存放在芯片中。

内存地址 0x00000001 0x00000002 0x00000003 0x00000004
大端模式 0x12 0x34 0x56 0x78
小端模式 0x78 0x56 0x34 0x12

再换一种图示:同样以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value

百度百科

不管是大端还是小端模式,我们在读取和存储数据的时候一定都是从内存的低地址依次向高地址读取或写入。另外注意,x86平台是小端的,ARM平台是小端的,而PowerPC平台是大端的。

字节高低位

一般左边为高位,右边为低位(这个高低来自于人类的阅读习惯,数字从左向右,表示由大到小)

一个16位(双字节)的数据,比如0xFF1A,那么高位字节就是0xFF,低位是0x1A
如果是32位的数据,比如0x3F68415B。高位字(不是字节)是0x3F68,低位字是0x415B

右边是低位位,左边是高位(人的阅读习惯)

LSB和MSB

最高有效位(most mignificant bit,msb)指的是一个n位二进制数字中的n-1位,具有最高的权值2^(n-1)。 有时也指Most Significant Byte(MSB),指多字节序列中具有最大权重的字节。

同理,最低有效位(least significant bit,lsb)和的是一个n位二进制数字中的0位,具有最低的权值2^0。有时也指Least Significant Byte(LSB),指多字节序列中具有最小权重的字节。

所以0x12345678的最高有效字节就是0x12,最低有效字节就是0x78,这样明白了吧!

举个栗子

当选择模数转换器(ADC)时,最低有效位(LSB)这一参数的含义是什么?

对于一个12位串行转换器,它会输出由1或0组成的12位数串。通常,转换器首先送出的是最高有效位(MSB)(即LSB + 11)。有些转换器也会先送出LSB。我们假设先送出的是MSB,然后依次送出MSB-1 (即 LSB + 10)和MSB -2(即LSB + 9)并依次类推。转换器最终送出MSB -11(即LSB)作为位串的末位。

LSB这一术语有着特定的含义,它表示的是数字流中的最后一位,也表示组成满量程输入范围的最小单位。对于12位转换器来说,LSB的值相当于模拟信号满量程输入范围除以2^12 或 4096的商。如果用真实的数字来表示的话,对于满量程输入范围为4.096V的情况,一个12位转换器对应的LSB大小为1mV。但是,将LSB定义为4096个可能编码中的一个编码对于我们的理解是有好处的。

截取自某12位ADC芯片数据手册

高位先行msb 、低位先行lsb

高位先行即在传输一个字节的时候先传输高位msb;低位先行即在传输一个字节的时候先传输低位lsb。高位先行和低位先行是针对串行数据传输方式来说的。常见的串行传输方式有串口(UAR)、I2C、SPI等。以串口传输方式为例,标准的串口传输方式是低位先行,芯片在通过TX引脚发送数据时,依次发送位0、位1……位7。

串口传输是低位先行

UART在数据传输时,协议规定了数据传输必须是低位先行,看下面的时序图你就知道了~

截图自STM32F407中文参考手册

IIC传输是高位先行

IIC的数据和地址均以8位字节传输,MSB 在前。从图中可以清楚地看到:

截图自STM32F407中文参考手册IIC部分

这一点也反映在代码中,我们随便找一个IIC的读字节和写字节的函数看看:

void i2c_SendByte(uint8_t _ucByte)
{
    uint8_t i;
    /* 先发送字节的高位bit7 */
    for (i = 0; i < 8; i++)
    {        
        if (_ucByte & 0x80)
        {
            I2C_SDA_1();
        }
        else
        {
            I2C_SDA_0();
        }
        i2c_Delay();
        I2C_SCL_1();
        i2c_Delay();    
        I2C_SCL_0();
        if (i == 7)
        {
             I2C_SDA_1(); // 释放总线
        }
        _ucByte <<= 1;    /* 左移一个bit */
        i2c_Delay();
    }
}

从第7行代码中可以看到,在发送一个字节时,首先将要发送的字节与0x80进行与运算,取出最高位,然后循环左移8次就可以将一个字节数据发送出去了。你有没有想过为什么这里我们不把要发送的字节与0x01进行与运算,取出最低位,然后循环右移8次也可以将一个字节数据发送出去呢

答:因为我们说了I2C在数据传输时,协议规定了数据传输必须是高位先行,所以你要发送一个字节的数据肯定必须先取出最高位,然后循环左移将数据发出,如果你与上0x01,就是低位先行,虽然你也将一个字节发出去了,但是你发的是歪门邪道的数据,人家单片机也不认识,对吧?你品,你细品~

同样在接收一个字节时,接收到的第1位认为是最高位,接收一个字节代码如下:

uint8_t i2c_ReadByte(void)
{
    uint8_t i;
    uint8_t value;
    /* 读到第1个bit为数据的bit7 */
    value = 0;
    for (i = 0; i < 8; i++)
    {
        value <<= 1;
        I2C_SCL_1();
        i2c_Delay();
        if (I2C_SDA_READ())
        {
            value++;
        }
        I2C_SCL_0();
        i2c_Delay();
    }
    return value;
}

所有使用I2C的设备必须遵循I2C协议,必须都是高位先行的,这样才能实现通用性。怎么样?是不是又get到了一个小技巧~

字节序、比特序

字节序就是串行发送多字节时发送的顺序,比如value=0x12345678,按字节发送是0x12、0x34、0x56、0x78顺序还是0x78、0x56、0x34、0x12顺序。

同理,比特序在bit层面进行排序,如果一个字节,指先发bit0还是bit7, 如果是一个Word型,先发bit31还是先发bit0

串口是lsb优先,I2C是msb优先,这里的msb、lsb指的是比特序,二进制位的位置。区别于【字节序】通信中,先发送低字节,还是高字节的问题,那是字节序的MSB还是LSB,当然也有人混称上面所说的为大端发送big-endian、小端发送little-endian

验证MCU平台存储方式?

这里以STM32开发单片机的keil平台为例,以下代码如果打印0x04就是小端存储,如果0x01则是大端存储。

因为0x04是低字节,读取数据是从低地址开始读,打印的是data的低地址,所以如果打印出的是0x04就表明低地址存储低字节,就为小端存储。明白了吗?

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "SEGGER_RTT.h"
#include "math.h"

int main(void)
{
    HAL_Init();                 //初始化HAL库    
    Stm32_Clock_Init(8,336,2,7);//设置时钟,168Mhz
    delay_init(168);              //初始化延时函数
  while(1)
  {  
    uint32_t data =0x01020304;
        char *p = (char*)&data;
    printf("0x0%x\n",*p);//看输出的是0x01还是0x04
        delay_ms(1000);
    }
}

编译、链接、下载,通过RTT查看试验结果:

JLink的RTT查看器

可以看出STM32是小端存储。

总结:内存的读写永远从低地址开始读/写。大小端存储指字节在内存存储方式,X86、ARM平台都是小端存储(低-低),MSB/LSB只发送字节序或者比特序,串口是比特序LSB,IIC是比特序MSB。也有人将MSB、big-endian、大端发送都混为一谈,这时候一般指字节序上MSB。
关注微信公众号:[果果小师弟],获取更多精彩内容!
智果芯—服务于百万大学生和电子工程师

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
相关文章
|
3月前
|
存储
计算机存储,字节分为大端和小端
计算机存储,字节分为大端和小端
57 1
|
10月前
|
数据处理 C++
C++-bit转hex(四位二进制转十六进制)
C++-bit转hex(四位二进制转十六进制)
108 0
|
存储
【计算机基础】 --- LSB、MSB与大/小端字节序
【计算机基础】 --- LSB、MSB与大/小端字节序
480 0
|
存储 网络协议 编译器
字节序,主机字节序与网络字节序
是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。在计算机中是以字节为单位,每个地址对应一个字节,一个字节8bit。在C中,除了8bit的char以外,还有16bit的short,32位的int,64位long,当然具体要由编译器决定,可以通过sizeof来获取不同类型在内存中占用的字节数。在计算机系统中,当物理单位的长度大于1个字节时(所以char数据就不用区分字节序),就要区分字节顺序。常见的字节顺序有两种:大端和小端,当然还有其他字节顺序,但不常见,例如Middle Endian,通俗的讲,字节序就是cpu对内存中进行存储的顺序(以字节为单位进行存取)
174 0
|
存储 网络协议 芯片
清晰讲解LSB、MSB和大小端模式及网络字节序
清晰讲解LSB、MSB和大小端模式及网络字节序
1807 0
|
存储 小程序 编译器
大小端字节序详解
在开始正文之前,我想先问一下大家,内存中是怎样存放一个数的?当然啊,我这里问的不是数据存储的形式(比如整数存原码,负数存补码),而是一个数据存放的顺序 我们先看下面这个例子,当我们看看当把十六进制函数0x11223344存进内存中会是怎么样的。
大小端字节序详解
|
存储
理解字节序
计算机硬件有两种储存数据的方式:**大端字节序**(big endian)和**小端字节序**(little endian)
75 0
|
存储 Java C++
大端(Big Endian)与小端(Little Endian)简介与实现
【大端(Big Endian)与小端(Little Endian)简介】Byte Endian是指字节在内存中的组织,所以也称它为Byte Ordering,或Byte Order。     对于数据中跨越多个字节的对象,我们必须为它建立这样的约定: (1) 它的地址是多少? (2) 它的字节在内存中是如何组织的?     针对第一个问题,有这样的解释:     对于跨
2307 0