日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【正点原子STM32连载】第七章 认识HAL库 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

發(fā)布時(shí)間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【正点原子STM32连载】第七章 认识HAL库 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1)實(shí)驗(yàn)平臺(tái):正點(diǎn)原子MiniPro H750開發(fā)板
2)平臺(tái)購(gòu)買地址:https://detail.tmall.com/item.htm?id=677017430560
3)全套實(shí)驗(yàn)源碼+手冊(cè)+視頻下載地址:http://www.openedv.com/thread-336836-1-1.html
4)對(duì)正點(diǎn)原子STM32感興趣的同學(xué)可以加群討論:879133275

第七章 認(rèn)識(shí)HAL庫(kù)

HAL,英文全稱Hardware Abstraction Layer,即硬件抽象層。HAL庫(kù)是ST公司提供的外設(shè)驅(qū)動(dòng)代碼的驅(qū)動(dòng)庫(kù),用戶只需要調(diào)用庫(kù)的API函數(shù),便可間接配置寄存器。我們要寫程序控制STM32芯片,其實(shí)最終就是控制它的寄存器,HAL庫(kù)就為了更方便我們?nèi)タ刂萍拇嫫?#xff0c;從而節(jié)約開發(fā)時(shí)間。
本章將分為如下幾個(gè)小節(jié):
7.1 初識(shí)STM32 HAL庫(kù)
7.2 HAL庫(kù)驅(qū)動(dòng)包
7.3 HAL庫(kù)框架結(jié)構(gòu)
7.4 如何使用HAL庫(kù)
7.5 HAL庫(kù)使用注意事項(xiàng)

7.1 初識(shí)STM32 HAL庫(kù)

STM32開發(fā)中常說的HAL庫(kù)開發(fā),指的是利用HAL庫(kù)固件包里封裝好的c語(yǔ)言編寫的驅(qū)動(dòng)文件,來實(shí)現(xiàn)對(duì)STM32內(nèi)部和外圍電器元件的控制的過程。但只有HAL庫(kù)還不能直接驅(qū)動(dòng)一個(gè)STM32的芯片,其它的組件已經(jīng)由ARM與眾多芯片硬件、軟件廠商制定的通用的軟件開發(fā)標(biāo)準(zhǔn)CMSIS實(shí)現(xiàn)了,本文只簡(jiǎn)單介紹這個(gè)標(biāo)準(zhǔn),等大家熟悉開發(fā)后再研究這個(gè)框架。
簡(jiǎn)單地了解HAL庫(kù)的發(fā)展和作用,可以方便學(xué)習(xí)者確定HAL庫(kù)是否適合作為學(xué)習(xí)者自己長(zhǎng)期開發(fā)STM32的工具,以降低開發(fā)、學(xué)習(xí)的成本。
7.1.1 CMSIS標(biāo)準(zhǔn)
根據(jù)一些調(diào)查研究表明,軟件開發(fā)已經(jīng)被嵌入式行業(yè)公認(rèn)為最主要的開發(fā)成本,為了降低這個(gè)成本,ARM與Atmel、IAR、KEIL、SEGGER和ST等諸多芯片和軟件工具廠商合作,制定了一個(gè)將所有Cortex芯片廠商的產(chǎn)品的軟件接口標(biāo)準(zhǔn)化的標(biāo)準(zhǔn)CMSIS(Cortex Microcon troller Software Interface Standard)。下面來看ARM官方提供的CMSIS規(guī)范架構(gòu)圖,如圖7.1.1.1所示:

圖7.1.1.1 CorteX芯片的CMSIS分級(jí)實(shí)現(xiàn)
從圖中可以看出這個(gè)標(biāo)準(zhǔn)分級(jí)明顯,從用戶程序到內(nèi)核底層實(shí)現(xiàn)做了分層。按照這個(gè)分級(jí),HAL庫(kù)屬于CMSIS-Pack中的“Peripheral HAL”層。CMSIS規(guī)定的最主要的3個(gè)部分為:核內(nèi)外設(shè)訪問層(由ARM負(fù)責(zé)實(shí)現(xiàn))、片上外設(shè)訪問層和外設(shè)訪問函數(shù)(后面兩個(gè)由芯片廠商負(fù)責(zé)實(shí)現(xiàn))。ARM整合并提供了大量的模版,各廠商根據(jù)自己的芯片差異修改模版,這其中包括匯編文件startup_device.s、system_.h和system_.c這些與初始化和系統(tǒng)相關(guān)的函數(shù)。
結(jié)合STM32H7的芯片來說,其CMSIS應(yīng)用程序的簡(jiǎn)單結(jié)構(gòu)框圖,不包括實(shí)時(shí)操作系統(tǒng)和中間設(shè)備等組件,其結(jié)構(gòu)如圖7.1.1.2所示。

圖7.1.1.2 CMSIS分級(jí)下的STM32H7的文件分布
上面的框架是根據(jù)我們現(xiàn)在已經(jīng)學(xué)習(xí)到的知識(shí)回過頭來作的一個(gè)總結(jié),這里只是作簡(jiǎn)單介紹,告訴大家它們之間存在一定聯(lián)系,關(guān)于組成這些部分的文件、文件的作用及各文件如何組合、各分層的作用和意義,我們會(huì)在今后的學(xué)習(xí)過程中慢慢學(xué)習(xí)。
7.1.2 HAL庫(kù)簡(jiǎn)介
庫(kù)函數(shù)的引入,大大降低STM主控芯片開發(fā)的難度。ST公司為了方便用戶開發(fā)STM32芯片開發(fā)提供了三種庫(kù)函數(shù),從時(shí)間產(chǎn)生順序是:標(biāo)準(zhǔn)庫(kù)、HAL庫(kù)和LL庫(kù)。目前ST已經(jīng)逐漸暫停對(duì)部分標(biāo)準(zhǔn)庫(kù)的支持,ST的庫(kù)函數(shù)維護(hù)重點(diǎn)對(duì)角已經(jīng)轉(zhuǎn)移到HAL庫(kù)和LL庫(kù)上,下面我們分別為這三種庫(kù)作一下簡(jiǎn)單的介紹。
1.標(biāo)準(zhǔn)外設(shè)庫(kù)(Standard Peripheral Libraries)
標(biāo)準(zhǔn)外設(shè)庫(kù)(Standard Peripherals Library)是對(duì)STM32芯片的一個(gè)完整的封裝,包括所有標(biāo)準(zhǔn)器件外設(shè)的器件驅(qū)動(dòng)器,是ST最早推出的針對(duì)STM系列主控的庫(kù)函數(shù)。標(biāo)準(zhǔn)庫(kù)的設(shè)計(jì)的初衷是減少用戶的程序編寫時(shí)間,進(jìn)而降低開發(fā)成本。幾乎全部使用C語(yǔ)言實(shí)現(xiàn)并嚴(yán)格按照“Strict ANSI-C”、MISRA-C 2004等多個(gè)C語(yǔ)言標(biāo)準(zhǔn)編寫。但標(biāo)準(zhǔn)外設(shè)庫(kù)仍然接近于寄存器操作,主要就是將一些基本的寄存器操作封裝成了C函數(shù)。開發(fā)者仍需要關(guān)注所使用的外設(shè)是在哪個(gè)總線之上,具體寄存器的配置等底層信息。

