一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

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

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

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

由淺入深盡享STM32標(biāo)準(zhǔn)庫(kù)開(kāi)發(fā)的樂(lè)趣

Q4MP_gh_c472c21 ? 來(lái)源:華為云社區(qū) ? 作者:華為云社區(qū) ? 2021-06-26 15:02 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

摘要:從STM32新建工程、編譯下載程序出發(fā),讓新手由淺入深,盡享STM32標(biāo)準(zhǔn)庫(kù)開(kāi)發(fā)的樂(lè)趣。

自從CubeMX等圖像配置軟件的出現(xiàn),同學(xué)們往往點(diǎn)幾下鼠標(biāo)就解決了單片機(jī)的配置問(wèn)題。對(duì)于追求開(kāi)發(fā)速度的業(yè)務(wù)場(chǎng)景下,使用快速配置軟件是合理的,高效的,但對(duì)于學(xué)生的學(xué)習(xí)場(chǎng)景下,更為重要的是知其然并知其所以然。

以下是學(xué)習(xí)(包括但不限于)嵌入式的三個(gè)重要內(nèi)容,

1、學(xué)會(huì)如何參考官方的手冊(cè)和官方的代碼來(lái)獨(dú)立寫(xiě)自己的程序。

2、積累常用代碼段,知道哪里的問(wèn)題需要哪些代碼處理。

3、跟隨大佬步伐,一步一個(gè)腳印。

首先:我們都知道編程時(shí)一般查的是《參考手冊(cè)》,而進(jìn)行芯片選型或需要芯片數(shù)據(jù)時(shí),查閱的是《數(shù)據(jù)手冊(cè)》。此外市面上所有關(guān)于STM32的書(shū)籍都是立足于前二者(+Cortex內(nèi)核手冊(cè))進(jìn)行編著。

其次:要分清什么是內(nèi)核外設(shè)與內(nèi)核之外的外設(shè),為了便于區(qū)分,按照網(wǎng)上的一種說(shuō)法,將“內(nèi)核之外的外設(shè)”以“處理器外設(shè)”代替。再者:如今很少使用標(biāo)準(zhǔn)庫(kù)了,都是HAL庫(kù),但作為高校目前教學(xué)方式,我們將以STM32f10xxx為例對(duì)標(biāo)準(zhǔn)庫(kù)開(kāi)發(fā)進(jìn)行概覽。

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

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

內(nèi)核IP

從結(jié)構(gòu)框圖上看,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è)DMA總線(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)工。

處理器外設(shè)(內(nèi)核之外的外設(shè))

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

二、寄存器什么是寄存器?寄存器是內(nèi)置于各個(gè)IP外設(shè)中,是一種用于配置外設(shè)功能的存儲(chǔ)器,并且有想對(duì)應(yīng)的地址。一切庫(kù)的封裝始于映射。

是不是“又臭又長(zhǎng)”,如果進(jìn)行寄存器開(kāi)發(fā),就需要懟地址以及對(duì)寄存器進(jìn)行字節(jié)賦值,不僅效率低而且容易出錯(cuò)。

來(lái),開(kāi)個(gè)玩笑。

你也許聽(tīng)說(shuō)過(guò)“國(guó)際C語(yǔ)言亂碼大賽(IOCCC)”下面這個(gè)例子就是網(wǎng)上廣為流傳的 一個(gè)經(jīng)典作品:

#include 《stdio.h》

main(t,_,a)char *a;{return!0《t?t《3?main(-79,-13,a+main(-87,1-_,

main(-86,0,a+1)+a)):1,t《_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_《13?

main(2,_+1,“%s %d %d

”):9:16:t《0?t《-72?main(_,t,

“@n‘+,#’/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#

;#q#n+,/+k#;*+,/‘r :’d*‘3,}{w+K w’K:‘+}e#’;dq#‘l

q#’+d‘K#!/+k#;q#’r}eKK#}w‘r}eKK{nl]’/#;#q#n‘){)#}w’){){nl]‘/+#n’;d}rw‘ i;#

){nl]!/n{n#’; r{#w‘r nc{nl]’/#{l,+‘K {rw’ iK{;[{nl]‘/w#q#n’wk nw‘

iwk{KK{nl]!/w{%’l##w#‘ i; :{nl]’/*{q#‘ld;r’}{nlwb!/*de}‘c

;;{nl’-{}rw]‘/+,}##’*}#nc,‘,#nw]’/+kd‘+e}+;#’rdq#w! nr‘/ ’) }+}{rl#‘{n’ ‘)#

}’+}##(?。?”)

:t《-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a==‘/’)+t,_,a+1)

