在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

51單片機(jī)工程師跳槽到stm32的方法

fcsde-sh ? 來(lái)源:互聯(lián)網(wǎng) ? 作者:佚名 ? 2017-11-11 06:12 ? 次閱讀
你可能此前一直學(xué)習(xí)或者從事的工作都是使用的4 位或者8 位單片機(jī), 比如51 類(lèi)單片機(jī)。 因?yàn)?1是如此的深入人心,可以輕易獲得大量關(guān)于他的學(xué)習(xí)資料, 在書(shū)店51 類(lèi)的書(shū)籍教材甚至用幾個(gè)架子來(lái)擺放,某寶上銷(xiāo)量最大的開(kāi)發(fā)板一定是51 開(kāi)發(fā)板, 很少有哪個(gè)嵌入式工程師或者學(xué)生曾經(jīng)避開(kāi)過(guò)51 而直達(dá)別的平臺(tái),我們從51 學(xué)到了MCU 的概念,學(xué)到了控制的概念。但到了今天,51 的低成本,易用,已經(jīng)不占優(yōu)勢(shì), 反觀現(xiàn)在的微控領(lǐng)域應(yīng)用,對(duì)MCU 的資源要求越來(lái)高,51 越來(lái)越不適應(yīng)。

8 位內(nèi)核的51 類(lèi)MCU 的資源往往是最大幾K-100K 的flash。 100-幾K 字節(jié)的RAM, IO, 串口,定時(shí)器,8 位數(shù)據(jù)總線(xiàn), AD 等簡(jiǎn)單的資源。 目標(biāo)確定,單一。結(jié)構(gòu)簡(jiǎn)單,指令簡(jiǎn)單。 易于理解和操作,這些特點(diǎn)也是51 能深入人心的因素。 目前依然是高校的主導(dǎo)實(shí)驗(yàn)平臺(tái)。 也是很多企業(yè)的應(yīng)用平臺(tái)。

隨著coretex-m3 內(nèi)核的STM32 在中國(guó)的興起,引起了廣大51 使用者的注意,對(duì)于我當(dāng)初進(jìn)入時(shí)的認(rèn)識(shí),我覺(jué)得STM32 速度非常快, flash,ram 好大。 能操作SD 卡,這簡(jiǎn)直相當(dāng)于微控制器的硬盤(pán)了。Usb功能這一個(gè)51 以前從來(lái)沒(méi)有的東西,終于可以和計(jì)算機(jī)不需要串口就可以實(shí)現(xiàn)通信了。定時(shí)器那么多路,可以使我做多少的PWM 控制啊。16 位的FSMC 總線(xiàn),實(shí)現(xiàn)了高分辨率的LCD 也一樣可以高速控制了, 再不是51 那個(gè)僅僅能使用一些低分辨率且昂貴的LCM 比如12864 這些行將沒(méi)落的東東。 以前在51 想都不要想的ucos ucgui 都可以STM32 上盡情發(fā)揮了。還有好多好的功能,can 控制器輕易實(shí)現(xiàn)以前要組合電路才能實(shí)現(xiàn)的can 通信以及以太網(wǎng)的應(yīng)用等等。 這是真正意義的微控領(lǐng)域的SOC 芯片

初入STM32,可能我們最親切的就是在51 使用過(guò)的keil , 在51 它叫keil c51, 在arm 它叫RealviewMKD-ARM,簡(jiǎn)稱(chēng)它MDK, 現(xiàn)在版本是MDK4.22, 操作方法基本類(lèi)似于keil。 我們常用的功能除了編輯工程,編譯代碼,還會(huì)用到下載,調(diào)試。 我們?cè)?1 時(shí),可能會(huì)很少有人用仿真功能,因?yàn)?1 足夠的簡(jiǎn)單,腦子想的往往就是你所看到的。 直接下載到目標(biāo)板對(duì)你來(lái)說(shuō)更快捷。 所以在51 最常見(jiàn)的是下載器。 但在arm階段,資源繁雜,寄存器復(fù)雜。變量眾多,沒(méi)有一個(gè)仿真器,會(huì)感到那么的無(wú)助。因此 coretex-m3 的使用者基本都會(huì)擁有仿真器,一般分為ST-LINK ULINK 及JLINK,尤以JLINK 在中國(guó)的應(yīng)用最為普及。 我們都懂的原因,JLINK V8 的性?xún)r(jià)比是這幾個(gè)最好的。 所以,你需要在獲得了MDK 后,再擁有一個(gè)JLINK。 它不僅僅只支持STM32,它支持絕大多數(shù)的ARM 芯片。

51 使用者初入STM32,都會(huì)存在一個(gè)平臺(tái)轉(zhuǎn)換帶來(lái)迷惘的一個(gè)短暫過(guò)程,這是器件類(lèi)型變化較大造成的認(rèn)知差異。

但調(diào)整一下,這個(gè)不適會(huì)很快過(guò)去的。

??1先看看 51 和STM32 具有的相同類(lèi)型資源是哪些。根據(jù)你對(duì)51 的熟悉程度, 你會(huì)從STM32 的手冊(cè)上看到。 這些往往是較簡(jiǎn)單的,也是最容易理解的。 比如IO 口線(xiàn)控制,等等。

??2 STM32 高級(jí)一些的資源,往往也是需要較多精力去理解的。這可以在入門(mén)后再行學(xué)習(xí),比如USB,SDIO等。

??4編程方式的不同, 比如在 51,用置位或者復(fù)位指令就可以很方便的控制IO,而在STM32,由于所有資源的功能都和該資源對(duì)應(yīng)的32 位寄存器組的操作有關(guān)系。 因此對(duì)于資源的設(shè)置和操作都可能需要操作一個(gè)或者多個(gè)寄存器, 如果用多條指令來(lái)控制的話(huà),會(huì)引起閱讀的障礙,以及日后代碼維護(hù)復(fù)雜,因此ST 公司引入了庫(kù)函數(shù)的概念。用執(zhí)行庫(kù)函數(shù)的方式解決復(fù)雜的資源操作的問(wèn)題。

??4STM32 例程的MDK 工程都有相似的程序結(jié)構(gòu),結(jié)合手冊(cè)多看例程,會(huì)使你快速的形成對(duì)STM32 例程模板的認(rèn)識(shí),這個(gè)認(rèn)識(shí)一旦形成,剩下的代碼細(xì)節(jié)就好比是你預(yù)測(cè)到的填空題目。

當(dāng)你做好了想學(xué)習(xí)新平臺(tái)的準(zhǔn)備,那就義無(wú)反顧的投入CORETEX-M3 的懷抱吧。它會(huì)使你進(jìn)步到一個(gè)新的境界。 帶給你愉悅的技術(shù)享受。

如何迅速入門(mén)STM32單片機(jī)?

網(wǎng)上有大神說(shuō)如果會(huì)51單片機(jī)C語(yǔ)言一天可入門(mén)STM32,僅一天的時(shí)間,是否有真的這么快。這個(gè)要看自己給自己定的入門(mén)的標(biāo)準(zhǔn)了。

我眼中的入門(mén):(前提是你學(xué)過(guò)51 單片機(jī)和C 語(yǔ)言)

??1知道參考官方的什么資料來(lái)學(xué)習(xí),而不是陷入一大堆資料中無(wú)從下手。

??2知道如何參考官方的手冊(cè)和官方的代碼來(lái)獨(dú)立寫(xiě)自己的程序,而不是一味的看到人家寫(xiě)的代碼就覺(jué)得人家很牛逼。

??3消除對(duì)STM32 的恐懼,消除對(duì)庫(kù)開(kāi)發(fā)的恐懼,學(xué)習(xí)是一個(gè)快樂(lè)而富有成就感的過(guò)程。

學(xué)習(xí)本文時(shí),配合《STM32 中文參考手冊(cè)》GPIO 章節(jié)一起閱讀,效果會(huì)更佳,特別是涉及到寄存器說(shuō)明的部分。

1、51 與STM32 簡(jiǎn)介

51 是嵌入式學(xué)習(xí)中一款入門(mén)級(jí)的精典MCU,因其結(jié)構(gòu)簡(jiǎn)單,易于教學(xué),且可以通過(guò)串口編程而不需要額外的仿真器,所以在教學(xué)時(shí)被大量采用,至今很多大學(xué)在嵌入式教學(xué)中用的還是51。51 誕生于70 年代,屬于傳統(tǒng)的8 位單片機(jī),如今,久經(jīng)歲月的洗禮,既有其輝煌又有其不足。現(xiàn)在的市場(chǎng)產(chǎn)品競(jìng)爭(zhēng)激烈,對(duì)成本極其敏感,相應(yīng)地對(duì)MCU 的要求也更苛刻:功能更多,功耗更低,易用界面和多任務(wù)。面對(duì)這些要求,51 現(xiàn)有的資源就顯得得抓襟見(jiàn)肘了。所以無(wú)論是高校教學(xué)還是市場(chǎng)需求,都急需一款新的MCU 來(lái)為這個(gè)領(lǐng)域注入新的活力。

基于這市場(chǎng)的需求, ARM 公司推出了其全新的基于ARMv7 架構(gòu)的32 位Cortex-M3微控制器內(nèi)核。緊隨其后,ST(意法半導(dǎo)體)公司就推出了基于Cortex-M3 內(nèi)核的MCU—STM32。STM32 憑借其產(chǎn)品線(xiàn)的多樣化、極高的性?xún)r(jià)比、簡(jiǎn)單易用的庫(kù)開(kāi)發(fā)方式,迅速在眾多Cortex-M3 MCU 中脫穎而出,成為最閃亮的一顆新星。STM32 一上市就迅速占領(lǐng)了中低端MCU 市場(chǎng),受到了市場(chǎng)和工程師的無(wú)比青睞,頗有星火燎原之勢(shì)。

作為一名合格的嵌入式工程師,面對(duì)新出現(xiàn)的技術(shù),我們不是充耳不聞,而是要盡快吻合市場(chǎng)的需要,跟上技術(shù)的潮流。如今STM32 的出現(xiàn)就是一種趨勢(shì),一種潮流,我們要做的就是搭上這趟快車(chē),讓自己的技術(shù)更有競(jìng)爭(zhēng)力。

51 與STM32 架構(gòu)的區(qū)別

我們先普及一個(gè)概念,單片機(jī)(即MCU)里面有什么。一個(gè)人最重要的是大腦,身體的各個(gè)部分都在大腦的指揮下工作。MCU 跟人體很像,簡(jiǎn)單來(lái)說(shuō)是由一個(gè)最重要的內(nèi)核加其他外設(shè)組成,內(nèi)核就相當(dāng)于人的大腦,外設(shè)就如人體的各個(gè)功能器官。

下面我們來(lái)簡(jiǎn)單介紹下51 和STM32 的結(jié)構(gòu)。

151 系統(tǒng)結(jié)構(gòu)

51 系統(tǒng)結(jié)構(gòu)框圖

圖1 51 系統(tǒng)結(jié)構(gòu)框圖

我們說(shuō)的51 一般是指51 系列的單片機(jī),型號(hào)有很多,常見(jiàn)的有STC89C51、AT89S51,其中國(guó)內(nèi)用的最多的是STC89C51/2,下面我們就以STC89C51 來(lái)講解,并以51 簡(jiǎn)稱(chēng)。

內(nèi)核

51 由一個(gè)IP 核和片上外設(shè)組成,IP 核就是上圖中的CPU,片上外設(shè)就是上圖中的:時(shí)鐘電路、SFR 和RAM、ROM、定時(shí)/計(jì)數(shù)器、并行I/O 口、串行I/O 口、中斷系統(tǒng)。IP核跟外設(shè)之間由系統(tǒng)總線(xiàn)連接,且是8bit 的,速度有限。