圖7.1.2.1 ST的標(biāo)準(zhǔn)庫(kù)函數(shù)家族
ST為各系列提供的標(biāo)準(zhǔn)外設(shè)庫(kù)稍微有些區(qū)別。例如,STM32F1x的庫(kù)和STM32F3x的庫(kù)在文件結(jié)構(gòu)上就有些不同,此外,在內(nèi)部的實(shí)現(xiàn)上也稍微有些區(qū)別,這個(gè)在具體使用(移植)時(shí),需要注意一下!但是,不同系列之間的差別并不是很大,而且在設(shè)計(jì)上是相同的。
STM32的標(biāo)準(zhǔn)外設(shè)庫(kù)涵蓋以下3個(gè)抽象級(jí)別:
? 包含位,位域和寄存器在內(nèi)的完整的寄存器地址映射。
? 涵蓋所有外圍功能(具有公共API的驅(qū)動(dòng)器)的例程和數(shù)據(jù)結(jié)構(gòu)的集合。
? 一組包含所有可用外設(shè)的示例,其中包含最常用的開發(fā)工具的模板項(xiàng)目。
關(guān)于更詳細(xì)的信息,可以參考ST的官方文檔《STM32 固件庫(kù)使用手冊(cè)中文翻譯版》,文檔中對(duì)于標(biāo)準(zhǔn)外設(shè)庫(kù)函數(shù)命名、文件結(jié)構(gòu)等都有詳細(xì)的說明,這里我們就不多介紹了。
值得一提的是由于STM32的產(chǎn)品性能及標(biāo)準(zhǔn)庫(kù)代碼的規(guī)范和易讀性以及例程的全覆蓋性,使STM32的開發(fā)難度大大下降,更多。但ST從L1以后的芯片L0、L4和F7等系列就沒有再推出相應(yīng)的標(biāo)準(zhǔn)庫(kù)支持包了。
2.HAL庫(kù)
HAL是Hardware Abstraction Layer的縮寫,即硬件抽象層。是ST為可以更好的確保跨STM32產(chǎn)品的最大可移植性而推出的MCU操作庫(kù)。這種程序設(shè)計(jì)由于抽離應(yīng)用程序和硬件底層的操作,更加符合跨平臺(tái)和多人協(xié)作開發(fā)的需要。
HAL庫(kù)是基于一個(gè)非限制性的BSD許可協(xié)議(Berkeley Software Distribution)而發(fā)布的開源代碼。ST制作的中間件堆棧(USB主機(jī)和設(shè)備庫(kù),STemWin)帶有允許輕松重用的許可模式, 只要是在ST公司的MCU 芯片上使用,庫(kù)中的中間件(USB主機(jī)/設(shè)備庫(kù),STemWin)協(xié)議棧即被允許修改,并可以反復(fù)使用。至于基于其它著名的開源解決方案商的中間件(FreeRTOS,FatFs,LwIP和PolarSSL)也都具有友好的用戶許可條款。
HAL庫(kù)是從ST公司從自身芯片的整個(gè)生產(chǎn)生態(tài)出發(fā),為了方便維護(hù)而作的一次整合,以改變標(biāo)準(zhǔn)外設(shè)庫(kù)帶來各系列芯片操作函數(shù)結(jié)構(gòu)差異大、分化大、不利于跨系列移植的情況。相比標(biāo)準(zhǔn)外設(shè)庫(kù),STM32Cube HAL庫(kù)表現(xiàn)出更高的抽象整合水平,HAL庫(kù)的API集中關(guān)注各外設(shè)的公共函數(shù)功能,這樣便于定義一套通用的用戶友好的API函數(shù)接口,從而可以輕松實(shí)現(xiàn)從一個(gè)STM32產(chǎn)品移植到另一個(gè)不同的STM32系列產(chǎn)品。但由于封閉函數(shù)為了適應(yīng)最大的兼容性,HAL庫(kù)的一些代碼實(shí)際上的執(zhí)行效率要遠(yuǎn)低于寄存器操作。但即便如此,HAL庫(kù)仍是ST未來主推的庫(kù)。
3.LL庫(kù)
LL庫(kù)(Low Layer)目前與HAL庫(kù)捆綁發(fā)布,它的設(shè)計(jì)為比HAL庫(kù)更接近于硬件底層的操作,代碼更輕量級(jí),代碼執(zhí)行效率更高的庫(kù)函數(shù)組件,可以完全獨(dú)立于HAL庫(kù)來使用,但LL庫(kù)不匹配復(fù)雜的外設(shè),如USB等。所以LL庫(kù)并不是每個(gè)外設(shè)都有對(duì)應(yīng)的完整驅(qū)動(dòng)配置程序。使用LL庫(kù)需要對(duì)芯片的功能有一定的認(rèn)知和了解,它可以:
? 獨(dú)立使用,該庫(kù)完全獨(dú)立實(shí)現(xiàn),可以完全拋開HAL庫(kù),只用LL庫(kù)編程完成。
? 混合使用,和HAL庫(kù)結(jié)合使用。
對(duì)于HAL庫(kù)和LL庫(kù)的關(guān)系,如圖7.1.2.2 Cube的軟件框架所示,可以看出它們?cè)O(shè)計(jì)為彼此獨(dú)立的分支,但又同屬于HAL庫(kù)體系。

圖7.1.2.2 Cube的軟件框架
通過以上簡(jiǎn)介我們對(duì)目前主流的STM32開發(fā)庫(kù)有了一個(gè)初步的印象。標(biāo)準(zhǔn)庫(kù)和HAL庫(kù)、LL庫(kù)完全相互獨(dú)立,HAL庫(kù)更傾向于外設(shè)通用化,擴(kuò)展組件中解決芯片差異操作部分;LL傾向于最簡(jiǎn)單的寄存器操作,ST在未來還將重點(diǎn)維護(hù)和建設(shè)HAL庫(kù),標(biāo)準(zhǔn)庫(kù)已經(jīng)部分停止更新。HAL庫(kù)和LL庫(kù)的應(yīng)用將是未來的一個(gè)趨勢(shì)。
7.1.3 HAL庫(kù)能做什么
用過標(biāo)準(zhǔn)庫(kù)的朋友應(yīng)該知道,使用標(biāo)準(zhǔn)庫(kù)可以忽略很多芯片寄存器的細(xì)節(jié),根據(jù)提供的接口函數(shù)快速配置和使用一個(gè)STM32芯片,使用HAL庫(kù)也是如此。不論何種庫(kù),本質(zhì)都是配置指定寄存器使芯片工作在我們需要的工作模式下。HAL庫(kù)在設(shè)計(jì)的時(shí)候會(huì)更注重軟硬件分離。HAL庫(kù)的API集中關(guān)注各個(gè)外設(shè)的公共函數(shù)功能,便于定義通用性更好、更友好的API函數(shù)接口,從而具有更好的可移植性。HAL庫(kù)寫的代碼在不同的STM32產(chǎn)品上移植,非常方便。
我們需要學(xué)會(huì)調(diào)用HAL庫(kù)的API函數(shù),配置對(duì)應(yīng)外設(shè)按照我們的要求工作,這就是HAL庫(kù)能做的事。但是無論庫(kù)封裝得多高級(jí),最終還是要通過配置寄存器來實(shí)現(xiàn)。所以我們學(xué)習(xí)HAL庫(kù)的同時(shí),也建議同時(shí)學(xué)習(xí)外設(shè)的工作原理和寄存器的配置。只有掌握了原理,才能更好的使用HAL庫(kù),一旦發(fā)生問題也能更快速了定位和解決問題。
HAL庫(kù)還可以和STM32CubeMX(圖形化軟件配置工具)配套一起使用,開發(fā)者可以使用該工具進(jìn)行可視化配置,并且自動(dòng)生成配置好的初始化代碼,大大的節(jié)省開發(fā)時(shí)間。

7.2 HAL庫(kù)驅(qū)動(dòng)包

HAL庫(kù)是一系列封裝好的驅(qū)動(dòng)函數(shù),本節(jié)將從下載渠道、固件包的內(nèi)容分析及在實(shí)際開發(fā)中用到的幾個(gè)文件的詳細(xì)介紹。
7.2.1 如何獲取HAL庫(kù)固件包
HAL庫(kù)是ST推出的STM32Cube軟件生態(tài)下的一個(gè)分支。STM32Cube是ST公司提供的一套免費(fèi)開發(fā)工具和STM32Cube 固件包,旨在通過減少開發(fā)工作、時(shí)間和成本來簡(jiǎn)化開發(fā)人員的工作,并且覆蓋整個(gè)STM32產(chǎn)品。它包含兩個(gè)關(guān)鍵部分:
1、允許用戶通過圖形化向?qū)砩蒀語(yǔ)言工程的圖形配置工具STM32CubeMX。可以通過CubeMX實(shí)現(xiàn)方便地下載各種軟件或開發(fā)固件包。
2、包括由STM32Cube硬件抽象層(HAL),還有一組一致的中間件組件(RTOS、USB、FAT文件系統(tǒng)、圖形、TCP/IP和以太網(wǎng)),以及一系列完整的例程組成的STM32Cube固件包。
ST提供了多種獲取固件包的方法。本節(jié)只介紹從ST官方網(wǎng)站上直接獲取固件庫(kù)的方法。網(wǎng)頁(yè)登陸:www.st.com,在打開的頁(yè)面中依次選擇:“Tools & Software”->“Ecosystem”-> “STM32Cube”->新頁(yè)面->選擇“Prodcut selector”,具體如下圖所示:

圖7.2.1.1 找到STM32CubeH7固件包的下載位置
在展開的頁(yè)面中選擇我們需要和固件,這里展開“STM32CubeH7”即可看到我們需要的H7的固件包,按下圖操作,在新的窗口中拉到底部,選擇適合自己的下載方式,注冊(cè)帳號(hào)即可獲取相應(yīng)的驅(qū)動(dòng)包。

