摘要:你知道記憶體是怎么讀取資料的嗎?知道資料是怎么一個一個位元組發送的嗎?是低位元組先發還是高位元組先發?是bit0先發還是bit7先發?是從低地址開始讀還是從高地址開始讀?看完本篇比應該就明白了~
記憶體的讀寫永遠從低地址開始讀/寫,從低到高!從低到高!從低到高!重要的話說三遍
大端模式和小端模式
大端模式和小端是實際的位元組順序和存盤的地址順序對應關系的兩種模式,

大端模式:高位位元組存放在低地址中,低位位元組存放在高地址中,最直觀的位元組序,
小端模式:高位位元組存放在高地址中,低位位元組存放在低地址中,最符合人的思維的位元組序,x86、ARM都這么搞(STM32就是小端模式存盤),
用圖表示更加容易理解,以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個可能編碼中的一個編碼對于我們的理解是有好處的,

高位先行msb 、低位先行lsb
高位先行即在傳輸一個位元組的時候先傳輸高位msb;低位先行即在傳輸一個位元組的時候先傳輸低位lsb,高位先行和低位先行是針對串行資料傳輸方式來說的,常見的串行傳輸方式有串口(UAR)、I2C、SPI等,以串口傳輸方式為例,標準的串口傳輸方式是低位先行,芯片在通過TX引腳發送資料時,依次發送位0、位1……位7,
串口傳輸是低位先行
UART在資料傳輸時,協議規定了資料傳輸必須是低位先行,看下面的時序圖你就知道了~

IIC傳輸是高位先行
IIC的資料和地址均以8位位元組傳輸,MSB 在前,從圖中可以清楚地看到:

這一點也反映在代碼中,我們隨便找一個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查看試驗結果:

可以看出STM32是小端存盤,
總結:記憶體的讀寫永遠從低地址開始讀/寫,大小端存盤指位元組在記憶體存盤方式,X86、ARM平臺都是小端存盤(低-低),MSB/LSB只發送位元組序或者位元序,串口是位元序LSB,IIC是位元序MSB,也有人將MSB、big-endian、大端發送都混為一談,這時候一般指位元組序上MSB,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/293000.html
標籤:其他