51 內(nèi)核是上個(gè)世紀(jì)70 年代intel 公司設(shè)計(jì)的,速度只有12M,外設(shè)是IC 廠(chǎng)商(STC)在內(nèi)核的基礎(chǔ)上添加的,不同的IC 廠(chǎng)商會(huì)在內(nèi)核上添加不同的外設(shè),從而設(shè)計(jì)出各具特色的單片機(jī)。這里intel 屬于IP 核廠(chǎng)商,STC 屬于IC 廠(chǎng)商。我們后面要講的STM32 也一樣,ARM 屬于IP 核廠(chǎng)商,ARM 給ST 授權(quán),ST 公司在Cortex-M3 內(nèi)核的基礎(chǔ)上設(shè)計(jì)出STM32 單片機(jī)。

外設(shè)

我們?cè)趯W(xué)習(xí)51 的時(shí)候,關(guān)于內(nèi)核部分接觸的比較少,使用的最多的是片上外設(shè),我們?cè)诰幊痰臅r(shí)候操作的也就是這些外設(shè)。

編程的時(shí)候操作的寄存器位于SFR 和RAM 這個(gè)部分,其中SFR(特殊功能寄存器)占有128 字節(jié)(實(shí)際上只用了26 個(gè)字節(jié),只有26 個(gè)寄存器,其他都屬于保留區(qū)),RAM占有128 字節(jié),我們?cè)诔绦蛑卸x的變量就是放在RAM 中。其中SFR 和RAM 在地址上是重合的,都是在80~FF 這個(gè)地址區(qū)間,但在物理區(qū)間上是分開(kāi)的,所以51 的RAM 是有256 個(gè)字節(jié)。

編寫(xiě)好的程序是燒寫(xiě)到ROM 區(qū)。剩下的外設(shè)都是我們非常熟悉的IO 口,串口、定時(shí)器、中斷這幾個(gè)外設(shè)。

2STM32 系統(tǒng)結(jié)構(gòu)

STM32 系統(tǒng)結(jié)構(gòu)框圖

圖2 STM32 系統(tǒng)結(jié)構(gòu)框圖

內(nèi)核

在系統(tǒng)結(jié)構(gòu)上,STM32 和51 都屬于單片機(jī),都是由內(nèi)核和片上外設(shè)組成。只是STM32 使用的Cortex-M3 內(nèi)核比51 復(fù)雜得多,優(yōu)秀得多,支持的外設(shè)也比51 多得多,同時(shí)總線(xiàn)寬度也上升到32bit,無(wú)論速度、功耗、外設(shè)都強(qiáng)與51。

從結(jié)構(gòu)框圖上看,對(duì)比51 內(nèi)核只有一種總線(xiàn),取指和取數(shù)共用。Cortex-M3 內(nèi)部有若干個(gè)總線(xiàn)接口,以使CM3 能同時(shí)取址和訪(fǎng)內(nèi)(訪(fǎng)問(wèn)內(nèi)存),它們是:

指令存儲(chǔ)區(qū)總線(xiàn)(兩條)、系統(tǒng)總線(xiàn)、私有外設(shè)總線(xiàn)。有兩條代碼存儲(chǔ)區(qū)總線(xiàn)負(fù)責(zé)對(duì)代碼存儲(chǔ)區(qū)(即FLASH 外設(shè))的訪(fǎng)問(wèn),分別是I-Code 總線(xiàn)和D-Code 總線(xiàn)。

I-Code 用于取指,D-Code 用于查表等操作,它們按最佳執(zhí)行速度進(jìn)行優(yōu)化。

系統(tǒng)總線(xiàn)(System)用于訪(fǎng)問(wèn)內(nèi)存和外設(shè),覆蓋的區(qū)域包括SRAM,片上外設(shè),片外RAM,片外擴(kuò)展設(shè)備,以及系統(tǒng)級(jí)存儲(chǔ)區(qū)的部分空間。

私有外設(shè)總線(xiàn)負(fù)責(zé)一部分私有外設(shè)的訪(fǎng)問(wèn),主要就是訪(fǎng)問(wèn)調(diào)試組件。它們也在系統(tǒng)級(jí)存儲(chǔ)區(qū)。

還有一個(gè)MDA 總線(xiàn),從字面上看,DMA 是data memory access 的意思,是一種連接內(nèi)核和外設(shè)的橋梁,它可以訪(fǎng)問(wèn)外設(shè)、內(nèi)存,傳輸不受CPU 的控制,并且是雙向通信。簡(jiǎn)而言之,這個(gè)家伙就是一個(gè)速度很快的且不受老大控制的數(shù)據(jù)搬運(yùn)工,這個(gè)在51 里面是沒(méi)有的。

外設(shè)

從結(jié)構(gòu)框圖上看,STM32 比51 的外設(shè)多得多,51 有的串口、定時(shí)器、IO 口等外設(shè)STM32 都有。STM32 還多了很多特色外設(shè):如FSMC、SDIO、SPI、I2C 等,這些外設(shè)按照速度的不同,分別掛載到AHB、APB2、APB1 這三條總線(xiàn)上

3、小結(jié)

從內(nèi)核和外設(shè)這兩大方面來(lái)比較,STM32 之于51 就是一個(gè)升級(jí)版的單片機(jī)。它適應(yīng)市場(chǎng),引流潮流,在中低端的微控制器中流光溢彩。

2、學(xué)習(xí)方法的區(qū)別

學(xué)習(xí)51 用寄存器,學(xué)習(xí)STM32 用庫(kù)。

以前我們?cè)趯W(xué)習(xí)51 的時(shí)候,用的是寄存器編程的方法,想要實(shí)現(xiàn)什么效果,直接往寄存器里面賦值,優(yōu)點(diǎn)是直觀,簡(jiǎn)單粗暴,知道自己具體干了啥,心里踏實(shí)。

直接操作寄存器之所以在51 上可行,究其原因,我想有兩點(diǎn):

??151 主頻不高,資源有限,必須注重程序執(zhí)行的效率,只能直接操作寄存器。關(guān)鍵的地方還得用匯編,不適合用固件庫(kù)。

要知道當(dāng)初我們學(xué)習(xí)51 單片機(jī)的時(shí)候用的還是匯編,連現(xiàn)在的C 編程都不是,就更別說(shuō)什么庫(kù)函數(shù)編程。

??251 功能簡(jiǎn)單,寄存器不多。以國(guó)內(nèi)普及最廣的STC89C52 為例,寄存器全部加起來(lái)不到30 個(gè)。按照功能區(qū)分來(lái)記的話(huà),可以把每個(gè)寄存器背的滾瓜爛熟,并且寄存器每一位的功能都可以記得住,在編程的時(shí)候做到了然于胸。

現(xiàn)在從51 過(guò)度到STM32 的學(xué)習(xí),很多人還是喜歡沿用51 的學(xué)習(xí)方法。接受不了庫(kù),在學(xué)習(xí)庫(kù)的時(shí)候陷入迷糊之中,來(lái)回幾個(gè)月下來(lái),都不知道到底有沒(méi)學(xué)會(huì)STM32,因?yàn)樵谶@一路的學(xué)習(xí)中都是在調(diào)用庫(kù)函數(shù),壓根就沒(méi)有操作過(guò)寄存器,心里面很不踏實(shí)。其實(shí)大家在調(diào)用庫(kù)函數(shù)的時(shí)候心中難道就沒(méi)有疑問(wèn),庫(kù)的底層是怎么實(shí)現(xiàn)的?難道就沒(méi)有勇氣對(duì)庫(kù)的底層一探究竟。可最后當(dāng)我們開(kāi)始跟蹤庫(kù)函數(shù)底層的時(shí)候,看到一堆的宏定義、結(jié)構(gòu)體、指針、各種的文件包含,而且注釋全部都是英文的,是不是又心生忌憚。

鑒于此,我想用兩個(gè)原因來(lái)總結(jié)下很多初學(xué)者畏懼庫(kù)不愿意用庫(kù)的原因。

??1C 語(yǔ)言知識(shí)點(diǎn)的欠缺

庫(kù)在實(shí)現(xiàn)寄存器映像時(shí)使用的宏定義,強(qiáng)制類(lèi)型轉(zhuǎn)換,在定義寄存器時(shí)使用的結(jié)構(gòu)體,在外設(shè)初始化函數(shù)時(shí)使用的指針,在組織頭文件時(shí)使用的條件編譯等C 語(yǔ)言知識(shí),在大學(xué)課程中很少涉及,大多數(shù)老師也基本是不講。在一些簡(jiǎn)單的51 單片機(jī)編程中又很少會(huì)用到這些知識(shí)。學(xué)單片機(jī),做嵌入式開(kāi)發(fā)其實(shí)80%的工作都跟C 語(yǔ)言編程相關(guān),剩下的20%的工作就是閱讀各種數(shù)據(jù)手冊(cè),熟悉各種硬件外設(shè)。所以掌握這些基本的C 語(yǔ)言知識(shí),是嵌入式學(xué)習(xí)中一道邁不過(guò)去的坎,STM32 的庫(kù)則給了我們一次提升C 的機(jī)會(huì)。凡是可以從書(shū)本中找到的,相信我們基本都可以學(xué)會(huì),很多初學(xué)者并不是不夠聰明或者勤奮,只是缺少方向性的指導(dǎo)罷了。對(duì)于這欠缺的知識(shí)點(diǎn)我們稍微花點(diǎn)時(shí)間就可以掌握,剩下的就是不斷地實(shí)踐調(diào)試。這里我為大家推薦一本C 語(yǔ)言的書(shū)籍《C 和指針》。

??2程序架構(gòu)設(shè)計(jì)思想的欠缺

這個(gè)比較難搞,很多C 語(yǔ)言學(xué)習(xí)得挺好好的人,也比較難掌握。還好我們遇到了STM32 的庫(kù),這給了我們一個(gè)學(xué)習(xí)和提升C 語(yǔ)言絕佳的機(jī)會(huì)。庫(kù)的整個(gè)架構(gòu)是如何搭建起來(lái)的,代碼上是如何如何一步一步寫(xiě)出來(lái)的:從寄存器映像開(kāi)始,到寄存器的封裝,然后到函數(shù)的編寫(xiě),到每個(gè)外設(shè)函數(shù)對(duì)應(yīng)的驅(qū)動(dòng)文件,這里面涉及到了大量的條件編譯,文件包含的思想,對(duì)應(yīng)剛寫(xiě)過(guò)幾行51 單片機(jī)的初學(xué)者來(lái)說(shuō)簡(jiǎn)直就是噩夢(mèng)。但是,如果你把這一系列的關(guān)系弄明白了,那么對(duì)庫(kù)的整個(gè)架構(gòu)也了解的差不多了,以后你就不用嚷嚷著說(shuō)要操作寄存器了。

如果你一開(kāi)始不喜歡用庫(kù),對(duì)庫(kù)開(kāi)發(fā)很忌憚,那么請(qǐng)自問(wèn):是不是我的C 語(yǔ)學(xué)得不夠好。庫(kù)是一種全新的學(xué)習(xí)方法,是一種潮流,我更把它看做是與C 語(yǔ)言的又一次歷練和提升。是否用庫(kù),只差你一個(gè)閃亮的回眸。

3、用寄存器點(diǎn)亮LED

為了順利過(guò)渡到庫(kù)開(kāi)發(fā),在STM32 編程的開(kāi)始,我們對(duì)照51 點(diǎn)亮一個(gè)LED 的方法,給大家演示一下STM32 如何用操作寄存器的方法點(diǎn)亮一個(gè)LED,然后再慢慢講解到底什么是庫(kù),讓大家知道庫(kù)跟寄存器的關(guān)系。