圖7.2.1.2 下載STM32CubeH7固件包
STM32Cube固件包,我們已經(jīng)給大家下載好并且放到A盤8,STM32參考資料1,STM32CubeH7固件包,當(dāng)前固件包版本是:STM32Cube_FW_H7_V1.6.0。因?yàn)楝F(xiàn)在是STM32H750的學(xué)習(xí),所以我們準(zhǔn)備好的固件包是H7的。大家要根據(jù)自己學(xué)習(xí)的芯片,下載對(duì)應(yīng)的固件包。如果需要最新的固件包,大家按照上述的方法到官網(wǎng)重新獲取即可。
7.2.2 STM32Cube固件包分析
STM32Cube 固件包完全兼容STM32CubeMX。對(duì)于圖形配置工具STM32CubeMX入門使用,由于需要STM32F1基礎(chǔ)才能入門使用,所以我們安排在后面第十章給大家講解。本小節(jié),我們主要講解STM32Cube固件包的結(jié)構(gòu)。
解壓STM32CubeH7固件包后,我們看看其目錄結(jié)構(gòu),如圖7.2.2.1所示。

圖7.2.2.1 STM32CubeH7固件包的目錄結(jié)構(gòu)
下面對(duì)STM32CubeH7固件包進(jìn)行簡(jiǎn)要介紹。對(duì)于Documentation文件夾,里面是一個(gè)STM32CubeH7英文說明文檔,這里我們就不做過多解釋。接下來我們通過幾個(gè)表格依次來介紹一下STM32CubeH7中幾個(gè)關(guān)鍵的文件夾。
(1)Drivers文件夾
Drivers文件夾包含BSP,CMSIS和STM32H7xx_HAL_Driver三個(gè)子文件夾。三個(gè)子文件夾具體說明請(qǐng)參考下表7.2.2.1:

表7.2.2.1 Drivers文件夾介紹
(2)Middlewares文件夾
該文件夾下面有ST和Third_Party 2個(gè)子文件夾。ST文件夾下面存放的是STM32相關(guān)的一些文件,包括STemWin和USB庫(kù)等。Third_Party文件夾是第三方中間件,這些中間價(jià)都是非常成熟的開源解決方案。具體說明請(qǐng)見下表7.2.2.2:

表7.2.2.2 Middlewares文件夾介紹
(3)Projects文件夾
該文件夾存放的是ST官方的開發(fā)板的適配例程,每個(gè)文件夾對(duì)應(yīng)一個(gè)ST官方的Demo板,根據(jù)型號(hào)的不同提供MDK和IAR等類型的例程。里面有很多實(shí)例,讀者可以根據(jù)自己的需要來作為參考。比如我們要查看STM32H750相關(guān)工程,所以我們直接打開子文件夾STM32H750B-DK即可。里面有很多實(shí)例,我們都可以用來參考。這里大家注意,每個(gè)工程下面都有一個(gè)MDK-ARM子文件夾,該子文件夾內(nèi)部會(huì)有名稱為Project.uvprojx的工程文件,我們只需要雙擊它就可在MDK中打開工程。
(4)Utilities文件夾
該文件夾是一些公用組件,也是主要為ST官方的Demo板提供的,在我們的例程中使用得不多。有興趣的同學(xué)可以深入研究一下,這里我們不做過多介紹。
(5)其它幾個(gè)文件
文件夾中還有幾個(gè)單獨(dú)的文件,用于聲明軟件版本或者版權(quán)信息,我們使用ST的芯片已經(jīng)默認(rèn)得到這個(gè)軟件的版權(quán)使用授權(quán),可以簡(jiǎn)單了解一下各文件的內(nèi)容,實(shí)際項(xiàng)目中我們一般不添加。
License.md:用于聲明軟件版權(quán)信息的文件。
package.xml:描述固件包版本信息的文件。
Release_Notes.html:超文本文件,用瀏覽器打開可知它是對(duì)固件包的補(bǔ)充描述和固件版本更新的記錄說明。
7.2.3 CMSIS文件夾關(guān)鍵文件
上一節(jié)中我們對(duì)STM32cube固件包的主要目錄結(jié)構(gòu)做了分析。這一小節(jié)在上一小節(jié)的基礎(chǔ)上,我們來分析一下CMSIS文件夾:由命名可知該文件夾和7.1.1小節(jié)中提到的CMSIS標(biāo)準(zhǔn)是一致的,CMSIS為軟件包的內(nèi)容制定了標(biāo)準(zhǔn),包括文件目錄的命名和內(nèi)容構(gòu)成,CMSIS版本5.7.0的規(guī)定軟件包目錄如表7.2.3.1所示:

表7.2.3.1 CMSIS v5.7.0的文件夾規(guī)范
知道了CMSIS規(guī)定的組件及其文件目錄的大概內(nèi)容后,我們?cè)賮砜纯碨T提供的CMSIS文件夾,如上節(jié)提到的,它的位置是“STM32Cube_FW_H7_V1.6.0\Drivers\CMSIS”。打開文件夾內(nèi)容如圖7.2.3.1所示,可以發(fā)現(xiàn)它的目錄結(jié)構(gòu)完全按照CMSIS標(biāo)準(zhǔn)執(zhí)行,僅僅是作了部分刪減。

圖7.2.3.1 STM32CubeH7固件包的CMSIS文件夾
CMSIS文件夾中的Device和Include這兩個(gè)文件夾中的文件是我們工程中最常用到的。下面對(duì)這兩個(gè)文件夾作簡(jiǎn)單的介紹:
(1)Device文件夾
Device文件夾關(guān)鍵文件介紹如下表7.2.3.1所示:

表7.2.3.1 Device文件夾關(guān)鍵文件介紹
表7.1.2.1列出的文件都是正式工程中必須的文件。固件包的CMSIS文件包括了所有STM32H7芯片型號(hào)的文件,而我們只用到STM32H750系列,所以只是挑我們用到的系列文件來講。
(2)Include文件夾
Include文件夾存放了符合CMSIS標(biāo)準(zhǔn)的 Cortex-M 內(nèi)核頭文件。 想要深入學(xué)習(xí)內(nèi)核的朋友可以配合內(nèi)核相關(guān)的手冊(cè)去學(xué)習(xí)。對(duì)于STM32H7的工程,我們只要把我們需要的添加到工程即可,需要的頭文件有:cmsis_armcc.h、cmsis_armclang.h、cmsis_compiler.h、cmsis_version.h、core_cm7.h和mpu_armv7.h。這幾個(gè)頭文件,對(duì)比起來,我們會(huì)比較多接觸的是core_cm7.h。
core_cm7.h是內(nèi)核底層的文件,由ARM公司提供,包含一些AMR內(nèi)核指令,如軟件復(fù)位,開關(guān)中斷等功能。今后在需要的例程再去講解其程序,現(xiàn)在之所以提到,是因?yàn)樗艘粋€(gè)重要的頭文件stdint.h。
7.2.4 stdint.h簡(jiǎn)介
stdint.h是從c99中引進(jìn)的一個(gè)標(biāo)準(zhǔn)C庫(kù)的文件。在2000年3月,ANSI 采納了 C99 標(biāo)準(zhǔn)。ANSI C被幾乎所有廣泛使用的編譯器(如:MDK、IAR)支持。多數(shù)C代碼是在ANSI C基礎(chǔ)上寫的。任何僅僅使用標(biāo)準(zhǔn)C并且沒有和任何硬件有依賴的代碼實(shí)際上能保證在任何平臺(tái)上用遵循C標(biāo)準(zhǔn)的編譯器編譯成功。就是說這套標(biāo)準(zhǔn)不依賴硬件,獨(dú)立于任何硬件,可以跨平臺(tái)。
stdint.h可以在MDK安裝目錄下找到,如MDK5安裝在C盤時(shí),可以在路徑:C:\Keil_v5\ARM\ARMCC\include找到。stdint.h的作用就是提供了類型定義,其部分類型定義代碼如下:

/* exact-width signed integer types */ typedef signed char int8_t; typedef signed short int int16_t; typedef signed int int32_t; typedef signed __INT64 int64_t;/* exact-width unsigned integer types */ typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; typedef unsigned __INT64 uint64_t;

在今后的程序,我們都將會(huì)使用這些類型,比如:uint32_t(無符號(hào)整型)、int16_t等。

7.3 HAL庫(kù)框架結(jié)構(gòu)

