學(xué)習(xí)本章時,配合《STM32F4xx中文參考手冊》"通用I/O(GPIO)"章節(jié)一起閱讀,效果會更佳,特別是涉及到寄存器說明的部分。關(guān)于建立工程時使用KEIL5的基本操作,請參考前面的章節(jié)。
7.1 GPIO簡介
GPIO是通用輸入輸出端口的簡稱,簡單來說就是STM32可控制的引腳,STM32芯片的GPIO引腳與外部設(shè)備連接起來,從而實現(xiàn)與外部通訊、控制以及數(shù)據(jù)采集的功能。STM32芯片的GPIO被分成很多組,每組有16個引腳,如型號為STM32F4IGT6型號的芯片有GPIOA、GPIOB、GPIOC至GPIOI共9組GPIO,芯片一共176個引腳,其中GPIO就占了一大部分,所有的GPIO引腳都有基本的輸入輸出功能。
最基本的輸出功能是由STM32控制引腳輸出高、低電平,實現(xiàn)開關(guān)控制,如把GPIO引腳接入到LED燈,那就可以控制LED燈的亮滅,引腳接入到繼電器或三極管,那就可以通過繼電器或三極管控制外部大功率電路的通斷。
最基本的輸入功能是檢測外部輸入電平,如把GPIO引腳連接到按鍵,通過電平高低區(qū)分按鍵是否被按下。
7.2 GPIO框圖剖析
圖71 GPIO結(jié)構(gòu)框圖
通過GPIO硬件結(jié)構(gòu)框圖,就可以從整體上深入了解GPIO外設(shè)及它的各種應(yīng)用模式。該圖從最右端看起,最右端就是代表STM32芯片引出的GPIO引腳,其余部件都位于芯片內(nèi)部。
7.2.1基本結(jié)構(gòu)分析
下面我們按圖中的編號對GPIO端口的結(jié)構(gòu)部件進行說明。
引腳的兩保護個二級管可以防止引腳外部過高或過低的電壓輸入,當(dāng)引腳電壓高于VDD_FT時,上方的二極管導(dǎo)通,當(dāng)引腳電壓低于VSS時,下方的二極管導(dǎo)通,防止不正常電壓引入芯片導(dǎo)致芯片燒毀。盡管有這樣的保護,并不意味著STM32的引腳能直接外接大功率驅(qū)動器件,如直接驅(qū)動電機,強制驅(qū)動要么電機不轉(zhuǎn),要么導(dǎo)致芯片燒壞,必須要加大功率及隔離電路驅(qū)動。具體電壓、電流范圍可查閱《STM32F4xx規(guī)格書》。
上拉、下拉電阻,從它的結(jié)構(gòu)我們可以看出,通過上、下拉對應(yīng)的開關(guān)配置,我們可以控制引腳默認(rèn)狀態(tài)的電壓,開啟上拉的時候引腳電壓為高電平,開啟下拉的時候引腳電壓為低電平,這樣可以消除引腳不定狀態(tài)的影響。如引腳外部沒有外接器件,或者外部的器件不干擾該引腳電壓時,STM32的引腳都會有這個默認(rèn)狀態(tài)。
也可以設(shè)置"既不上拉也不下拉模式",我們也把這種狀態(tài)稱為浮空模式,配置成這個模式時,直接用電壓表測量其引腳電壓為1點幾伏,這是個不確定值。所以一般來說我們都會選擇給引腳設(shè)置"上拉模式"或"下拉模式"使它有默認(rèn)狀態(tài)。
STM32的內(nèi)部上拉是"弱上拉",即通過此上拉輸出的電流是很弱的,如要求大電流還是需要外部上拉。
通過"上拉/下拉寄存器GPIOx_PUPDR"控制引腳的上、下拉以及浮空模式。
2.P-MOS管和N-MOS管
GPIO引腳線路經(jīng)過上、下拉電阻結(jié)構(gòu)后,向上流向"輸入模式"結(jié)構(gòu),向下流向"輸出模式"結(jié)構(gòu)。先看輸出模式部分,線路經(jīng)過一個由P-MOS和N-MOS管組成的單元電路。這個結(jié)構(gòu)使GPIO具有了"推挽輸出"和"開漏輸出"兩種模式。
所謂的推挽輸出模式,是根據(jù)這兩個MOS管的工作方式來命名的。在該結(jié)構(gòu)中輸入高電平時,上方的P-MOS導(dǎo)通,下方的N-MOS關(guān)閉,對外輸出高電平;而在該結(jié)構(gòu)中輸入低電平時,N-MOS管導(dǎo)通,P-MOS關(guān)閉,對外輸出低電平。當(dāng)引腳高低電平切換時,兩個管子輪流導(dǎo)通,一個負(fù)責(zé)灌電流,一個負(fù)責(zé)拉電流,使其負(fù)載能力和開關(guān)速度都比普通的方式有很大的提高。推挽輸出的低電平為0伏,高電平為3.3伏,參考圖72左側(cè),它是推挽輸出模式時的等效電路。
圖72等效電路
而在開漏輸出模式時,上方的P-MOS管完全不工作。如果我們控制輸出為0,低電平,則P-MOS管關(guān)閉,N-MOS管導(dǎo)通,使輸出接地,若控制輸出為1 (它無法直接輸出高電平)時,則P-MOS管和N-MOS管都關(guān)閉,所以引腳既不輸出高電平,也不輸出低電平,為高阻態(tài)。為正常使用時必須接上拉電阻(可用STM32的內(nèi)部上拉,但建議在STM32外部再接一個上拉電阻),參考圖72中的右側(cè)等效電路。它具"線與"特性,也就是說,若有很多個開漏模式引腳連接到一起時,只有當(dāng)所有引腳都輸出高阻態(tài),才由上拉電阻提供高電平,此高電平的電壓為外部上拉電阻所接的電源的電壓。若其中一個引腳為低電平,那線路就相當(dāng)于短路接地,使得整條線路都為低電平,0伏。
推挽輸出模式一般應(yīng)用在輸出電平為0和3.3伏而且需要高速切換開關(guān)狀態(tài)的場合。在STM32的應(yīng)用中,除了必須用開漏模式的場合,我們都習(xí)慣使用推挽輸出模式。
開漏輸出一般應(yīng)用在I2C、SMBUS通訊等需要"線與"功能的總線電路中。除此之外,還用在電平不匹配的場合,如需要輸出5伏的高電平,就可以在外部接一個上拉電阻,上拉電源為5伏,并且把GPIO設(shè)置為開漏模式,當(dāng)輸出高阻態(tài)時,由上拉電阻和電源向外輸出5伏的電平。
通過"輸出類型寄存器GPIOx_OTYPER"可以控制GPIO端口是推挽模式還是開漏模式。
3.輸出數(shù)據(jù)寄存器
前面提到的雙MOS管結(jié)構(gòu)電路的輸入信號,是由GPIO"輸出數(shù)據(jù)寄存器GPIOx_ODR"提供的,因此我們通過修改輸出數(shù)據(jù)寄存器的值就可以修改GPIO引腳的輸出電平。而"置位/復(fù)位寄存器GPIOx_BSRR"可以通過修改輸出數(shù)據(jù)寄存器的值從而影響電路的輸出。
4.復(fù)用功能輸出
"復(fù)用功能輸出"中的"復(fù)用"是指STM32的其它片上外設(shè)對GPIO引腳進行控制,此時GPIO引腳用作該外設(shè)功能的一部分,算是第二用途。從其它外設(shè)引出來的"復(fù)用功能輸出信號"與GPIO本身的數(shù)據(jù)據(jù)寄存器都連接到雙MOS管結(jié)構(gòu)的輸入中,通過圖中的梯形結(jié)構(gòu)作為開關(guān)切換選擇。
例如我們使用USART串口通訊時,需要用到某個GPIO引腳作為通訊發(fā)送引腳,這個時候就可以把該GPIO引腳配置成USART串口復(fù)用功能,由串口外設(shè)控制該引腳,發(fā)送數(shù)據(jù)。
5.輸入數(shù)據(jù)寄存器
看GPIO結(jié)構(gòu)框圖的上半部分,它是GPIO引腳經(jīng)過上、下拉電阻后引入的,它連接到施密特觸發(fā)器,信號經(jīng)過觸發(fā)器后,模擬信號轉(zhuǎn)化為0、1的數(shù)字信號,然后存儲在"輸入數(shù)據(jù)寄存器GPIOx_IDR"中,通過讀取該寄存器就可以了解GPIO引腳的電平狀態(tài)。
6.復(fù)用功能輸入
與"復(fù)用功能輸出"模式類似,在"復(fù)用功能輸出模式"時,GPIO引腳的信號傳輸?shù)絊TM32其它片上外設(shè),由該外設(shè)讀取引腳狀態(tài)。
同樣,如我們使用USART串口通訊時,需要用到某個GPIO引腳作為通訊接收引腳,這個時候就可以把該GPIO引腳配置成USART串口復(fù)用功能,使USART可以通過該通訊引腳的接收遠(yuǎn)端數(shù)據(jù)。
7.模擬輸入輸出
當(dāng)GPIO引腳用于ADC采集電壓的輸入通道時,用作"模擬輸入"功能,此時信號是不經(jīng)過施密特觸發(fā)器的,因為經(jīng)過施密特觸發(fā)器后信號只有0、1兩種狀態(tài),所以ADC外設(shè)要采集到原始的模擬信號,信號源輸入必須在施密特觸發(fā)器之前。類似地,當(dāng)GPIO引腳用于DAC作為模擬電壓輸出通道時,此時作為"模擬輸出"功能,DAC的模擬信號輸出就不經(jīng)過雙MOS管結(jié)構(gòu)了,在GPIO結(jié)構(gòu)框圖的右下角處,模擬信號直接輸出到引腳。同時,當(dāng)GPIO用于模擬功能時(包括輸入輸出),引腳的上、下拉電阻是不起作用的,這個時候即使在寄存器配置了上拉或下拉模式,也不會影響到模擬信號的輸入輸出。
7.2.2GPIO工作模式
總結(jié)一下,由GPIO的結(jié)構(gòu)決定了GPIO可以配置成以下模式:
1.輸入模式(上拉/下拉/浮空)
在輸入模式時,施密特觸發(fā)器打開,輸出被禁止。數(shù)據(jù)寄存器每隔1個AHB1時鐘周期更新一次,可通過輸入數(shù)據(jù)寄存器GPIOx_IDR讀取I/O狀態(tài)。其中AHB1的時鐘如按默認(rèn)配置一般為180MHz。
用于輸入模式時,可設(shè)置為上拉、下拉或浮空模式。
2.輸出模式(推挽/開漏,上拉/下拉)
在輸出模式中,輸出使能,推挽模式時雙MOS管以方式工作,輸出數(shù)據(jù)寄存器GPIOx_ODR可控制I/O輸出高低電平。開漏模式時,只有N-MOS管工作,輸出數(shù)據(jù)寄存器可控制I/O輸出高阻態(tài)或低電平。輸出速度可配置,有2MHz\25MHz\50MHz\100MHz的選項。此處的輸出速度即I/O支持的高低電平狀態(tài)最高切換頻率,支持的頻率越高,功耗越大,如果功耗要求不嚴(yán)格,把速度設(shè)置成最大即可。
此時施密特觸發(fā)器是打開的,即輸入可用,通過輸入數(shù)據(jù)寄存器GPIOx_IDR可讀取I/O的實際狀態(tài)。
用于輸出模式時,可使用上拉、下拉模式或浮空模式。但此時由于輸出模式時引腳電平會受到ODR寄存器影響,而ODR寄存器對應(yīng)引腳的位為0,即引腳初始化后默認(rèn)輸出低電平,所以在這種情況下,上拉只起到小幅提高輸出電流能力,但不會影響引腳的默認(rèn)狀態(tài)。
3.復(fù)用功能(推挽/開漏,上拉/下拉)
復(fù)用功能模式中,輸出使能,輸出速度可配置,可工作在開漏及推挽模式,但是輸出信號源于其它外設(shè),輸出數(shù)據(jù)寄存器GPIOx_ODR無效;輸入可用,通過輸入數(shù)據(jù)寄存器可獲取I/O實際狀態(tài),但一般直接用外設(shè)的寄存器來獲取該數(shù)據(jù)信號。
用于復(fù)用功能時,可使用上拉、下拉模式或浮空模式。同輸出模式,在這種情況下,初始化后引腳默認(rèn)輸出低電平,上拉只起到小幅提高輸出電流能力,但不會影響引腳的默認(rèn)狀態(tài)。
4.模擬輸入輸出
模擬輸入輸出模式中,雙MOS管結(jié)構(gòu)被關(guān)閉,施密特觸發(fā)器停用,上/下拉也被禁止。其它外設(shè)通過模擬通道進行輸入輸出。
通過對GPIO寄存器寫入不同的參數(shù),就可以改變GPIO的應(yīng)用模式,再強調(diào)一下,要了解具體寄存器時一定要查閱《STM32F4xx參考手冊》中對應(yīng)外設(shè)的寄存器說明。在GPIO外設(shè)中,通過設(shè)置"模式寄存器GPIOx_MODER"可配置GPIO的輸入/輸出/復(fù)用/模擬模式,"輸出類型寄存器GPIOx_OTYPER"配置推挽/開漏模式,配置"輸出速度寄存器GPIOx_OSPEEDR"可選2/25/50/100MHz輸出速度,"上/下拉寄存器GPIOx_PUPDR"可配置上拉/下拉/浮空模式,各寄存器的具體參數(shù)值見表71。
表71 GPIO寄存器的參數(shù)配置
7.3 實驗:使用寄存器點亮LED燈
本小節(jié)中,我們以實例講解如何通過控制寄存器來點亮LED燈。此處側(cè)重于講解原理,請您直接用KEIL5軟件打開我們提供的實驗例程配合閱讀,先了解原理,學(xué)習(xí)完本小節(jié)后,再嘗試自己建立一個同樣的工程。本節(jié)配套例程名稱為"GPIO輸出—寄存器點亮LED燈",在工程目錄下找到后綴為".uvprojx"的文件,用KEIL5打開即可。
自己嘗試新建工程時,請對照查閱《用KEIL5新建工程模版寄存器版本》章節(jié)。
若沒有安裝KEIL5軟件,請參考《如何安裝KEIL5》章節(jié)。
打開該工程,見圖73,可看到一共有三個文件,分別startup_stm32f429_439xx.s、stm32f4xx.h以及main.c,下面我們對這三個工程進行講解。
圖73工程文件結(jié)構(gòu)
7.3.1硬件連接
在本教程中STM32芯片與LED燈的連接見圖74。
圖74 LED燈電路連接圖
圖中從3個LED燈的陽極引出連接到3.3V電源,陰極各經(jīng)過1個電阻引入至STM32的3個GPIO引腳PH10、PH11、PH12中,所以我們只要控制這三個引腳輸出高低電平,即可控制其所連接LED燈的亮滅。如果您的實驗板STM32連接到LED燈的引腳或極性不一樣,只需要修改程序到對應(yīng)的GPIO引腳即可,工作原理都是一樣的。
我們的目標(biāo)是把GPIO的引腳設(shè)置成推挽輸出模式并且默認(rèn)下拉,輸出低電平,這樣就能讓LED燈亮起來了。
7.3.2啟動文件
名為"startup_stm32f429_439xx.s"的文件,它里邊使用匯編語言寫好了基本程序,當(dāng)STM32芯片上電啟動的時候,首先會執(zhí)行這里的匯編程序,從而建立起C語言的運行環(huán)境,所以我們把這個文件稱為啟動文件。該文件使用的匯編指令是Cortex-M4內(nèi)核支持的指令,可從《Cortex-M4 Technical Reference Manual》查到,也可參考《Cortex-M3權(quán)威指南中文》,M3跟M4大部分匯編指令相同。
startup_stm32f429_439xx.s文件是由官方提供的,一般有需要也是在官方的基礎(chǔ)上修改,不會自己完全重寫。該文件可以從KEIL5安裝目錄找到,也可以從ST庫里面找到,找到該文件后把啟動文件添加到工程里面即可。不同型號的芯片以及不同編譯環(huán)境下使用的匯編文件是不一樣的,但功能相同。
對于啟動文件這部分我們主要總結(jié)它的功能,不詳解講解里面的代碼,其功能如下:
- 初始化堆棧指針SP;
- 初始化程序計數(shù)器指針PC;
- 設(shè)置堆、棧的大小;
- 設(shè)置中斷向量表的入口地址;
- 配置外部SRAM作為數(shù)據(jù)存儲器(這個由用戶配置,一般的開發(fā)板可沒有外部SRAM);
- 調(diào)用SystemIni()函數(shù)配置STM32的系統(tǒng)時鐘。
- 設(shè)置C庫的分支入口"__main"(最終用來調(diào)用main函數(shù));
先去除繁枝細(xì)節(jié),挑重點的講,主要理解最后兩點,在啟動文件中有一段復(fù)位后立即執(zhí)行的程序,代碼見代碼清單71。在實際工程中閱讀時,可使用編輯器的搜索(Ctrl+F)功能查找這段代碼在文件中的位置。
代碼清單71復(fù)位后執(zhí)行的程序
1;Reset handler
2Reset_HandlerPROC
3EXPORT Reset_Handler [WEAK]
4 IMPORTSystemInit
5 IMPORT__main
6
7 LDR R0, =SystemInit
8 BLX R0
9 LDR R0, =__main
10 BXR0
11 ENDP
開頭的是程序注釋,在匯編里面注釋用的是";",相當(dāng)于C語言的"http://"注釋符
第二行是定義了一個子程序:Reset_Handler。PROC是子程序定義偽指令。這里就相當(dāng)于C語言里定義了一個函數(shù),函數(shù)名為Reset_Handler。
第三行EXPORT表示Reset_Handler這個子程序可供其他模塊調(diào)用。相當(dāng)于C語言的函數(shù)聲明。關(guān)鍵字[WEAK]表示弱定義,如果編譯器發(fā)現(xiàn)在別處定義了同名的函數(shù),則在鏈接時用別處的地址進行鏈接,如果其它地方?jīng)]有定義,編譯器也不報錯,以此處地址進行鏈接,如果不理解WEAK,那就忽略它好了。
第四行和第五行IMPORT說明SystemInit和__main這兩個標(biāo)號在其他文件,在鏈接的時候需要到其他文件去尋找。相當(dāng)于C語言中,從其它文件引入函數(shù)聲明。以便下面對外部函數(shù)進行調(diào)用。
SystemInit需要由我們自己實現(xiàn),即我們要編寫一個具有該名稱的函數(shù),用來初始化STM32芯片的時鐘,一般包括初始化AHB、APB等各總線的時鐘,需要經(jīng)過一系列的配置STM32才能達(dá)到穩(wěn)定運行的狀態(tài)。
__main其實不是我們定義的(不要與C語言中的main函數(shù)混淆),當(dāng)編譯器編譯時,只要遇到這個標(biāo)號就會定義這個函數(shù),該函數(shù)的主要功能是:負(fù)責(zé)初始化棧、堆,配置系統(tǒng)環(huán)境,準(zhǔn)備好C語言并在最后跳轉(zhuǎn)到用戶自定義的main函數(shù),從此來到C的世界。
第六行把SystemInit的地址加載到寄存器R0。
第七行程序跳轉(zhuǎn)到R0中的地址執(zhí)行程序,即執(zhí)行SystemInit函數(shù)的內(nèi)容。
第八行把__main的地址加載到寄存器R0。
第九行程序跳轉(zhuǎn)到R0中的地址執(zhí)行程序,即執(zhí)行__main函數(shù),執(zhí)行完畢之后就去到我們熟知的C世界,進入main函數(shù)。
第十行表示子程序的結(jié)束。
總之,看完這段代碼后,了解到如下內(nèi)容即可:我們需要在外部定義一個SystemInit函數(shù)設(shè)置STM32的時鐘;STM32上電后,會執(zhí)行SystemInit函數(shù),最后執(zhí)行我們C語言中的main函數(shù)。
7.3.3stm32f4xx.h文件
看完啟動文件,那我們立即寫SystemInit和main函數(shù)吧?別著急,定義好了SystemInit函數(shù)和main我們又能寫什么內(nèi)容?連接LED燈的GPIO引腳,是要通過讀寫寄存器來控制的,就這樣空著手,如何控制寄存器呢。在上一章,我們知道寄存器就是特殊的內(nèi)存空間,可以通過指針操作訪問寄存器。所以此處我們根據(jù)STM32的存儲分配先定義好各個寄存器的地址,把這些地址定義都統(tǒng)一寫在stm32f4xx.h文件中,見代碼清單72。
代碼清單72外設(shè)地址定義
1/*片上外設(shè)基地址*/
2#define PERIPH_BASE ((unsigned int)0x40000000)
3/*總線基地址*/
4#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)
5/*GPIO外設(shè)基地址*/
6#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00)
8/* GPIOH寄存器地址,強制轉(zhuǎn)換成指針*/
9#define GPIOH_MODER *(unsigned int*)(GPIOH_BASE+0x00)
10#define GPIOH_OTYPER *(unsigned int*)(GPIOH_BASE+0x04)
11#define GPIOH_OSPEEDR *(unsigned int*)(GPIOH_BASE+0x08)
12#define GPIOH_PUPDR *(unsigned int*)(GPIOH_BASE+0x0C)
13#define GPIOH_IDR *(unsigned int*)(GPIOH_BASE+0x10)
14#define GPIOH_ODR *(unsigned int*)(GPIOH_BASE+0x14)
15#define GPIOH_BSRR *(unsigned int*)(GPIOH_BASE+0x18)
16#define GPIOH_LCKR *(unsigned int*)(GPIOH_BASE+0x1C)
17#define GPIOH_AFRL *(unsigned int*)(GPIOH_BASE+0x20)
18#define GPIOH_AFRH *(unsigned int*)(GPIOH_BASE+0x24)
20/*RCC外設(shè)基地址*/
21#define RCC_BASE (AHB1PERIPH_BASE + 0x3800)
22/*RCC的AHB1時鐘使能寄存器地址,強制轉(zhuǎn)換成指針*/
23#define RCC_AHB1ENR *(unsigned int*)(RCC_BASE+0x30)
GPIO外設(shè)的地址跟上一章講解的相同,不過此處把寄存器的地址值都直接強制轉(zhuǎn)換成了指針,方便使用。代碼的最后兩段是RCC外設(shè)寄存器的地址定義,RCC外設(shè)是用來設(shè)置時鐘的,以后我們會詳細(xì)分析,本實驗中只要了解到使用GPIO外設(shè)必須開啟它的時鐘即可。
7.3.4main文件
現(xiàn)在就可以開始編寫程序了,在main文件中先編寫一個main函數(shù),里面什么都沒有,暫時為空。
1intmain (void)
2{
3}
此時直接編譯的話,會出現(xiàn)如下錯誤:
"Error: L6218E: Undefined symbol SystemInit (referred from startup_stm32f429_439xx.o)"
錯誤提示SystemInit沒有定義。從分析啟動文件時我們知道,Reset_Handler調(diào)用了該函數(shù)用來初始化SMT32系統(tǒng)時鐘,為了簡單起見,我們在main文件里面定義一個SystemInit空函數(shù),什么也不做,為的是騙過編譯器,把這個錯誤去掉。關(guān)于配置系統(tǒng)時鐘我們在后面再寫。當(dāng)我們不配置系統(tǒng)時鐘時,STM32芯片會自動按系統(tǒng)內(nèi)部的默認(rèn)時鐘運行,程序還是能跑的。我們在main中添加如下函數(shù):
1//函數(shù)為空,目的是為了騙過編譯器不報錯
2voidSystemInit(void)
3{
4}
這時再編譯就沒有錯了,完美解決。還有一個方法就是在啟動文件中把有關(guān)SystemInit的代碼注釋掉也可以,見代碼清單73。
代碼清單73注釋掉啟動文件中調(diào)用SystemInit的代碼
1; Reset handler
2Reset_HandlerPROC
3EXPORT Reset_Handler [WEAK]
4;IMPORT SystemInit
5IMPORT__main
6
7;LDR R0, =SystemInit
8;BLX R0
9LDR R0, =__main
10BXR0
11ENDP
接下來在main函數(shù)中添加代碼,對寄存器進行控制,寄存器的控制參數(shù)可參考表71(點擊可跳轉(zhuǎn))或《STM32F4xx參考手冊》。
1.GPIO模式
首先我們把連接到LED燈的PH10引腳配置成輸出模式,即配置GPIO的MODER寄存器,見圖75。MODER中包含0-15號引腳,每個引腳占用2個寄存器位。這兩個寄存器位設(shè)置成"01"時即為GPIO的輸出模式,見代碼清單74。
代碼清單74配置輸出模式
1/*GPIOH MODER10清空*/
2GPIOH_MODER &= ~( 0x03<< (2*10));
3/*PH10 MODER10 = 01b輸出模式*/
圖75 MODER寄存器說明(摘自《STM32F4xx參考手冊》)
在代碼中,我們先把GPIOH MODER寄存器的MODER10對應(yīng)位清0,然后再向它賦值"01",從而使GPIOH10引腳設(shè)置成輸出模式。
代碼中使用了"&=~"、"|="這種復(fù)雜位操作方法是為了避免影響到寄存器中的其它位,因為寄存器不能按位讀寫,假如我們直接給MODER寄存器賦值:
1GPIOH_MODER = 0x00100000;
這時MODER10的兩個位被設(shè)置成"01"輸出模式,但其它GPIO引腳就有意見了,因為其它引腳的MODER位都已被設(shè)置成輸入模式。
如果對此處"&=""|="這樣的位操作方法還不理解,請閱讀前面的《規(guī)范的位操作方法》小節(jié)。熟悉這種方法之后,會發(fā)現(xiàn)這樣按位操作其實比直接賦值還要直觀。
2.輸出類型
GPIO輸出有推挽和開漏兩種類型,我們了解到開漏類型不能直接輸出高電平,要輸出高電平還要在芯片外部接上拉電阻,不符合我們的硬件設(shè)計,所以我們直接使用推挽模式。配置OTYPER寄存中的OTYPER10寄存器位,該位設(shè)置為0時PH10引腳即為推挽模式,見代碼清單75。
代碼清單75設(shè)置為推挽模式
1/*GPIOH OTYPER10清空*/
2GPIOH_OTYPER &= ~(1<<1*10);
3/*PH10 OTYPER10 = 0b推挽模式*/
4GPIOH_OTYPER |= (0<<1*10);
3.輸出速度
GPIO引腳的輸出速度是引腳支持高低電平切換的最高頻率,本實驗可以隨便設(shè)置。此處我們配置OSPEEDR寄存器中的寄存器位OSPEEDR10即可控制PH10的輸出速度,見代碼清單76。
代碼清單76設(shè)置輸出速度為2MHz
1/*GPIOH OSPEEDR10清空*/
2GPIOH_OSPEEDR &= ~(0x03<<2*10);
3/*PH10 OSPEEDR10 = 0b速率2MHz*/
4GPIOH_OSPEEDR |= (0<<2*10);
4.上/下拉模式
當(dāng)GPIO引腳用于輸入時,引腳的上/下拉模式可以控制引腳的默認(rèn)狀態(tài)。但現(xiàn)在我們的GPIO引腳用于輸出,引腳受ODR寄存器影響,ODR寄存器對應(yīng)引腳位初始初始化后默認(rèn)值為0,引腳輸出低電平,所以這時我們配置上/下拉模式都不會影響引腳電平狀態(tài)。但因此處上拉能小幅提高電流輸出能力,我們配置它為上拉模式,即配置PUPDR寄存器的PUPDR10位,設(shè)置為二進制值"01",見代碼清單77。
代碼清單77設(shè)置為下拉模式
1/*GPIOH PUPDR10清空*/
2GPIOH_PUPDR &= ~(0x03<<2*10);
3/*PH10 PUPDR10 = 01b下拉模式*/
4GPIOH_PUPDR |= (1<<2*10);
5.控制引腳輸出電平
在輸出模式時,對BSRR寄存器和ODR寄存器寫入?yún)?shù)即可控制引腳的電平狀態(tài)。簡單起見,此處我們使用BSRR寄存器控制,對相應(yīng)的BR10位設(shè)置為1時PH10即為低電平,點亮LED燈,對它的BS10位設(shè)置為1時PH10即為高電平,關(guān)閉LED燈,見代碼清單78。
代碼清單78控制引腳輸出電平
1/*PH10 BSRR寄存器的BR10置1,使引腳輸出低電平*/
2GPIOH_BSRR |= (1<<16<<10);
3
4/*PH10 BSRR寄存器的BS10置1,使引腳輸出高電平*/
5GPIOH_BSRR |= (1<<10);
6.開啟外設(shè)時鐘
設(shè)置完GPIO的引腳,控制電平輸出,以為現(xiàn)在總算可以點亮LED了吧,其實還差最后一步。
在《STM32芯片架構(gòu)》的外設(shè)章節(jié)中提到STM32外設(shè)很多,為了降低功耗,每個外設(shè)都對應(yīng)著一個時鐘,在芯片剛上電的時候這些時鐘都是被關(guān)閉的,如果想要外設(shè)工作,必須把相應(yīng)的時鐘打開。
STM32的所有外設(shè)的時鐘由一個專門的外設(shè)來管理,叫RCC(reset and clockcontrol),RCC在《STM32中文參考手冊》的第六章。
所有的GPIO都掛載到AHB1總線上,所以它們的時鐘由AHB1外設(shè)時鐘使能寄存器(RCC_AHB1ENR)來控制,其中GPIOH端口的時鐘由該寄存器的位7寫1使能,開啟GPIOH端口時鐘。以后我們還會詳細(xì)解釋STM32的時鐘系統(tǒng),此處我們了解到在訪問GPIO的寄存器之前,要先使能它的時鐘即可,使用代碼清單79中的代碼可以開啟GPIOH時鐘。
代碼清單79開啟端口時鐘
1/*開啟GPIOH時鐘,使用外設(shè)時都要先開啟它的時鐘*/
2RCC_AHB1ENR |= (1<<7);
7.水到渠成
開啟時鐘,配置引腳模式,控制電平,經(jīng)過這三步,我們總算可以控制一個LED了。現(xiàn)在我們完整組織下用STM32控制一個LED的代碼,見代碼清單710。
代碼清單710 main文件中控制LED燈的代碼
1
2/*
3使用寄存器的方法點亮LED燈
4*/
5#include"stm32f4xx.h"
6
7
8/**
9*主函數(shù)
10*/
11intmain(void)
12{
13/*開啟GPIOH時鐘,使用外設(shè)時都要先開啟它的時鐘*/
14RCC_AHB1ENR |= (1<<7);
15
16/* LED端口初始化*/
17
18/*GPIOH MODER10清空*/
19GPIOH_MODER &= ~( 0x03<< (2*10));
20/*PH10 MODER10 = 01b輸出模式*/
21GPIOH_MODER |= (1<<2*10);
22
23/*GPIOH OTYPER10清空*/
24GPIOH_OTYPER &= ~(1<<1*10);
25/*PH10 OTYPER10 = 0b推挽模式*/
26GPIOH_OTYPER |= (0<<1*10);
27
28/*GPIOH OSPEEDR10清空*/
29GPIOH_OSPEEDR &= ~(0x03<<2*10);
30/*PH10 OSPEEDR10 = 0b速率2MHz*/
31GPIOH_OSPEEDR |= (0<<2*10);
32
33/*GPIOH PUPDR10清空*/
34GPIOH_PUPDR &= ~(0x03<<2*10);
35/*PH10 PUPDR10 = 01b上拉模式*/
36GPIOH_PUPDR |= (1<<2*10);
37
38/*PH10 BSRR寄存器的BR10置1,使引腳輸出低電平*/
39GPIOH_BSRR |= (1<<16<<10);
40
41/*PH10 BSRR寄存器的BS10置1,使引腳輸出高電平*/
42//GPIOH_BSRR |= (1<<10);
43
44while(1);
46}
47
48//函數(shù)為空,目的是為了騙過編譯器不報錯
49voidSystemInit(void)
50{
51}
在本章節(jié)中,要求完全理解stm32f4xx.h文件及main文件的內(nèi)容(RCC相關(guān)的除外)。
編輯:hfy
-
led燈
+關(guān)注
關(guān)注
22文章
1592瀏覽量
108226 -
寄存器
+關(guān)注
關(guān)注
31文章
5363瀏覽量
120909 -
STM32
+關(guān)注
關(guān)注
2270文章
10921瀏覽量
356983 -
GPIO
+關(guān)注
關(guān)注
16文章
1216瀏覽量
52267
發(fā)布評論請先 登錄
相關(guān)推薦
評論