1用51 點(diǎn)亮一個(gè)LED

在用STM32 點(diǎn)亮一個(gè)LED 之前,我們先來(lái)復(fù)習(xí)下用51 如何點(diǎn)亮一個(gè)LED。

硬件上我們假設(shè)51 單片機(jī)的P0 口的第0 位接了一個(gè)LED,負(fù)邏輯亮。如果我們要點(diǎn)亮這個(gè)LED,代碼上我們會(huì)這么寫(xiě):

這里面我們用的是總線(xiàn)操作的方法,即是對(duì)P0 口的8 個(gè)IO 同時(shí)操作,但起作用的只是P0^0。

除了這種總線(xiàn)操作的方法,我們還學(xué)習(xí)過(guò)位操作,利用51 編譯器的關(guān)鍵字sbit,我們可以定義一個(gè)位變量:

那么LED = 0;就點(diǎn)亮了LED,LED = 1;就關(guān)閉了LED。為了讓程序看起來(lái)見(jiàn)名知義,我們定義兩個(gè)宏:

點(diǎn)亮和關(guān)閉LED 的代碼就變成了:

上面總線(xiàn)和位操作的的方法,學(xué)過(guò)51 的朋友是非常熟悉的,也很容易理解。

那么我們?cè)僬f(shuō)一下大家容易忽略的幾個(gè)知識(shí)點(diǎn)。

??1什么是寄存器

在點(diǎn)亮LED 的時(shí)候,我們都是用操作寄存器的方法來(lái)實(shí)現(xiàn)的,那大家是否想過(guò),這個(gè)寄存器到底是什么?為什么我們可以直接操作P0 口?

解答上面的問(wèn)題之前,我們先簡(jiǎn)單介紹下51 單片機(jī)的主要組成部分,這對(duì)我們學(xué)習(xí)其他單片機(jī)也有好處。

我們以國(guó)內(nèi)的STC89C51 為例,該單片機(jī)主要由51 內(nèi)核、外設(shè)IP、和總線(xiàn)這三大部分組成。內(nèi)核是由Intel 公司生產(chǎn)的,外設(shè)IP 就是STC 公司在內(nèi)核的基礎(chǔ)上添加的諸如定時(shí)器、串口、IO 口等這些東西,總線(xiàn)就是用來(lái)連接內(nèi)核和外設(shè)的接口單元。Intel 在這里屬于IP 核設(shè)計(jì)公司,STC 屬于IC 設(shè)計(jì)公司。世界上能設(shè)計(jì)IP 核的公司屈指可數(shù)。我們非常熟悉的ARM 公司就屬于IP 核設(shè)計(jì)公司,ARM 給其他公司授權(quán),其他IC 公司就在ARM 內(nèi)核上設(shè)計(jì)出各具特色的MCU,我們后面要學(xué)習(xí)的STM32 就是屬于一中基于ARM 內(nèi)核的MCU。

寄存器則是內(nèi)置于各個(gè)IP 外設(shè)中,是一種用于配置外設(shè)功能的存儲(chǔ)器,就是一種內(nèi)存,并且有想對(duì)應(yīng)的地址。學(xué)過(guò)C 語(yǔ)言我們就知道,要操作這些內(nèi)存就可以使用C 語(yǔ)言中的指針,通過(guò)尋址的方式來(lái)操作這些具有特殊功能的內(nèi)存—寄存器。比如P0 口對(duì)應(yīng)的地址是0X80,那么我們要修改0X80 這個(gè)地址對(duì)應(yīng)的內(nèi)存的內(nèi)容的話(huà),按照常理可以這樣操作:

可當(dāng)我們編譯的時(shí)候,編譯器會(huì)報(bào)錯(cuò),在51 里面只能通過(guò)SFR 和SBIT 這兩個(gè)關(guān)鍵字來(lái)實(shí)現(xiàn)寄存器映像,不能直接操作寄存器對(duì)應(yīng)的地址,這是51 相較于STM32 不同的地方。

51 單片機(jī)的這些寄存器位于地址80H~FFH 中,對(duì)應(yīng)著128 個(gè)地址,但不是每個(gè)地址都是有效的,51 系列的單片機(jī)有21 個(gè),52 系列的則有26 個(gè),其他的都是保留區(qū)。

圖3 51 寄存器映射

??2寄存器映射

實(shí)際上我們?cè)诰幊痰臅r(shí)候并不是通過(guò)指針來(lái)操作寄存器的,而是直接給P0、P1 這些端口寄存器賦值。那么這些外設(shè)資源是如何與地址建立一一對(duì)應(yīng)的關(guān)系(寄存器映射定義),這得益與51 特有的兩個(gè)關(guān)鍵字:SFR 和sbit,其他單片機(jī)沒(méi)有,只能用其他的方式來(lái)實(shí)現(xiàn)寄存器映射。這兩個(gè)關(guān)鍵字幫我們實(shí)現(xiàn)了所有寄存器的定義,所以我們才可以像操作普通變量一個(gè)來(lái)操作寄存器。其實(shí)我們一開(kāi)始提到的點(diǎn)亮LED 的代碼,全貌應(yīng)該是這樣的:

為了方便起見(jiàn),我們可以把寄存器映射全部寫(xiě)好封裝在一個(gè)頭文件里面,不用每用一個(gè)寄存器就定義一次。其實(shí)這方面的工作不用我們做,我們?cè)诰幊痰臅r(shí)候都會(huì)在開(kāi)始的地方添加一個(gè)頭文件:

這個(gè)頭文件已經(jīng)實(shí)現(xiàn)了全部寄存器的定義,該文件是keil 自帶,在安裝目錄:KeilC51INC 下可以找到。這個(gè)文件實(shí)現(xiàn)了字節(jié)寄存器和位寄存器的定義。

??3啟動(dòng)文件—STARTUP.A51

還有一個(gè)就是啟動(dòng)代碼,這個(gè)也是很多初學(xué)者容易忽略的地方,對(duì)于這部分我們主要總結(jié)下它的功能,不詳解講解里面的代碼。

單片機(jī)在上電復(fù)位后,首先執(zhí)行的是啟動(dòng)文件—STARTUP.A51,而不是我們通常看到的main 函數(shù)。我們新建51 工程的時(shí)候會(huì)有一個(gè)提示:是否拷貝啟動(dòng)代碼到當(dāng)前的工程,我們一般選擇是。

圖4 是否添加啟動(dòng)代碼

啟動(dòng)代碼用匯編語(yǔ)言編寫(xiě),主要實(shí)現(xiàn)了以下功能:清除內(nèi)部數(shù)據(jù)存儲(chǔ)器、清除外部數(shù)據(jù)存儲(chǔ)器、清除外部頁(yè)儲(chǔ)存器、初始化small 模式下的可重入棧和指針、初始化large 模式下可重入棧和指針、初始化compact 模式下的可重入棧和指針、初始化8051 硬件棧指針、傳遞初始化全局變量的控制命令或者在沒(méi)有初始化全局變量時(shí)給main 函數(shù)傳遞命令。然后程序就跳轉(zhuǎn)到main 函數(shù),來(lái)到我們熟知的C 世界。

??4總結(jié)

在講解用51 點(diǎn)亮LED 的時(shí)候,我們補(bǔ)充了什么是寄存器、寄存器映射、啟動(dòng)代碼這三部分的內(nèi)容,這三部分內(nèi)容本來(lái)是放到STM32 里面講解的,但考慮到大家已經(jīng)有51 的基礎(chǔ),并且對(duì)51 比較熟悉,那我再添加點(diǎn)內(nèi)容,大家自然沒(méi)有那么抗拒,并且可以根據(jù)上面講的內(nèi)容親自實(shí)踐,學(xué)習(xí)得也會(huì)更深入。那當(dāng)我再在STM32 講解這幾個(gè)內(nèi)容的時(shí)候,大家就會(huì)對(duì)比著學(xué)習(xí),對(duì)STM32 也就沒(méi)有那么忌憚。

2用STM32 點(diǎn)亮一個(gè)LED

對(duì)比著51 點(diǎn)亮LED 的方法,我們先用操作寄存器的方法用STM32 點(diǎn)亮一個(gè)LED,然后再一步步完善代碼,構(gòu)建最簡(jiǎn)單的庫(kù)函數(shù),讓我們知道庫(kù)是怎么建立起來(lái)的。

在寫(xiě)代碼之前,我們先建一個(gè)工程。大家要注意的是,雖然51 跟STM32 用的都是keil,但是針對(duì)的MCU 是不一樣,軟件在安裝的時(shí)候要安裝在不同的目錄且不能安裝在英文目錄,不然會(huì)起沖突。我們這里用的是keil5,MDK5.15 版本。

??1新建工程

用KEIL5 新建一個(gè)工程,把工程放在一個(gè)事先建好的文件夾內(nèi),工程命名為REG 后保存。然后在工程目錄下添加啟動(dòng)文件:startup_stm32f10x_hd.s,該文件可以從KEIL5 安裝目錄找到,也可以從ST 庫(kù)里面找到,然后把啟動(dòng)文件添加到工程里面。

??2啟動(dòng)文件—startup_stm32f10x_hd.s

啟動(dòng)文件由匯編語(yǔ)言編寫(xiě),具體功能跟51 里面的啟動(dòng)文件:STARTUP.A51 差不多。

STM32 的啟動(dòng)文件主要實(shí)現(xiàn)了:

1、設(shè)置初始SP 。

2、設(shè)置初始PC=Reset_Handler

3、設(shè)置向量表入口地址,并初始化向量表。

4、調(diào)用庫(kù)函數(shù)SystemInit,把系統(tǒng)時(shí)鐘配置成72M,SystemInit 在庫(kù)文件system_stm32f10.c 定義。

5、跳轉(zhuǎn)到標(biāo)號(hào)_mian,最終來(lái)到C 的世界。這里我們先去除繁枝細(xì)節(jié),挑重點(diǎn)的講,主要理解第四和第五點(diǎn),在啟動(dòng)文件的147~155 行,是復(fù)位處理函數(shù),代碼如下:

這里我們簡(jiǎn)單介紹下這10 行代碼。

第一行是程序注釋?zhuān)趨R編里面注釋用的是“;”,跟C 語(yǔ)言不一樣。

第二行是定義了一個(gè)子程序:Reset_Handler。PROC 是子程序定義偽指令。一般用法為:

其中NEAR 和FAR 是屬性詞。NEAR 屬性(段內(nèi)近調(diào)用): 調(diào)用程序和子程序在同一代碼段中,只能被相同代碼段的其他程序調(diào)用。FAR 屬性(段間遠(yuǎn)調(diào)用): 調(diào)用程序和子程序不在同一代碼段中,可以被相同或不同代碼段的程序調(diào)用。

第三行EXPORT 表示Reset_Handler 這個(gè)子程序可供其他模塊調(diào)用。

關(guān)鍵字[WEAK] 表示弱定義,如果編譯器發(fā)現(xiàn)在別處定義了同名的函數(shù),則在鏈接時(shí)用別處的地址進(jìn)行鏈接,如果其它地方?jīng)]有定義,編譯器也不報(bào)錯(cuò),以此處地址進(jìn)行鏈接。

第四行和第五行IMPORT 說(shuō)明SystemInit 和__main 這兩個(gè)標(biāo)號(hào)在其他文件,在鏈接的時(shí)候需要到其他文件去尋找。