這一節(jié)我們將簡(jiǎn)要分析一下HAL驅(qū)動(dòng)文件夾下的驅(qū)動(dòng)文件,
7.3.1 HAL庫(kù)文件夾結(jié)構(gòu)
HAL庫(kù)頭文件和源文件在STM32Cube固件包的STM32H7xx_HAL_Driver文件夾中,打開該文件夾,如圖7.3.1.1所示。

圖7.3.1.1 STM32H7xx_HAL_Driver文件夾目錄結(jié)構(gòu)
STM32H7xx_HAL_Driver文件夾下的Src(Source的簡(jiǎn)寫)文件夾存放是所有外設(shè)的驅(qū)動(dòng)程序源碼,Inc(Include的簡(jiǎn)寫)文件夾存放的是對(duì)應(yīng)源碼的頭文件。Release_Notes.html是HAL庫(kù)的版本更新信息。最后三個(gè)是庫(kù)的用戶手冊(cè),這個(gè)需要可以去熟悉一下,查閱起來很方便。
打開Src和Inc文件夾,大家會(huì)發(fā)現(xiàn)基本都是stm32h7xx_hal_和stm32h7xx_ll_開頭的.c和
.h文件。剛學(xué)HAL庫(kù)的朋友可能會(huì)說,stm32h7xx_hal_開頭的是HAL庫(kù),我能理解。那么stm32h7xx_ll_開頭的文件又是什么?stm32h7xx_ll_開頭的文件是前面介紹過的LL庫(kù)的文件。
7.3.2 HAL庫(kù)文件介紹
HAL庫(kù)關(guān)鍵文件介紹如下表7.3.2.1所示,表中ppp代表任意外設(shè)。

表7.3.2.1 HAL庫(kù)關(guān)鍵文件介紹
以上是HAL庫(kù)最常見的文件的列表,在Src/Inc下面還有Legacy文件夾,用于特殊外設(shè)的補(bǔ)充說明。我們的教程中用到的比較少,這里不展開描述。
不止文件命名有一定規(guī)則,stm32h7xx_hal_ppp (c/h)中的函數(shù)和變量命名也嚴(yán)格按照命名規(guī)則,如表7.3.2.2所示的命名規(guī)則在大部分情況下都是正確的:

表7.3.2.2 HAL庫(kù)函數(shù)、變量命名規(guī)則
對(duì)于HAL的API函數(shù),常見的有以下幾種:
?初始化/反初始化函數(shù):HAL_PPP_Init(), HAL_PPP_DeInit()
?外設(shè)讀寫函數(shù):HAL_PPP_Read(),HAL_PPP_Write(),HAL_PPP_Transmit(),HAL_PPP_Receive()
?控制函數(shù):HAL_PPP_Set (),HAL_PPP_Get ()
?狀態(tài)和錯(cuò)誤:HAL_PPP_GetState (), HAL_PPP_GetError ()
HAL庫(kù)封裝的很多函數(shù)都是通過定義好的結(jié)構(gòu)體將參數(shù)一次性傳給所需函數(shù),參數(shù)也有一定的規(guī)律,主要有以下三種:
? 配置和初始化用的結(jié)構(gòu)體
一般為PPP_InitTypeDef或PPP_ ConfTypeDef的結(jié)構(gòu)體類型,根據(jù)外設(shè)的寄存器設(shè)計(jì)成易于理解和記憶的結(jié)構(gòu)體成員。
? 特殊處理的結(jié)構(gòu)體
專為不同外設(shè)而設(shè)置的,帶有“Process”的字樣,實(shí)現(xiàn)一些特異化的中間處理操作等。
? 外設(shè)句柄結(jié)構(gòu)體
HAL驅(qū)動(dòng)的重要參數(shù),可以同時(shí)定義多個(gè)句柄結(jié)構(gòu)以支持多外設(shè)多模式。HAL驅(qū)動(dòng)的操作結(jié)果也可以通過這個(gè)句柄獲得。有些HAL驅(qū)動(dòng)的頭文件中還定義了一些跟這個(gè)句柄相關(guān)的一些外設(shè)操作。如用外設(shè)結(jié)構(gòu)體句柄與HAL定義的一些宏操作配合,即可實(shí)現(xiàn)一些常用的寄存器位操作。

表7.3.2.3 HAL庫(kù)驅(qū)動(dòng)部分與外設(shè)句柄相關(guān)的宏
但對(duì)于SYSTICK/NVIC/RCC/FLASH/ GPIO這些內(nèi)核外設(shè)或共享資源,不使用PPP_HandleTypedef這類外設(shè)句柄進(jìn)行控制,如:HAL_GPIO_Init() 只需要初始化的GPIO編號(hào)和具體的初始化參數(shù)。
HAL_StatusTypeDef HAL_GPIO_Init (GPIO_TypeDef* GPIOx, GPIO_InitTypeDef Init)
{
/ GPIO 初始化程序…… */
}
最后要分享的是HAL庫(kù)的回調(diào)函數(shù),這部分允許用戶重定義,并在其中實(shí)現(xiàn)用戶自定義的功能,也是我們使用HAL庫(kù)的最常用的接口之一:

表7.3.2.4 HAL庫(kù)驅(qū)動(dòng)中常用的回調(diào)函數(shù)接口
至此,我們大概對(duì)HAL庫(kù)驅(qū)動(dòng)文件的一些通用格式和命名規(guī)則有了初步印象,記住這些規(guī)則可以幫助我們快速對(duì)HAL庫(kù)的驅(qū)動(dòng)進(jìn)行歸類和判定這些驅(qū)動(dòng)函數(shù)的用法。
ST官方給我們提供了快速查找API函數(shù)的幫助文檔。在路徑“STM32Cube_FW_H7 _V1.6.0\Drivers\STM32H7xx_HAL_Driver”下有幾個(gè)chm格式的文檔,根據(jù)我們開發(fā)板主控芯片STM32H750VBT6我們沒有找到直接可用的,但可以查看型號(hào)接近的:STM32H753xx_User _Manual.chm。雙擊打開后,可以看到左邊目錄下有四個(gè)主題,我們來查看Modules。以外設(shè)GPIO為例,講一下怎么使用這個(gè)文檔。點(diǎn)擊GPIO外設(shè)的主題下的IO operation functions /functions看看里面的API函數(shù)接口描述,如圖7.3.2.1所示。

圖7.3.2.1 文檔的API函數(shù)描述
這個(gè)文檔提供的信息很全,不看源碼都可以直接使用它來編寫代碼,還給我們指示源碼位置,非常方便。大家多翻一下其他主題了解一下文檔的信息結(jié)構(gòu),很容易使用。
下面舉個(gè)例子,比如我們要讓PB4輸出高電平。先看函數(shù)功能,HAL_GPIO_WritePin函數(shù)就是我們的GPIO口輸出設(shè)置函數(shù)。
函數(shù)有三個(gè)形參:
第一個(gè)形參是GPIO_TypeDef *GPIOx,形參描述說:x可以是A到K之間任何一個(gè),而我們是PB4引腳,所以第一個(gè)形參確認(rèn)是GPIOB。
第二個(gè)形參是uint16_t GPIO_Pin,看形參描述:該參數(shù)可以是GPIO_PIN_x,x可以1到15,那么我們第二個(gè)形參就是GPIO_PIN_4。
第三個(gè)形參是GPIO_PinState PinState,看形參描述:該參數(shù)可以是枚舉里的兩個(gè)數(shù),一個(gè)是GPIO_PIN_RESET:表示該位清零,另一個(gè)是GPIO_PIN_SET:表示設(shè)置該位,即置1,我們要輸出1,所以要置1該位,那么我們第三個(gè)形參就是GPIO_PIN_SET。
最后看函數(shù)返回值:None,沒有返回值。
所以最后得出我們要調(diào)用的函數(shù)是:
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
文檔的使用就講到這。
7.4 如何使用HAL庫(kù)
我們要先知道STM32芯片的某個(gè)外設(shè)的性能和工作模式,才能借助HAL庫(kù)來幫助我們編程,甚至修改HAL庫(kù)來適配我們的開發(fā)項(xiàng)目。HAL庫(kù)的API雖多,但是查找和使用有規(guī)律可循,只要學(xué)會(huì)其中一個(gè),其他的外設(shè)就是類似的,只是添加自己的特性的API而已。
7.4.1 學(xué)會(huì)用HAL庫(kù)組織開發(fā)工具鏈
需要按照芯片使用手冊(cè)建議的步驟去配置芯片。HAL庫(kù)驅(qū)動(dòng)提供了芯片的驅(qū)動(dòng)接口,但我們需要強(qiáng)調(diào)一個(gè)概念是使用HAL庫(kù)的開發(fā)是對(duì)芯片功能的開發(fā),而不是開發(fā)這個(gè)庫(kù),也不是由這個(gè)庫(kù)能就直接開發(fā)。如果我們對(duì)芯片的功能不作了解的話,仍然不知道按照怎樣的步驟和尋找哪些可用的接口去實(shí)現(xiàn)想要實(shí)現(xiàn)的功能。
ST提供芯片使用手冊(cè)《STM32H7xx參考手冊(cè)_V7(英文版).pdf》告訴我們使用某一外設(shè)功能時(shí)如何具體地去操作每一個(gè)用到的寄存器的細(xì)節(jié),后面我們的例程講解過程也會(huì)結(jié)合這個(gè)手冊(cè)來分析配置過程。
嵌入式的軟件開發(fā)流程總遵循以下步驟:組織工具鏈、編寫代碼、生成可執(zhí)行文件、燒錄到芯片、芯片根據(jù)內(nèi)部指令執(zhí)行我們編程生成的可執(zhí)行代碼。
在HAL庫(kù)學(xué)習(xí)前期,建議以模仿和操作體驗(yàn)為基礎(chǔ),通過例程來學(xué)習(xí)如何配置和驅(qū)動(dòng)外設(shè)。下面根據(jù)我們后續(xù)要學(xué)習(xí)的工程梳理出來的基于CMSIS的一個(gè)HAL庫(kù)應(yīng)用程序文件結(jié)構(gòu),幫助讀者學(xué)習(xí)和體會(huì)這些文件的組成意義,如下表7.4.1.1所示。

