<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > 從STM32的位帶操作重談嵌入式中尋址與對齊的理解

從STM32的位帶操作重談嵌入式中尋址與對齊的理解

作者: 時(shí)間:2016-11-19 來(lái)源:網(wǎng)絡(luò ) 收藏
覺(jué)得這篇文章很不錯,普及知識:

本文引用地址:http://dyxdggzs.com/article/201611/318091.htm

初接觸STM32的人一定花了不少時(shí)間用于理解其位帶操作(bit banding)的原理與步驟。位帶操作允許編程人員以字的單位讀/寫(xiě)單一bit位?;叵胛覀兤綍r(shí)對于一個(gè)bit位的操作比如:↓

@-> PIN0 |= (1<<3);

@-> PIN0 &= ~(1<<5);

雖然這只是一行代碼,但是實(shí)際上這一行做了好幾步的工作。比如第一行,首先讀出當前PIN0的值放到緩存區,將1左移三位放入緩存區,將二者進(jìn)行“或”操作,即將當前PIN0的第三位置位1,將結果存入到實(shí)際PIN0所在的地址,即更新了PIN0的值。當然實(shí)際寫(xiě)成匯編后可能步驟不見(jiàn)得一定一樣,但是這幾步工作是一定得做的。

而對于位帶操作,STM32中將上述PIN0(假設它處于允許重新映射的區域,即位帶區->Bit Band Region)的每一個(gè)bit位重新映射到了一個(gè)單獨的地址,只需對這一個(gè)新的地址進(jìn)行寫(xiě)操作,則原PIN0值的對應位自動(dòng)置位或清零。假設剛才我們PIN0的第3bit位重新映射的地址我們用變量PIN0BIT3表示,則剛才的操作可以寫(xiě)作如下↓

@-> PIN0BIT3 = 1; //等同于PIN0 |= (1<<3), 這是由地址重映射保證的。

這一行的操作是,將1寫(xiě)入到PIN0BIT3所在的地址,即更新了PIN0BIT3的值,結束。由于地址重映射,將保證PIN0的第三bit位被置一了??梢钥闯?,操作步驟比之前簡(jiǎn)單,因此同樣的操作處理的速度更快了。

好,以上就是位帶操作的原理,全部介紹完了,是不是很簡(jiǎn)單。接下來(lái)我們自然就想問(wèn)了,這個(gè)PIN0第三bit位重新映射的地址在哪?這樣地址重映射不是把內存擴大了么,允許重映射的地址會(huì )不會(huì )有限制?原地址跟重映射的地址之間有沒(méi)有個(gè)換算公式將他們對應上?

我們自然而然會(huì )去尋找STM32的官方手冊的說(shuō)明。在STM32F1系列的的編程參考以及官方手冊里均有提到位帶操作的感念,那份編程參考里更是提到了計算二者聯(lián)系的公式。

在編程參考P25頁(yè)可以找到,允許bit位重新映射的位帶區只有兩處,一處是SRAM區,一處是片內的外設區Peripheral,均有1M大小。熟悉的人一眼就看出來(lái)了,SRAM區里存放的是堆棧(heap, stack)、全局變量等,外設區Peripheral區就是我們操作這塊CPU經(jīng)常打交道的GPIO, TIMER, PWM, A/D等各個(gè)功能的寄存器的所在地址。重新映射的區域叫位帶別名區(Bit band alias),均有32MB大小。也就是說(shuō),我們最終操作的地址都僅僅是1MB,那擴充出來(lái)的32MB空間無(wú)外乎是為了操作方便快速而設定的,最終還是得影響到那1MB空間才能起作用。編程參考的P30頁(yè)以SRAM區介紹了這一對應關(guān)系↓

以0x20000000(1MB的開(kāi)頭)這SRAM最低地址為例,其第一bit位重新映射到了0x22000000(32MB的開(kāi)頭)地址上,第7bit位映射到了0x2200001C地址上,以此類(lèi)推,到SRAM最高地址0x200FFFF(1MB的結尾)F的第7bit位映射到了0x23FFFFFC(32MB的結尾)。注意到上面跟下面的區域之間每個(gè)方格的地址增長(cháng)區別,下面(bit-band region)每塊方格地址增長(cháng)1,而上面(alias region)地址增長(cháng)4,因此有了編程參考的第P30頁(yè)的關(guān)系轉換計算公式↓

好了,對于基礎扎實(shí)熟悉的人來(lái)說(shuō)到這里已經(jīng)可以了,但是對于我,或者現在隱隱覺(jué)得有點(diǎn)疑問(wèn)的人來(lái)說(shuō),可能對于這個(gè)換算的結果(1MB對應32MB)有點(diǎn)想進(jìn)一步搞清楚這是為什么。為什么一會(huì )是字偏移(word_offset),一會(huì )是字節偏移(byte_offset),等等,字,bit,字節,是怎么對應的?等等,不是說(shuō)寄存器都是32位的,怎么上面的對應圖都是8bit(一字節)一對應的?暈了。所以這里有必要鞏固一下這方面的基礎知識。