SystemInit 在庫(kù)文件system_stm32f10x.c 實(shí)現(xiàn),用來(lái)初始化STM32 的一系列時(shí)鐘,把系統(tǒng)時(shí)鐘設(shè)置為72MHZ。STM32 的時(shí)鐘比51 單片機(jī)復(fù)雜,需要經(jīng)過(guò)一系列的配置才能達(dá)到穩(wěn)定運(yùn)行的狀態(tài)。

__main 其實(shí)不是我們定義的,當(dāng)編譯器編譯時(shí),只要遇到這個(gè)標(biāo)號(hào)就會(huì)定義這個(gè)函數(shù),該函數(shù)的主要功能是:負(fù)責(zé)初始化棧、堆,配置系統(tǒng)環(huán)境,并在最后跳轉(zhuǎn)到用戶(hù)自定義的main 函數(shù),從此來(lái)到C 的世界。

第六行把SystemInit 的地址加載到寄存器R0。

第七行程序跳轉(zhuǎn)到R0 中的地址執(zhí)行程序,之后系統(tǒng)的時(shí)鐘就被設(shè)置成72MHZ。

第八行把_main 的地址加載到寄存器R0。

第九行程序跳轉(zhuǎn)到R0 中的地址執(zhí)行程序,執(zhí)行完畢之后就去到我們熟知的C 世界。

第十行表示子程序的結(jié)束。

總結(jié)下就是,Reset_Handler 這個(gè)函數(shù)執(zhí)行了兩個(gè)函數(shù)調(diào)用,一個(gè)是SystemInit,把系統(tǒng)時(shí)鐘設(shè)置成72M,令一個(gè)是__main,初始化好系統(tǒng)環(huán)境,最終調(diào)用C 的main,從此去到C 的世界。

等下我們點(diǎn)亮LED 的時(shí)候采用最簡(jiǎn)單的方法,直接使用內(nèi)部的LSI 時(shí)鐘(8MHZ)作為主時(shí)鐘即可,不使用外部時(shí)鐘LSE。

__main 函數(shù)由編譯器生成,負(fù)責(zé)初始化棧、堆等,并在最后跳轉(zhuǎn)到用戶(hù)自定義的main()函數(shù),來(lái)到C 的世界。

??3新建main.c

用記事本新建一個(gè)main.c 文件放到工程目錄下,然后把main.c 添加到工程中。

現(xiàn)在我們就可以開(kāi)始編寫(xiě)程序了,我們先編寫(xiě)一個(gè)main 函數(shù),里面啥都沒(méi)有,暫時(shí)為空。這時(shí)跟編寫(xiě)51 程序時(shí)是不是很像。

現(xiàn)在我們可以編譯看看,看看有啥現(xiàn)象。

這時(shí)候出現(xiàn)如下錯(cuò)誤:

錯(cuò)誤提示說(shuō)SystemInit 沒(méi)有定義。從分析啟動(dòng)文件時(shí)我們知道,Reset_Handler 調(diào)用了該函數(shù)用來(lái)初始化系統(tǒng)時(shí)鐘,而該函數(shù)是在庫(kù)文件system_stm32f10x.c 中實(shí)現(xiàn)的。我們重新寫(xiě)一個(gè)這樣的函數(shù)也可以,把功能完整實(shí)現(xiàn)一遍,但是為了簡(jiǎn)單起見(jiàn),我們?cè)趍ain 文件里面定義一個(gè)SystemInit 空函數(shù),為的是騙過(guò)編譯器,把這個(gè)錯(cuò)誤去掉。關(guān)于配置系統(tǒng)時(shí)鐘我們?cè)诤竺嬖賹?xiě)簡(jiǎn)單的代碼。

這時(shí)我們?cè)倬幾g就沒(méi)有錯(cuò)了,完美解決。還有一個(gè)方法就是在啟動(dòng)文件中把有關(guān)SystemInit 的代碼注釋掉也可以,代碼如下所示:

??4控制IO 口

下面我們從三個(gè)方面來(lái)講解STM32 的IO 在控制LED 時(shí)跟51 的區(qū)別。有關(guān)STM32 的IO 的寄存器介紹,我們可以看《STM32 中文參考手冊(cè)》的第八章即可,下面涉及到的IO寄存器均來(lái)自這一章的第二小節(jié):8.2 GPIO 寄存器描述

電平控制

51 單片機(jī)的IO 口如果要輸出1 和0,可以直接賦值,不用控制其他寄存器。

而STM32 的IO 口比較復(fù)雜,如果要輸出1 和0,則要通過(guò)控制:端口輸出數(shù)據(jù)寄存器ODR 來(lái)實(shí)現(xiàn),ODR 是:Output data register 的簡(jiǎn)寫(xiě),在STM32 里面,其寄存器的命名名稱(chēng)都是英文的簡(jiǎn)寫(xiě),很容易記住。從手冊(cè)上我們知道ODR 是一個(gè)32 位的寄存器,低16位有效,高16 位保留。低16 位對(duì)應(yīng)著IO0~IO16,只要往相應(yīng)的位置寫(xiě)入0 或者1 就可以輸出低或者高電平。

PB0 輸出低電平,代碼如下:

這時(shí)候編譯,我們會(huì)發(fā)現(xiàn)有個(gè)錯(cuò)誤,說(shuō)GPIOB_ODR 沒(méi)有定義,不過(guò)我們確實(shí)沒(méi)有定義。在51 單片機(jī)中,我們可以直接往P0 口賦值,那是因?yàn)樵趓eg51.h 這個(gè)頭文件中實(shí)現(xiàn)了P0 口這個(gè)寄存器的映像,用的是51 特有的關(guān)鍵字SFR 來(lái)定義的。

STM32 跟51 不一樣,沒(méi)有SFR,只能用其他的方式來(lái)實(shí)現(xiàn)寄存器映像。因?yàn)榧拇嫫鲗?shí)際上就是具有特殊功能的內(nèi)存,那么我們可以通過(guò)宏定義來(lái)實(shí)現(xiàn)寄存器映像,其實(shí)ST的庫(kù)函數(shù)中用的也是這種方法。

從手冊(cè)中我們看到ODR 寄存器的地址偏移是:0CH,這個(gè)偏移地址是基于端口的起始地址而言的。在STM32 中,每個(gè)外設(shè)都有一個(gè)起始地址,叫做外設(shè)基地址,外設(shè)的寄存器就以這個(gè)基地址為標(biāo)準(zhǔn)按照順序排列,跟結(jié)構(gòu)體里面的成員差不多。

在手冊(cè)中的第二章:存儲(chǔ)器和總線(xiàn)構(gòu)架的2.3:存儲(chǔ)器映像小節(jié)中可以查看到所有外設(shè)的基地址,如下:

圖5 STM32 寄存器組起始地址

其中GPIOB 的起始地址是:0X4001 0C00,這樣就可以算出GPIOB_ODR 寄存器的地址是:0X4001 0C00 + 0X0C = 0X4001 0C0C。現(xiàn)在我們就可以定義GPIOB_ODR 這個(gè)寄存器了,代碼如下:

有了這個(gè)寄存器定義,我們就可以直接操作GPIOB_ODR 了。

方向控制

雖然配置了ODR 寄存器,但是這個(gè)時(shí)候還不能點(diǎn)亮LED,因?yàn)镾TM32 的IO 口還要配置方向,這個(gè)由端口配置寄存器來(lái)控制。端口配置寄存器分為高低兩個(gè),每4bit 控制一個(gè)IO 口,所以端口配置低寄存器:CRL 控制這IO 口的低8 位,端口配置高寄存器:CRH控制這IO 口的高8bit。在4 位一組的控制位中,CNFy[1:0] 用來(lái)控制端口的輸入輸出,MODEy[1:0]用來(lái)控制輸出模式的速率,即輸出時(shí),IO 電平翻轉(zhuǎn)的速度。

輸入有三種模式,輸出有4 中模式,我們?cè)诳刂芁ED 的時(shí)候選擇通用推挽輸出。

輸出速率有三種模式:2M、10M、50M,這里我們選擇2M。

同GPIOB_ODR 一樣,我們也可以算出GPIO_CRL 的地址為:0x40010C00。那么設(shè)置PB0 為通用推挽輸出,輸出速率為2M 的代碼則如下所示:

時(shí)鐘控制

當(dāng)我們?cè)O(shè)置了IO 口的方向,并在相應(yīng)的輸出寄存器里面輸入了值的時(shí)候,以為現(xiàn)在總算可以點(diǎn)亮LED 了吧,其實(shí)還差最后一步。

STM32 外設(shè)很多,為了降低功耗,每個(gè)外設(shè)都對(duì)應(yīng)著一個(gè)時(shí)鐘,在系統(tǒng)復(fù)位的時(shí)候這些時(shí)鐘都是被關(guān)閉的,如果想要外設(shè)工作,必須把相應(yīng)的時(shí)鐘打開(kāi)。

STM32 的所有外設(shè)的時(shí)鐘由一個(gè)專(zhuān)門(mén)的外設(shè)來(lái)管理,叫RCC(reset and clockcontrol),RCC 在STM32 中文參考手冊(cè)的第六章。

STM32 的外設(shè)因?yàn)樗俾实牟煌謩e掛載到三條總系上:AHB、APB2、APB1,APB為高速總線(xiàn),APB2 次之,APB1 再次之。所以的IO 口都掛載到APB2 總線(xiàn)上,屬于高速外設(shè)。時(shí)鐘由APB2 外設(shè)時(shí)鐘使能寄存器(RCC_APB2ENR)來(lái)控制,其中PB 端口的時(shí)鐘由該寄存器的位3 寫(xiě)1 使能。

同ODR 和CRL,我們可以算出RCC_APB2ENR 的地址為:0x40021018。那么使能PB 口的時(shí)鐘代碼則如下所示:

如果你足夠細(xì)心,你會(huì)發(fā)現(xiàn)我們雖然開(kāi)了端口時(shí)鐘,那這個(gè)時(shí)鐘到底是多大?時(shí)鐘到底是從哪里來(lái)的?

如果我們用的是庫(kù),那么有個(gè)庫(kù)函數(shù)SystemInit,會(huì)幫我們把系統(tǒng)時(shí)鐘設(shè)置成72M。現(xiàn)在我們沒(méi)有使用庫(kù),那現(xiàn)在時(shí)鐘是多少?答案是8M,當(dāng)外部HSE 沒(méi)有開(kāi)啟或者出現(xiàn)故障的時(shí)候,系統(tǒng)時(shí)鐘由內(nèi)部低速時(shí)鐘LSI 提供,現(xiàn)在我們是沒(méi)有開(kāi)啟HSE,所以系統(tǒng)默認(rèn)的時(shí)鐘是LSI=8M。至于更深入的細(xì)節(jié)我們?cè)诤竺娴腞CC 時(shí)鐘樹(shù)中再詳細(xì)分析。如果你想自己先嘗鮮,那么看RCC 外設(shè)中的:時(shí)鐘控制寄存器(RCC_CR)和時(shí)鐘配置寄存器(RCC_CFGR)這兩個(gè)寄存器即可。

水到渠成

控制了電平,配置了方向,開(kāi)啟了時(shí)鐘,經(jīng)過(guò)這三步,我們總算可以控制一個(gè)LED了。比起51 直接輸出電平,控制STM32 的IO 多了兩步:即配置方向可開(kāi)啟時(shí)鐘。比起AVR 和PIC 這兩種單片機(jī)則多了開(kāi)啟時(shí)鐘這一步。

現(xiàn)在我們完整組織下用STM32 控制一個(gè)LED 的代碼:

很多人說(shuō)學(xué)習(xí)STM32 很難,一堆的寄存器,不知道怎么操作,特別是那些剛學(xué)習(xí)完51 的朋友,不知道怎么過(guò)度。這里我們對(duì)比了51 的編程方法,寫(xiě)了個(gè)簡(jiǎn)單的用STM32 寄存器點(diǎn)亮LED 的方法,希望可以起到拋磚引玉的作用。