表7.4.1.1基于CMSIS應(yīng)用程序文件描述
把這些文件組織起來的方法,我們會(huì)在后續(xù)章節(jié)新建工程中介紹,這只提前告訴一下大家組成我們需要的編譯工具鏈大概需要哪些文件。
7.4.2 HAL庫(kù)的用戶配置文件
stm32h7xx_hal_conf.h用于裁剪HAL庫(kù)和定義一些變量,官方?jīng)]有直接提供這個(gè)文件,但在STM32Cube_FW_H7_V1.6.0\Drivers\STM32H7xx_HAL_Driver\Inc這個(gè)路徑下提供了一個(gè)模版文件《stm32h7xx_hal_conf_template.h》,我們可以直接復(fù)制這個(gè)文件重命名為stm32h7xx_ hal_conf.h,做一些簡(jiǎn)單的修改即可,也可以從在官方的例程中直接復(fù)制過來。我們一般都直接從官方的模板例程中直接復(fù)制過來即可。因?yàn)槲覀兊男酒荢TM32H750系列,所以選擇的路徑是:STM32Cube_FW_H7_V1.6.0\Projects\STM32H750B-DK\Templates\Template_Project\ Inc。
(1)stm32h7xx_hal_conf.h文件里面的內(nèi)容不多,對(duì)我們來說最重要的是HSE_VALUE這個(gè)參數(shù),這個(gè)參數(shù)表示我們的外部高速晶振的頻率。這個(gè)參數(shù)請(qǐng)務(wù)必根據(jù)我們板子外部焊接的晶振頻率來修改,官方默認(rèn)是25M。正點(diǎn)原子STM32H750MINI PRO開發(fā)板外部高速晶振的頻率是8MHZ。注意事項(xiàng):我們要修改這個(gè)參數(shù),源碼在99行,具體修改如下:

#if !defined (HSE_VALUE) #define HSE_VALUE ((uint32_t)8000000) /* 外部高速振蕩器的值,單位HZ */ #endif /* HSE_VALUE */

(2)還有一個(gè)參數(shù)就是外部低速晶振頻率,這個(gè)官方默認(rèn)是32.768KHZ,我們開發(fā)板的低速晶振也是這個(gè)頻率,所以不用修改,源碼在128行。

#if !defined (LSE_VALUE)#define LSE_VALUE ((uint32_t)32768) /* 外部低速振蕩器的值,單位HZ */ #endif /* LSE_VALUE */

其他源碼都可以不作修改,按照默認(rèn)的配置即可。下面我們?cè)賮砹私庖幌缕渌绦颉?br /> (3)用戶配置文件可以用來選擇使能何種外設(shè),源碼配置在37行到90行,代碼如下。

/* ########################## Module Selection ############################# */ /*** @brief This is the list of modules to be used in the HAL driver */ #define HAL_MODULE_ENABLED #define HAL_ADC_MODULE_ENABLED #define HAL_CEC_MODULE_ENABLED #define HAL_COMP_MODULE_ENABLED #define HAL_CORTEX_MODULE_ENABLED ...中間省略... #define HAL_UART_MODULE_ENABLED #define HAL_USART_MODULE_ENABLED #define HAL_WWDG_MODULE_ENABLED 我們只要屏蔽某個(gè)外設(shè)的宏,則這個(gè)外設(shè)的驅(qū)動(dòng)代碼機(jī)會(huì)被屏蔽,從而不可用。比如我們屏蔽GPIO外設(shè)的宏,源碼在53行,屏蔽就是把這個(gè)宏注釋掉,具體如下。 #define HAL_GPIO_MODULE_ENABLED 然后打開stm32h7xx_hal_gpio.c文件,看到第118行。 #ifdef HAL_GPIO_MODULE_ENABLED #include "stm32h7xx_hal_gpio.h" #endif

這是一個(gè)條件編譯符,與#endif配合使用。這里的要表達(dá)的意思是,只要工程中定義了HAL_GPIO_MODULE_ENABLED這個(gè)宏,#ifdef到#endif之間的程序(119行到550行)就會(huì)參與編譯,否則不編譯。所以只要我們屏蔽了stm32h7xx_hal_conf.h文件53行的宏,GPIO的驅(qū)動(dòng)代碼就不被編譯。也就起到選擇使能何種外設(shè)的功能,其他外設(shè)同理。
可以看官方的示范例程,就是通過屏蔽外設(shè)的宏的方法來選擇使能何種外設(shè)。好處就是編譯時(shí)間會(huì)變短,因?yàn)槠帘瘟藳]有用的程序,編譯時(shí)間自然就短了。正點(diǎn)原子的例程選擇另外一中方法,就是工程中只保留需要的stm32h7xx_hal_ppp.c,不需要的不添加到工程里,這樣編譯時(shí)間就不會(huì)太長(zhǎng)。
(4)大家看到stm32h7xx_hal_conf.h文件的159行。
#define TICK_INT_PRIORITY ((uint32_t)0x0F) /*!< tick interrupt priority */
宏定義TICK_INT_PRIORITY是滴答定時(shí)器的優(yōu)先級(jí)。這個(gè)優(yōu)先級(jí)很重要,因?yàn)槿绻渌耐庠O(shè)驅(qū)動(dòng)程序的延時(shí)是通過滴答定時(shí)器提供的時(shí)間基準(zhǔn),來實(shí)現(xiàn)延時(shí)的話,又由于實(shí)現(xiàn)方式是滴答定時(shí)器對(duì)寄存器進(jìn)行計(jì)數(shù),所以當(dāng)我們?cè)谄渌袛喾?wù)程序里調(diào)用基于此時(shí)間基準(zhǔn)的延遲函數(shù) HAL_Delay,那么假如該中斷的優(yōu)先級(jí)高于滴答定時(shí)器的優(yōu)先級(jí),就會(huì)導(dǎo)致滴答定時(shí)器中斷服務(wù)函數(shù)一直得不到運(yùn)行,從而程序卡死在這里。所以滴答定時(shí)器的中斷優(yōu)先級(jí)一定要比這些中斷高。
請(qǐng)注意這個(gè)時(shí)間基準(zhǔn)可以是滴答定時(shí)器提供,也可以是其他的定時(shí)器,默認(rèn)是用滴答定時(shí)器。
(5)下面說一下關(guān)于斷言這個(gè)功能,這個(gè)功能用來判斷函數(shù)的形參是否有效,在HAL庫(kù)的API里面有用到。這個(gè)功能的使能開關(guān)代碼是一個(gè)宏,在源碼的180行,默認(rèn)是關(guān)閉的,代碼如下。

