摘要:你知道內(nèi)存是怎么讀取數(shù)據(jù)的嗎?知道數(shù)據(jù)是怎么一個一個字節(jié)發(fā)送的嗎?是低字節(jié)先發(fā)還是高字節(jié)先發(fā)?是bit0先發(fā)還是bit7先發(fā)?是從低地址開始讀還是從高地址開始讀?看完本篇你應(yīng)該就明白了~
內(nèi)存的讀寫永遠從低地址開始讀/寫,從低到高!從低到高!從低到高!重要的話說三遍
大端模式和小端模式
大端模式和小端是實際的字節(jié)順序和存儲的地址順序?qū)?yīng)關(guān)系的兩種模式。
大端模式:高位字節(jié)存放在低地址中,低位字節(jié)存放在高地址中。最直觀的字節(jié)序。
小端模式:高位字節(jié)存放在高地址中,低位字節(jié)存放在低地址中。最符合人的思維的字節(jié)序,x86、ARM都這么搞(STM32就是小端模式存儲)。
用圖表示更加容易理解。以unsigned int value = 0x12345678為例,分別按照大端模式和小端模式存放在芯片中。
內(nèi)存地址 | 0x00000001 | 0x00000002 | 0x00000003 | 0x00000004 |
---|---|---|---|---|
大端模式 | 0x12 | 0x34 | 0x56 | 0x78 |
小端模式 | 0x78 | 0x56 | 0x34 | 0x12 |
再換一種圖示:同樣以unsigned int value = 0x12345678為例,分別看看在兩種字節(jié)序下其存儲情況,我們可以用unsigned char buf[4]來表示value。
百度百科
不管是大端還是小端模式,我們在讀取和存儲數(shù)據(jù)的時候一定都是從內(nèi)存的低地址依次向高地址讀取或?qū)懭搿A硗庾⒁猓瑇86平臺是小端的,ARM平臺是小端的,而PowerPC平臺是大端的。
字節(jié)高低位
一般左邊為高位,右邊為低位(這個高低來自于人類的閱讀習(xí)慣,數(shù)字從左向右,表示由大到小)
一個16位(雙字節(jié))的數(shù)據(jù),比如0xFF12,那么高位字節(jié)就是0xFF,低位是0x12。如果是32位的數(shù)據(jù),比如0x12345678。高位字(不是字節(jié))是0x1234,低位字是0x5678。
右邊是低位位,左邊是高位(人的閱讀習(xí)慣)
LSB和MSB
最高有效位(most mignificant bit,msb)指的是一個n位二進制數(shù)字中的n-1位,具有最高的權(quán)值2^(n-1)。有時也指Most Significant Byte(MSB),指多字節(jié)序列中具有最大權(quán)重的字節(jié)。
同理,最低有效位(least significant bit,lsb)和的是一個n位二進制數(shù)字中的0位,具有最低的權(quán)值2^0。有時也指Least Significant Byte(LSB),指多字節(jié)序列中具有最小權(quán)重的字節(jié)。
所以0x12345678的最高有效字節(jié)就是0x12,最低有效字節(jié)就是0x78,這樣明白了吧!
舉個栗子
當(dāng)選擇模數(shù)轉(zhuǎn)換器(ADC)時,最低有效位(LSB)這一參數(shù)的含義是什么?
對于一個12位串行轉(zhuǎn)換器,它會輸出由1或0組成的12位數(shù)串。通常,轉(zhuǎn)換器首先送出的是最高有效位(MSB)(即LSB + 11)。有些轉(zhuǎn)換器也會先送出LSB。我們假設(shè)先送出的是MSB,然后依次送出MSB-1 (即 LSB + 10)和MSB -2(即LSB + 9)并依次類推。轉(zhuǎn)換器最終送出MSB -11(即LSB)作為位串的末位。
LSB這一術(shù)語有著特定的含義,它表示的是數(shù)字流中的最后一位,也表示組成滿量程輸入范圍的最小單位。對于12位轉(zhuǎn)換器來說,LSB的值相當(dāng)于模擬信號滿量程輸入范圍除以2^12 或 4096的商。如果用真實的數(shù)字來表示的話,對于滿量程輸入范圍為4.096V的情況,一個12位轉(zhuǎn)換器對應(yīng)的LSB大小為1mV。但是,將LSB定義為4096個可能編碼中的一個編碼對于我們的理解是有好處的。
截取自某12位ADC芯片數(shù)據(jù)手冊
高位先行msb 、低位先行l(wèi)sb
高位先行即在傳輸一個字節(jié)的時候先傳輸高位msb;低位先行即在傳輸一個字節(jié)的時候先傳輸?shù)臀籰sb。高位先行和低位先行是針對串行數(shù)據(jù)傳輸方式來說的。常見的串行傳輸方式有串口(UART)、I2C、SPI等。以串口傳輸方式為例,標準的串口傳輸方式是低位先行,芯片在通過TX引腳發(fā)送數(shù)據(jù)時,依次發(fā)送位0、位1……位7。
串口傳輸是低位先行
UART在數(shù)據(jù)傳輸時,協(xié)議規(guī)定了數(shù)據(jù)傳輸必須是低位先行,看下面的時序圖你就知道了~
截圖自STM32F407中文參考手冊
IIC傳輸是高位先行
IIC的數(shù)據(jù)和地址均以8位字節(jié)傳輸,MSB 在前。從圖中可以清楚地看到:
截圖自STM32F407中文參考手冊IIC部分
這一點也反映在代碼中,我們隨便找一個IIC的讀字節(jié)和寫字節(jié)的函數(shù)看看:
voidi2c_SendByte(uint8_t_ucByte) { uint8_ti; /*先發(fā)送字節(jié)的高位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行代碼中可以看到,在發(fā)送一個字節(jié)時,首先將要發(fā)送的字節(jié)與0x80進行與運算,取出最高位,然后循環(huán)左移8次就可以將一個字節(jié)數(shù)據(jù)發(fā)送出去了。你有沒有想過為什么這里我們不把要發(fā)送的字節(jié)與0x01進行與運算,取出最低位,然后循環(huán)右移8次也可以將一個字節(jié)數(shù)據(jù)發(fā)送出去呢?
答:因為我們說了I2C在數(shù)據(jù)傳輸時,協(xié)議規(guī)定了數(shù)據(jù)傳輸必須是高位先行,所以你要發(fā)送一個字節(jié)的數(shù)據(jù)肯定必須先取出最高位,然后循環(huán)左移將數(shù)據(jù)發(fā)出,如果你與上0x01,就是低位先行,雖然你也將一個字節(jié)發(fā)出去了,但是你發(fā)的是歪門邪道的數(shù)據(jù),人家單片機也不認識,對吧?你品,你細品
同樣在接收一個字節(jié)時,接收到的第1位認為是最高位,接收一個字節(jié)代碼如下:
uint8_ti2c_ReadByte(void) { uint8_ti; uint8_tvalue; /*讀到第1個bit為數(shù)據(jù)的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的設(shè)備必須遵循I2C協(xié)議,必須都是高位先行的,這樣才能實現(xiàn)通用性。怎么樣?是不是又get到了一個小技巧~
字節(jié)序、比特序
字節(jié)序就是串行發(fā)送多字節(jié)時發(fā)送的順序,比如value=0x12345678,按字節(jié)發(fā)送是0x12、0x34、0x56、0x78順序還是0x78、0x56、0x34、0x12順序。
同理,比特序在bit層面進行排序,如果一個字節(jié),指先發(fā)bit0還是bit7, 如果是一個Word型,先發(fā)bit31還是先發(fā)bit0。串口是lsb優(yōu)先,I2C是msb優(yōu)先,這里的msb、lsb指的是比特序,二進制位的位置。
驗證MCU平臺存儲方式?
這里以STM32開發(fā)單片機的keil平臺為例,以下代碼如果打印0x04就是小端存儲,如果0x01則是大端存儲。
因為0x04是低字節(jié),讀取數(shù)據(jù)是從低地址開始讀,打印的是data的低地址,所以如果打印出的是0x04就表明低地址存儲低字節(jié),就為小端存儲。明白了嗎?
#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);//設(shè)置時鐘,168Mhz delay_init(168);//初始化延時函數(shù) while(1) { uint32_tdata=0x01020304; char*p=(char*)&data; printf("0x0%x ",*p);//看輸出的是0x01還是0x04 delay_ms(1000); } }
編譯、鏈接、下載,通過RTT查看試驗結(jié)果:
JLink的RTT查看器
可以看出STM32是小端存儲。
總結(jié):內(nèi)存的讀寫永遠從低地址開始讀/寫。大小端存儲指字節(jié)在內(nèi)存存儲方式,X86、ARM平臺都是小端存儲(低-低),MSB/LSB只發(fā)送字節(jié)序或者比特序,串口是比特序LSB,IIC是比特序MSB。也有人將MSB、big-endian、大端發(fā)送都混為一談,這時候一般指字節(jié)序上MSB。
原文標題:干貨|一文帶你搞懂內(nèi)存中數(shù)據(jù)的讀寫方式
文章出處:【微信公眾號:電子工程世界】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7241瀏覽量
90993 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
3108瀏覽量
74985 -
STM32
+關(guān)注
關(guān)注
2289文章
11011瀏覽量
362351 -
存儲數(shù)據(jù)
+關(guān)注
關(guān)注
0文章
89瀏覽量
14285
原文標題:干貨|一文帶你搞懂內(nèi)存中數(shù)據(jù)的讀寫方式
文章出處:【微信號:電子工程世界,微信公眾號:電子工程世界】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
問題:讀取數(shù)據(jù)時報錯內(nèi)存不足
TDMS文件,讀取顯示內(nèi)存已滿
一次讀取大數(shù)據(jù)占用內(nèi)存很大
Linux學(xué)習(xí)記錄——寄存器與內(nèi)存
利用SRIO接口從FPGA向6678的共享內(nèi)存發(fā)送數(shù)據(jù),請問相比于單核從共享內(nèi)存讀取數(shù)據(jù)會慢多少?
狀態(tài)機運行沒有約束是為什么?
嵌入式C語言一些關(guān)鍵字的相關(guān)資料推薦
java之volatile并發(fā)

windows應(yīng)用程序讀取進程的內(nèi)存工具免費下載
CPU是如何調(diào)度任務(wù)的?

干貨|一文帶你搞懂內(nèi)存中數(shù)據(jù)的讀寫方式

關(guān)于Vivado Non-project,我們應(yīng)知道的一些問題
PCI Express橋:指南上游內(nèi)存讀取性能優(yōu)化

蘇州旗芯微半導(dǎo)體專利:內(nèi)存數(shù)據(jù)讀取方法、系統(tǒng)及計算機設(shè)備

評論