摘要:你知道內存是怎么讀取數據的嗎?知道數據是怎么一個一個字節發送的嗎?是低字節先發還是高字節先發?是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位(雙字節)的數據,比如0xFF12,那么高位字節就是0xFF,低位是0x12。如果是32位的數據,比如0x12345678。高位字(不是字節)是0x1234,低位字是0x5678。
右邊是低位位,左邊是高位(人的閱讀習慣)
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。高位先行和低位先行是針對串行數據傳輸方式來說的。常見的串行傳輸方式有串口(UART)、I2C、SPI等。以串口傳輸方式為例,標準的串口傳輸方式是低位先行,芯片在通過TX引腳發送數據時,依次發送位0、位1……位7。
串口傳輸是低位先行
UART在數據傳輸時,協議規定了數據傳輸必須是低位先行,看下面的時序圖你就知道了~
截圖自STM32F407中文參考手冊
IIC傳輸是高位先行
IIC的數據和地址均以8位字節傳輸,MSB 在前。從圖中可以清楚地看到:
截圖自STM32F407中文參考手冊IIC部分
這一點也反映在代碼中,我們隨便找一個IIC的讀字節和寫字節的函數看看:
voidi2c_SendByte(uint8_t_ucByte) { uint8_ti; /*先發送字節的高位bit7*/ for(i=0;i8;?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_ti2c_ReadByte(void) { uint8_ti; uint8_tvalue; /*讀到第1個bit為數據的bit7*/ value=0; for(i=0;i8;?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指的是比特序,二進制位的位置。
驗證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" intmain(void) { HAL_Init();//初始化HAL庫 Stm32_Clock_Init(8,336,2,7);//設置時鐘,168Mhz delay_init(168);//初始化延時函數 while(1) { uint32_tdata=0x01020304; char*p=(char*)&data; printf("0x0%x ",*p);//看輸出的是0x01還是0x04 delay_ms(1000); } }
編譯、鏈接、下載,通過RTT查看試驗結果:
JLink的RTT查看器
可以看出STM32是小端存儲。
總結:內存的讀寫永遠從低地址開始讀/寫。大小端存儲指字節在內存存儲方式,X86、ARM平臺都是小端存儲(低-低),MSB/LSB只發送字節序或者比特序,串口是比特序LSB,IIC是比特序MSB。也有人將MSB、big-endian、大端發送都混為一談,這時候一般指字節序上MSB。
原文標題:干貨|一文帶你搞懂內存中數據的讀寫方式
文章出處:【微信公眾號:電子工程世界】歡迎添加關注!文章轉載請注明出處。
-
數據
+關注
關注
8文章
7048瀏覽量
89073 -
內存
+關注
關注
8文章
3028瀏覽量
74076 -
STM32
+關注
關注
2270文章
10901瀏覽量
356200 -
存儲數據
+關注
關注
0文章
88瀏覽量
14103
原文標題:干貨|一文帶你搞懂內存中數據的讀寫方式
文章出處:【微信號:電子工程世界,微信公眾號:電子工程世界】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論