:0《t?main(2,2,“%s”):*a==‘/’||main(0,main(-61,*a,

“!ek;dc i@bK‘(q)-[w]*%n+r3#l,{}:

uwloca-O;m.vpbks,fxntdCeghiry”),a+1);}

庫(kù)的存在就是為了解決這類(lèi)問(wèn)題,將代碼語(yǔ)義化。語(yǔ)義化思想不僅僅是嵌入式有的,前端代碼也在追求語(yǔ)義特性。

三、萬(wàn)物始于點(diǎn)燈

(1)內(nèi)核庫(kù)文件分析

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ī)直接移植。

(2)處理器外設(shè)庫(kù)文件分析

startup_stm32f10x_hd.s

這個(gè)是由匯編編寫(xiě)的啟動(dòng)文件,是STM32上電啟動(dòng)的第一個(gè)程序,啟動(dòng)文件主要實(shí)現(xiàn)了

初始化堆棧指針 SP;

設(shè)置 PC 指針=Reset_Handler ;

設(shè)置向量表的地址,并 初始化向量表,向量表里面放的是 STM32 所有中斷函數(shù)的入口地址

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

跳轉(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è)頭文件非常重要,這個(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;

這是一般的操作方法?,F(xiàn)在這個(gè)頭文件里面有關(guān)于 ADON 位的位定義:

#define ADC_CR2_ADON ((uint32_t)0x00000001)

有了這個(gè)位定義,我們剛剛的代碼就變成了:

ADC-》CR2=ADC_CR2_ADON

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ù)。

(3)SystemInit

工程中新建main.c 。

在此文件中編寫(xiě)main函數(shù)后直接編譯會(huì)報(bào)錯(cuò):

?

Undefined symbol SystemInit (referred from startup_stm32f10x_hd.o)。

?錯(cuò)誤提示說(shuō)SystemInit沒(méi)有定義。從分析啟動(dòng)文件startup_stm32f10x_hd.s時(shí)我們知道,

;Reset handler

Reset_Handler PROC

EXPORT Reset_Handler [WEAK]

IMPORT __main

;IMPORT SystemInit

;LDR R0, =SystemInit

BLX R0

LDR R0, =__main

BX R0

ENDP

匯編中;分號(hào)是注釋的意思

第五行第六行代碼Reset_Handler調(diào)用了SystemInit該函數(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í)鐘之后會(huì)出文章RCC時(shí)鐘樹(shù)詳細(xì)介紹,主要配置時(shí)鐘控制寄存器(RCC_CR)和時(shí)鐘配置寄存器(RCC_CFGR)這兩個(gè)寄存器,但最好是直接使用CubeMX直接生成,因?yàn)樗呐渲眠^(guò)程有些冗長(zhǎng)。