/* #define USE_FULL_ASSERT 1 */ 通過宏USE_FULL_ASSERT來選擇功能,在源碼413行到432,代碼如下。 /* Exported macro -----------------------------------------------------------*/ #ifdef USE_FULL_ASSERT /*** @brief The assert_param macro is used for function's parameters check.* @param expr: If expr is false, it calls assert_failed function* which reports the name of the source file and the source* line number of the call that failed. * If expr is true, it returns no value.* @retval None*/#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__)) /* Exported functions ------------------------------------------------------ */void assert_failed(uint8_t* file, uint32_t line); #else#define assert_param(expr) ((void)0U) #endif /* USE_FULL_ASSERT */#ifdef __cplusplus } #endif#endif /* __STM32H7xx_HAL_CONF_H */

也是通過條件編譯符來選擇對(duì)應(yīng)的功能。當(dāng)用戶自己需要使用斷言功能,怎么做呢?首先需要定義宏USE_FULL_ASSERT來使能斷言功能,即把源碼的180行的注釋去掉即可。然后看到源碼423行的assert_failed()這個(gè)函數(shù)。其實(shí)這個(gè)函數(shù)是需要我們自己實(shí)現(xiàn)的,我們把這個(gè)函數(shù)定義在正點(diǎn)原子提供的sys.c文件里面。后面再跟大家講sys.c文件,現(xiàn)在我們把a(bǔ)ssert_failed()這個(gè)函數(shù)拿出來給大家先講,assert_failed()函數(shù)的定義在sys.c的176行到190行,具體如下:

#ifdef USE_FULL_ASSERT/*** @brief 當(dāng)編譯提示出錯(cuò)的時(shí)候此函數(shù)用來報(bào)告錯(cuò)誤的文件和所在行* @param file:指向源文件* line:指向在文件中的行數(shù)* @retval 無*/ void assert_failed(uint8_t* file, uint32_t line) { while (1){} } #endif