4、再接再厲—構(gòu)建庫(kù)的雛形

學(xué)習(xí)STM32 存在著一個(gè)用寄存器好還是用庫(kù)好的爭(zhēng)議點(diǎn),就好比編程是用匯編好還是用C 好一樣。其實(shí)孰優(yōu)孰劣,市場(chǎng)自有定論,用戶(hù)群說(shuō)明一切。

雖然我們上面用寄存器點(diǎn)亮了LED,乍看一下好像代碼也很簡(jiǎn)單,但是我們別僥幸以后就可以一直用寄存器開(kāi)發(fā)。在用寄存器點(diǎn)亮LED 的時(shí)候,我們是否發(fā)現(xiàn)STM32 的寄存器都是32 位的,在配置的時(shí)候非常容易出錯(cuò),而且代碼還很不好理解。所以學(xué)習(xí)TM32 最好的方法是用庫(kù),然后在庫(kù)的基礎(chǔ)上了解底層,看遍所有寄存器。

但是很多人對(duì)庫(kù)還是很忌憚,因?yàn)橐婚_(kāi)始用庫(kù)的時(shí)候有很多代碼,很多文件,不知道如何入手。不知道你是否認(rèn)同這么一句話(huà):一切的恐懼都來(lái)源于認(rèn)知的空缺。我們對(duì)庫(kù)忌憚那是因?yàn)槲覀儾恢朗裁词菐?kù),不知道庫(kù)是怎么實(shí)現(xiàn)的。

接下來(lái),我們?cè)诩拇嫫鼽c(diǎn)亮LED 的代碼上繼續(xù)完善,把代碼一層層封裝,實(shí)現(xiàn)庫(kù)的最初的雛形,相信經(jīng)過(guò)這一步的學(xué)習(xí)后,你會(huì)對(duì)庫(kù)的運(yùn)用做到游刃有余。這里我們只講關(guān)于GPIO 庫(kù),其他外設(shè)的我們直接參考庫(kù)學(xué)習(xí)即可,不必自己寫(xiě)。

1定義外設(shè)寄存器結(jié)構(gòu)體

上面我們?cè)诓僮骷拇嫫鞯臅r(shí)候,操作的是寄存器的絕對(duì)地址,如果每個(gè)寄存器都這樣操作,那將非常麻煩。

我們考慮到外設(shè)寄存器的地址都是基于外設(shè)基地址的偏移地址,都是在外設(shè)基地址上逐個(gè)連續(xù)遞增的,每個(gè)寄存器占32 個(gè)或者16 個(gè)字節(jié),這種方式跟結(jié)構(gòu)體里面的成員類(lèi)似。所以我們可以定義一種外設(shè)結(jié)構(gòu)體,結(jié)構(gòu)體的地址等于外設(shè)的基地址,結(jié)構(gòu)體的成員等于寄存器,成員的排列順序跟寄存器的順序一樣。這樣我們操作寄存器的時(shí)候就不用每次都找到絕對(duì)地址,只要知道外設(shè)的基地址就可以操作外設(shè)的全部寄存器,即操作結(jié)構(gòu)體的成員即可。

下面我們先定義一個(gè)GPIO 寄存器結(jié)構(gòu)體,結(jié)構(gòu)體里面的成員是GPIO 的寄存器,成員的順序按照寄存器的偏移地址從低到高排列,成員類(lèi)型跟寄存器類(lèi)型一樣。

在《STM32 中文參考手冊(cè)》8.2 寄存器描述章節(jié),我們可以找到結(jié)構(gòu)體里面的7 個(gè)寄存器描述。在點(diǎn)亮LED 的時(shí)候我們只用了CRL 和ODR 這兩個(gè)寄存器,至于其他寄存器的功能大家可以自行看手冊(cè)了解。

在GPIO 結(jié)構(gòu)體里面我們用了兩個(gè)數(shù)據(jù)類(lèi)型,一個(gè)是uint32_t,表示無(wú)符號(hào)的32 位整型,因?yàn)镚PIO 的寄存器都是32 位的。這個(gè)類(lèi)型聲明在標(biāo)準(zhǔn)頭文件stdint.h 里面,我們?cè)诔绦蛏现灰@個(gè)頭文件即可。

另外一個(gè)是__IO,這個(gè)是我們自己定義的,原型是volatile,作用就是告訴編譯器不要因優(yōu)化而省略此指令,必須每次都直接讀寫(xiě)其值,這樣就能確保每次讀或者寫(xiě)寄存器都真正執(zhí)行到位。

關(guān)于這兩個(gè)數(shù)據(jù)類(lèi)型,我們添加如下代碼:

2外設(shè)聲明

現(xiàn)在GPIO 寄存器結(jié)構(gòu)體已經(jīng)定義好了,STM32F1 系列的GPIO 端口分A~G,即GPIOA、GPIOB。。。。。。GPIOG。每個(gè)端口都含有GPIO_TypeDef 結(jié)構(gòu)體里面的寄存器,我們可以根據(jù)各個(gè)端口的基地址把GPIO 的各個(gè)端口定義成一個(gè)GPIO_TypeDef 類(lèi)型的指針,然后我們就可以根據(jù)端口名(實(shí)際上現(xiàn)在是結(jié)構(gòu)體指針了)來(lái)操作各個(gè)端口的寄存器,碼實(shí)現(xiàn)如下:

對(duì)于其他外設(shè)我們也可以這樣把外設(shè)的名字定義成一個(gè)外設(shè)寄存器結(jié)構(gòu)體類(lèi)型的指針,這里我們只講GPIO。

對(duì)于每個(gè)GPIO 的基地址我們可以從《STM32 中文參考手冊(cè)》2.3 小節(jié):存儲(chǔ)器映像中找到,如下所示:

圖6 APB2 總線(xiàn)外設(shè)寄存器起始地址

3外設(shè)內(nèi)存映射

講到基地址的時(shí)候我們?cè)僖艘粋€(gè)知識(shí)點(diǎn):Cortex-M3 存儲(chǔ)器系統(tǒng),這個(gè)知識(shí)點(diǎn)在《Cortex-M3 權(quán)威指南》第5 章里面講到。CM3 的地址空間是4GB,如下圖所示:

圖7 CM3 內(nèi)存映射

我們這里要講的是片上外設(shè),就是我們所說(shuō)的寄存器的根據(jù)地,其大小總共有512MB,512MB 是其極限空間,并不是每個(gè)單片機(jī)都用得完,實(shí)際上各個(gè)MCU 廠(chǎng)商都只是用了一部分而已。STM32F1 系列用到了:0x4000 0000 ~0x5003 FFFF。

??1APB1、APB2、AHB 總線(xiàn)基地址

現(xiàn)在我們說(shuō)的STM32 的寄存器就是位于這個(gè)區(qū)域,這里面ST 設(shè)計(jì)了三條總線(xiàn):AHB、APB2 和APB1,其中AHB 和APB2 是高速總線(xiàn),APB1 是低速總線(xiàn)。不同的外設(shè)根據(jù)速度不同分別掛載到這三條總線(xiàn)上。從下往上依次是:APB1、APB2、AHB,每個(gè)總線(xiàn)對(duì)應(yīng)的地址分別是:APB1:0x40000000,APB2:0x4001 0000,AHB:0x4001 8000。

這三條總線(xiàn)的基地址我們是從《STM32 中文參考手冊(cè)》2.3 小節(jié)—存儲(chǔ)器映像得到的:APB1 的基地址是TIM2 定時(shí)器的起始地址,APB2 的基地址是AFIO 的起始地址,AHB 的基地址是SDIO 的起始地址。

其中APB1 地址又叫做外設(shè)基地址,是所有外設(shè)的基地址,叫做PERIPH_BASE。

現(xiàn)在我們把這三條總線(xiàn)地址用宏定義出來(lái),以后我們?cè)诙x其他外設(shè)基地址的時(shí)候,只需要在這三條總線(xiàn)的基址上加上偏移地址即可,代碼如下:

??2 GPIO 端口基地址

因?yàn)镚PIO 掛載到APB2 總線(xiàn)上,那么現(xiàn)在我們就可以根據(jù)APB2 的基址算出各個(gè)GPIO 端口的基地址,用宏定義實(shí)現(xiàn)代碼如下:

現(xiàn)在我們把上面的代碼稍微整理下,如下:

在點(diǎn)亮LED 的時(shí)候,我們還開(kāi)了GPIO 的時(shí)鐘,用到了RCC 這個(gè)外設(shè),現(xiàn)在我們也定義一個(gè)RCC 寄存器結(jié)構(gòu)體,加上那些地址定義,總體代碼如下:

跟GPIO 不同的是,RCC 這個(gè)外設(shè)是掛載到AHB 總線(xiàn)上。

現(xiàn)在我們點(diǎn)亮LED 的函數(shù)就變成了

對(duì)比之前的代碼

一個(gè)用的是結(jié)構(gòu)體,一個(gè)用的是宏,僅僅從這三行代碼看不出有啥區(qū)別,但是如果要操作其他寄存器的時(shí)候,用結(jié)構(gòu)體就可以直接操作,用宏就還要一個(gè)個(gè)找到寄存器的絕對(duì)地址重新定義。

比如我們要操作GPIOB 的BSRR(bit reset register)的時(shí)候,用結(jié)構(gòu)體時(shí)我們就可以這樣操作:

這時(shí)候PB0 就輸出低電平,LED 被點(diǎn)亮。注意:BRR 低16 位有效,只能以字的形式操作,功能是復(fù)位相應(yīng)的IO 口,寫(xiě)1 清0,寫(xiě)0 沒(méi)有影響。

圖8 GPIO 端口位清除寄存器

現(xiàn)在我們?cè)僬硐麓a,如下所示:

4小結(jié)流程

現(xiàn)在我們來(lái)總結(jié)下上面代碼實(shí)現(xiàn)的過(guò)程,這個(gè)過(guò)程也是我們從零開(kāi)始點(diǎn)亮LED 的過(guò)程,代碼全部由我們自己編寫(xiě)(除了啟動(dòng)代碼),每一行都有根有據(jù),都可以從《STM32中文參考手冊(cè)》查到。

①、定義一個(gè)外設(shè)(GPIO)寄存器結(jié)構(gòu)體,結(jié)構(gòu)體的成員包含該外設(shè)的所有寄存器,成員的排列順序跟寄存器偏移地址一樣,成員的數(shù)據(jù)類(lèi)型跟寄存器的一樣。

②外設(shè)內(nèi)存映射,即把地址跟外設(shè)建立起一一對(duì)應(yīng)的關(guān)系。51 單片機(jī)中用SFR 實(shí)現(xiàn),STM32 中用宏定義實(shí)現(xiàn)。

③外設(shè)聲明,即把外設(shè)的名字定義成一個(gè)外設(shè)寄存器結(jié)構(gòu)體類(lèi)型的指針。

④操作寄存器,實(shí)現(xiàn)點(diǎn)亮LED。

5新建頭文件stm32f10x.h

為了使代碼看起來(lái)不那么臃腫,我們這里引入文件的概念,讓不同功能的代碼放在不同的文件里面。在main.c 里面我們只保留main 函數(shù)和一些頭文件,把其他的宏定義放到一個(gè)單獨(dú)的文件。

新建一個(gè)stm32f10x.h,跟寄存器相關(guān)的代碼都放在這里,主要是寄存器映像,跟51單片機(jī)里面的reg51.h 這個(gè)頭文件差不多。然后我們?cè)趍ain.c 里面包含這個(gè)頭文件即可,現(xiàn)在我們的主函數(shù)就變成這樣:

6新建tm32f10x_gpio.h

上面我們?cè)诳刂艷PIO 輸出內(nèi)容的時(shí)候控制的是ODR(Output data register)寄存器,ODR 是一個(gè)16 位的寄存器,必須以字的形式控制,相當(dāng)于51 里面的總線(xiàn)操作。

其實(shí)我們還可以控制BSRR 和BRR 這兩個(gè)寄存器來(lái)控制IO 的電平,下面我們簡(jiǎn)單介紹下BRR 寄存器的功能,BSRR 自行看手冊(cè)研究。

BRR:bit reset register

圖9 GPIO 端口位清除寄存器

位清除寄存器BRR 只能實(shí)現(xiàn)位清0 操作,是一個(gè)32 位寄存器,低16 位有效,寫(xiě)0 沒(méi)影響,寫(xiě)1 清0。

現(xiàn)在我們要使PB0 輸出低電平,點(diǎn)亮LED,則只要往BRR 的BR0 位寫(xiě)1 即可,其他位為0,代碼如下:

這時(shí)PB0 就輸出了低電平,LED 就被點(diǎn)亮了。

如果要PB2 輸出低電平,則是:

如果要PB3/4/5/6。。。。。。這些IO 輸出低電平呢?道理是一樣的,只要往BRR 的相應(yīng)位置賦不同的值即可。因?yàn)锽RR 是一個(gè)16 位的寄存器,位數(shù)比較多,賦值的時(shí)候容易出錯(cuò),而且從賦值的16 進(jìn)制數(shù)字我們很難清楚的知道控制的是哪個(gè)IO。這時(shí),我們是否可以把BRR 的每個(gè)位置1 都用宏定義來(lái)實(shí)現(xiàn),如GPIO_Pin_0 就表示0X0001,GPIO_Pin_2 就表示0X0004。只要我們定義一次,以后都可以使用,而且還見(jiàn)名知意。

GPIO_pins_define 代碼如下:

這時(shí)PB0 就輸出了低電平的代碼就變成了:

為了不使main 函數(shù)看起來(lái)冗余,GPIO_pins_define 的代碼不應(yīng)該放在main 里面,因?yàn)槠涫歉鶪PIO 相關(guān)的,我們可以把這些宏放在一個(gè)單獨(dú)的頭文件里面。

在工程目錄下新建stm32f10x_gpio.h,把GPIO_pins_define 代碼放里面,然后把這個(gè)文件添加到工程里面。這時(shí)我們只需要在main.c 里面包含這個(gè)頭文件即可。

7新建stm32f10x_gpio.c

我們點(diǎn)亮LED 的時(shí)候,控制的是PB0 這個(gè)IO,如果LED 接到的是其他IO,我們就需要把GPIOB 修改成其他的端口,其實(shí)這樣修改起來(lái)也很快很方便。但是為了提高程序的可讀性和可移植性,我們是否可以編寫(xiě)一個(gè)專(zhuān)門(mén)的函數(shù)用來(lái)復(fù)位GPIO 的某個(gè)位,這個(gè)函數(shù)有兩個(gè)形參,一個(gè)是GPIOX(X=A...G),另外一個(gè)是GPIO_Pin(0...15),函數(shù)的主體則是根據(jù)形參GPIOX 和GPIO_Pin 來(lái)控制BRR 寄存器,代碼如下:

這時(shí),PB0 輸出低電平,點(diǎn)亮LED 的代碼就變成了:

同樣,因?yàn)檫@個(gè)函數(shù)是控制GPIO 的函數(shù),我們可以新建一個(gè)專(zhuān)門(mén)的文件來(lái)放跟gpio有關(guān)的函數(shù)。

在工程目錄下新建stm32f10x_gpio.c,把GPIO 相關(guān)的函數(shù)放里面。

這時(shí)我們是否發(fā)現(xiàn)剛剛新建了一個(gè)頭文件stm32f10x_gpio.h,這兩個(gè)文件存放的都是跟外設(shè)GPIO 相關(guān)的。C 文件里面的函數(shù)會(huì)用到h 頭文件里面的定義,這兩個(gè)文件是相輔相成的,故我們?cè)趕tm32f10x_gpio.c 文件中也包含stm32f10x_gpio.h 這個(gè)頭文件。別忘了把stm32f10x.h 這個(gè)頭文件也包含進(jìn)去,因?yàn)橛嘘P(guān)寄存器的所有定義都在這個(gè)頭文件里面。

如果我們寫(xiě)其他外設(shè)的函數(shù),我們也應(yīng)該跟GPIO 一樣,新建兩個(gè)文件專(zhuān)門(mén)來(lái)存函數(shù),比如RCC 這個(gè)外設(shè)我們可以新建stm32f10x_rcc.c 和stm32f10x_rcc.h。其他外依葫蘆畫(huà)瓢即可。

stm32f10x_gpio.c 文件代碼如下:

我們還要記得把void GPIO_ResetBits()在stm32f10x_gpio.h 里面聲明下,這樣其他文件只要包含stm32f10x_gpio.h 這個(gè)頭文件就可以使用GPIO_ResetBits()這個(gè)函數(shù)了。以后不論新增加了什么函數(shù)都應(yīng)該在自己的頭文件下聲明,這是個(gè)C 語(yǔ)言的常識(shí)問(wèn)題。

點(diǎn)亮LED 會(huì)了,那關(guān)閉LED 怎么辦,我們可以控制BSRR 這個(gè)寄存器來(lái)實(shí)現(xiàn),這里我就直接寫(xiě)代碼了:

先寫(xiě)一個(gè)GPIO 端口置位函數(shù),放到stm32f10x_gpio.c 文件中,同樣在stm32f10x_gpio.h 頭文件聲明。

PB0 輸出高電平,關(guān)閉LED,代碼如下:

現(xiàn)在我們?cè)賮?lái)看看main 函數(shù),看看點(diǎn)亮LED 的代碼是如何一步一步進(jìn)化的:

8小結(jié)

我們從寄存器映像開(kāi)始,把內(nèi)存跟寄存器建立起一一對(duì)應(yīng)的關(guān)系,然后操作寄存器點(diǎn)亮LED,再到把寄存器操作封裝成一個(gè)個(gè)函數(shù)。為了把不同外設(shè)的函數(shù)歸類(lèi),我們引入了相應(yīng)的文件來(lái)放這些函數(shù),這一步一步走來(lái),我們實(shí)現(xiàn)了庫(kù)最簡(jiǎn)單的雛形,知道庫(kù)是怎么來(lái)的。后面的工作就是不斷的增加操作外設(shè)的函數(shù),并且把所有的外設(shè)都寫(xiě)完,這樣一個(gè)完整的庫(kù)就實(shí)現(xiàn)了。

什么是庫(kù),這就是庫(kù)。

下面我們用一張圖來(lái)描述下我們剛剛的代碼,讓大家有一個(gè)整體的把握。

5、新的嘗試—用庫(kù)函數(shù)點(diǎn)亮LED

1新建工程

??1新建本地工程文件夾

為了工程目錄更加清晰,我們?cè)诒镜?a target="_blank">電腦上新建6 個(gè)文件夾,具體如下:

表格1 工程目錄文件夾清單

圖10 工程文件夾目錄

在本地新建好文件夾后,把準(zhǔn)備好的庫(kù)文件添加到相應(yīng)的文件夾下:

表格2 工程目錄文件夾內(nèi)容清單

??2新建工程

打開(kāi)KEIL5,新建一個(gè)工程,工程名根據(jù)喜好命名,我這里取LED-LIB,保存在ProjectRVMDK(uv4)文件夾下。

選擇CPU 型號(hào)

這個(gè)根據(jù)你開(kāi)發(fā)板使用的CPU 具體的型號(hào)來(lái)選擇,比如MINI 選STM32F103VE,ISO 選STM32F103ZE。

圖11 選擇具體的CPU 型號(hào)

在線(xiàn)添加庫(kù)文件

等下我們手動(dòng)添加庫(kù)文件,這里我們點(diǎn)擊關(guān)掉。

圖12 庫(kù)文件管理

添加組文件夾

在新建的工程中添加5 個(gè)組文件夾,用來(lái)存放各種不同的文件,文件從本地建好的工程文件夾下獲取:

表格3 工程內(nèi)組文件夾內(nèi)容清掉

圖13 如何在工程中添加文件夾

配置魔術(shù)棒選項(xiàng)卡

這一步的配置工作很重要,很多人串口用不了printf 函數(shù),編譯有問(wèn)題,下載有問(wèn)題,都是這個(gè)步驟的配置出了錯(cuò)。

①在Target 中選中微庫(kù),為的是在日后編寫(xiě)串口驅(qū)動(dòng)的時(shí)候可以使用printf 函數(shù)

圖14 添加微庫(kù)

②在Output 選項(xiàng)卡中把輸出文件夾定位到我們工程目錄下的output 文件夾,如果想在編譯的過(guò)程中生成hex 文件,那么那Create HEX File 選項(xiàng)勾上。

圖15 配置Output 選項(xiàng)卡

③在Listing 選項(xiàng)卡中把輸出文件夾定位到我們工程目錄下的Listing 文件夾。

圖16 配置Listing 選項(xiàng)卡

④在C/C++選項(xiàng)卡中添加處理宏,和編譯器編譯的時(shí)候查找的頭文件路徑。

STM32F10X_HD:這個(gè)宏是為了區(qū)分使用STM32F103 系列中不同容量型號(hào)的單片機(jī)庫(kù)。我們用的單片機(jī)的FLASH 的容量都是512K,屬于大容量

STM32F10X_HD:FLASH 大小在256K~512K 之間的STM32F101xx 和STM32F103xx控制器。STM32F10X_MD:FLASH 大小在64K~128K 之間的STM32F101xx 和STM32F103xx 控制器。STM32F10X_LD:FLASH 大小在16K~32K 之間的STM32F101xx和STM32F103xx 控制器。

USE_STDPERIPH_DRIVER:為了包含stm32f10x_conf.h 這個(gè)頭文件。

在編譯器中添加宏的好處就是,只要用了這個(gè)模版,就不用源文件中修改代碼或者添加頭文件。

圖17 配置C/C++ 選項(xiàng)卡

Include Paths 這里添加的是頭文件的路徑,如果編譯的時(shí)候提示說(shuō)找不到頭文件,一般就是這里配置出了問(wèn)題。你把頭文件放到了哪個(gè)文件夾,就把該文件夾添加到這里即可。

下載器配置

這部分的配置最好是在安裝好下載器驅(qū)動(dòng),下載器連接了電腦和開(kāi)發(fā)板,且開(kāi)發(fā)板上電后來(lái)配置。

這里面需要根據(jù)你使用了什么仿真器來(lái)配置,常用的有三種仿真器:JLINK/ARMOB,ST-LINK,ULINK2,而且這個(gè)配置不是配置完一次之后以后就不會(huì)改變,當(dāng)你換了芯片型號(hào),或者其他操作(具體原因不明)都會(huì)改變下載器的配置。

①JLINK/ARM-OB 配置

要先安裝了JLINK 驅(qū)動(dòng)之后,該配置才能下載,兩者缺一不可。

圖18 JLINK/ARM-OB 下載配置

②ST-LINK 配置

要先安裝了ST-LINK 驅(qū)動(dòng)之后,該配置才能下載,兩者缺一不可。

圖19 ST-LINK 下載配置

③ULINK2 配置

要先安裝了ULINK2 驅(qū)動(dòng)之后,該配置才能下載,兩者缺一不可。要注意的是設(shè)置成ULINK2,而不是ULINK。

圖20 ULINK2 下載配置

