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