可以看到這個(gè)函數(shù)里面沒有實(shí)現(xiàn)如何功能,就是一個(gè)什么不做的死循環(huán),具體功能請(qǐng)根據(jù)自己的需求去實(shí)現(xiàn)。file是指向源文件的指針,line是指向源文件的行數(shù)。__FILE__是表示源文件名,__LINE__是表示在源文件中的行數(shù)。比如我們可以實(shí)現(xiàn)打印出這個(gè)錯(cuò)誤的兩個(gè)信息等等。
總的來說斷言功能就是,在HAL庫(kù)中,如果定義了USE_FULL_ASSERT這個(gè)宏,那么所有的HAL庫(kù)函數(shù)將會(huì)檢查函數(shù)的形參是否正確。如果錯(cuò)誤將會(huì)調(diào)用assert_failed()這個(gè)函數(shù),這個(gè)函數(shù)我們默認(rèn)是個(gè)什么事不做的死循環(huán),用戶請(qǐng)根據(jù)自己的需求設(shè)計(jì)功能。使用斷言功能將會(huì)增加了代碼量,減慢運(yùn)行速度等,所以一般只是在調(diào)試的時(shí)候用,正式發(fā)布的軟件是不推薦的。
7.4.3 stm32h7xx_hal.c文件
這個(gè)文件內(nèi)容比較多,包括HAL庫(kù)的初始化、系統(tǒng)滴答、基準(zhǔn)電壓配置、IO補(bǔ)償、低功耗、EXTI配置等都集合在這個(gè)文件里面。下面我們對(duì)該文件進(jìn)行講解。

  • HAL_Init()函數(shù)
    源碼在134行到172行,簡(jiǎn)化函數(shù)如下:
  • HAL_StatusTypeDef HAL_Init(void) {/* 設(shè)置中斷優(yōu)先級(jí)分組 */HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);/* 使用滴答定時(shí)器作為時(shí)鐘基準(zhǔn),配置1ms滴答(重置后默認(rèn)的時(shí)鐘源為HSI) */if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK){return HAL_ERROR;}/* 初始化硬件 */HAL_MspInit();/* 返回函數(shù)狀態(tài) */return HAL_OK; }

    該函數(shù)是HAL庫(kù)的初始化函數(shù),在程序中必須優(yōu)先調(diào)用,其主要實(shí)現(xiàn)如下功能:
    1)設(shè)置NVIC優(yōu)先級(jí)分組為4。
    2)配置滴答定時(shí)器每1ms產(chǎn)生一個(gè)中斷。
    3)在這個(gè)階段,系統(tǒng)時(shí)鐘還沒有配置好,因此系統(tǒng)還是默認(rèn)使用內(nèi)部高速時(shí)鐘源HSI在跑程序。對(duì)于H7來說,HSI的主頻是64MHZ。所以如果用戶不配置系統(tǒng)時(shí)鐘的話,那么系統(tǒng)將會(huì)使用HIS作為系統(tǒng)時(shí)鐘源。
    4)調(diào)用HAL_MspInit函數(shù)初始化底層硬件,HAL_MspInit函數(shù)在stm32h7xx_hal.c文件里面做了弱定義。關(guān)于弱定義這個(gè)概念,后面會(huì)有講解,現(xiàn)在不理解沒關(guān)系。正點(diǎn)原子的HAL庫(kù)例程是沒有使用到這個(gè)函數(shù)去初始化底層硬件,而是單獨(dú)調(diào)用需要用到的硬件初始化函數(shù)。用戶可以根據(jù)自己的需求選擇是否重新定義該函數(shù)來初始化自己的硬件。
    注意事項(xiàng):
    為了方便和兼容性,正點(diǎn)原子的HAL庫(kù)例程中的中斷優(yōu)先級(jí)分組設(shè)置為分組2,即把源碼的145行改為如下代碼:
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
    中斷優(yōu)先級(jí)分組為2,也就是2位搶占優(yōu)先級(jí),2位響應(yīng)優(yōu)先級(jí),搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)的值的范圍均為0-3。

    2. HAL_DeInit ()函數(shù)

    源碼在179行到214行,函數(shù)如下:

    HAL_StatusTypeDef HAL_DeInit(void) {/* 復(fù)位所有外設(shè) */__HAL_RCC_AHB3_FORCE_RESET();__HAL_RCC_AHB3_RELEASE_RESET();__HAL_RCC_AHB1_FORCE_RESET();__HAL_RCC_AHB1_RELEASE_RESET();__HAL_RCC_AHB2_FORCE_RESET();__HAL_RCC_AHB2_RELEASE_RESET();__HAL_RCC_AHB4_FORCE_RESET();__HAL_RCC_AHB4_RELEASE_RESET();__HAL_RCC_APB3_FORCE_RESET();__HAL_RCC_APB3_RELEASE_RESET();__HAL_RCC_APB1L_FORCE_RESET();__HAL_RCC_APB1L_RELEASE_RESET();__HAL_RCC_APB1H_FORCE_RESET();__HAL_RCC_APB1H_RELEASE_RESET();__HAL_RCC_APB2_FORCE_RESET();__HAL_RCC_APB2_RELEASE_RESET();__HAL_RCC_APB4_FORCE_RESET();__HAL_RCC_APB4_RELEASE_RESET();/* 對(duì)底層硬件初始化進(jìn)行復(fù)位 */HAL_MspDeInit();/* 返回函數(shù)狀態(tài) */return HAL_OK; }

    該函數(shù)取消初始化HAL庫(kù)的公共部分,并且停止systick,是一個(gè)可選的函數(shù)。該函數(shù)做了一下的事:
    1)復(fù)位了AHB1、AHB2、AHB3、AHB4、APB1L、APB1H、APB2、APB3、APB4的時(shí)鐘。
    2)調(diào)用HAL_MspDeInit函數(shù),對(duì)底層硬件初始化進(jìn)行復(fù)位。HAL_MspDeInit也在stm32h7xx _hal.c文件里面做了弱定義,并且與HAL_MspInit函數(shù)是一對(duì)存在。HAL_MspInit函數(shù)負(fù)責(zé)對(duì)底層硬件初始化,HAL_MspDeInit函數(shù)則是對(duì)底層硬件初始化進(jìn)行復(fù)位。這兩個(gè)函數(shù)都是需要用戶根據(jù)自己的需求去實(shí)現(xiàn)功能,也可以不使用。

    3. HAL_InitTick ()函數(shù)

    源碼在254行到302行,簡(jiǎn)化函數(shù)如下:

    __weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) {/* uwTickFreq是個(gè)枚舉類型,如果檢測(cè)到uwTickFreq為零,則返回 */if((uint32_t)uwTickFreq == 0UL){return HAL_ERROR;}/* 配置滴答定時(shí)器1ms產(chǎn)生一次中斷 */if (HAL_SYSTICK_Config(SystemCoreClock /(1000UL / (uint32_t)uwTickFreq))> 0U){return HAL_ERROR;} #endif/* 配置滴答定時(shí)器中斷優(yōu)先級(jí) */if (TickPriority < (1UL << __NVIC_PRIO_BITS)){HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);uwTickPrio = TickPriority;}else{return HAL_ERROR;}/* 返回函數(shù)狀態(tài) */return HAL_OK; }

    該函數(shù)用于初始化滴答定時(shí)器的時(shí)鐘基準(zhǔn),主要功能如下:
    1)配置滴答定時(shí)器1ms產(chǎn)生一次中斷。
    2)配置滴答定時(shí)器的中斷優(yōu)先級(jí)。
    3)該函數(shù)是__weak定義的“弱函數(shù)”,用戶可以重新定義該函數(shù)。
    該函數(shù)可以通過HAL_Init()或者HAL_RCC_ClockConfig()重置時(shí)鐘。在默認(rèn)情況下,滴答定時(shí)器是時(shí)間基準(zhǔn)的來源。如果其他中斷服務(wù)函數(shù)調(diào)用了HAL_Delay(),必須小心,滴答定時(shí)器中斷必須具有比調(diào)用了HAL_Delay()函數(shù)的其他中斷服務(wù)函數(shù)的優(yōu)先級(jí)高(數(shù)值較低),否則會(huì)導(dǎo)致滴答定時(shí)器中斷服務(wù)函數(shù)一直得不到執(zhí)行,從而卡死在這里。

    4. 滴答定時(shí)器相關(guān)的函數(shù)

    源碼在331行到463行,相關(guān)函數(shù)如下:
    /* 該函數(shù)在滴答定時(shí)器時(shí)鐘中斷服務(wù)函數(shù)中被調(diào)用,一般滴答定時(shí)器1ms中斷一次,
    所以函數(shù)每1ms讓全局變量uwTick計(jì)數(shù)值加1 */

    __weak void HAL_IncTick(void) {uwTick += (uint32_t)uwTickFreq; }/* 獲取全局變量uwTick當(dāng)前計(jì)算值 */ __weak uint32_t HAL_GetTick(void) {return uwTick; }/* 獲取滴答時(shí)鐘優(yōu)先級(jí) */ uint32_t HAL_GetTickPrio(void) {return uwTickPrio; }/* 設(shè)置滴答定時(shí)器中斷頻率 */ HAL_StatusTypeDef HAL_SetTickFreq(HAL_TickFreqTypeDef Freq) {HAL_StatusTypeDef status = HAL_OK;HAL_TickFreqTypeDef prevTickFreq;assert_param(IS_TICKFREQ(Freq));if (uwTickFreq != Freq){/* 備份滴答定時(shí)器中斷頻率 */prevTickFreq = uwTickFreq;/* 更新被HAL_InitTick()調(diào)用的全局變量uwTickFreq */uwTickFreq = Freq;/* 應(yīng)用新的滴答定時(shí)器中斷頻率 */status = HAL_InitTick(uwTickPrio);if (status != HAL_OK){/* 恢復(fù)之前的滴答定時(shí)器中斷頻率 */uwTickFreq = prevTickFreq;}}return status; }/* 獲取滴答定時(shí)器中斷頻率 */ HAL_TickFreqTypeDef HAL_GetTickFreq(void) {return uwTickFreq; }/*HAL庫(kù)的延時(shí)函數(shù),默認(rèn)延時(shí)單位ms */ __weak void HAL_Delay(uint32_t Delay) {uint32_t tickstart = HAL_GetTick();uint32_t wait = Delay;/* Add a freq to guarantee minimum wait */if (wait < HAL_MAX_DELAY){wait += (uint32_t)(uwTickFreq);}while ((HAL_GetTick() - tickstart) < wait){} }/* 掛起滴答定時(shí)器中斷,全局變量uwTick計(jì)數(shù)停止 */ __weak void HAL_SuspendTick(void) {/* 禁止滴答定時(shí)器中斷 */SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; }/* 恢復(fù)滴答定時(shí)器中斷,恢復(fù)全局變量uwTick計(jì)數(shù) */ __weak void HAL_ResumeTick(void) {/* 使能滴答定時(shí)器中斷 */SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; }

    這些函數(shù)不是很難,請(qǐng)參照注釋理解。注意:如果函數(shù)被前綴__weak定義,則用戶可以重新定義該函數(shù)。更多的內(nèi)容可以參考8.1.5小節(jié)。

    5. HAL庫(kù)版本相關(guān)的函數(shù)

    源碼在465行到517行,相關(guān)函數(shù)聲明如下:

    uint32_t HAL_GetHalVersion(void); /* 獲取HAL庫(kù)驅(qū)動(dòng)程序版本 */ uint32_t HAL_GetREVID(void); /* 獲取設(shè)備修訂標(biāo)識(shí)符 */ uint32_t HAL_GetDEVID(void); /* 獲取設(shè)備標(biāo)識(shí)符 */ uint32_t HAL_GetUIDw0(void); /* 獲取唯一設(shè)備標(biāo)識(shí)符的第一個(gè)字 */ uint32_t HAL_GetUIDw1(void); /* 獲取唯一設(shè)備標(biāo)識(shí)符的第二個(gè)字 */ uint32_t HAL_GetUIDw2(void); /* 獲取唯一設(shè)備標(biāo)識(shí)符的第三個(gè)字 */

    這些函數(shù)了解一下就好了,用得不多。
    6. 芯片內(nèi)部電壓基準(zhǔn)相關(guān)函數(shù)
    源碼在519行到602行,函數(shù)聲明如下:

    void HAL_SYSCFG_VREFBUF_VoltageScalingConfig(uint32_t VoltageScaling); void HAL_SYSCFG_VREFBUF_HighImpedanceConfig(uint32_t Mode); void HAL_SYSCFG_VREFBUF_TrimmingConfig(uint32_t TrimmingValue); HAL_StatusTypeDef HAL_SYSCFG_EnableVREFBUF(void); void HAL_SYSCFG_DisableVREFBUF(void);

    HAL_SYSCFG_VREFBUF_VoltageScalingConfig函數(shù)用于配置芯片內(nèi)部電壓基準(zhǔn)大小,形參有四個(gè)值可以選擇:
    1)當(dāng)形參為SYSCFG_VREFBUF_VOLTAGE_SCALE0時(shí),
    電壓輸出基準(zhǔn)為2.048V,條件是VDDA >= 2.4V。
    2)當(dāng)形參為SYSCFG_VREFBUF_VOLTAGE_SCALE1時(shí),
    電壓輸出基準(zhǔn)為2.5V,條件是VDDA >= 2.8V。
    3)當(dāng)形參為SYSCFG_VREFBUF_VOLTAGE_SCALE2時(shí),
    電壓輸出基準(zhǔn)為1.5V,條件是VDDA >= 1.8V。
    4)當(dāng)形參為SYSCFG_VREFBUF_VOLTAGE_SCALE3時(shí),
    電壓輸出基準(zhǔn)為1.8V,條件是VDDA >= 2.1V。
    HAL_SYSCFG_VREFBUF_HighImpedanceConfig函數(shù)用于配置芯片內(nèi)部電壓是否與VREF+引腳連接,即是否選擇高阻抗模式,有兩個(gè)形參選擇:
    1)當(dāng)形參為SYSCFG_VREFBUF_HIGH_IMPEDANCE_DISABLE,表示導(dǎo)通。
    2)當(dāng)形參為SYSCFG_VREFBUF_HIGH_IMPEDANCE_ENABLE,表示高阻抗,即不導(dǎo)通。
    HAL_SYSCFG_VREFBUF_TrimmingConfig函數(shù)用于調(diào)整校準(zhǔn)內(nèi)部電壓基準(zhǔn)。
    HAL_SYSCFG_EnableVREFBUF函數(shù)用于使能內(nèi)部電壓基準(zhǔn)參考。
    HAL_SYSCFG_DisableVREFBUF函數(shù)用于禁止內(nèi)部電壓基準(zhǔn)參考。
    7. 以太網(wǎng)PHY接口選擇函數(shù)
    源碼在605行到619行,函數(shù)聲明如下:
    void HAL_SYSCFG_ETHInterfaceSelect(uint32_t SYSCFG_ETHInterface);
    該函數(shù)用于以太網(wǎng)PHY接口的選擇,可以是MII或RMII接口。
    8. HAL_SYSCFG_AnalogSwitchConfig()函數(shù)
    源碼在622行到650行,函數(shù)聲明如下:
    void HAL_SYSCFG_AnalogSwitchConfig(uint32_t SYSCFG_AnalogSwitch ,
    uint32_t SYSCFG_SwitchState );
    當(dāng)PA0、PA1、PC2、PC3引腳復(fù)用為ADC的時(shí)候,還有一組對(duì)應(yīng)的可選引腳PA0_C、PA1_C、PC2_C、PC3_C。該函數(shù)的作用就是切換這些可選的引腳。關(guān)于這個(gè)不理解,請(qǐng)參考圖7.4.3.1。該函數(shù)操作了SYSCFG_PMCR寄存器,關(guān)于該寄存器請(qǐng)查閱參考手冊(cè)。

    圖7.4.3.1 連接到ADC輸入的模擬輸入
    9. Booster的使能和禁止函數(shù)(用于ADC)
    源碼在653行到676行,函數(shù)聲明如下:
    void HAL_SYSCFG_EnableBOOST(void); /* 使能Booster /
    void HAL_SYSCFG_DisableBOOST(void); / 禁止Booster */
    如果使能Booster,當(dāng)供電電壓低于2.7V時(shí),能夠減少模擬開關(guān)總的諧波失真。這樣就使得模擬開關(guān)的性能和供電正常的情況時(shí)一樣,能夠正常工作。
    10. HAL_SYSCFG_CM7BootAddConfig()函數(shù)
    源碼在680行到712行,函數(shù)如下:
    void HAL_SYSCFG_CM7BootAddConfig(uint32_t BootRegister, uint32_t BootAddress)
    該函數(shù)用于配置程序啟動(dòng)模式,BOOT=0或者BOOT=1,來選擇啟動(dòng)地址。詳細(xì)的內(nèi)容請(qǐng)看第九章的9.1小節(jié)。
    11. IO補(bǔ)償、低功耗、EXTI等相關(guān)函數(shù)
    IO補(bǔ)償、低功耗、EXTI等相關(guān)函數(shù),這里先不進(jìn)行講解了,后面用到再進(jìn)行說明。
    7.4.4 HAL庫(kù)中斷處理
    中斷是STM32開發(fā)的一個(gè)很重要的概念,這里我們可以簡(jiǎn)單地理解為:STM32暫停了當(dāng)前手中的事并優(yōu)先去處理更重要的事務(wù)。而這些“更重要的事務(wù)”是由軟件開發(fā)人員在軟件中定義的。關(guān)于STM32中斷的概念,我們會(huì)在中斷例程的講解再跟大家詳細(xì)介紹。
    由于HAL庫(kù)中斷處理的邏輯比較統(tǒng)一,我們將這個(gè)處理過程抽象為圖7.4.4.1所表示的業(yè)務(wù)邏輯:

    圖7.4.4.1 HAL驅(qū)動(dòng)中斷處理流程
    結(jié)合以前的HAL庫(kù)文件介紹章節(jié),以上的流程大概就是:設(shè)置外設(shè)的控制句柄結(jié)構(gòu)體PPP_HandleType和初始化PPP_InitType結(jié)構(gòu)體的參數(shù),然后調(diào)用HAL庫(kù)對(duì)應(yīng)這個(gè)驅(qū)動(dòng)的初始化HAL_PPP_Init(),由于這個(gè)API中有針對(duì)外設(shè)初始化細(xì)節(jié)的接口Hal_PPP_Mspinit(),我們需要重新實(shí)現(xiàn)這個(gè)函數(shù)并完成外設(shè)時(shí)鐘、IO等細(xì)節(jié)差異的設(shè)置, 完成各細(xì)節(jié)處理后,使用HAL_NVIC_SetPriority()、HAL_NVIC_EnableIRQ()來使能我們的外設(shè)中斷;定義中斷處理函數(shù)PPP_IRQHandler,并在中斷函數(shù)中調(diào)用HAL_ppp_function_IRQHandler()來判斷和處理中斷標(biāo)記; HAL庫(kù)中斷處理完成后,根據(jù)對(duì)應(yīng)中的調(diào)用我們需要自定義的中斷回調(diào)接口HAL_ PPP_ProcessCpltCallback();如串口接收函數(shù)HAL_UART_RxCpltCallback(),我們?cè)谶@個(gè)函數(shù)中實(shí)現(xiàn)我們對(duì)串口接收數(shù)據(jù)想做的處理;中斷響應(yīng)處理完成后,stm32芯片繼續(xù)順序執(zhí)行我們定義的主程序功能,按照以上處理的標(biāo)準(zhǔn)流程完成了一次中斷響應(yīng)。
    7.4.5 正點(diǎn)原子對(duì)HAL庫(kù)用法的個(gè)性化修改
    前面按ST官方建議的HAL庫(kù)的使用方法給介紹了一個(gè)HAL庫(kù)。
    1、將中斷處理函數(shù)獨(dú)立到每個(gè)外設(shè)中,便于獨(dú)立驅(qū)動(dòng);同類型的外設(shè)驅(qū)動(dòng)處理函數(shù)不使用HAL回調(diào)函數(shù)接口處理操作而直接在中斷函數(shù)中處理判斷對(duì)應(yīng)中斷。
    2、我們把原來的中斷分組進(jìn)行了修改,由搶占式無子優(yōu)先級(jí)改為中斷分組2;便于管理同類外設(shè)的優(yōu)先級(jí)響應(yīng)。
    3、在很多芯片的初始化過程中,我們使用到了delay_ms()、delay_us()等函數(shù)進(jìn)行初始化,使用的是Systick作的精準(zhǔn)延時(shí),而HAL庫(kù)默認(rèn)也使用Systick作延時(shí)處理,為解決這種沖突和兼容我們大部分的驅(qū)動(dòng)代碼,我們?cè)诶讨惺褂胐elay.c中的延時(shí)函數(shù)取代Hal_Delay();取消原來HAL庫(kù)的Systick延時(shí)設(shè)置。

    7.5 HAL庫(kù)使用注意事項(xiàng)

    本小節(jié)根據(jù)經(jīng)驗(yàn)跟大家講述一些關(guān)于HAL庫(kù)使用的注意事項(xiàng),供讀者參考。
    1、即使我們已經(jīng)在使用庫(kù)函數(shù)作為開發(fā)工具了,我們可以忽略很多芯片的硬件外設(shè)使用上的細(xì)節(jié),但當(dāng)發(fā)生問題時(shí),我們?nèi)孕枰貧w到芯片使用手冊(cè)查看當(dāng)前操作是否違規(guī)或缺漏。
    2、使用HAL庫(kù)和其它第三方的庫(kù)開發(fā)類似,把我們需要編寫的軟件和第三方的庫(kù)分開成相互獨(dú)立的文件,開發(fā)過程中我們盡量不去修改第三方的軟件源碼,需要修改的部分盡量在自己的代碼中實(shí)現(xiàn);這樣一旦我們需要更新第三方庫(kù)時(shí),我們?cè)瓉砭帉懙墓δ芤材芎芸斓仄ヅ湫碌膸?kù)去執(zhí)行功能。
    3、即使HAL庫(kù)目前較以前已經(jīng)相對(duì)更完善了,但它仍無法覆蓋我們要想實(shí)現(xiàn)的所有細(xì)節(jié)功能,甚至可能存在錯(cuò)誤,我們要有懷疑精神,辯證地去使用好這個(gè)工具;如我們?cè)赑WM一節(jié)編碼時(shí)發(fā)現(xiàn)HAL庫(kù)中有個(gè)宏定義TIM_RESET_CAPTUREPOLARITY括號(hào)不匹配導(dǎo)致編譯報(bào)錯(cuò),這時(shí)我們不得不修改一下HAL庫(kù)的源碼了。
    4、注意HAL庫(kù)的執(zhí)行效率。由于HAL庫(kù)的驅(qū)動(dòng)對(duì)相同外設(shè)大多是可重入的,在執(zhí)行HAL驅(qū)動(dòng)的API函數(shù)的效率沒有直接寄存器操作來得高,如果在對(duì)時(shí)序要求比較嚴(yán)苛的代碼,建議使用簡(jiǎn)潔的寄存器操作代替。
    5、我們?cè)诶讨惺褂胐elay.c中的延時(shí)函數(shù)取代Hal_Delay();取消原來HAL庫(kù)的Systick延時(shí)設(shè)置;但這會(huì)有一個(gè)問題:原來HAL庫(kù)的超時(shí)處理機(jī)制不再適用,所以對(duì)于設(shè)置了超時(shí)的函數(shù),可能會(huì)導(dǎo)致停留在這個(gè)函數(shù)的處理中,無法按正常的超時(shí)退出。

    總結(jié)

    以上是生活随笔為你收集整理的【正点原子STM32连载】第七章 认识HAL库 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。