色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

51單片機工程師跳槽到stm32的方法

fcsde-sh ? 來源:互聯網 ? 作者:佚名 ? 2017-11-11 06:12 ? 次閱讀
你可能此前一直學習或者從事的工作都是使用的4 位或者8 位單片機, 比如51 類單片機。 因為51是如此的深入人心,可以輕易獲得大量關于他的學習資料, 在書店51 類的書籍教材甚至用幾個架子來擺放,某寶上銷量最大的開發板一定是51 開發板, 很少有哪個嵌入式工程師或者學生曾經避開過51 而直達別的平臺,我們從51 學到了MCU 的概念,學到了控制的概念。但到了今天,51 的低成本,易用,已經不占優勢, 反觀現在的微控領域應用,對MCU 的資源要求越來高,51 越來越不適應。

8 位內核的51 類MCU 的資源往往是最大幾K-100K 的flash。 100-幾K 字節的RAM, IO, 串口,定時器,8 位數據總線, AD 等簡單的資源。 目標確定,單一。結構簡單,指令簡單。 易于理解和操作,這些特點也是51 能深入人心的因素。 目前依然是高校的主導實驗平臺。 也是很多企業的應用平臺。

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

初入STM32,可能我們最親切的就是在51 使用過的keil , 在51 它叫keil c51, 在arm 它叫RealviewMKD-ARM,簡稱它MDK, 現在版本是MDK4.22, 操作方法基本類似于keil。 我們常用的功能除了編輯工程,編譯代碼,還會用到下載,調試。 我們在51 時,可能會很少有人用仿真功能,因為51 足夠的簡單,腦子想的往往就是你所看到的。 直接下載到目標板對你來說更快捷。 所以在51 最常見的是下載器。 但在arm階段,資源繁雜,寄存器復雜。變量眾多,沒有一個仿真器,會感到那么的無助。因此 coretex-m3 的使用者基本都會擁有仿真器,一般分為ST-LINK ULINK 及JLINK,尤以JLINK 在中國的應用最為普及。 我們都懂的原因,JLINK V8 的性價比是這幾個最好的。 所以,你需要在獲得了MDK 后,再擁有一個JLINK。 它不僅僅只支持STM32,它支持絕大多數的ARM 芯片。

51 使用者初入STM32,都會存在一個平臺轉換帶來迷惘的一個短暫過程,這是器件類型變化較大造成的認知差異。

但調整一下,這個不適會很快過去的。

??1先看看 51 和STM32 具有的相同類型資源是哪些。根據你對51 的熟悉程度, 你會從STM32 的手冊上看到。 這些往往是較簡單的,也是最容易理解的。 比如IO 口線控制,等等。

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

??4編程方式的不同, 比如在 51,用置位或者復位指令就可以很方便的控制IO,而在STM32,由于所有資源的功能都和該資源對應的32 位寄存器組的操作有關系。 因此對于資源的設置和操作都可能需要操作一個或者多個寄存器, 如果用多條指令來控制的話,會引起閱讀的障礙,以及日后代碼維護復雜,因此ST 公司引入了庫函數的概念。用執行庫函數的方式解決復雜的資源操作的問題。

??4STM32 例程的MDK 工程都有相似的程序結構,結合手冊多看例程,會使你快速的形成對STM32 例程模板的認識,這個認識一旦形成,剩下的代碼細節就好比是你預測到的填空題目。

當你做好了想學習新平臺的準備,那就義無反顧的投入CORETEX-M3 的懷抱吧。它會使你進步到一個新的境界。 帶給你愉悅的技術享受。

如何迅速入門STM32單片機?

網上有大神說如果會51單片機C語言一天可入門STM32,僅一天的時間,是否有真的這么快。這個要看自己給自己定的入門的標準了。

我眼中的入門:(前提是你學過51 單片機和C 語言)

??1知道參考官方的什么資料來學習,而不是陷入一大堆資料中無從下手。

??2知道如何參考官方的手冊和官方的代碼來獨立寫自己的程序,而不是一味的看到人家寫的代碼就覺得人家很牛逼。

??3消除對STM32 的恐懼,消除對庫開發的恐懼,學習是一個快樂而富有成就感的過程。

學習本文時,配合《STM32 中文參考手冊》GPIO 章節一起閱讀,效果會更佳,特別是涉及到寄存器說明的部分。

1、51 與STM32 簡介

51 是嵌入式學習中一款入門級的精典MCU,因其結構簡單,易于教學,且可以通過串口編程而不需要額外的仿真器,所以在教學時被大量采用,至今很多大學在嵌入式教學中用的還是51。51 誕生于70 年代,屬于傳統的8 位單片機,如今,久經歲月的洗禮,既有其輝煌又有其不足?,F在的市場產品競爭激烈,對成本極其敏感,相應地對MCU 的要求也更苛刻:功能更多,功耗更低,易用界面和多任務。面對這些要求,51 現有的資源就顯得得抓襟見肘了。所以無論是高校教學還是市場需求,都急需一款新的MCU 來為這個領域注入新的活力。

基于這市場的需求, ARM 公司推出了其全新的基于ARMv7 架構的32 位Cortex-M3微控制器內核。緊隨其后,ST(意法半導體)公司就推出了基于Cortex-M3 內核的MCU—STM32。STM32 憑借其產品線的多樣化、極高的性價比、簡單易用的庫開發方式,迅速在眾多Cortex-M3 MCU 中脫穎而出,成為最閃亮的一顆新星。STM32 一上市就迅速占領了中低端MCU 市場,受到了市場和工程師的無比青睞,頗有星火燎原之勢。

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

51 與STM32 架構的區別

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

下面我們來簡單介紹下51 和STM32 的結構。

151 系統結構

51 系統結構框圖

圖1 51 系統結構框圖

我們說的51 一般是指51 系列的單片機,型號有很多,常見的有STC89C51、AT89S51,其中國內用的最多的是STC89C51/2,下面我們就以STC89C51 來講解,并以51 簡稱。

內核