選擇CPU 型號(hào)

這一步的配置也不是配置一次之后完事,常常會(huì)因?yàn)楦鞣N原因需要重新選擇,當(dāng)你下載的時(shí)候,提示說(shuō)找不到Device 的時(shí)候,請(qǐng)確保該配置是否正確。有時(shí)候下載程序之后,不會(huì)自動(dòng)運(yùn)行,要手動(dòng)復(fù)位的時(shí)候,也回來(lái)看看這里的Reset and Run 配置是否失效。MINI 和ISO 用的STM32 的FLASH 都是512K,所以選擇512K 大容量,如果使用的是其他型號(hào)的,要根據(jù)實(shí)際情況選擇。

2固件庫(kù)分析

在寫(xiě)代碼之前,我們先來(lái)分析下固件庫(kù),看看每個(gè)文件的作用是什么,這對(duì)我們能否清晰的調(diào)用庫(kù)函數(shù)編程非常重要。

STM32 由Cortex-M3 內(nèi)核和內(nèi)核之外的各種外設(shè)組成,庫(kù)在編寫(xiě)的時(shí)候也遵循這中組成結(jié)構(gòu),把代碼分成兩大部分,一種是操作內(nèi)核外設(shè)的,另外一種是內(nèi)核之外的外設(shè),為了聽(tīng)起來(lái)不那么繞,下面我們把內(nèi)核之外的外設(shè)用處理器外設(shè)來(lái)代替。

下面我們大概分析下每個(gè)文件的作用。

??1處理器相關(guān)

startup_stm32f10x_hd.s

這個(gè)是由匯編編寫(xiě)的啟動(dòng)文件,是STM32 上電啟動(dòng)的第一個(gè)程序,啟動(dòng)文件主要實(shí)現(xiàn)了:1、初始化堆棧指針SP;2、設(shè)置PC 指針=Reset_Handler ;3、設(shè)置向量表的地址,并初始化向量表,向量表里面放的是STM32 所有中斷函數(shù)的入口地址4、調(diào)用庫(kù)函數(shù)SystemInit,把系統(tǒng)時(shí)鐘配置成72M,SystemInit 在庫(kù)文件stytem_stm32f10x.c 中定義;5、跳轉(zhuǎn)到標(biāo)號(hào)_main,最終去到C 的世界。

system_stm32f10x.c

這個(gè)文件的作用是里面實(shí)現(xiàn)了各種常用的系統(tǒng)時(shí)鐘設(shè)置函數(shù),有72M,56M,48,36,24,8M,我們使用的是是把系統(tǒng)時(shí)鐘設(shè)置成72M。

Stm32f10x.h

這個(gè)頭文件非常重要,可以說(shuō)是上帝之手。這個(gè)頭文件實(shí)現(xiàn)了:1、處理器外設(shè)寄存器的結(jié)構(gòu)體定義2、處理器外設(shè)的內(nèi)存映射3、處理器外設(shè)寄存器的位定義。

關(guān)于1 和2 我們?cè)谟眉拇嫫鼽c(diǎn)亮LED 的時(shí)候有講解。其中3:處理器外設(shè)寄存器的位定義,這個(gè)非常重要,具體是什么意思?我們知道一個(gè)寄存器有很多個(gè)位,每個(gè)位寫(xiě)1 或者寫(xiě)0 的功能都是不一樣的,處理器外設(shè)寄存器的位定義就是把外設(shè)的每個(gè)寄存器的每一個(gè)位寫(xiě)1 的16 進(jìn)制數(shù)定義成一個(gè)宏,宏名即用該位的名稱(chēng)表示,如果我們操作寄存器要開(kāi)啟某一個(gè)功能的話(huà),就不用自己親自去算這個(gè)值是多少,可以直接到這個(gè)頭文件里面找。

我們以片上外設(shè)ADC 為例,假設(shè)我們要啟動(dòng)ADC 開(kāi)始轉(zhuǎn)換,根據(jù)手冊(cè)我們知道是要控制ADC_CR2 寄存器的位0:ADON,即往位0 寫(xiě)1,即:ADC->CR2=0x00000001;這是一般的操作方法。現(xiàn)在這個(gè)頭文件里面有關(guān)于ADON 位的位定義:

#define ADC_CR2_ADON ((uint32_t)0x00000001),有了這個(gè)位定義,我們剛剛的代碼就變成了:ADC->CR2=ADC_CR2_ADON。這對(duì)于我們編程是何其方便,簡(jiǎn)直就是天降救星,感激之情無(wú)以言表。

無(wú)論是寄存器編程還是固件庫(kù)編程,都必須包含這個(gè)頭文件,有關(guān)外設(shè)寄存器的說(shuō)明都在這里面。

stm32f10x_xxx.h

stm32f10x_xxx.h:外設(shè)xxx 應(yīng)用函數(shù)庫(kù)頭文件,這里面主要定義了實(shí)現(xiàn)外設(shè)某一功能的結(jié)構(gòu)體,比如通用定時(shí)器有很多功能,有定時(shí)功能,有輸出比較功能,有輸入捕捉功能,而通用定時(shí)器有非常多的寄存器要實(shí)現(xiàn)某一個(gè)功能,比如定時(shí)功能,我們根本不知道具體要操作哪些寄存器,這個(gè)頭文件就為我們打包好了要實(shí)現(xiàn)某一個(gè)功能的寄存器,是以機(jī)構(gòu)體的形式定義的,比如通用定時(shí)器要實(shí)現(xiàn)一個(gè)定時(shí)的功能,我們只需要初始化TIM_TimeBaseInitTypeDef 這個(gè)結(jié)構(gòu)體里面的成員即可,里面的成員就是定時(shí)所需要操作的寄存器。有了這個(gè)頭文件,我們就知道要實(shí)現(xiàn)某個(gè)功能需要操作哪些寄存器,然后再回手冊(cè)中精度這些寄存器的說(shuō)明即可。

stm32f10x_xxx.c

stm32f10x_xxx.c:外設(shè)xxx 應(yīng)用函數(shù)庫(kù),這里面寫(xiě)好了操作xxx 外設(shè)的所有常用的函數(shù),我們使用庫(kù)編程的時(shí)候,使用的最多的就是這里的函數(shù)。

??2內(nèi)核相關(guān)

cor_cm3.h

這個(gè)頭文件實(shí)現(xiàn)了:1、內(nèi)核結(jié)構(gòu)體寄存器定義2、內(nèi)核寄存器內(nèi)存映射3、內(nèi)存寄存器位定義。跟處理器相關(guān)的頭文件stm32f10x.h 實(shí)現(xiàn)的功能一樣,一個(gè)是針對(duì)內(nèi)核的寄存器,一個(gè)是針對(duì)內(nèi)核之外,即處理器的寄存器。

misc.h

內(nèi)核應(yīng)用函數(shù)庫(kù)頭文件,對(duì)應(yīng)stm32f10x_xxx.h。

misc.c

內(nèi)核應(yīng)用函數(shù)庫(kù)文件,對(duì)應(yīng)stm32f10x_xxx.c。在CM3 這個(gè)內(nèi)核里面還有一些功能組件,如NVIC、SCB、ITM、MPU、CoreDebug,CM3 帶有非常豐富的功能組件,但是芯片廠(chǎng)商在設(shè)計(jì)MCU 的時(shí)候有一些并不是非要不可的,是可裁剪的,比如MPU、ITM 等在STM32 里面就沒(méi)有。其中NVIC 在每一個(gè)CM3 內(nèi)核的單片機(jī)中都會(huì)有,但都會(huì)被裁剪,只能是CM3 NVIC 的一個(gè)子集。在NVIC 里面還有一個(gè)SysTick,是一個(gè)系統(tǒng)定時(shí)器,可以提供時(shí)基,一般為操作系統(tǒng)定時(shí)器所用。

misc.h 和mics.c 這兩個(gè)文件提供了操作這些組件的函數(shù),并可以在CM3 內(nèi)核單片機(jī)直接移植。

3開(kāi)始寫(xiě)代碼

??1 如何管理庫(kù)的頭文件

這么多的庫(kù)文件,如何調(diào)用,如何管理?當(dāng)我們開(kāi)始調(diào)用庫(kù)函數(shù)寫(xiě)代碼的時(shí)候,有些庫(kù)我們不需要,在編譯的時(shí)候可以不編譯,可以通過(guò)一個(gè)總的頭文件stm32f10x_conf.h 來(lái)控制,該頭文件主要代碼如下:

代碼1 stm32f10x_conf.h 頭文件代碼

這里面包含了全部外設(shè)的頭文件,點(diǎn)亮一個(gè)LED 我們只需要RCC 和GPIO 這兩個(gè)外設(shè)的庫(kù)函數(shù)即可,其中RCC 控制的是時(shí)鐘,GPIO 控制的具體的IO 口。所以其他外設(shè)庫(kù)函數(shù)的頭文件我們注釋掉,當(dāng)我們需要的時(shí)候就把相應(yīng)頭文件的注釋去掉即可。

stm32f10x_conf.h 這個(gè)頭文件在stm32f10x.h 這個(gè)頭文件的最后面被包含,在第8296行:

代碼的意思是,如果定義了USE_STDPERIPH_DRIVER 這個(gè)宏的話(huà),就包含stm32f10x_conf.h 這個(gè)頭文件。我們?cè)谛陆üこ痰臅r(shí)候,在魔術(shù)棒選項(xiàng)卡C/C++中,我們定義了USE_STDPERIPH_DRIVER 這個(gè)宏,所以stm32f10x_conf.h 這個(gè)頭文件就被stm32f10x.h 包含了,我們?cè)趯?xiě)程序的時(shí)候只需要調(diào)用一個(gè)頭文件:stm32f10x.h 即可。

??2編寫(xiě)LED 初始化函數(shù)

經(jīng)過(guò)寄存器點(diǎn)亮LED 的操作,我們知道操作一個(gè)GPIO 輸出的編程要點(diǎn)大概如下:

1、開(kāi)啟GPIO 的端口時(shí)鐘

2、選擇要具體控制的IO 口,即pin

3、選擇IO 口輸出的速率,即speed

4、選擇IO 口輸出的模式,即mode

5、輸出高/低電平

STM32 的時(shí)鐘功能非常豐富,配置靈活,為了降低功耗,每個(gè)外設(shè)的時(shí)鐘都可以獨(dú)自的關(guān)閉和開(kāi)啟。STM32 中跟時(shí)鐘有關(guān)的功能都由RCC 這個(gè)外設(shè)控制,RCC 中有三個(gè)寄存器控制著所以外設(shè)時(shí)鐘的開(kāi)啟和關(guān)閉:RCC_APHENR、RCC_APB2ENR 和RCC_APB1ENR,AHB、APB2 和APB1 代表著三條總線(xiàn),所有的外設(shè)都是掛載到這三條總線(xiàn)上,GPIO 屬于高速的外設(shè),掛載到APB2 總線(xiàn)上,所以其時(shí)鐘有RCC_APB2ENR 控制。

GPIO 時(shí)鐘控制

固件庫(kù)函數(shù):RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE)函數(shù)的原型為:

當(dāng)程序編譯一次之后,把光標(biāo)定位到函數(shù)/變量/宏定義處,按鍵盤(pán)的F12 或鼠標(biāo)右鍵的Go to definition of,就可以找到原型。固件庫(kù)的底層操作的就是RCC 外設(shè)的APB2ENR這個(gè)寄存器,宏RCC_APB2Periph_GPIOB 的原型是:0x00000008,即(1<<3),還原成寄存器操作就是:RCC->APB2ENR |= 1<<<3。相比固件庫(kù)操作,寄存器操作的代碼可讀性就很差,只有才查閱寄存器配置才知道具體代碼的功能,而固件庫(kù)操作恰好相反,見(jiàn)名知意。