首先回顧最基本概念。

在二進(jìn)制中,從單純數學(xué)上講我們知道有

@-> 2^10=1024=1K

@-> 2^20=1024*1024=1M

@-> 2^30=1024*1024*1024=1G

最小二進(jìn)制單位為比特(bit),即單純的0,1,0,1,等等。對于音樂(lè )、圖像等模擬信號我們進(jìn)行壓縮時(shí)通常采用的單位為比特率(bps),比如MP3最大比特率320Kbps,即每秒有320K個(gè)bit位,也就是每秒采樣后的數字0,1的個(gè)數有320K個(gè)。一般CD的采樣率為1411.2Kbps,因此音質(zhì)就好很多了。普通VCD為1.25Mbps,DVD視頻為5Mbps,標準藍光為40Mbps,所以采用藍光光盤(pán)的PS3游戲機的內部通信帶寬比普通PC大很多也就是這個(gè)道理,因為每秒需要吞吐很大的數據量才能保證畫(huà)面的清晰。

一個(gè)字節(Byte)等于8個(gè)bit,按照慣例我手寫(xiě)的B大寫(xiě)了。字節是通常的計算機存儲的基本單位。我們通常所說(shuō)的500GB硬盤(pán)、2GB內存就是指500個(gè)G的字節(Byte)和2個(gè)G的字節(Byte)。通常我們所說(shuō)的32位處理器(比如ARM)的內存尋址范圍為4GB就很好理解了。從單純數學(xué)上講↓

@-> 2^32= 4 * 2^30=4*1G=4G

最后,4GB的后面加了個(gè)B,即字節(Byte),表示是4G個(gè)字節數,因此32位處理器尋址范圍為4G個(gè)字節。

若覺(jué)得4GB內存對于一些運算覺(jué)得不夠用,采用64位處理器就可以這一問(wèn)題,我們看看64位的尋址范圍↓

@-> 2^64=2^34 * 2^30=16G*G

看到了吧,尋址范圍能有16G*G個(gè)字節,遠遠大于32位處理器,連跳好幾個(gè)數量級,足夠滿(mǎn)足很多應用了。一般G*G就稱(chēng)為E了,即64位處理器尋址范圍為16EB。不過(guò)這么大的數我是已經(jīng)沒(méi)什么概念了。

最早的紅白機,任天堂的FC,是一臺8位機(MOS 6502),小時(shí)候玩的紅白機覺(jué)得畫(huà)面簡(jiǎn)單音樂(lè )粗糙,與其CPU性能不無(wú)關(guān)系。FC的接班人超任SFC采用了摩托羅拉的65836,3.58MHz的16位CPU,游戲畫(huà)面和音質(zhì)明顯上了一個(gè)檔次。掌機GameBoy(GB)和GameBoyColor(GBC)同為8位機。之后的GBA和NDS均采用了ARM系列芯片則直接是32位機了。這個(gè)網(wǎng)址可以很方便地查看GBA和NDS的硬件參數。32位主機時(shí)代PlayStation是王者可以說(shuō)毫無(wú)疑問(wèn),而PS2你猜猜有多少位?64?不,人家直接跳到128位了。天文數字不是么,雖然PS2的CPU(Emotion Engion 簡(jiǎn)稱(chēng)EE)主頻只有295Mhz。所以說(shuō)現在很多PC端的PS2模擬器并不能很好的模擬就是這個(gè)道理。而到了PS3時(shí)代又回到了64位。不過(guò)要理解,單純追求CPU的帶寬并不一定能帶來(lái)畫(huà)面和性能的提升,其中架構的合理,緩存、外設時(shí)鐘等等都會(huì )影響性能。

之后,為什么所有這些數字,4GB,16EB后面都要加個(gè)B(字節),為什么存儲的單位是字節?這個(gè)問(wèn)題我們先放一放,先來(lái)看看字(Word)的概念。

如果說(shuō)比特(bit),字節(Byte)的概念比較好理解,那么字(Word)的概念就容易把人搞暈了,因為,字的長(cháng)度并不統一,在不同CPU,不同時(shí)代,字的長(cháng)度并不一致。從前的8位機上,比如前面提到的紅白機的MOS 6502,字長(cháng)為8bit,即一個(gè)字節。在一些16位CPU上,比如著(zhù)名的8086,字長(cháng)是16位的,2個(gè)字節。而現在的32位CPU比如ARM和我們手中的PC,字長(cháng)是32位,即4個(gè)字節。