51 由一個IP 核和片上外設組成,IP 核就是上圖中的CPU,片上外設就是上圖中的:時鐘電路、SFR 和RAM、ROM、定時/計數器、并行I/O 口、串行I/O 口、中斷系統。IP核跟外設之間由系統總線連接,且是8bit 的,速度有限。

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

外設

我們在學習51 的時候,關于內核部分接觸的比較少,使用的最多的是片上外設,我們在編程的時候操作的也就是這些外設。

編程的時候操作的寄存器位于SFR 和RAM 這個部分,其中SFR(特殊功能寄存器)占有128 字節(實際上只用了26 個字節,只有26 個寄存器,其他都屬于保留區),RAM占有128 字節,我們在程序中定義的變量就是放在RAM 中。其中SFR 和RAM 在地址上是重合的,都是在80~FF 這個地址區間,但在物理區間上是分開的,所以51 的RAM 是有256 個字節。

編寫好的程序是燒寫到ROM 區。剩下的外設都是我們非常熟悉的IO 口,串口、定時器、中斷這幾個外設。

2STM32 系統結構

STM32 系統結構框圖

圖2 STM32 系統結構框圖

內核

在系統結構上,STM32 和51 都屬于單片機,都是由內核和片上外設組成。只是STM32 使用的Cortex-M3 內核比51 復雜得多,優秀得多,支持的外設也比51 多得多,同時總線寬度也上升到32bit,無論速度、功耗、外設都強與51。

從結構框圖上看,對比51 內核只有一種總線,取指和取數共用。Cortex-M3 內部有若干個總線接口,以使CM3 能同時取址和訪內(訪問內存),它們是:

指令存儲區總線(兩條)、系統總線、私有外設總線。有兩條代碼存儲區總線負責對代碼存儲區(即FLASH 外設)的訪問,分別是I-Code 總線和D-Code 總線。

I-Code 用于取指,D-Code 用于查表等操作,它們按最佳執行速度進行優化。

系統總線(System)用于訪問內存和外設,覆蓋的區域包括SRAM,片上外設,片外RAM,片外擴展設備,以及系統級存儲區的部分空間。

私有外設總線負責一部分私有外設的訪問,主要就是訪問調試組件。它們也在系統級存儲區。

還有一個MDA 總線,從字面上看,DMA 是data memory access 的意思,是一種連接內核和外設的橋梁,它可以訪問外設、內存,傳輸不受CPU 的控制,并且是雙向通信。簡而言之,這個家伙就是一個速度很快的且不受老大控制的數據搬運工,這個在51 里面是沒有的。

外設

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

3、小結

從內核和外設這兩大方面來比較,STM32 之于51 就是一個升級版的單片機。它適應市場,引流潮流,在中低端的微控制器中流光溢彩。

2、學習方法的區別

學習51 用寄存器,學習STM32 用庫。

以前我們在學習51 的時候,用的是寄存器編程的方法,想要實現什么效果,直接往寄存器里面賦值,優點是直觀,簡單粗暴,知道自己具體干了啥,心里踏實。

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

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

要知道當初我們學習51 單片機的時候用的還是匯編,連現在的C 編程都不是,就更別說什么庫函數編程。

??251 功能簡單,寄存器不多。以國內普及最廣的STC89C52 為例,寄存器全部加起來不到30 個。按照功能區分來記的話,可以把每個寄存器背的滾瓜爛熟,并且寄存器每一位的功能都可以記得住,在編程的時候做到了然于胸。

現在從51 過度到STM32 的學習,很多人還是喜歡沿用51 的學習方法。接受不了庫,在學習庫的時候陷入迷糊之中,來回幾個月下來,都不知道到底有沒學會STM32,因為在這一路的學習中都是在調用庫函數,壓根就沒有操作過寄存器,心里面很不踏實。其實大家在調用庫函數的時候心中難道就沒有疑問,庫的底層是怎么實現的?難道就沒有勇氣對庫的底層一探究竟。可最后當我們開始跟蹤庫函數底層的時候,看到一堆的宏定義、結構體、指針、各種的文件包含,而且注釋全部都是英文的,是不是又心生忌憚。

鑒于此,我想用兩個原因來總結下很多初學者畏懼庫不愿意用庫的原因。

??1C 語言知識點的欠缺

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

??2程序架構設計思想的欠缺

這個比較難搞,很多C 語言學習得挺好好的人,也比較難掌握。還好我們遇到了STM32 的庫,這給了我們一個學習和提升C 語言絕佳的機會。庫的整個架構是如何搭建起來的,代碼上是如何如何一步一步寫出來的:從寄存器映像開始,到寄存器的封裝,然后到函數的編寫,到每個外設函數對應的驅動文件,這里面涉及到了大量的條件編譯,文件包含的思想,對應剛寫過幾行51 單片機的初學者來說簡直就是噩夢。但是,如果你把這一系列的關系弄明白了,那么對庫的整個架構也了解的差不多了,以后你就不用嚷嚷著說要操作寄存器了。

如果你一開始不喜歡用庫,對庫開發很忌憚,那么請自問:是不是我的C 語學得不夠好。庫是一種全新的學習方法,是一種潮流,我更把它看做是與C 語言的又一次歷練和提升。是否用庫,只差你一個閃亮的回眸。

3、用寄存器點亮LED

為了順利過渡到庫開發,在STM32 編程的開始,我們對照51 點亮一個LED 的方法,給大家演示一下STM32 如何用操作寄存器的方法點亮一個LED,然后再慢慢講解到底什么是庫,讓大家知道庫跟寄存器的關系。

1用51 點亮一個LED

在用STM32 點亮一個LED 之前,我們先來復習下用51 如何點亮一個LED。

硬件上我們假設51 單片機的P0 口的第0 位接了一個LED,負邏輯亮。如果我們要點亮這個LED,代碼上我們會這么寫:

這里面我們用的是總線操作的方法,即是對P0 口的8 個IO 同時操作,但起作用的只是P0^0。

除了這種總線操作的方法,我們還學習過位操作,利用51 編譯器的關鍵字sbit,我們可以定義一個位變量:

那么LED = 0;就點亮了LED,LED = 1;就關閉了LED。為了讓程序看起來見名知義,我們定義兩個宏:

點亮和關閉LED 的代碼就變成了:

上面總線和位操作的的方法,學過51 的朋友是非常熟悉的,也很容易理解。

那么我們再說一下大家容易忽略的幾個知識點。

??1什么是寄存器

在點亮LED 的時候,我們都是用操作寄存器的方法來實現的,那大家是否想過,這個寄存器到底是什么?為什么我們可以直接操作P0 口?

解答上面的問題之前,我們先簡單介紹下51 單片機的主要組成部分,這對我們學習其他單片機也有好處。

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

寄存器則是內置于各個IP 外設中,是一種用于配置外設功能的存儲器,就是一種內存,并且有想對應的地址。學過C 語言我們就知道,要操作這些內存就可以使用C 語言中的指針,通過尋址的方式來操作這些具有特殊功能的內存—寄存器。比如P0 口對應的地址是0X80,那么我們要修改0X80 這個地址對應的內存的內容的話,按照常理可以這樣操作:

可當我們編譯的時候,編譯器會報錯,在51 里面只能通過SFR 和SBIT 這兩個關鍵字來實現寄存器映像,不能直接操作寄存器對應的地址,這是51 相較于STM32 不同的地方。

51 單片機的這些寄存器位于地址80H~FFH 中,對應著128 個地址,但不是每個地址都是有效的,51 系列的單片機有21 個,52 系列的則有26 個,其他的都是保留區。

圖3 51 寄存器映射

??2寄存器映射

實際上我們在編程的時候并不是通過指針來操作寄存器的,而是直接給P0、P1 這些端口寄存器賦值。那么這些外設資源是如何與地址建立一一對應的關系(寄存器映射定義),這得益與51 特有的兩個關鍵字:SFR 和sbit,其他單片機沒有,只能用其他的方式來實現寄存器映射。這兩個關鍵字幫我們實現了所有寄存器的定義,所以我們才可以像操作普通變量一個來操作寄存器。其實我們一開始提到的點亮LED 的代碼,全貌應該是這樣的:

為了方便起見,我們可以把寄存器映射全部寫好封裝在一個頭文件里面,不用每用一個寄存器就定義一次。其實這方面的工作不用我們做,我們在編程的時候都會在開始的地方添加一個頭文件:

這個頭文件已經實現了全部寄存器的定義,該文件是keil 自帶,在安裝目錄:KeilC51INC 下可以找到。這個文件實現了字節寄存器和位寄存器的定義。

??3啟動文件—STARTUP.A51

還有一個就是啟動代碼,這個也是很多初學者容易忽略的地方,對于這部分我們主要總結下它的功能,不詳解講解里面的代碼。

單片機在上電復位后,首先執行的是啟動文件—STARTUP.A51,而不是我們通??吹降膍ain 函數。我們新建51 工程的時候會有一個提示:是否拷貝啟動代碼到當前的工程,我們一般選擇是。

圖4 是否添加啟動代碼

啟動代碼用匯編語言編寫,主要實現了以下功能:清除內部數據存儲器、清除外部數據存儲器、清除外部頁儲存器、初始化small 模式下的可重入棧和指針、初始化large 模式下可重入棧和指針、初始化compact 模式下的可重入棧和指針、初始化8051 硬件棧指針、傳遞初始化全局變量的控制命令或者在沒有初始化全局變量時給main 函數傳遞命令。然后程序就跳轉到main 函數,來到我們熟知的C 世界。

??4總結

在講解用51 點亮LED 的時候,我們補充了什么是寄存器、寄存器映射、啟動代碼這三部分的內容,這三部分內容本來是放到STM32 里面講解的,但考慮到大家已經有51 的基礎,并且對51 比較熟悉,那我再添加點內容,大家自然沒有那么抗拒,并且可以根據上面講的內容親自實踐,學習得也會更深入。那當我再在STM32 講解這幾個內容的時候,大家就會對比著學習,對STM32 也就沒有那么忌憚。

2用STM32 點亮一個LED

對比著51 點亮LED 的方法,我們先用操作寄存器的方法用STM32 點亮一個LED,然后再一步步完善代碼,構建最簡單的庫函數,讓我們知道庫是怎么建立起來的。

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

??1新建工程

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

??2啟動文件—startup_stm32f10x_hd.s

啟動文件由匯編語言編寫,具體功能跟51 里面的啟動文件:STARTUP.A51 差不多。

STM32 的啟動文件主要實現了:

1、設置初始SP 。

2、設置初始PC=Reset_Handler

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

4、調用庫函數SystemInit,把系統時鐘配置成72M,SystemInit 在庫文件system_stm32f10.c 定義。

5、跳轉到標號_mian,最終來到C 的世界。這里我們先去除繁枝細節,挑重點的講,主要理解第四和第五點,在啟動文件的147~155 行,是復位處理函數,代碼如下:

這里我們簡單介紹下這10 行代碼。

第一行是程序注釋,在匯編里面注釋用的是“;”,跟C 語言不一樣。

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

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

第三行EXPORT 表示Reset_Handler 這個子程序可供其他模塊調用。

關鍵字[WEAK] 表示弱定義,如果編譯器發現在別處定義了同名的函數,則在鏈接時用別處的地址進行鏈接,如果其它地方沒有定義,編譯器也不報錯,以此處地址進行鏈接。

第四行和第五行IMPORT 說明SystemInit 和__main 這兩個標號在其他文件,在鏈接的時候需要到其他文件去尋找。

SystemInit 在庫文件system_stm32f10x.c 實現,用來初始化STM32 的一系列時鐘,把系統時鐘設置為72MHZ。STM32 的時鐘比51 單片機復雜,需要經過一系列的配置才能達到穩定運行的狀態。

__main 其實不是我們定義的,當編譯器編譯時,只要遇到這個標號就會定義這個函數,該函數的主要功能是:負責初始化棧、堆,配置系統環境,并在最后跳轉到用戶自定義的main 函數,從此來到C 的世界。

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

第七行程序跳轉到R0 中的地址執行程序,之后系統的時鐘就被設置成72MHZ。

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

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

