我們知道,存儲器本身沒有地址袋马,給存儲器分配地址的過程叫存儲器映射初澎,那什么叫寄存器映射?寄存器到底是什么?
在存儲器Block2
這塊區(qū)域,設(shè)計(jì)的是片上外設(shè)虑凛,它們以四個(gè)字節(jié)為一個(gè)單元碑宴,共32bit,每一個(gè)單元對應(yīng)不同的功能桑谍,當(dāng)我們控制這些單元時(shí)就可以驅(qū)動外設(shè)工作延柠。我們可以找到每個(gè)單元的起始地址,然后通過C
語言指針的操作方式來訪問這些單元锣披,如果每次都是通過這種地址的方式來訪問贞间,不僅不好記憶還容易出錯(cuò)贿条,這時(shí)我們可以根據(jù)每個(gè)單元功能的不同,以功能為名給這個(gè)內(nèi)存單元取一個(gè)別名增热,這個(gè)別名就是我們經(jīng)常說的寄存器整以,這個(gè)給已經(jīng)分配好地址的有特定功能的內(nèi)存單元取別名的過程就叫寄存器映射。
比如钓葫,我們找到GPIOB 端口的輸出數(shù)據(jù)寄存器ODR 的地址是0x4001 0C0C(至于這個(gè)地址如何找到可以先跳過悄蕾,后面我們會有詳細(xì)的講解),ODR
寄存器是32bit础浮,低16bit有效帆调,對應(yīng)著16 個(gè)外部IO,寫0/1 對應(yīng)的的IO 則輸出低/高電平《雇現(xiàn)在我們通過C 語言指針的操作方式番刊,讓GPIOB 的16
個(gè)IO 都輸出高電平。
1 // GPIOB 端口全部輸出 高電平
2 *(unsigned int*)(0x4001 0C0C) = 0xFFFF;
0x4001 0C0C 在我們看來是GPIOB 端口ODR
的地址影锈,但是在編譯器看來芹务,這只是一個(gè)普通的變量,是一個(gè)立即數(shù)鸭廷,要想讓編譯器也認(rèn)為是指針枣抱,我們得進(jìn)行強(qiáng)制類型轉(zhuǎn)換,把它轉(zhuǎn)換成指針辆床,即(unsigned int
*)0x4001 0C0C佳晶,然后再對這個(gè)指針進(jìn)行 * 操作。剛剛我們說了讼载,通過絕對地址訪問內(nèi)存單元不好記憶且容易出錯(cuò)轿秧,我們可以通過寄存器的方式來操作。
1 // GPIOB 端口全部輸出 高電平
2 #define GPIOB_ODR (unsigned int*)(GPIOB_BASE+0x0C)
3 * GPIOB_ODR = 0xFF;
為了方便操作咨堤,我們干脆把指針操作“*”也定義到寄存器別名里面菇篡。
1 // GPIOB 端口全部輸出 高電平
2#define GPIOB_ODR * (unsigned int*)(GPIOB_BASE+0x0C)
3 GPIOB_ODR = 0xFF;
STM32 的外設(shè)地址映射
片上外設(shè)區(qū)分為三條總線,根據(jù)外設(shè)速度的不同一喘,不同總線掛載著不同的外設(shè)驱还,APB1掛載低速外設(shè),APB2 和AHB
掛載高速外設(shè)凸克。相應(yīng)總線的最低地址我們稱為該總線的基地址铝侵,總線基地址也是掛載在該總線上的首個(gè)外設(shè)的地址。其中APB1
總線的地址最低触徐,片上外設(shè)從這里開始,也叫外設(shè)基地址狐赡。
1. 總線基地址
2. 外設(shè)基地址
總線上掛載著各種外設(shè)撞鹉,這些外設(shè)也有自己的地址范圍,特定外設(shè)的首個(gè)地址稱為“XX 外設(shè)基地址”,也叫XX 外設(shè)的邊界地址鸟雏。具體有關(guān)STM32F10xx
外設(shè)的邊界地址請參考《STM32F10xx 參考手冊》的2.3 小節(jié)的存儲器映射的表1:STM32F10xx 寄存器邊界地址享郊。
這里面我們以GPIO 這個(gè)外設(shè)來講解外設(shè)的基地址,GPIO 屬于高速的外設(shè) 孝鹊,掛載到APB2 總線上炊琉。
3. 外設(shè)寄存器
在XX 外設(shè)的地址范圍內(nèi),分布著的就是該外設(shè)的寄存器又活。以GPIO 外設(shè)為例苔咪,GPIO是通用輸入輸出端口的簡稱,簡單來說就是STM32
可控制的引腳柳骄,基本功能是控制引腳輸出高電平或者低電平团赏。最簡單的應(yīng)用就是把GPIO 的引腳連接到LED 燈的陰極,LED 燈的陽極接電源耐薯,然后通過STM32
控制該引腳的電平舔清,從而實(shí)現(xiàn)控制LED 燈的亮滅。
GPIO
有很多個(gè)寄存器曲初,每一個(gè)都有特定的功能体谒。每個(gè)寄存器為32bit,占四個(gè)字節(jié)臼婆,在該外設(shè)的基地址上按照順序排列抒痒,寄存器的位置都以相對該外設(shè)基地址的偏移地址來描述。這里我們以GPIOB
端口為例目锭,來說明GPIO都有哪些寄存器评汰。
有關(guān)外設(shè)的寄存器說明可參考《STM32F10xx 參考手冊》中具體章節(jié)的寄存器描述部分,在編程的時(shí)候我們需要反復(fù)的查閱外設(shè)的寄存器說明痢虹。
這里我們以“GPIO 端口置位/復(fù)位寄存器”為例被去,告訴大家如何理解寄存器的說明。
〗蔽ā①名稱
寄存器說明中首先列出了該寄存器中的名稱惨缆,“(GPIOx_BSRR)(x=A…E)”這段的意思是該寄存器名為“GPIOx_BSRR”其中的“x”可以為A-E,也就是說這個(gè)寄存器說明適用于GPIOA丰捷、GPIOB
至GPIOE坯墨,這些GPIO端口都有這樣的一個(gè)寄存器。
〔⊥②偏移地址
偏移地址是指本寄存器相對于這個(gè)外設(shè)的基地址的偏移捣染。本寄存器的偏移地址是0x18,從參考手冊中我們可以查到GPIOA 外設(shè)的基地址為0x4001
0800 停巷,我們就可以算出GPIOA的這個(gè)GPIOA_BSRR 寄存器的地址為:0x4001 0800+0x18 ;同理耍攘,由于GPIOB
的外設(shè)基地址為0x4001 0C00榕栏,可算出GPIOB_BSRR 寄存器的地址為:0x4001 0C00+0x18 。其他GPIO端口以此類推即可蕾各。
“谴拧③寄存器位表
緊接著的是本寄存器的位表,表中列出它的0-31 位的名稱及權(quán)限式曲。表上方的數(shù)字為位編號妨托,中間為位名稱,最下方為讀寫權(quán)限吝羞,其中w 表示只寫兰伤,r
表示只讀,rw 表示可讀寫脆贵。本寄存器中的位權(quán)限都是w医清,所以只能寫,如果讀本寄存器卖氨,是無法保證讀取到它真正內(nèi)容的会烙。而有的寄存器位只讀,一般是用于表示STM32
外設(shè)的某種工作狀態(tài)的筒捺,由STM32硬件自動更改柏腻,程序通過讀取那些寄存器位來判斷外設(shè)的工作狀態(tài)。
∠悼浴④位功能說明
位功能是寄存器說明中最重要的部分五嫂,它詳細(xì)介紹了寄存器每一個(gè)位的功能肯尺。例如本寄存器中有兩種寄存器位沃缘,分別為BRy 及BSy,其中的y
數(shù)值可以是0-15则吟,這里的0-15表示端口的引腳號槐臀,如BR0、BS0 用于控制GPIOx 的第0 個(gè)引腳氓仲,若x 表示GPIOA水慨,那就是控制GPIOA的第0
引腳,而BR1敬扛、BS1 就是控制GPIOA 第1 個(gè)引腳晰洒。
其中BRy 引腳的說明是“0:不會對相應(yīng)的ODRx 位執(zhí)行任何操作;1:對相應(yīng)ODRx位進(jìn)行復(fù)位”。這里的“復(fù)位”是將該位設(shè)置為0
的意思啥箭,而“置位”表示將該位設(shè)置為1;說明中的ODRx 是另一個(gè)寄存器的寄存器位谍珊,我們只需要知道ODRx 位為1 的時(shí)候,對應(yīng)的引腳x 輸出高電平急侥,為0
的時(shí)候?qū)?yīng)的引腳輸出低電平即可(感興趣的讀者可以查詢該寄存器GPIOx_ODR 的說明了解)抬驴。所以炼七,如果對BR0 寫入“1”的話,那么GPIOx 的第0
個(gè)引腳就會輸出“低電平”布持,但是對BR0 寫入“0”的話,卻不會影響ODR0 位陕悬,所以引
腳電平不會改變题暖。要想該引腳輸出“高電平”,就需要對“BS0”位寫入“1”捉超,寄存器位BSy 與BRy 是相反的操作胧卤。
C 語言對寄存器的封裝
1. 封裝總線和外設(shè)基地址
在編程上為了方便理解和記憶,我們把總線基地址和外設(shè)基地址都以相應(yīng)的宏定義起來拼岳,總線或者外設(shè)都以他們的名字作為宏名枝誊。
首先定義了 “片上外設(shè)”基地址PERIPH_BASE,接著在PERIPH_BASE 上加入各個(gè)總線的地址偏移惜纸, 得到APB1 叶撒、APB2
總線的地址APB1PERIPH_BASE
、APB2PERIPH_BASE耐版,在其之上加入外設(shè)地址的偏移祠够,得到GPIOA-G的外設(shè)地址,最后在外設(shè)地址上加入各寄存器的地址偏移粪牲,得到特定寄存器的地址古瓤。一旦有了具體地址,就可以用指針讀寫腺阳。
該代碼使用 (unsigned int *) 把GPIOB_BSRR
宏的數(shù)值強(qiáng)制轉(zhuǎn)換成了地址落君,然后再用“*”號做取指針操作,對該地址的賦值亭引,從而實(shí)現(xiàn)了寫寄存器的功能绎速。同樣,讀寄存器也是用取指針操作痛侍,把寄存器中的數(shù)據(jù)取到變量里朝氓,從而獲取STM32
外設(shè)的狀態(tài)。
2. 封裝寄存器列表
用上面的方法去定義地址主届,還是稍顯繁瑣赵哲,例如GPIOA-GPIOE
都各有一組功能相同的寄存器,如GPIOA_ODR/GPIOB_ODR/GPIOC_ODR
等等君丁,它們只是地址不一樣枫夺,但卻要為每個(gè)寄存器都定義它的地址。為了更方便地訪問寄存器绘闷,我們引入C 語言中的結(jié)構(gòu)體語法對寄存器進(jìn)行封裝橡庞。
這段代碼用typedef 關(guān)鍵字聲明了名為GPIO_TypeDef 的結(jié)構(gòu)體類型较坛,結(jié)構(gòu)體內(nèi)有7 個(gè)成員變量,變量名正好對應(yīng)寄存器的名字扒最。C
語言的語法規(guī)定丑勤,結(jié)構(gòu)體內(nèi)變量的存儲空間是連續(xù)的,其中32 位的變量占用4 個(gè)字節(jié)吧趣,16 位的變量占用2 個(gè)字節(jié)法竞。
也就是說,我們定義的這個(gè)GPIO_TypeDef 强挫,假如這個(gè)結(jié)構(gòu)體的首地址為0x40010C00(這也是第一個(gè)成員變量CRL 的地址)岔霸,
那么結(jié)構(gòu)體中第二個(gè)成員變量CRH 的地址即為0x4001 0C00 +0x04 ,加上的這個(gè)0x04 俯渤,正是代表CRL 所占用的4
個(gè)字節(jié)地址的偏移量呆细,其它成員變量相對于結(jié)構(gòu)體首地址的偏移,在上述代碼右側(cè)注釋已給八匠。
這樣的地址偏移與STM32 GPIO
外設(shè)定義的寄存器地址偏移一一對應(yīng)絮爷,只要給結(jié)構(gòu)體設(shè)置好首地址,就能把結(jié)構(gòu)體內(nèi)成員的地址確定下來臀叙,然后就能以結(jié)構(gòu)體的形式訪問寄存器略水。
這段代碼先用GPIO_TypeDef 類型定義一個(gè)結(jié)構(gòu)體指針GPIOx,并讓指針指向地址GPIOB_BASE(0x4001
0C00)劝萤,使用地址確定下來渊涝,然后根據(jù)C 語言訪問結(jié)構(gòu)體的語法,用GPIOx->ODR 及GPIOx->IDR 等方式讀寫寄存器床嫌。
最后跨释,我們更進(jìn)一步,直接使用宏定義好GPIO_TypeDef
類型的指針厌处,而且指針指向各個(gè)GPIO端口的首地址鳖谈,使用時(shí)我們直接用該宏訪問寄存器即可。
這里我們僅是以GPIO 這個(gè)外設(shè)為例阔涉,給大家講解了C
語言對寄存器的封裝缆娃。以此類推,其他外設(shè)也同樣可以用這種方法來封裝瑰排。好消息是贯要,這部分工作都由固件庫幫我們完成了,這里我們只是分析了下這個(gè)封裝的過程椭住,讓大家知其然崇渗,也只其所以然。
stm32視頻學(xué)習(xí)資料
STM32可以這樣玩
http://www.makeru.com.cn/live/4034_1460.html?s=45051
分析STM32的的開發(fā)方式
http://www.makeru.com.cn/live/3523_1673.html?s=45051
釋放潛能:學(xué)習(xí)效率提升、編程能力提升
http://www.makeru.com.cn/live/3507_1276.html?s=45051
從單片機(jī)到嵌入式linux我們需要做什么
http://www.makeru.com.cn/live/5413_1994.html?s=45051
stm32 如何用DMA搬運(yùn)數(shù)據(jù)
http://www.makeru.com.cn/live/detail/1484.html?s=45051
(STM32中斷系統(tǒng))
http://www.makeru.com.cn/live/1392_1124.html?s=45051
pdf以及相關(guān)文檔下載群:830802928