如果我們用的是庫(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。

(4)庫(kù)封裝層級(jí)

達(dá)到第四層級(jí)便是我們所熟知的固件庫(kù)或HAL庫(kù)的效果。當(dāng)然庫(kù)的編寫(xiě)還需要考慮許多問(wèn)題,不止于這些內(nèi)容。我們需要的是了解庫(kù)封裝的大概過(guò)程。

將庫(kù)封裝等級(jí)分為四級(jí)來(lái)介紹是為了有層次感,就像打怪升級(jí)一樣,進(jìn)行認(rèn)知理解的升級(jí)。

我們都知道,操作GPIO輸出分三大步:

時(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,AHB為高速總線(xiàn),APB2 次之,APB1 再次之。所以的IO 口都掛載到APB2 總線(xiàn)上,屬于高速外設(shè)。

模式配置:

這個(gè)由端口配置寄存器來(lái)控制。端口配置寄存器分為高低兩個(gè),每4bit 控制一個(gè)IO 口,所以端口配置低寄存器:CRL 控制這IO 口的低8 位,端口配置高寄存器:CRH控制這IO 口的高8bit。

在4 位一組的控制位中,CNFy[1:0] 用來(lái)控制端口的輸入輸出,MODEy[1:0]用來(lái)控制輸出模式的速率,又稱(chēng)驅(qū)動(dòng)電路的響應(yīng)速度,注意此處速率與程序無(wú)關(guān),GPIO引腳速度、翻轉(zhuǎn)速度、輸出速度區(qū)別輸入有4種模式,輸出有4種模式,我們?cè)诳刂芁ED 的時(shí)候選擇通用推挽輸出。

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

電平控制:

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就可以輸出低或者高電平。

第一層級(jí):基地址宏定義

5aac4f3c-d5b9-11eb-9e57-12bb97331649.png

時(shí)鐘控制:

5b267a3c-d5b9-11eb-9e57-12bb97331649.png

在STM32中,每個(gè)外設(shè)都有一個(gè)起始地址,叫做外設(shè)基地址,外設(shè)的寄存器就以這個(gè)基地址為標(biāo)準(zhǔn)按照順序排列,且每個(gè)寄存器32位,(后面作為結(jié)構(gòu)體里面的成員正好內(nèi)存對(duì)齊)。

查表看到時(shí)鐘由APB2外設(shè)時(shí)鐘使能寄存器(RCC_APB2ENR)來(lái)控制,其中PB端口的時(shí)鐘由該寄存器的位3寫(xiě)1使能。我們可以通過(guò)基地址+偏移量0x18,算出RCC_APB2ENR的地址為:0x40021018。那么使能PB口的時(shí)鐘代碼則如下所示:

#define RCC_APB2ENR *(volatile unsigned long *)0x40021018

// 開(kāi)啟端口B 時(shí)鐘

RCC_APB2ENR |= 1《《3;

模式配置:

5b3fd31a-d5b9-11eb-9e57-12bb97331649.png

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

5bc2d59e-d5b9-11eb-9e57-12bb97331649.png

同上,從手冊(cè)中我們看到ODR寄存器的地址偏移是:0CH,可以算出GPIOB_ODR寄存器的地址是:0X4001 0C00 + 0X0C = 0X4001 0C0C?,F(xiàn)在我們就可以定義GPIOB_ODR這個(gè)寄存器了,代碼如下:

#define GPIOB_ODR *(volatile unsigned long *)0x40010C0C//PB0 輸出低電平

GPIOB_ODR = 0《《0;

第一層級(jí):基地址宏定義完成用STM32控制一個(gè)LED的完整代碼:

#define RCC_APB2ENR *(volatile unsigned long *)0x40021018#define GPIOB_CRL *(volatile unsigned long *)0x40010C00#define GPIOB_ODR *(volatile unsigned long *)0x40010C0Cint main(void)

{

// 開(kāi)啟端口B 的時(shí)鐘

RCC_APB2ENR |= 1《《3;

// 配置PB0 為通用推挽輸出模式,速率為2M

GPIOB_CRL = (2《《0) | (0《《2);

// PB0 輸出低電平,點(diǎn)亮LED

GPIOB_ODR = 0《《0;

}

void SystemInit(void)

{

}

第二層級(jí):基地址宏定義+結(jié)構(gòu)體封裝

外設(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)型一樣。

typedef struct

{

volatile uint32_t CRL;

volatile uint32_t CRH;

volatile uint32_t IDR;

volatile uint32_t ODR;

volatile uint32_t BSRR;

volatile uint32_t BRR;

volatile uint32_t LCKR;

} GPIO_TypeDef;

在《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 里面使用typedef對(duì)unsigned int重命名,我們?cè)诔绦蛏现灰@個(gè)頭文件即可。

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

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

#define GPIOA ((GPIO_TypeDef *) 0X4001 0800)#define GPIOB ((GPIO_TypeDef *) 0X4001 0C00)#define GPIOC ((GPIO_TypeDef *) 0X4001 1000)#define GPIOD ((GPIO_TypeDef *) 0X4001 1400)#define GPIOE ((GPIO_TypeDef *) 0X4001 1800)#define GPIOF ((GPIO_TypeDef *) 0X4001 1C00)#define GPIOG ((GPIO_TypeDef *) 0X4001 2000)

外設(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

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

APB1、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)的基址上加上偏移地址即可,代碼如下:

#define PERIPH_BASE ((uint32_t)0x40000000)#define APB1PERIPH_BASE PERIPH_BASE#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)

GPIO 端口基地址

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

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)

第二層級(jí):基地址宏定義+結(jié)構(gòu)體封裝完成用STM32控制一個(gè)LED的完整代碼:

#include 《stdint.h》#define __IO volatiletypedef struct