第十行表示子程序的結束。

總結下就是,Reset_Handler 這個函數執行了兩個函數調用,一個是SystemInit,把系統時鐘設置成72M,令一個是__main,初始化好系統環境,最終調用C 的main,從此去到C 的世界。

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

__main 函數由編譯器生成,負責初始化棧、堆等,并在最后跳轉到用戶自定義的main()函數,來到C 的世界。

??3新建main.c

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

現在我們就可以開始編寫程序了,我們先編寫一個main 函數,里面啥都沒有,暫時為空。這時跟編寫51 程序時是不是很像。

現在我們可以編譯看看,看看有啥現象。

這時候出現如下錯誤:

錯誤提示說SystemInit 沒有定義。從分析啟動文件時我們知道,Reset_Handler 調用了該函數用來初始化系統時鐘,而該函數是在庫文件system_stm32f10x.c 中實現的。我們重新寫一個這樣的函數也可以,把功能完整實現一遍,但是為了簡單起見,我們在main 文件里面定義一個SystemInit 空函數,為的是騙過編譯器,把這個錯誤去掉。關于配置系統時鐘我們在后面再寫簡單的代碼。

這時我們再編譯就沒有錯了,完美解決。還有一個方法就是在啟動文件中把有關SystemInit 的代碼注釋掉也可以,代碼如下所示:

??4控制IO 口

下面我們從三個方面來講解STM32 的IO 在控制LED 時跟51 的區別。有關STM32 的IO 的寄存器介紹,我們可以看《STM32 中文參考手冊》的第八章即可,下面涉及到的IO寄存器均來自這一章的第二小節:8.2 GPIO 寄存器描述

電平控制

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

而STM32 的IO 口比較復雜,如果要輸出1 和0,則要通過控制:端口輸出數據寄存器ODR 來實現,ODR 是:Output data register 的簡寫,在STM32 里面,其寄存器的命名名稱都是英文的簡寫,很容易記住。從手冊上我們知道ODR 是一個32 位的寄存器,低16位有效,高16 位保留。低16 位對應著IO0~IO16,只要往相應的位置寫入0 或者1 就可以輸出低或者高電平。

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

這時候編譯,我們會發現有個錯誤,說GPIOB_ODR 沒有定義,不過我們確實沒有定義。在51 單片機中,我們可以直接往P0 口賦值,那是因為在reg51.h 這個頭文件中實現了P0 口這個寄存器的映像,用的是51 特有的關鍵字SFR 來定義的。

STM32 跟51 不一樣,沒有SFR,只能用其他的方式來實現寄存器映像。因為寄存器實際上就是具有特殊功能的內存,那么我們可以通過宏定義來實現寄存器映像,其實ST的庫函數中用的也是這種方法。

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

在手冊中的第二章:存儲器和總線構架的2.3:存儲器映像小節中可以查看到所有外設的基地址,如下:

圖5 STM32 寄存器組起始地址

其中GPIOB 的起始地址是:0X4001 0C00,這樣就可以算出GPIOB_ODR 寄存器的地址是:0X4001 0C00 + 0X0C = 0X4001 0C0C?,F在我們就可以定義GPIOB_ODR 這個寄存器了,代碼如下:

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

方向控制

雖然配置了ODR 寄存器,但是這個時候還不能點亮LED,因為STM32 的IO 口還要配置方向,這個由端口配置寄存器來控制。端口配置寄存器分為高低兩個,每4bit 控制一個IO 口,所以端口配置低寄存器:CRL 控制這IO 口的低8 位,端口配置高寄存器:CRH控制這IO 口的高8bit。在4 位一組的控制位中,CNFy[1:0] 用來控制端口的輸入輸出,MODEy[1:0]用來控制輸出模式的速率,即輸出時,IO 電平翻轉的速度。

輸入有三種模式,輸出有4 中模式,我們在控制LED 的時候選擇通用推挽輸出。

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

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

時鐘控制

當我們設置了IO 口的方向,并在相應的輸出寄存器里面輸入了值的時候,以為現在總算可以點亮LED 了吧,其實還差最后一步。

STM32 外設很多,為了降低功耗,每個外設都對應著一個時鐘,在系統復位的時候這些時鐘都是被關閉的,如果想要外設工作,必須把相應的時鐘打開。

STM32 的所有外設的時鐘由一個專門的外設來管理,叫RCC(reset and clockcontrol),RCC 在STM32 中文參考手冊的第六章。

STM32 的外設因為速率的不同,分別掛載到三條總系上:AHB、APB2、APB1,APB為高速總線,APB2 次之,APB1 再次之。所以的IO 口都掛載到APB2 總線上,屬于高速外設。時鐘由APB2 外設時鐘使能寄存器(RCC_APB2ENR)來控制,其中PB 端口的時鐘由該寄存器的位3 寫1 使能。

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

如果你足夠細心,你會發現我們雖然開了端口時鐘,那這個時鐘到底是多大?時鐘到底是從哪里來的?

如果我們用的是庫,那么有個庫函數SystemInit,會幫我們把系統時鐘設置成72M?,F在我們沒有使用庫,那現在時鐘是多少?答案是8M,當外部HSE 沒有開啟或者出現故障的時候,系統時鐘由內部低速時鐘LSI 提供,現在我們是沒有開啟HSE,所以系統默認的時鐘是LSI=8M。至于更深入的細節我們在后面的RCC 時鐘樹中再詳細分析。如果你想自己先嘗鮮,那么看RCC 外設中的:時鐘控制寄存器(RCC_CR)和時鐘配置寄存器(RCC_CFGR)這兩個寄存器即可。

水到渠成

控制了電平,配置了方向,開啟了時鐘,經過這三步,我們總算可以控制一個LED了。比起51 直接輸出電平,控制STM32 的IO 多了兩步:即配置方向可開啟時鐘。比起AVR 和PIC 這兩種單片機則多了開啟時鐘這一步。

現在我們完整組織下用STM32 控制一個LED 的代碼:

很多人說學習STM32 很難,一堆的寄存器,不知道怎么操作,特別是那些剛學習完51 的朋友,不知道怎么過度。這里我們對比了51 的編程方法,寫了個簡單的用STM32 寄存器點亮LED 的方法,希望可以起到拋磚引玉的作用。

4、再接再厲—構建庫的雛形

學習STM32 存在著一個用寄存器好還是用庫好的爭議點,就好比編程是用匯編好還是用C 好一樣。其實孰優孰劣,市場自有定論,用戶群說明一切。

雖然我們上面用寄存器點亮了LED,乍看一下好像代碼也很簡單,但是我們別僥幸以后就可以一直用寄存器開發。在用寄存器點亮LED 的時候,我們是否發現STM32 的寄存器都是32 位的,在配置的時候非常容易出錯,而且代碼還很不好理解。所以學習TM32 最好的方法是用庫,然后在庫的基礎上了解底層,看遍所有寄存器。

但是很多人對庫還是很忌憚,因為一開始用庫的時候有很多代碼,很多文件,不知道如何入手。不知道你是否認同這么一句話:一切的恐懼都來源于認知的空缺。我們對庫忌憚那是因為我們不知道什么是庫,不知道庫是怎么實現的。

接下來,我們在寄存器點亮LED 的代碼上繼續完善,把代碼一層層封裝,實現庫的最初的雛形,相信經過這一步的學習后,你會對庫的運用做到游刃有余。這里我們只講關于GPIO 庫,其他外設的我們直接參考庫學習即可,不必自己寫。

1定義外設寄存器結構體

上面我們在操作寄存器的時候,操作的是寄存器的絕對地址,如果每個寄存器都這樣操作,那將非常麻煩。

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

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

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

在GPIO 結構體里面我們用了兩個數據類型,一個是uint32_t,表示無符號的32 位整型,因為GPIO 的寄存器都是32 位的。這個類型聲明在標準頭文件stdint.h 里面,我們在程序上只要包含這個頭文件即可。

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

關于這兩個數據類型,我們添加如下代碼:

2外設聲明

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

對于其他外設我們也可以這樣把外設的名字定義成一個外設寄存器結構體類型的指針,這里我們只講GPIO。

對于每個GPIO 的基地址我們可以從《STM32 中文參考手冊》2.3 小節:存儲器映像中找到,如下所示:

圖6 APB2 總線外設寄存器起始地址

3外設內存映射

講到基地址的時候我們再引人一個知識點:Cortex-M3 存儲器系統,這個知識點在《Cortex-M3 權威指南》第5 章里面講到。CM3 的地址空間是4GB,如下圖所示:

圖7 CM3 內存映射

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

??1APB1、APB2、AHB 總線基地址

現在我們說的STM32 的寄存器就是位于這個區域,這里面ST 設計了三條總線:AHB、APB2 和APB1,其中AHB 和APB2 是高速總線,APB1 是低速總線。不同的外設根據速度不同分別掛載到這三條總線上。從下往上依次是:APB1、APB2、AHB,每個總線對應的地址分別是:APB1:0x40000000,APB2:0x4001 0000,AHB:0x4001 8000。

這三條總線的基地址我們是從《STM32 中文參考手冊》2.3 小節—存儲器映像得到的:APB1 的基地址是TIM2 定時器的起始地址,APB2 的基地址是AFIO 的起始地址,AHB 的基地址是SDIO 的起始地址。

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

現在我們把這三條總線地址用宏定義出來,以后我們在定義其他外設基地址的時候,只需要在這三條總線的基址上加上偏移地址即可,代碼如下:

??2 GPIO 端口基地址

因為GPIO 掛載到APB2 總線上,那么現在我們就可以根據APB2 的基址算出各個GPIO 端口的基地址,用宏定義實現代碼如下:

現在我們把上面的代碼稍微整理下,如下:

在點亮LED 的時候,我們還開了GPIO 的時鐘,用到了RCC 這個外設,現在我們也定義一個RCC 寄存器結構體,加上那些地址定義,總體代碼如下:

跟GPIO 不同的是,RCC 這個外設是掛載到AHB 總線上。

現在我們點亮LED 的函數就變成了

對比之前的代碼

一個用的是結構體,一個用的是宏,僅僅從這三行代碼看不出有啥區別,但是如果要操作其他寄存器的時候,用結構體就可以直接操作,用宏就還要一個個找到寄存器的絕對地址重新定義。

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

這時候PB0 就輸出低電平,LED 被點亮。注意:BRR 低16 位有效,只能以字的形式操作,功能是復位相應的IO 口,寫1 清0,寫0 沒有影響。

圖8 GPIO 端口位清除寄存器

現在我們再整理下代碼,如下所示:

4小結流程

現在我們來總結下上面代碼實現的過程,這個過程也是我們從零開始點亮LED 的過程,代碼全部由我們自己編寫(除了啟動代碼),每一行都有根有據,都可以從《STM32中文參考手冊》查到。

①、定義一個外設(GPIO)寄存器結構體,結構體的成員包含該外設的所有寄存器,成員的排列順序跟寄存器偏移地址一樣,成員的數據類型跟寄存器的一樣。

②外設內存映射,即把地址跟外設建立起一一對應的關系。51 單片機中用SFR 實現,STM32 中用宏定義實現。

③外設聲明,即把外設的名字定義成一個外設寄存器結構體類型的指針。

④操作寄存器,實現點亮LED。

5新建頭文件stm32f10x.h

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

新建一個stm32f10x.h,跟寄存器相關的代碼都放在這里,主要是寄存器映像,跟51單片機里面的reg51.h 這個頭文件差不多。然后我們在main.c 里面包含這個頭文件即可,現在我們的主函數就變成這樣:

6新建tm32f10x_gpio.h

上面我們在控制GPIO 輸出內容的時候控制的是ODR(Output data register)寄存器,ODR 是一個16 位的寄存器,必須以字的形式控制,相當于51 里面的總線操作。

其實我們還可以控制BSRR 和BRR 這兩個寄存器來控制IO 的電平,下面我們簡單介紹下BRR 寄存器的功能,BSRR 自行看手冊研究。

BRR:bit reset register

圖9 GPIO 端口位清除寄存器