可以參考這張wiki表對照歷史上CPU們對字長(cháng)的規定。

如果說(shuō),字節(Byte)對應于存儲的單位大小,那么字(Word)則對應了CPU一次處理數據/指令的大小,因此才為了方便起了個(gè)字(Word)這個(gè)名字。對于A(yíng)RM來(lái)說(shuō),字長(cháng)是32位的,也就是4個(gè)字節?;叵肫餉RM里所有的寄存器,是不是每個(gè)寄存器都是32位的?所以,以這個(gè)32位為單位進(jìn)行操作,因此這個(gè)32位即為一個(gè)字(Word)。那么為什么之前說(shuō)字節(Byte)是存儲的基本單位呢?

對于A(yíng)RM里面,數據的地址值跟數據自己本身都是32位的,這樣做的好處是操作起來(lái)方便,統一。當然,對于A(yíng)RMv4架構里的指令來(lái)說(shuō),有著(zhù)32位的ARM指令集和16位的Thumb指令集,甚至對于Cortex M3來(lái)說(shuō)都是32位或16位的Thumb指令集。這里先不討論這種指令集之前的區別,僅僅以允許的最大指令為32位來(lái)討論。另外,對于Cortex這一重回哈弗架構的CPU來(lái)說(shuō),指令和數據是分開(kāi)的,完全可以不用同樣的帶寬訪(fǎng)問(wèn)(當然實(shí)際上STM32二者帶寬還是一樣的,方便操作,只是分開(kāi)了而已)。有興趣的可以參考這篇文章對照指令集與架構的區別。

現代主流CPU的存儲單元為字節(Byte),即物理地址的編碼是以字節為單位編碼的,一個(gè)地址對應于一個(gè)字節(Byte)或8個(gè)bit的空間,這一地址加上1,則對應于下一個(gè)字節或下一組8bit。這種物理地址的編碼方式是由CPU的架構所保證的,并且為現在主流CPU所采用,因此說(shuō)32位CPU的尋址范圍是4GB就是指可找到物理地址上總共4G范圍的區域,每一個(gè)區域上都有1個(gè)字節(Byte)的空間用于存放數據或指令。

那么很明顯,對于A(yíng)RM的寄存器來(lái)說(shuō),一塊這樣的1個(gè)字節區域肯定是不夠的,每個(gè)32位的寄存器需要4個(gè)這樣的區域來(lái)存放才可以。我們經(jīng)??梢钥吹皆诙x寄存器時(shí)使用了下面的語(yǔ)句↓

/* General Purpose Input/Output (GPIO) */#define IOPIN0         (*((volatile unsigned long *) 0xE0028000))#define IOSET0         (*((volatile unsigned long *) 0xE0028004))#define IODIR0         (*((volatile unsigned long *) 0xE0028008))#define IOCLR0         (*((volatile unsigned long *) 0xE002800C))#define IOPIN1         (*((volatile unsigned long *) 0xE0028010))#define IOSET1         (*((volatile unsigned long *) 0xE0028014))#define IODIR1         (*((volatile unsigned long *) 0xE0028018))#define IOCLR1         (*((volatile unsigned long *) 0xE002801C))

以上寄存器在內存里是相互連續的,我們可以很清楚的看到,他們之間的地址值的增量為4。這就很清楚了,相鄰寄存器地址值差4,實(shí)際上之間有4*1Byte的空間,即4*8bit=32bit的空間,這一空間剛好可以容下一個(gè)32bit的寄存器值存放。實(shí)際上,你可以看到幾乎所有訪(fǎng)問(wèn)寄存器時(shí)的地址值的末尾均為0,4,8,C,即寄存器們一個(gè)挨著(zhù)一個(gè),32bit為一組,塞滿(mǎn)了他們所在的一片物理地址區域。因此對于32位CPU來(lái)說(shuō),出于效率一般均按字訪(fǎng)問(wèn),即訪(fǎng)問(wèn)地址末尾為0,4,8,C的物理地址,一次訪(fǎng)問(wèn)到4個(gè)字節,不會(huì )單獨訪(fǎng)問(wèn)其他地址,比如地址末尾為1的物理地址。當然,還有所謂的以半字(Half-Word)方式訪(fǎng)問(wèn),例如Thumb指令集,一次訪(fǎng)問(wèn)2個(gè)字節,訪(fǎng)問(wèn)地址末尾為2的倍數的物理地址。

好了,那怎么保證訪(fǎng)問(wèn)到這個(gè)地址時(shí)能讀取到32bit的數據,且他們并不錯位、順序相反呢?這就涉及到字節的對齊問(wèn)題。

我們先分析一下前面的一條預定義