GPIO 端口配置

GPIO 的pin,速度,模式,都由GPIO 的端口配置寄存器來(lái)控制,其中IO0~IO7 由端口配置低寄存器CRL 控制,IO8~IO15 由端口配置高寄存器CRH 配置。

寄存器方式

相比寄存器一句話(huà)的代碼,固件庫(kù)的操作就顯得有些復(fù)雜,但換來(lái)的是簡(jiǎn)單明了。固件庫(kù)把端口配置的pin,速度和模式封裝成一個(gè)結(jié)構(gòu)體:

pin 可以是GPIO_Pin_0~GPIO_Pin_15 或者是GPIO_Pin_All,這些都是庫(kù)預(yù)先定義好的宏。

speed 也被封裝成一個(gè)結(jié)構(gòu)體:

速度可以是10M,2M 或者50M,這個(gè)由端口配置寄存器的MODE 位控制,速度是針對(duì)IO 口輸出的時(shí)候而言,在輸入的時(shí)候可以不用設(shè)置。

mode 也被封裝成一個(gè)結(jié)構(gòu)體:

IO 口的模式有8 種,輸入輸出各4 種,由端口配置寄存器的CNF 配置。平時(shí)用的最多的就是通用推挽輸出,可以輸出高低電平,驅(qū)動(dòng)能力大,一般用于接數(shù)字器件。至于剩下的七種模式的用法和電路原理,我們?cè)诤竺娴腉PIO 章節(jié)再詳細(xì)講解。

所以GPIO 端口的配置,最終用固件庫(kù)實(shí)現(xiàn)就變成這樣:

配置好pin,speed,mode 之后,我們最后調(diào)用庫(kù)函數(shù)GPIO_Init()把剛剛的參數(shù)寫(xiě)到CRL 或者CRH 這兩個(gè)寄存器中。

GPIO 輸出控制

GPIO 輸出控制,可以通過(guò)端口數(shù)據(jù)輸出寄存器ODR、端口位設(shè)置/清除寄存器BSRR和端口位清除寄存器BRR 這三個(gè)來(lái)控制。

端口輸出寄存器ODR 是一個(gè)32 位的寄存器,低16 位有效,對(duì)應(yīng)著IO0~IO15,只能以字的形式操作,不能單獨(dú)對(duì)某一個(gè)位置位/清除。

代碼2 寄存器操作ODR

圖21 ODR 寄存器

端口位清除寄存器BRR 是一個(gè)32 位的寄存器,低十六位有效,對(duì)應(yīng)著IO0~IO15,只能以字的形式操作,可以單獨(dú)對(duì)某一個(gè)位操作,寫(xiě)1 清0。

代碼3 寄存器操作BRR

代碼4 固件庫(kù)操作BRR

圖22 BRR 寄存器

BSRR 是一個(gè)32 位的寄存器,低16 位用于置位,寫(xiě)1 有效,高16 位用于復(fù)位,寫(xiě)1有效,相當(dāng)于BRR 寄存器。高16 位我們一般不用,而是操作BRR 這個(gè)寄存器,所以BSRR 這個(gè)寄存器一般用來(lái)置位操作。

代碼5 固件庫(kù)操作BSRR

圖23 BSRR 寄存器

LED GPIO 初始化函數(shù)

代碼6 寄存器LED GPIO 初始化函數(shù)

代碼7 固件庫(kù)LED GPIO 初始化函數(shù)

軟件延時(shí)

簡(jiǎn)單的通過(guò)軟件來(lái)延時(shí),具體時(shí)間不確定,并不能像51 那么通過(guò)計(jì)算每條指令執(zhí)行的時(shí)間來(lái)確切的計(jì)算延時(shí)時(shí)間。要想精確延時(shí),必須通過(guò)定時(shí)器實(shí)現(xiàn)。

主函數(shù)

初始化LED 用到的GPIO,在while 死循環(huán)中讓LED 閃爍。在程序來(lái)到main 函數(shù)前,系統(tǒng)時(shí)鐘已經(jīng)初始化成了72M,有關(guān)時(shí)鐘部分我們?cè)赗CC 這個(gè)章節(jié)中會(huì)詳細(xì)講解,這里不是重點(diǎn)。

GPIO 其他庫(kù)函數(shù)

有關(guān)GPIO 的其他庫(kù)函數(shù),我們可以在stm32f10x_gpio.h 中找到聲明,然后在stm32f10x_gpio.c 中找到函數(shù)的原型,根據(jù)函數(shù)的注釋?zhuān)梢灾烂總€(gè)函數(shù)的作用。閱讀這些庫(kù)函數(shù)的時(shí)候,最好配合《STM32 中文參考手冊(cè)》寄存器描述部分一起看,這樣學(xué)習(xí)的效果會(huì)非常好。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • PWM控制
    +關(guān)注

    關(guān)注

    7

    文章

    194

    瀏覽量

    26504
  • C51
    C51
    +關(guān)注

    關(guān)注

    5

    文章

    284

    瀏覽量

    58329

原文標(biāo)題:如何從快速51單片機(jī)轉(zhuǎn)戰(zhàn)STM32?

文章出處:【微信號(hào):fcsde-sh,微信公眾號(hào):fcsde-sh】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    單片機(jī)工程師招聘

        招聘單片機(jī)工程師(專(zhuān)職或項(xiàng)目發(fā)包皆可) : 3名要求:熟練C51,兩年以上實(shí)際開(kāi)發(fā)經(jīng)驗(yàn),并有實(shí)際完成過(guò)開(kāi)發(fā)項(xiàng)目,請(qǐng)發(fā)個(gè)
    發(fā)表于 12-19 16:26

    單片機(jī)工程師招聘

        招聘單片機(jī)工程師(專(zhuān)職或項(xiàng)目發(fā)包皆可) : 3名要求:熟練C51,兩年以上實(shí)際開(kāi)發(fā)經(jīng)驗(yàn),并有實(shí)際完成過(guò)開(kāi)發(fā)項(xiàng)目,請(qǐng)發(fā)個(gè)
    發(fā)表于 12-19 16:27

    單片機(jī)工程師招聘

        招聘單片機(jī)工程師(專(zhuān)職或項(xiàng)目發(fā)包皆可) : 3名要求:熟練C51,兩年以上實(shí)際開(kāi)發(fā)經(jīng)驗(yàn),并有實(shí)際完成過(guò)開(kāi)發(fā)項(xiàng)目,請(qǐng)發(fā)個(gè)
    發(fā)表于 12-19 16:29

    單片機(jī)工程師考試大綱

    單片機(jī)工程師考試大綱
    發(fā)表于 08-13 23:00

    我看單片機(jī)工程師

    我看單片機(jī)工程師
    發(fā)表于 08-19 22:40

    單片機(jī)工程師

    怎么考單片機(jī)工程師
    發(fā)表于 09-10 21:02

    單片機(jī)初學(xué)者邁向單片機(jī)工程師——好東西,趕緊收下吧~

    附件為一位工程師分享的從單片機(jī)初學(xué)者邁向單片機(jī)工程師學(xué)習(xí)心得。由簡(jiǎn)單深入,好東西,快快收下吧~~~
    發(fā)表于 03-24 11:51

    如何成為一個(gè)合格單片機(jī)工程師

    做為一個(gè)合格單片機(jī)工程師,你應(yīng)該具有這7種能力!
    發(fā)表于 02-25 06:45

    51單片機(jī)工程涉及的模塊包括哪些?

    51單片機(jī)工程涉及的模塊包括哪些?如何去編寫(xiě)51單片機(jī)工程涉及的模塊代碼?
    發(fā)表于 07-19 08:38

    學(xué)單片機(jī)什么程度才算是入門(mén)呢?單片機(jī)工程師能干多少歲?

    學(xué)單片機(jī)什么程度才算是入門(mén)呢?單片機(jī)工程師能干多少歲?
    發(fā)表于 08-30 07:39

    單片機(jī)工程師是程序員嗎

    從業(yè)這么多年,我也一直在糾結(jié)一個(gè)概念,我做的單片機(jī)軟件開(kāi)發(fā)是否屬于IT行業(yè)? 大家都知道IT這個(gè)概念,但很少人找到單片機(jī)。一、單片機(jī)工程師是程序員嗎?那從我的認(rèn)知來(lái)說(shuō),我覺(jué)得“算是”。因?yàn)?b class='flag-5'>單片
    發(fā)表于 11-09 09:14

    單片機(jī)初學(xué)者邁向單片機(jī)工程師

    單片機(jī)初學(xué)者邁向單片機(jī)工程師
    發(fā)表于 04-24 10:31

    單片機(jī)初學(xué)者邁向單片機(jī)工程師

    單片機(jī)初學(xué)者邁向單片機(jī)工程師單片機(jī)初學(xué)者邁向單片機(jī)工程師
    發(fā)表于 01-15 16:33 ?45次下載

    單片機(jī)學(xué)那個(gè)好?單片機(jī)工程師工資為什么不高?

    最近在網(wǎng)上發(fā)現(xiàn)很多所謂的有經(jīng)驗(yàn)的工程師,說(shuō)學(xué)習(xí)單片機(jī)就直接學(xué)習(xí)STM32,51單片機(jī)已經(jīng)淘汰了,沒(méi)有學(xué)習(xí)的價(jià)值?這是真的嘛?顯然是不對(duì)的,我在深圳從事
    發(fā)表于 11-13 13:06 ?2次下載
    <b class='flag-5'>單片機(jī)</b>學(xué)那個(gè)好?<b class='flag-5'>單片機(jī)工程師</b>工資為什么不高?

    51單片機(jī)工程模板創(chuàng)建方法

    本教程將向大家介紹如何使用KEIL C51 軟件以及如何創(chuàng)建51 單片機(jī)工程。通過(guò)本教程的學(xué)習(xí),讓大家創(chuàng)建屬于自己的51 單片機(jī)工程模板,為
    發(fā)表于 07-15 15:27 ?5005次閱讀
    <b class='flag-5'>51</b><b class='flag-5'>單片機(jī)工程</b>模板創(chuàng)建<b class='flag-5'>方法</b>
    主站蜘蛛池模板: 欧美日韩性高爱潮视频 | 亚洲成a人片77777kkk | 台湾佬自偷自拍情侣在线 | 最近2018中文字幕2019视频 | 成人sese| 黄色大片网站 | 色猫成人网 | 久久精品午夜 | 偷操| 老司机精品免费视频 | 亚洲无线码一区在线观看 | 午夜精品久久久久久 | 天堂资源最新版在线官网 | 亚洲国产香蕉视频欧美 | 人人草人人射 | 最近2018中文字幕免费看手机 | 大又大又粗又爽女人毛片 | 在线观看视频高清视频 | 免费看黄在线观看 | 亚洲成a人v在线观看 | 色视频网站人成免费 | 亚洲人成网站色7799在线播放 | 一级大片免费观看 | 国产偷窥女洗浴在线观看亚洲 | 成年人一级毛片 | 亚洲 欧美 精品专区 极品 | 天天操天天干天天玩 | 免费高清一级欧美片在线观看 | 久久在草| 亚洲人成网站在线 | 韩国理论片在线看2828dy | 国产美女视频免费 | 狠狠色狠狠色综合日日不卡 | 高清性色生活片欧美在线 | 男女无遮挡一进一出性视频 | 久久涩精品 | 人人看人人看人做人人模 | 国产精品臀控福利在线观看 | 日韩欧美一区二区三区视频 | 狠狠色综合久久婷婷 | 免费边摸边吃奶边叫床视频gif |