位清除寄存器BRR 只能實現位清0 操作,是一個32 位寄存器,低16 位有效,寫0 沒影響,寫1 清0。

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

這時PB0 就輸出了低電平,LED 就被點亮了。

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

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

GPIO_pins_define 代碼如下:

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

為了不使main 函數看起來冗余,GPIO_pins_define 的代碼不應該放在main 里面,因為其是跟GPIO 相關的,我們可以把這些宏放在一個單獨的頭文件里面。

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

7新建stm32f10x_gpio.c

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

這時,PB0 輸出低電平,點亮LED 的代碼就變成了:

同樣,因為這個函數是控制GPIO 的函數,我們可以新建一個專門的文件來放跟gpio有關的函數。

在工程目錄下新建stm32f10x_gpio.c,把GPIO 相關的函數放里面。

這時我們是否發現剛剛新建了一個頭文件stm32f10x_gpio.h,這兩個文件存放的都是跟外設GPIO 相關的。C 文件里面的函數會用到h 頭文件里面的定義,這兩個文件是相輔相成的,故我們在stm32f10x_gpio.c 文件中也包含stm32f10x_gpio.h 這個頭文件。別忘了把stm32f10x.h 這個頭文件也包含進去,因為有關寄存器的所有定義都在這個頭文件里面。

如果我們寫其他外設的函數,我們也應該跟GPIO 一樣,新建兩個文件專門來存函數,比如RCC 這個外設我們可以新建stm32f10x_rcc.c 和stm32f10x_rcc.h。其他外依葫蘆畫瓢即可。

stm32f10x_gpio.c 文件代碼如下:

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

點亮LED 會了,那關閉LED 怎么辦,我們可以控制BSRR 這個寄存器來實現,這里我就直接寫代碼了:

先寫一個GPIO 端口置位函數,放到stm32f10x_gpio.c 文件中,同樣在stm32f10x_gpio.h 頭文件聲明。

PB0 輸出高電平,關閉LED,代碼如下:

現在我們再來看看main 函數,看看點亮LED 的代碼是如何一步一步進化的:

8小結

我們從寄存器映像開始,把內存跟寄存器建立起一一對應的關系,然后操作寄存器點亮LED,再到把寄存器操作封裝成一個個函數。為了把不同外設的函數歸類,我們引入了相應的文件來放這些函數,這一步一步走來,我們實現了庫最簡單的雛形,知道庫是怎么來的。后面的工作就是不斷的增加操作外設的函數,并且把所有的外設都寫完,這樣一個完整的庫就實現了。

什么是庫,這就是庫。

下面我們用一張圖來描述下我們剛剛的代碼,讓大家有一個整體的把握。

5、新的嘗試—用庫函數點亮LED

1新建工程

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

為了工程目錄更加清晰,我們在本地電腦上新建6 個文件夾,具體如下:

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

圖10 工程文件夾目錄

在本地新建好文件夾后,把準備好的庫文件添加到相應的文件夾下:

表格2 工程目錄文件夾內容清單

??2新建工程

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

選擇CPU 型號

這個根據你開發板使用的CPU 具體的型號來選擇,比如MINI 選STM32F103VE,ISO 選STM32F103ZE。

圖11 選擇具體的CPU 型號

在線添加庫文件

等下我們手動添加庫文件,這里我們點擊關掉。

圖12 庫文件管理

添加組文件夾

在新建的工程中添加5 個組文件夾,用來存放各種不同的文件,文件從本地建好的工程文件夾下獲?。?/span>

表格3 工程內組文件夾內容清掉

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

配置魔術棒選項卡

這一步的配置工作很重要,很多人串口用不了printf 函數,編譯有問題,下載有問題,都是這個步驟的配置出了錯。

①在Target 中選中微庫,為的是在日后編寫串口驅動的時候可以使用printf 函數

圖14 添加微庫

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

圖15 配置Output 選項卡

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

圖16 配置Listing 選項卡

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

STM32F10X_HD:這個宏是為了區分使用STM32F103 系列中不同容量型號的單片機庫。我們用的單片機的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 這個頭文件。

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

圖17 配置C/C++ 選項卡

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

下載器配置

這部分的配置最好是在安裝好下載器驅動,下載器連接了電腦和開發板,且開發板上電后來配置。

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

①JLINK/ARM-OB 配置

要先安裝了JLINK 驅動之后,該配置才能下載,兩者缺一不可。

圖18 JLINK/ARM-OB 下載配置

②ST-LINK 配置

要先安裝了ST-LINK 驅動之后,該配置才能下載,兩者缺一不可。

圖19 ST-LINK 下載配置

③ULINK2 配置

要先安裝了ULINK2 驅動之后,該配置才能下載,兩者缺一不可。要注意的是設置成ULINK2,而不是ULINK。

圖20 ULINK2 下載配置

選擇CPU 型號

這一步的配置也不是配置一次之后完事,常常會因為各種原因需要重新選擇,當你下載的時候,提示說找不到Device 的時候,請確保該配置是否正確。有時候下載程序之后,不會自動運行,要手動復位的時候,也回來看看這里的Reset and Run 配置是否失效。MINI 和ISO 用的STM32 的FLASH 都是512K,所以選擇512K 大容量,如果使用的是其他型號的,要根據實際情況選擇。

2固件庫分析

在寫代碼之前,我們先來分析下固件庫,看看每個文件的作用是什么,這對我們能否清晰的調用庫函數編程非常重要。

STM32 由Cortex-M3 內核和內核之外的各種外設組成,庫在編寫的時候也遵循這中組成結構,把代碼分成兩大部分,一種是操作內核外設的,另外一種是內核之外的外設,為了聽起來不那么繞,下面我們把內核之外的外設用處理器外設來代替。

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

??1處理器相關

startup_stm32f10x_hd.s

這個是由匯編編寫的啟動文件,是STM32 上電啟動的第一個程序,啟動文件主要實現了:1、初始化堆棧指針SP;2、設置PC 指針=Reset_Handler ;3、設置向量表的地址,并初始化向量表,向量表里面放的是STM32 所有中斷函數的入口地址4、調用庫函數SystemInit,把系統時鐘配置成72M,SystemInit 在庫文件stytem_stm32f10x.c 中定義;5、跳轉到標號_main,最終去到C 的世界。