{

__IO uint32_t CRL;

__IO uint32_t CRH;

__IO uint32_t IDR;

__IO uint32_t ODR;

__IO uint32_t BSRR;

__IO uint32_t BRR;

__IO uint32_t LCKR;

} GPIO_TypeDef;

typedef struct

{

__IO uint32_t CR;

__IO uint32_t CFGR;

__IO uint32_t CIR;

__IO uint32_t APB2RSTR;

__IO uint32_t APB1RSTR;

__IO uint32_t AHBENR;

__IO uint32_t APB2ENR;

__IO uint32_t APB1ENR;

__IO uint32_t BDCR;

__IO uint32_t CSR;

} RCC_TypeDef;

#define PERIPH_BASE ((uint32_t)0x40000000)

#define APB1PERIPH_BASE PERIPH_BASE#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)

#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)

#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)#define RCC_BASE (AHBPERIPH_BASE + 0x1000)#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)

#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)

#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)#define RCC ((RCC_TypeDef *) RCC_BASE)#define RCC_APB2ENR *(volatile unsigned long *)0x40021018

#define GPIOB_CRL *(volatile unsigned long *)0x40010C00#define GPIOB_ODR *(volatile unsigned long *)0x40010C0Cint main(void)

{

// 開(kāi)啟端口B 的時(shí)鐘

RCC-》APB2ENR |= 1《《3;

// 配置PB0 為通用推挽輸出模式,速率為2M

GPIOB-》CRL = (2《《0) | (0《《2);

// PB0 輸出低電平,點(diǎn)亮LED

GPIOB-》ODR = 0《《0;

}

void SystemInit(void)

{

}

第二層級(jí)變化:

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

②外設(shè)內(nèi)存映射,即把地址跟外設(shè)建立起一一對(duì)應(yīng)的關(guān)系。

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

④通過(guò)結(jié)構(gòu)體操作寄存器,實(shí)現(xiàn)點(diǎn)亮LED。

第三層級(jí):基地址宏定義+結(jié)構(gòu)體封裝+“位封裝”(每一位的對(duì)應(yīng)字節(jié)封裝)

上面我們?cè)诳刂艷PIO輸出內(nèi)容的時(shí)候控制的是ODR(Output data register)寄存器,ODR是一個(gè)16位的寄存器,必須以字的形式控制其實(shí)我們還可以控制BSRR和BRR這兩個(gè)寄存器來(lái)控制IO的電平,下面我們簡(jiǎn)單介紹下BRR寄存器的功能,BSRR自行看手冊(cè)研究。

位清除寄存器BRR只能實(shí)現(xiàn)位清0操作,是一個(gè)32位寄存器,低16位有效,寫(xiě)0沒(méi)影響,寫(xiě)1清0?,F(xiàn)在我們要使PB0輸出低電平,點(diǎn)亮LED,則只要往BRR的BR0位寫(xiě)1即可,其他位為0,代碼如下:

GPIOB-》BRR = 0X0001;

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

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

GPIOB-》BRR = 0X0004;

如果要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)名知意?!拔环庋b”(每一位的對(duì)應(yīng)字節(jié)封裝) 代碼如下:

#define GPIO_Pin_0 ((uint16_t)0x0001)

/*!

《 Pin 0 selected */#define GPIO_Pin_1 ((uint16_t)0x0002)

/*!《 Pin 1 selected */#define GPIO_Pin_2 ((uint16_t)0x0004) /*!《 Pin 2 selected */#define GPIO_Pin_3 ((uint16_t)0x0008)

/*!《 Pin 3 selected */#define GPIO_Pin_4 ((uint16_t)0x0010)

/*!《 Pin 4 selected */#define GPIO_Pin_5 ((uint16_t)0x0020) /*!《 Pin 5 selected */#define GPIO_Pin_6 ((uint16_t)0x0040)

/*!《 Pin 6 selected */#define GPIO_Pin_7 ((uint16_t)0x0080)

/*!《 Pin 7 selected */#define GPIO_Pin_8 ((uint16_t)0x0100)

/*!《 Pin 8 selected */#define GPIO_Pin_9 ((uint16_t)0x0200) /*!《 Pin 9 selected */#define GPIO_Pin_10 ((uint16_t)0x0400)

/*!《 Pin 10 selected */#define GPIO_Pin_11 ((uint16_t)0x0800)

/*!《 Pin 11 selected */#define GPIO_Pin_12 ((uint16_t)0x1000)

/*!《 Pin 12 selected */#define GPIO_Pin_13 ((uint16_t)0x2000) /*!《 Pin 13 selected */#define GPIO_Pin_14 ((uint16_t)0x4000)