@->#defineIOPIN0 (*((volatile unsigned long *) 0xE0028000))

這是一個(gè)指針的寫(xiě)法。首先當訪(fǎng)問(wèn)一個(gè)已知地址值的內容時(shí)我們可以先定義一個(gè)指針,比如↓

@-> (uint32*)0xE0028000//當然也可以是unsigned int來(lái)代替uint32,都可以。

即將地址位于0xE0028000的數據用指針來(lái)表達。對于這一指針,uint32是一個(gè)32位的數據結構,限制了這一指針指向的內容是以0xE0028000開(kāi)始往地址增長(cháng)方向,共計4個(gè)Byte,32bit的這么一塊區域,其數據結構是uint32。之后我們需要得到這個(gè)指針的值,那么很簡(jiǎn)單,用*運算取值即可↓

@-> ( *( (uint32*)0xE0028000 ) )//我故意多留了空格,目的是為了看得清楚。

這樣一整塊就得到了0xE0028000這一地址上的值,剩下想要讀取或寫(xiě)入都可以了。原本的宏定義中用到的數據類(lèi)型是unsigned long,也是32位無(wú)符號型整數,加上volatile修飾,表示編譯器對這個(gè)數不做優(yōu)化處理。大小確定了之后,現在我們看著(zhù)這4個(gè)字節,假如其中的內容如下(還記得每個(gè)地址上存放的是一個(gè)字節么),以十六進(jìn)制表示↓

@-> 0xE0028000 :0xDD

@-> 0xE0028001 :0xCC

@-> 0xE0028002 :0xBB

@-> 0xE0028003 :0xAA

當讀取時(shí),你認為我們最終得到的值是什么樣的?是0xDDCCBBAA(高位數存在地址低位),還是反過(guò)來(lái)的0xAABBCCDD(高位數存在地址高位)?想一想。

關(guān)于這一點(diǎn),就是CPU在設計時(shí)最有爭議的地方,許多芯片廠(chǎng)商在設計時(shí)也并沒(méi)有很好的統一。習慣上將,規定第一種存儲方式,即高位數存放在地址低位,稱(chēng)為大端(Big-endian),而第二種存儲方式,即高位數存放在地址高位,稱(chēng)為小端(Small-endian)。對于我們來(lái)說(shuō),覺(jué)得小端對齊方式更符合常規思維,高位對應高地址,地位對應低地址??梢詮倪@個(gè)wiki網(wǎng)址參考有哪些硬件使用大端,哪些使用小端。注意ARM架構是可以Bi-endian的,即可設置為大小端的一種,只不過(guò)我們常用的ARM芯片被制造商設置為小端,大小端設置的寄存器位往往設為只讀,只能通過(guò)REV指令零時(shí)調換存儲大小端而已。

回過(guò)頭看看我們訪(fǎng)問(wèn)寄存器時(shí),已知了地址值0xE0028000,并且我們需要讀取4Byte,即32bit因此需要設立變量為unsinged long,我們也知道了讀取后的字節順序為小端,因此對(*((volatile unsigned long *) 0xE0028000)) 這樣一句話(huà)的操作就恰好對應為我們需要的4個(gè)Byte的順序正確的寄存器值,我們在對嵌入式的寄存器進(jìn)行操作時(shí)也都是這么做的而且運行的很好。

之前提到的兩個(gè)區域,SRAM區和Peripheral區都有位帶操作區,這樣一來(lái)↓

IN A NUTSHELL:

@->位帶區(Bit band region)中的每一個(gè)bit均擴充到別名區(Bit band alias)上的一個(gè)字(Word),即4個(gè)字節(Byte),32個(gè)bit,因此總共1MB的位帶區被擴充為32MB的別名區。

@->為什么每一個(gè)bit位要擴充為一個(gè)字(Word)而不是字節(Byte)?因為CPU進(jìn)行常規操作都是以字(Word)為單位訪(fǎng)問(wèn)地址的。所以位帶區的相鄰一bit映射到別名區的地址增量是4,正好是4個(gè)字節(Byte),一個(gè)字(Word)。

之前提到的,編程手冊中給出的別名區和位帶區之間的計算公式,我想只要你有高中知識,用數學(xué)歸納法就可以推導出來(lái)了。選擇幾個(gè)實(shí)際地址試試看,你就明白了。↓

在實(shí)際操作中,根據Cortex-M3權威指南,可以根據如下宏定義進(jìn)行位帶操作。以GPIOA口的控制輸出引腳寄存器ODR為例,有如下定義

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C #define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //OutPut

使用時(shí)只需要寫(xiě)

@-> PAout(4)=1

就可以將GPIOA口的第四個(gè)bit位置為1了。



關(guān)鍵詞: STM32位帶操作尋址與對

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>