system_stm32f10x.c

這個文件的作用是里面實現了各種常用的系統時鐘設置函數,有72M,56M,48,36,24,8M,我們使用的是是把系統時鐘設置成72M。

Stm32f10x.h

這個頭文件非常重要,可以說是上帝之手。這個頭文件實現了:1、處理器外設寄存器的結構體定義2、處理器外設的內存映射3、處理器外設寄存器的位定義。

關于1 和2 我們在用寄存器點亮LED 的時候有講解。其中3:處理器外設寄存器的位定義,這個非常重要,具體是什么意思?我們知道一個寄存器有很多個位,每個位寫1 或者寫0 的功能都是不一樣的,處理器外設寄存器的位定義就是把外設的每個寄存器的每一個位寫1 的16 進制數定義成一個宏,宏名即用該位的名稱表示,如果我們操作寄存器要開啟某一個功能的話,就不用自己親自去算這個值是多少,可以直接到這個頭文件里面找。

我們以片上外設ADC 為例,假設我們要啟動ADC 開始轉換,根據手冊我們知道是要控制ADC_CR2 寄存器的位0:ADON,即往位0 寫1,即:ADC->CR2=0x00000001;這是一般的操作方法。現在這個頭文件里面有關于ADON 位的位定義:

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

無論是寄存器編程還是固件庫編程,都必須包含這個頭文件,有關外設寄存器的說明都在這里面。

stm32f10x_xxx.h

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

stm32f10x_xxx.c

stm32f10x_xxx.c:外設xxx 應用函數庫,這里面寫好了操作xxx 外設的所有常用的函數,我們使用庫編程的時候,使用的最多的就是這里的函數。

??2內核相關

cor_cm3.h

這個頭文件實現了:1、內核結構體寄存器定義2、內核寄存器內存映射3、內存寄存器位定義。跟處理器相關的頭文件stm32f10x.h 實現的功能一樣,一個是針對內核的寄存器,一個是針對內核之外,即處理器的寄存器。

misc.h

內核應用函數庫頭文件,對應stm32f10x_xxx.h。

misc.c

內核應用函數庫文件,對應stm32f10x_xxx.c。在CM3 這個內核里面還有一些功能組件,如NVIC、SCB、ITM、MPU、CoreDebug,CM3 帶有非常豐富的功能組件,但是芯片廠商在設計MCU 的時候有一些并不是非要不可的,是可裁剪的,比如MPU、ITM 等在STM32 里面就沒有。其中NVIC 在每一個CM3 內核的單片機中都會有,但都會被裁剪,只能是CM3 NVIC 的一個子集。在NVIC 里面還有一個SysTick,是一個系統定時器,可以提供時基,一般為操作系統定時器所用。

misc.h 和mics.c 這兩個文件提供了操作這些組件的函數,并可以在CM3 內核單片機直接移植。

3開始寫代碼

??1 如何管理庫的頭文件

這么多的庫文件,如何調用,如何管理?當我們開始調用庫函數寫代碼的時候,有些庫我們不需要,在編譯的時候可以不編譯,可以通過一個總的頭文件stm32f10x_conf.h 來控制,該頭文件主要代碼如下:

代碼1 stm32f10x_conf.h 頭文件代碼

這里面包含了全部外設的頭文件,點亮一個LED 我們只需要RCC 和GPIO 這兩個外設的庫函數即可,其中RCC 控制的是時鐘,GPIO 控制的具體的IO 口。所以其他外設庫函數的頭文件我們注釋掉,當我們需要的時候就把相應頭文件的注釋去掉即可。

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

代碼的意思是,如果定義了USE_STDPERIPH_DRIVER 這個宏的話,就包含stm32f10x_conf.h 這個頭文件。我們在新建工程的時候,在魔術棒選項卡C/C++中,我們定義了USE_STDPERIPH_DRIVER 這個宏,所以stm32f10x_conf.h 這個頭文件就被stm32f10x.h 包含了,我們在寫程序的時候只需要調用一個頭文件:stm32f10x.h 即可。

??2編寫LED 初始化函數

經過寄存器點亮LED 的操作,我們知道操作一個GPIO 輸出的編程要點大概如下:

1、開啟GPIO 的端口時鐘

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

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

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

5、輸出高/低電平

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

GPIO 時鐘控制

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

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

GPIO 端口配置

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

寄存器方式

相比寄存器一句話的代碼,固件庫的操作就顯得有些復雜,但換來的是簡單明了。固件庫把端口配置的pin,速度和模式封裝成一個結構體:

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

speed 也被封裝成一個結構體:

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

mode 也被封裝成一個結構體:

IO 口的模式有8 種,輸入輸出各4 種,由端口配置寄存器的CNF 配置。平時用的最多的就是通用推挽輸出,可以輸出高低電平,驅動能力大,一般用于接數字器件。至于剩下的七種模式的用法和電路原理,我們在后面的GPIO 章節再詳細講解。

所以GPIO 端口的配置,最終用固件庫實現就變成這樣:

配置好pin,speed,mode 之后,我們最后調用庫函數GPIO_Init()把剛剛的參數寫到CRL 或者CRH 這兩個寄存器中。

GPIO 輸出控制

GPIO 輸出控制,可以通過端口數據輸出寄存器ODR、端口位設置/清除寄存器BSRR和端口位清除寄存器BRR 這三個來控制。

端口輸出寄存器ODR 是一個32 位的寄存器,低16 位有效,對應著IO0~IO15,只能以字的形式操作,不能單獨對某一個位置位/清除。

代碼2 寄存器操作ODR

圖21 ODR 寄存器

端口位清除寄存器BRR 是一個32 位的寄存器,低十六位有效,對應著IO0~IO15,只能以字的形式操作,可以單獨對某一個位操作,寫1 清0。

代碼3 寄存器操作BRR

代碼4 固件庫操作BRR

圖22 BRR 寄存器