/*!《 Pin 14 selected */#define GPIO_Pin_15 ((uint16_t)0x8000) /*!《 Pin 15 selected */#define GPIO_Pin_All ((uint16_t)0xFFFF)

/*!《 All pins selected */

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

GPIOB-》BRR = GPIO_Pin_0;

(如果同時(shí)讓PB0/PB15輸出低電平,用或運(yùn)算,代碼:

GPIOB-》BRR = GPIO_Pin_0|GPIO_Pin_15;

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

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

第四層級(jí):基地址宏定義+結(jié)構(gòu)體封裝+“位封裝”+函數(shù)封裝

我們點(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寄存器,代碼如下:

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

{

GPIOx-》BRR = GPIO_Pin;

}

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

GPIO_ResetBits(GPIOB,GPIO_Pin_0);

同理, 我們可以控制BSRR這個(gè)寄存器來(lái)實(shí)現(xiàn)關(guān)閉LED,代碼如下:

// GPIO 端口置位函數(shù)void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

{

GPIOx-》BSRR = GPIO_Pin;

}

這時(shí),PB0輸出高電平,關(guān)閉LED的代碼就變成了:

GPIO_SetBits(GPIOB,GPIO_Pin_0);

同樣,因?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à)瓢即可。

(5)實(shí)例編寫(xiě)

以上,是對(duì)庫(kù)封住過(guò)程的概述,下面我們正在地使用庫(kù)函數(shù)編寫(xiě)LED程序

①管理庫(kù)的頭文件

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

//#include “stm32f10x_adc.h”//#include “stm32f10x_bkp.h”//#include “stm32f10x_can.h”//

#include “stm32f10x_cec.h”//#include “stm32f10x_crc.h”//#include “stm32f10x_dac.h”//

#include “stm32f10x_dbgmcu.h”//#include “stm32f10x_dma.h”//

#include “stm32f10x_exti.h”//#include “stm32f10x_flash.h”//

#include “stm32f10x_fsmc.h”#include “stm32f10x_gpio.h”//

#include “stm32f10x_i2c.h”//#include “stm32f10x_iwdg.h”//#include “stm32f10x_pwr.h”

#include “stm32f10x_rcc.h”//#include “stm32f10x_rtc.h”//

#include “stm32f10x_sdio.h”//#include “stm32f10x_spi.h”//#include “stm32f10x_tim.h”/

/#include “stm32f10x_usart.h”//

#include “stm32f10x_wwdg.h”//#include “misc.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行:

#ifdef USE_STDPERIPH_DRIVER#include “stm32f10x_conf.h”#endif

代碼的意思是,如果定義了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即可。

②編寫(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ù)的

原型為:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph,

FunctionalState NewState)

{

/* Check the parameters */

assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));

assert_param(IS_FUNCTIONAL_STATE(NewState));

if (NewState != DISABLE)

{

RCC-》APB2ENR |= RCC_APB2Periph;

}

else

{

RCC-》APB2ENR &= ~RCC_APB2Periph;

}

}

當(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配置。固件庫(kù)把端口配置的pin,速度和模式封裝成一個(gè)結(jié)構(gòu)體:

typedef struct

{

uint16_t GPIO_Pin;

GPIOSpeed_TypeDef GPIO_Speed;

GPIOMode_TypeDef GPIO_Mode;

} GPIO_InitTypeDef;

pin可以是GPIO_Pin_0~GPIO_Pin_15或者是GPIO_Pin_All,這些都是庫(kù)預(yù)先定義好的宏。speed也被封裝成一個(gè)結(jié)構(gòu)體:

typedef enum

{

GPIO_Speed_10MHz = 1,

GPIO_Speed_2MHz,

GPIO_Speed_50MHz

} GPIOSpeed_TypeDef;

速度可以是10M,2M或者50M,這個(gè)由端口配置寄存器的MODE位控制,速度是針對(duì)IO口輸出的時(shí)候而言,在輸入的時(shí)候可以不用設(shè)置。mode也被封裝成一個(gè)結(jié)構(gòu)體:

typedef enum

{

GPIO_Mode_AIN = 0x0, // 模擬輸入

GPIO_Mode_IN_FLOATING = 0x04, // 浮空輸入(復(fù)位后的狀態(tài))

GPIO_Mode_IPD = 0x28, // 下拉輸入

GPIO_Mode_IPU = 0x48, // 上拉輸入

GPIO_Mode_Out_OD = 0x14, // 通用開(kāi)漏輸出

GPIO_Mode_Out_PP = 0x10, // 通用推挽輸出

GPIO_Mode_AF_OD = 0x1C, // 復(fù)用開(kāi)漏輸出

GPIO_Mode_AF_PP = 0x18 // 復(fù)用推挽輸出

} GPIOMode_TypeDef;

IO口的模式有8種,輸入輸出各4種,由端口配置寄存器的CNF配置。平時(shí)用的最多的就是通用推挽輸出,可以輸出高低電平,驅(qū)動(dòng)能力大,一般用于接數(shù)字器件。

最終用固件庫(kù)實(shí)現(xiàn)就變成這樣:

// 定義一個(gè)GPIO_InitTypeDef 類(lèi)型的結(jié)構(gòu)體

GPIO_InitTypeDef GPIO_InitStructure;

// 選擇要控制的IO 口

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

// 設(shè)置引腳為推挽輸出

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

// 設(shè)置引腳速率為50MHz

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

/*調(diào)用庫(kù)函數(shù),初始化GPIOB0*/

GPIO_Init(GPIOB, &GPIO_InitStructure);

倘若同一端口下不同引腳有不同的模式配置,每次對(duì)每個(gè)引腳配置完成后都要調(diào)用GPIO初始化函數(shù),代碼如下:

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉輸入

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO 輸出控制

GPIO輸出控制,可以通過(guò)端口數(shù)據(jù)輸出寄存器ODR、端口位設(shè)置/清除寄存器BSRR和端口位清除寄存器BRR這三個(gè)來(lái)控制。端口輸出寄存器ODR是一個(gè)32位的寄存器,低16位有效,對(duì)應(yīng)著IO0~IO15,只能以字的形式操作,一般使用寄存器操作。

// PB0 輸出高電平,點(diǎn)亮LED

GPIOB-》ODR = 1《《0;

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

// PB0 輸出低電平,點(diǎn)亮LED

GPIO_ResetBits(GPIOB, GPIO_Pin_0);

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

// PB0 輸出高電平,熄滅LED

GPIO_SetBits(GPIOB, GPIO_Pin_0);

綜上:固件庫(kù)LED GPIO初始化函數(shù)

void LED_GPIO_Config(void)

{

// 定義一個(gè)GPIO_InitTypeDef 類(lèi)型的結(jié)構(gòu)體

GPIO_InitTypeDef GPIO_InitStructure;

// 開(kāi)啟GPIOB 的時(shí)鐘

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);

// 選擇要控制的IO 口

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

// 設(shè)置引腳為推挽輸出

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

// 設(shè)置引腳速率為50MHz

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

/*調(diào)用庫(kù)函數(shù),初始化GPIOB0*/

GPIO_Init(GPIOB, &GPIO_InitStructure);

// 關(guān)閉LED

GPIO_SetBits(GPIOB, GPIO_Pin_0);

}

主函數(shù)

#include “stm32f10x.h”void SOFT_Delay(__IO uint32_t nCount);

void LED_GPIO_Config(void);

int main(void)

{

// 程序來(lái)到main 函數(shù)之前,啟動(dòng)文件:statup_stm32f10x_hd.s 已經(jīng)調(diào)用

// SystemInit()函數(shù)把系統(tǒng)時(shí)鐘初始化成72MHZ

// SystemInit()在system_stm32f10x.c 中定義

// 如果用戶(hù)想修改系統(tǒng)時(shí)鐘,可自行編寫(xiě)程序修改

LED_GPIO_Config();

while ( 1 )

{

// 點(diǎn)亮LED

GPIO_ResetBits(GPIOB, GPIO_Pin_0);

Time_Delay(0x0FFFFF);

// 熄滅LED

GPIO_SetBits(GPIOB, GPIO_Pin_0);

Time_Delay(0x0FFFFF);

}

}

// 簡(jiǎn)陋的軟件延時(shí)函數(shù)void Time_Delay(volatile uint32_t Count)

{

for (; Count != 0; Count--);

}

注意void Time_Delay(volatile uint32_t Count)只是一個(gè)簡(jiǎn)陋的軟件延時(shí)函數(shù),如果小伙伴們有興趣可以看一看MultiTimer,它是一個(gè)軟件定時(shí)器擴(kuò)展模塊,可無(wú)限擴(kuò)展所需的定時(shí)器任務(wù),取代傳統(tǒng)的標(biāo)志位判斷方式, 更優(yōu)雅更便捷地管理程序的時(shí)間觸發(fā)時(shí)序。