BSRR 是一個32 位的寄存器,低16 位用于置位,寫1 有效,高16 位用于復位,寫1有效,相當于BRR 寄存器。高16 位我們一般不用,而是操作BRR 這個寄存器,所以BSRR 這個寄存器一般用來置位操作。

代碼5 固件庫操作BSRR

圖23 BSRR 寄存器

LED GPIO 初始化函數

代碼6 寄存器LED GPIO 初始化函數

代碼7 固件庫LED GPIO 初始化函數

軟件延時

簡單的通過軟件來延時,具體時間不確定,并不能像51 那么通過計算每條指令執行的時間來確切的計算延時時間。要想精確延時,必須通過定時器實現。

主函數

初始化LED 用到的GPIO,在while 死循環中讓LED 閃爍。在程序來到main 函數前,系統時鐘已經初始化成了72M,有關時鐘部分我們在RCC 這個章節中會詳細講解,這里不是重點。

GPIO 其他庫函數

有關GPIO 的其他庫函數,我們可以在stm32f10x_gpio.h 中找到聲明,然后在stm32f10x_gpio.c 中找到函數的原型,根據函數的注釋,可以知道每個函數的作用。閱讀這些庫函數的時候,最好配合《STM32 中文參考手冊》寄存器描述部分一起看,這樣學習的效果會非常好。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • PWM控制
    +關注

    關注

    7

    文章

    194

    瀏覽量

    26301
  • C51
    C51
    +關注

    關注

    5

    文章

    283

    瀏覽量

    58136

原文標題:如何從快速51單片機轉戰STM32?

文章出處:【微信號:fcsde-sh,微信公眾號:fcsde-sh】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    單片機工程師招聘

        招聘單片機工程師(專職或項目發包皆可) : 3名要求:熟練C51,兩年以上實際開發經驗,并有實際完成過開發項目,請發個
    發表于 12-19 16:26

    單片機工程師招聘

        招聘單片機工程師(專職或項目發包皆可) : 3名要求:熟練C51,兩年以上實際開發經驗,并有實際完成過開發項目,請發個
    發表于 12-19 16:27

    單片機工程師招聘

        招聘單片機工程師(專職或項目發包皆可) : 3名要求:熟練C51,兩年以上實際開發經驗,并有實際完成過開發項目,請發個
    發表于 12-19 16:29

    單片機工程師考試大綱

    單片機工程師考試大綱
    發表于 08-13 23:00

    我看單片機工程師

    我看單片機工程師
    發表于 08-19 22:40

    單片機工程師

    怎么考單片機工程師
    發表于 09-10 21:02

    單片機初學者邁向單片機工程師——好東西,趕緊收下吧~

    附件為一位工程師分享的從單片機初學者邁向單片機工程師學習心得。由簡單深入,好東西,快快收下吧~~~
    發表于 03-24 11:51

    如何成為一個合格單片機工程師?

    做為一個合格單片機工程師,你應該具有這7種能力!
    發表于 02-25 06:45

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

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

    單片機什么程度才算是入門呢?單片機工程師能干多少歲?

    單片機什么程度才算是入門呢?單片機工程師能干多少歲?
    發表于 08-30 07:39

    單片機工程師是程序員嗎

    從業這么多年,我也一直在糾結一個概念,我做的單片機軟件開發是否屬于IT行業? 大家都知道IT這個概念,但很少人找到單片機。一、單片機工程師是程序員嗎?那從我的認知來說,我覺得“算是”。因為單片
    發表于 11-09 09:14

    單片機初學者邁向單片機工程師

    單片機初學者邁向單片機工程師
    發表于 04-24 10:31

    單片機初學者邁向單片機工程師

    單片機初學者邁向單片機工程師單片機初學者邁向單片機工程師
    發表于 01-15 16:33 ?45次下載

    單片機學那個好?單片機工程師工資為什么不高?

    最近在網上發現很多所謂的有經驗的工程師,說學習單片機就直接學習STM32,51單片機已經淘汰了,沒有學習的價值?這是真的嘛?顯然是不對的,我在深圳從事
    發表于 11-13 13:06 ?2次下載
    <b class='flag-5'>單片機</b>學那個好?<b class='flag-5'>單片機工程師</b>工資為什么不高?

    51單片機工程模板創建方法

    本教程將向大家介紹如何使用KEIL C51 軟件以及如何創建51 單片機工程。通過本教程的學習,讓大家創建屬于自己的51 單片機工程模板,為
    發表于 07-15 15:27 ?4914次閱讀
    <b class='flag-5'>51</b><b class='flag-5'>單片機工程</b>模板創建<b class='flag-5'>方法</b>
    主站蜘蛛池模板: WWW夜片内射视频在观看视频| 国产人妻XXXX精品HD电影| 一级毛片美国| 在线观看视频中文字幕| 中文字幕亚洲欧美日韩2019| 2017日日干| xxx性欧美在线| 国产成人精选免费视频| 国产在线精品亚洲另类| 久久精品国产99欧美精品亚洲| 老湿司午夜爽爽影院榴莲视频| 欧美精品熟妇乱| 污污内射久久一区二区欧美日韩| 亚洲精品一本之道高清乱码| 樱花草在线影视WWW日本动漫| 999www成人免费视频| 大胸美女脱内衣黄网站| 国产伦精品一区二区三区免费| 激情床戏揉胸吃胸视频| 麻豆国产精品久久人妻| 日韩欧美 亚洲视频| 性肥胖BWBWBW| 2022国产麻豆剧传媒剧情| 成年人在线视频免费观看| 国产乱人精品视频AV麻豆| 久久综合电影| 欧洲馒头大肥p| 亚洲bt区| 99国产这里只有精品视频| 国产h视频在线观看免费| 久久9精品区-无套内射无码| 欧美另类一区| 亚洲国产成人一区二区在线| 92国产精品午夜免费福利视频| 俄罗斯性xxxx| 久草在线在线精品观看99| 欧美亚洲国产激情一区二区| 亚洲国产综合另类视频| 99久久国产露脸精品竹菊传煤| 国产精品久久久久久久久99热| 久久亚洲欧美国产综合|