責(zé)任編輯:haq

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

    關(guān)注

    6067

    文章

    44992

    瀏覽量

    650504
  • STM32
    +關(guān)注

    關(guān)注

    2293

    文章

    11032

    瀏覽量

    364986

原文標(biāo)題:從這些知識(shí)點(diǎn)入手,學(xué)單片機(jī)就簡(jiǎn)單多了

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

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    STM32F4標(biāo)準(zhǔn)外設(shè)庫(kù)資料

    stm32f4xx.h,標(biāo)準(zhǔn)外設(shè)庫(kù)
    發(fā)表于 06-08 09:49 ?0次下載

    基于STM32 HAL庫(kù)標(biāo)準(zhǔn)庫(kù)的esp8266接入機(jī)智云方案(二)

    在《基于STM32HAL庫(kù)標(biāo)準(zhǔn)庫(kù)的esp8266接入機(jī)智云方案(一)》中,我們?cè)敿?xì)介紹了硬件連接和機(jī)智云客戶(hù)端的創(chuàng)建。本篇將重點(diǎn)講解如何下載代碼、分析下載的代碼,并直接使用這些代碼連
    的頭像 發(fā)表于 05-28 18:02 ?479次閱讀
    基于<b class='flag-5'>STM32</b> HAL<b class='flag-5'>庫(kù)</b>與<b class='flag-5'>標(biāo)準(zhǔn)</b><b class='flag-5'>庫(kù)</b>的esp8266接入機(jī)智云方案(二)

    STM32已經(jīng)用標(biāo)準(zhǔn)庫(kù)寫(xiě)好的代碼,怎么導(dǎo)入RT THREAD?

    之前已經(jīng)在裸機(jī)上實(shí)現(xiàn)了SAE J1939協(xié)議,因?yàn)橐砑覫OT 功能,現(xiàn)在想添加RT THREAD.因?yàn)橹皩?xiě)裸機(jī)程序的時(shí)候用的是標(biāo)準(zhǔn)庫(kù)。而RT THREAD STUDIO自動(dòng)生成的是HAL庫(kù),請(qǐng)問(wèn)有什么好方法在我原來(lái)的代碼上添
    發(fā)表于 05-27 06:01

    PCB標(biāo)準(zhǔn)封裝庫(kù)文件

    PCB標(biāo)準(zhǔn)封裝庫(kù)文件
    發(fā)表于 05-22 17:43 ?3次下載

    stm32哪個(gè)系列芯片支持2個(gè)USB,主設(shè)備,且usb可以支持標(biāo)準(zhǔn)的uvc協(xié)議,哪里可以下載usb開(kāi)發(fā)庫(kù)?

    stm32哪個(gè)系列芯片支持2個(gè)USB,主設(shè)備,且usb可以支持標(biāo)準(zhǔn)的uvc協(xié)議,哪里可以下載usb開(kāi)發(fā)庫(kù)
    發(fā)表于 04-27 08:16

    STM32開(kāi)發(fā)入門(mén)進(jìn)階必備!《STM32嵌入式系統(tǒng)開(kāi)發(fā)—基于STM32CubeMX和HAL庫(kù)》新書(shū)發(fā)布!

    近日,由華清遠(yuǎn)見(jiàn)教育科技集團(tuán)精心編撰的教育部高等學(xué)校電子信息類(lèi)專(zhuān)業(yè)教學(xué)指導(dǎo)委員會(huì)規(guī)劃教材、普通高等教育電子信息類(lèi)專(zhuān)業(yè)系列教材——《STM32嵌入式系統(tǒng)開(kāi)發(fā)—基于STM32CubeMX和HAL庫(kù)
    的頭像 發(fā)表于 04-03 14:54 ?748次閱讀
    <b class='flag-5'>STM32</b><b class='flag-5'>開(kāi)發(fā)</b>入門(mén)進(jìn)階必備!《<b class='flag-5'>STM32</b>嵌入式系統(tǒng)<b class='flag-5'>開(kāi)發(fā)</b>—基于<b class='flag-5'>STM32</b>CubeMX和HAL<b class='flag-5'>庫(kù)</b>》新書(shū)發(fā)布!

    STM32的幾種開(kāi)發(fā)方式,你都知道嗎?(可下載)

    形式如下:1. 使用標(biāo)準(zhǔn)外設(shè)庫(kù)開(kāi)發(fā) 2.使用寄存器開(kāi) 3.使用 CubeMax 生成代碼工具開(kāi)發(fā) 下面我們先來(lái)說(shuō)一下目前這幾種形式的區(qū)別:1.使用標(biāo)
    發(fā)表于 04-03 11:24 ?0次下載

    STM32WBA52CEU可以用標(biāo)準(zhǔn)庫(kù)開(kāi)發(fā)嗎?

    看到STM32WBA52CEU選項(xiàng)共2種:STM32WBA52CEUx和STM32WBA52CEUxT。 我常用的是標(biāo)準(zhǔn)庫(kù),hal
    發(fā)表于 03-13 06:34

    HAL庫(kù)標(biāo)準(zhǔn)庫(kù)你會(huì)選擇哪種庫(kù)?

    HAL庫(kù)標(biāo)準(zhǔn)庫(kù)你會(huì)選擇哪種庫(kù)
    發(fā)表于 03-10 06:25

    STM32開(kāi)發(fā)板教程之STM32開(kāi)發(fā)指南免費(fèi)下載

      本開(kāi)發(fā)指南將由淺入深,帶領(lǐng)大家進(jìn)入 STM32 的世界。本指南總共分為三篇:1,硬件篇,主要介紹本指南的實(shí)驗(yàn)平臺(tái);2,軟件篇,主要介紹 STM32
    發(fā)表于 02-28 09:08 ?179次下載

    STM32F1開(kāi)發(fā)指南

    這本書(shū)詳細(xì)講述了STM32HAL庫(kù)開(kāi)發(fā)的配置流程,非常詳細(xì)
    發(fā)表于 01-21 15:33 ?3次下載

    HAL庫(kù)標(biāo)準(zhǔn)庫(kù)的區(qū)別 HAL庫(kù)與CMSIS的關(guān)系

    在嵌入式系統(tǒng)開(kāi)發(fā)中,HAL(硬件抽象層)庫(kù)標(biāo)準(zhǔn)庫(kù)是兩種常用的軟件庫(kù),它們?cè)诠δ芎褪褂脠?chǎng)景上有所不同。 1.
    的頭像 發(fā)表于 12-02 14:02 ?3265次閱讀

    HAL庫(kù)STM32開(kāi)發(fā)中的重要性

    HAL庫(kù)(Hardware Abstraction Layer Library,硬件抽象層庫(kù))在STM32開(kāi)發(fā)中扮演著至關(guān)重要的角色。以下是HAL庫(kù)
    的頭像 發(fā)表于 12-02 13:35 ?1333次閱讀

    千視P3助力國(guó)慶直播,超長(zhǎng)續(xù)航高清視覺(jué)盛宴

    直播中高畫(huà)質(zhì)、低延時(shí)的視覺(jué)盛宴,也讓更多年輕人深入了解了紅色歷史和傳統(tǒng)文化的魅力。01直播中的挑戰(zhàn)從10月1日至5日,團(tuán)隊(duì)連續(xù)進(jìn)行了3場(chǎng)直播活動(dòng),每場(chǎng)包括彩排持
    的頭像 發(fā)表于 10-18 08:02 ?880次閱讀
    千視P3助力國(guó)慶直播,超長(zhǎng)續(xù)航<b class='flag-5'>盡</b><b class='flag-5'>享</b>高清視覺(jué)盛宴

    課程上線(xiàn) | STM32單片機(jī)入門(mén)教程(1)基于HAL庫(kù)的多核心開(kāi)發(fā)(F1/F4/G0/U5)

    ?好消息!華清遠(yuǎn)見(jiàn)STM32入門(mén)視頻課程《基于HAL庫(kù)STM32多核心開(kāi)發(fā)》正式上線(xiàn),已更新90講,持續(xù)更新中,8月份將全部上線(xiàn)。課程優(yōu)勢(shì)真人出鏡,手把手教學(xué)一板玩轉(zhuǎn)
    的頭像 發(fā)表于 08-08 16:42 ?762次閱讀
    課程上線(xiàn) | <b class='flag-5'>STM32</b>單片機(jī)入門(mén)教程(1)基于HAL<b class='flag-5'>庫(kù)</b>的多核心<b class='flag-5'>開(kāi)發(fā)</b>(F1/F4/G0/U5)