1.1 GPIO工作原理
熟悉單片機(jī)的朋友都知道,學(xué)習(xí)的第一個(gè)例程就是流水燈,要想實(shí)現(xiàn)流水燈,首先必須了解GPIO的工作原理。GPIO的基本結(jié)構(gòu)如圖1所示。
STM32 的 IO 口可以由軟件配置成如下 8 種模式:
- 輸入模式
- 浮空輸入:浮空(floating)就是邏輯器件的輸入引腳即不接高電平,也不接低電平。由于邏輯器件的內(nèi)部結(jié)構(gòu),當(dāng)它輸入引腳懸空時(shí),相當(dāng)于該引腳接了高電平。一般實(shí)際運(yùn)用時(shí),引腳不建議懸空,易受干擾。 通俗講就是讓管腳什么都不接,浮空著。信號進(jìn)入芯片內(nèi)部后,既沒有接上拉電阻也沒有接下拉電阻,經(jīng)由觸發(fā)器輸入。配置成這個(gè)模式后,用電壓變量引腳電壓為1點(diǎn)幾伏,這是個(gè)不確定值。由于其輸入阻抗比較大,一般把這種模式用于標(biāo)準(zhǔn)的通訊協(xié)議,比如IIC、USART的等。該模式是STM32復(fù)位之后的默認(rèn)模式。
- 上拉輸入:上拉就是把電位拉高,比如拉到Vcc。上拉就是將不確定的信號通過一個(gè)電阻嵌位在高電平,電阻同時(shí)起限流作用,弱強(qiáng)只是上拉電阻的阻值不同,沒有什么嚴(yán)格區(qū)分。上拉輸入就是信號進(jìn)入芯片后加了一個(gè)上拉電阻,再經(jīng)過施密特觸發(fā)器轉(zhuǎn)換成0、1信號,讀取此時(shí)的引腳電平為高電平;
- 下拉輸入:就是把電壓拉低,拉到GND。與上拉原理相似。下拉輸入就是信號進(jìn)入芯片后加了一個(gè)下拉電阻,再經(jīng)過施密特觸發(fā)器轉(zhuǎn)換成0、1信號,讀取此時(shí)的引腳電平為低電平;
- 模擬輸入:信號進(jìn)入后不經(jīng)過上拉電阻或者下拉電阻,關(guān)閉施密特觸發(fā)器,經(jīng)由另一線路把電壓信號傳送到片上外設(shè)模塊。模擬輸入是指傳統(tǒng)方式的輸入,數(shù)字輸入是輸入PCM數(shù)字信號,即0、1的二進(jìn)制數(shù)字信號,通過數(shù)模轉(zhuǎn)換,轉(zhuǎn)換成模擬信號,經(jīng)前級放大進(jìn)入功率放大器,功率放大器還是模擬的。比如傳送給ADC模塊,由ADC采集電壓信號。所以可以理解為模擬輸入的信號是未經(jīng)處理的信號,是原汁原味的信號。
- 輸出模式
- 開漏輸出:一般用在電平不匹配的場合,如需要輸出5V的高電平。輸出端相當(dāng)于三極管的集電極,要得到高電平狀態(tài)需要上拉電阻才行。適合于做電流型的驅(qū)動,其吸收電流的能力相對強(qiáng)(一般20mA以內(nèi))。
- 復(fù)用開漏輸出:可以理解為GPIO口被用作第二功能時(shí)的配置情況(即并非作為通用IO口使用)。端口必須配置成復(fù)用開漏功能輸出模式。
- 推挽式輸出:可以輸出高、低電平,連接數(shù)字器件;推挽結(jié)構(gòu)一般是指兩個(gè)三極管分別受兩個(gè)互補(bǔ)信號的控制,總是在一個(gè)三極管導(dǎo)通的時(shí)候另一個(gè)截止。高低電平由IC的電源決定。推挽電路是兩個(gè)參數(shù)相同的三極管或MOSFET,以推挽方式存在于電路中,各負(fù)責(zé)正負(fù)半周的波形放大任務(wù),電路工作時(shí),兩只對稱的功率開關(guān)管每次只有一個(gè)導(dǎo)通,所以導(dǎo)通損耗小、效率高。輸出既可以向負(fù)載灌電流,也可以從負(fù)載抽取電流。推拉式輸出級既提高電路的負(fù)載能力,又提高開關(guān)速度。
- 推挽式復(fù)用輸出
1.2 I/O復(fù)用和重映射
1.2.1 I/O復(fù)用
STM32 有很多的內(nèi)置外設(shè),這些外設(shè)的外部引腳都是與 GPIO 復(fù)用的。也就是說,一個(gè) GPIO如果可以復(fù)用為內(nèi)置外設(shè)的功能引腳,那么當(dāng)這個(gè) GPIO 作為內(nèi)置外設(shè)使用的時(shí)候,就叫做復(fù)用。當(dāng)I/O端口被配置為復(fù)用功能時(shí):
●在開漏或推挽式配置中,輸出緩沖器被打開
●內(nèi)置外設(shè)的信號驅(qū)動輸出緩沖器(復(fù)用功能輸出)
●施密特觸發(fā)輸入被激活
●弱上拉和下拉電阻被禁止
●在每個(gè)APB2時(shí)鐘周期,出現(xiàn)在I/O腳上的數(shù)據(jù)被采樣到輸入數(shù)據(jù)寄存器
●開漏模式時(shí),讀輸入數(shù)據(jù)寄存器時(shí)可得到I/O口狀態(tài)
●在推挽模式時(shí),讀輸出數(shù)據(jù)寄存器時(shí)可得到最后一次寫的值
大家都知道, MCU 都有串口, STM32 有好幾個(gè)串口。比如說 STM32F103ZET6 有 5 個(gè)串口,我們可以查手冊知道,串口 1 的引腳對應(yīng)的 IO 為 PA9,PA10.PA9, PA10 默認(rèn)功能是 GPIO,所以當(dāng)PA9,PA10 引腳作為串口 1 的 TX,RX 引腳使用的時(shí)候,那就是端口復(fù)用。
USART1_TX | PA9 |
---|---|
USART1_RX | PA10 |
復(fù)用端口初始化有幾個(gè)步驟:
1) GPIO 端口時(shí)鐘使能。要使用到端口復(fù)用,當(dāng)然要使能端口的時(shí)鐘了。RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
2)復(fù)用的外設(shè)時(shí)鐘使能。比如你要將端口 PA9,PA10 復(fù)用為串口,所以要使能串口時(shí)鐘。RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
3)端口模式配置。
關(guān)于串口的內(nèi)容后面的章節(jié)會詳細(xì)講解。
1.2.2 I/O重映射
為了使不同器件封裝的外設(shè) IO 功能數(shù)量達(dá)到最優(yōu),可以把一些復(fù)用功能重新映射到其他一些引腳上。 STM32 中有很多內(nèi)置外設(shè)的輸入輸出引腳都具有重映射(remap)的功能。我們知道每個(gè)內(nèi)置外設(shè)都有若干個(gè)輸入輸出引腳,一般這些引腳的輸出端口都是固定不變的,為了讓設(shè)計(jì)工程師可以更好地安排引腳的走向和功能,在 STM32 中引入了外設(shè)引腳重映射的概念,即一個(gè)外設(shè)的引腳除了具有默認(rèn)的端口外,還可以通過設(shè)置重映射寄存器的方式,把這個(gè)外設(shè)的引腳映射到其它的端口。
復(fù)用功能 | USART1_REMAP = 0 | USART1_REMAP = 1 |
---|---|---|
USART1_TX | PA9 | PB6 |
USART1_RX | PA10 | PB7 |
從表中可以看出,默認(rèn)情況下,串口 1 復(fù)用的時(shí)候的引腳位 PA9、PA10,同時(shí)我們可以將 TX 和 RX 重新映射到管腳 PB6 和 PB7 上面去。所以重映射我們同樣要使能復(fù)用功能的時(shí)候講解的 2 個(gè)時(shí)鐘外,還要使能 AFIO 功能時(shí)鐘,然后要調(diào)用重映射函數(shù)。
詳細(xì)步驟為:
1)使能 GPIOB 時(shí)鐘:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
2)使能串口 1 時(shí)鐘:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
3)使能 AFIO 時(shí)鐘:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
4)開啟重映射:
GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
這樣就將串口的 TX 和 RX 重映射到管腳 PB6 和 PB7 上面了。
1.3 GPIO流水燈硬件電路分析
發(fā)光二極管是屬于二極管的一種,具有二級管單向?qū)щ娞匦裕粗挥性谡螂妷海ǘO管的正極接正,負(fù)極接負(fù))下才能導(dǎo)通發(fā)光。PB0引腳接發(fā)光二極管(LED1)的正極,所以PB0引腳輸出高電平LED1亮,PB0引腳輸出低電平LED1熄滅,,LED2,LED3同理。
值得注意的,不同的開發(fā)板,LED連接的GPIO一般是不同的,請注意修改。
1.4 GPIO流水燈寄存器分析
要想真正掌握一款單片機(jī),分析寄存器是必不可少,但是對于STM32來再說,ST已經(jīng)將寄存器操作封裝成庫函數(shù),開發(fā)者只需要調(diào)用庫函數(shù)即可,對于初學(xué)者來說,只需學(xué)會使用使用函數(shù)即可,筆者在《入門篇》也說了為何要來使用庫函數(shù)以及使用庫開發(fā)的好處,因此對于沒有基礎(chǔ)的讀者朋友就不必細(xì)究每個(gè)寄存器,當(dāng)學(xué)到一定程度,再來一探究竟吧,筆者再這里只是給出GPIO的寄存配置相關(guān)配置表,在后面的章節(jié)也是如此。好了,繼續(xù)進(jìn)入正題吧。
每個(gè)GPIO端口都有兩個(gè)32位配置寄存器(GPIOx_CRL ,GPIOx_CRH) ,兩個(gè)32位數(shù)據(jù)寄存器 (GPIOx_IDR和GPIOx_ODR),一個(gè)32位置位/ 復(fù)位寄存器(GPIOx_BSRR),一個(gè)16位復(fù)位寄存器(GPIOx_BRR),一個(gè)32位鎖定寄存器(GPIOx_LCKR)。每個(gè)I/O端口位可以自由編程,然而I/O端口寄存器必須按32位字被訪問(不允許半字或字節(jié)訪問) 。
點(diǎn)亮LED,基本步驟是:配置寄存器;控制寄存器。庫開發(fā)只是將傳統(tǒng)的配置方式編程函數(shù),是的單片機(jī)開發(fā)變得簡單方便快捷。
我們常用的 IO 端口寄存器只有 4 個(gè): CRL、 CRH、 IDR、 ODR。其中CRL 和 CRH 控制著每個(gè) IO 口的模式及輸出速率。
低配置寄存器 CRL 的描述,如下圖和下表所示。
CRL和CRH類似,在此就不在贅述了,讀者朋友可以參看《STM32F10XXX參考手冊》數(shù)據(jù)輸入輸出寄存器是將對應(yīng)的IO口置位,從而進(jìn)行數(shù)據(jù)的輸入與輸出。
1.5 GPIO 流水燈實(shí)現(xiàn)流程
筆者在上文已經(jīng)分析了GPIO的原理及操作步驟,現(xiàn)在我們就來寫代碼吧。本書是用庫來對STM32來開發(fā)的,這是本書的第一個(gè)實(shí)例,筆者為了讀者比較直接配置寄存和庫開發(fā)的區(qū)別,筆者在此用了兩種方式進(jìn)行開發(fā),希望讀者能自己體會兩種方式的優(yōu)劣。這里筆者會使用兩種庫:標(biāo)準(zhǔn)庫和HAL庫。
不管使用何種實(shí)現(xiàn)方式,其流程都是一樣的。
GPIO是開發(fā)STM32最基本的配置,所以掌握GPIO的配置顯得尤為重要。要實(shí)現(xiàn)流水燈,一般步驟可以總結(jié)為如下:
- GPIO 時(shí)鐘使能;
- GPIO 端口模式設(shè)置;
- 初始化IO口;
- 編寫處理函數(shù);
1.6GPIO 流水燈實(shí)現(xiàn)-直接操作寄存器方式
先看主函數(shù)。
/*Includes*********************************************************************/
#include"./LED/stm32f103_led.h"
/*延時(shí)函數(shù)*/
void Delay(u32 xms);
/**
* @brief 主函數(shù)
* @param None
* @retval int
*/
int main(void)
{
/*LED 初始化 */
LED_GPIO_Config();
while(1)
{
//ODR GPIOB
//15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
//0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (復(fù)位值)
//1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
GPIOB->ODR= 0XFFFE; //低電平,GPIOB0(LED1)燈滅
Delay(0x0FFFFF);
GPIOB->ODR= 0XFFFF; //高電平,GPIOB0(LED1)燈亮
//ODR GPIOG
//15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
//0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (復(fù)位值)
//1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1
GPIOG->ODR= 0XFFBF; //低電平,GPIOG6(LED2)燈滅
Delay(0x0FFFFF);
GPIOG->ODR= 0XFFFF; //高電平,GPIOG6(LED2)燈亮
//ODR GPIOG
//15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
//0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (復(fù)位值)
//1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1
GPIOG->ODR= 0XFF7F; //低電平,GPIOG7(LED3)燈滅
Delay(0x0FFFFF);
GPIOG->ODR= 0XFFFF; //高電平,GPIOG7(LED3)燈亮
}
}
/**
* @brief 延時(shí)函數(shù)
* @param
xms 延時(shí)長度
* @retval None
*/
void Delay( u32 xms)
{
//for(;nCount != 0; nCount--);(方法一)
while(xms--);//(方法二)
}
首先是GPIO初始化,然后就是無限循環(huán),不斷設(shè)置GPIO的高低電平。
GPIO初始化代碼如下:
/**
* @brief 初始化LED的GPIO
* @param None
* @retval None
*/
void LED_GPIO_Config(void)
{
/*開啟 GPIO 時(shí)鐘,使用外設(shè)時(shí)都要先開啟它的時(shí)鐘*/
RCC->APB2ENR|= 1 << 3; //使能PORTB時(shí)鐘
RCC->APB2ENR|= 1 <<8 ; //使能PORTG時(shí)鐘
/* 配置GPIO為通用推挽輸出模式,速率為50M */
GPIOB->CRL&= 0XFFFFFFF0;
GPIOB->CRL|= 0X00000003; //PB.0 推挽輸出
GPIOB->ODR|= 1 << 0; //PB.0 輸出高
GPIOG->CRL&= 0X00FFFFFF;
GPIOG->CRL|= 0X33000000; //PG.6, PG.7推挽輸出
GPIOG->ODR|= 3 << 6; //PG.6, PG.7輸出高
}
控制LED的GPIO主要做了以下事情:
1.開啟GPIO時(shí)鐘
2.設(shè)置GPIO的輸出模式和速率,具體寄存器配置請參看CRL寄存器。
3.最后設(shè)置GPIO為高電平(非必須)。
最后在主函數(shù)不斷設(shè)置GPIO的高低電平即可實(shí)現(xiàn)流水燈。
1.7 GPIO 流水燈實(shí)現(xiàn)-標(biāo)準(zhǔn)庫
1.7.1 GPIO庫函數(shù)
GPIO庫函數(shù)相關(guān)的庫函數(shù)如下:
- GPIO_DeInit 將外設(shè) GPIOx 寄存器重設(shè)為缺省值;
- GPIO_AFIODeInit 將復(fù)用功能(重映射事件控制和 EXTI 設(shè)置)重設(shè)為缺省值;
- GPIO_Init 根據(jù) GPIO_InitStruct 中指定的參數(shù)初始化外設(shè) GPIOx 寄存器;
- GPIO_StructInit 把 GPIO_InitStruct 中的每一個(gè)參數(shù)按缺省值填入;
- GPIO_ReadInputDataBit讀取指定端口管腳的輸入;
- GPIO_ReadInputData讀取指定的 GPIO 端口輸入;
- GPIO_ReadOutputDataBit讀取指定端口管腳的輸出;
- GPIO_ReadOutputData讀取指定的 GPIO 端口輸出;
- GPIO_SetBits 設(shè)置指定的數(shù)據(jù)端口位;
- GPIO_ResetBits 清除指定的數(shù)據(jù)端口位;
- GPIO_WriteBit 設(shè)置或者清除指定的數(shù)據(jù)端口位;
- GPIO_Write 向指定 GPIO 數(shù)據(jù)端口寫入數(shù)據(jù);
- GPIO_PinLockConfig鎖定 GPIO 管腳設(shè)置寄存器;
- GPIO_EventOutputConfig選擇 GPIO 管腳用作事件輸出;
- GPIO_EventOutputCmd使能或者失能事件輸出;
- GPIO_PinRemapConfig改變指定管腳的映射;
- GPIO_EXTILineConfig選擇 GPIO 管腳用作外部中斷線路;
1.7.2使用固件庫方式
核心代碼如下:
/*Includes*********************************************************************/
#include"./LED/stm32f103_led.h"
/*簡單延時(shí)函數(shù)*/
void Delay(u32 xms);
/**
* @brief 主函數(shù)
* @param None
* @retval
*/
int main(void)
{
/*LED 初始化 */
LED_GPIO_Config();
while(1)
{
LED1(ON ); // 亮
Delay(0xfFfff);
LED1(OFF ); // 滅
LED2(ON ); // 亮
Delay(0xfFfff);
LED2(OFF ); // 滅
LED3(ON ); // 亮
Delay(0xffFff);
LED3(OFF ); // 滅
}
}
/**
* @brief 延時(shí)函數(shù)
* @param
xms 延時(shí)長度
* @retval None
*/
void Delay( u32 xms)
{
//for(;nCount != 0; nCount--);(方法一)
while(xms--);//(方法二)
}
使用庫函數(shù)的流程和寄存器版的一樣,唯一不同的是這里使用了庫函數(shù),而庫函數(shù)也是對寄存器的封裝與抽象。
1.8 GPIO 流水燈實(shí)現(xiàn)-HAL庫
1.8.1 STM32Cube新建工程
關(guān)于如何使用STM32Cube新建工程在前文已經(jīng)講解過了,這里直說配置GPIO部分內(nèi)容。本文要實(shí)現(xiàn)流水燈,其實(shí)輸出為初始化設(shè)置為高電平還是低電平都可以,因?yàn)榱魉疅粜枰粩喾崔D(zhuǎn)。
1.首先進(jìn)行系統(tǒng)配置,debug模式選擇可以選擇其他模式,默認(rèn)選擇No debug,Timebase Source選擇SysTick,關(guān)于Timebase Source在后文會詳細(xì)講解。
2.時(shí)鐘配置,外部高速時(shí)鐘。
配置系統(tǒng)時(shí)鐘配置72MHz,APB1總線配置36MHz。默認(rèn)高速時(shí)鐘是使用內(nèi)部(HSI),而且CPU時(shí)鐘配置的比較低。以我選擇的STM32F103,外部8M晶振為例(如下圖)。
3.GPIO初始化配置
我們將PB0、PG6、PG7配置輸出模式(高電平、低電平均可)、輸出速率、上/下拉等,默認(rèn)即可。
4.工程生成設(shè)置
其他默認(rèn)即可。最后點(diǎn)擊“生成代碼”即可生成代碼。
1.8.2 GPIO流水燈實(shí)現(xiàn)
我們要實(shí)現(xiàn)流水燈,需要在主函數(shù)添加一下代碼。
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_7);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_7);
1.8.3流水燈代碼分析
在分析代碼前,先看看流水燈編程流程:
1>使能GPIO端口時(shí)鐘;
2>初始化GPIO引腳,即為GPIO初始化結(jié)構(gòu)體賦值,并調(diào)用相應(yīng)初始化函數(shù)完成初始化配置;
3>根據(jù)實(shí)際需求控制流水燈。
STM32采用固件庫開發(fā),代碼比較多,因此,我們在看一個(gè)實(shí)際工程時(shí),只需從主函數(shù)開始,好了,接下來,筆者就帶領(lǐng)大家一步一步看看流水燈是怎么實(shí)現(xiàn)的。筆者先貼出主函數(shù)代碼。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCUConfiguration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and theSystick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_6);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_6);
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_7);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_7);
}
/* USER CODE END 3 */
}
注釋部分不用管,我們只看代碼。
1. 復(fù)位所有硬件
HAL_Init()函數(shù)為復(fù)位所有硬件,這個(gè)就不細(xì)說了。
HAL_StatusTypeDef HAL_Init(void)
{
/* Configure Flash prefetch */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) ||defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \\
defined(STM32F102x6) ||defined(STM32F102xB) || \\
defined(STM32F103x6) ||defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \\
defined(STM32F105xC) ||defined(STM32F107xC)
/* Prefetch buffer is not available on value line devices */
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* Use systick as time base source and configure 1ms tick (default clockafter Reset is HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
/* Init the low level hardware */
HAL_MspInit();
/* Return function status */
return HAL_OK;
}
2. 時(shí)鐘初始化函數(shù)
SystemClock_Config()函數(shù)用于時(shí)鐘初始化,也就是通過STM32Cube實(shí)現(xiàn)。
時(shí)鐘初始化函數(shù)如下:
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specifiedparameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) !=HAL_OK)
{
Error_Handler();
}
}
RCC_OscInitTypeDef結(jié)構(gòu)體類型定義時(shí)鐘來源和系統(tǒng)時(shí)鐘生成配置,就是圖9中的配置,本例程使用外部8MHz晶振,通過PLL鎖相環(huán)9倍頻后得到72M系統(tǒng)時(shí)鐘。
RCC_ClkInitTypeDef結(jié)構(gòu)體類型定義總線時(shí)鐘配置,一般選擇使能系統(tǒng)時(shí)鐘、AHB、APB1和APB2總線時(shí)鐘,其中只有APB1總線時(shí)鐘36MHz,其他都為72MHz。
HAL_RCC_ClockConfig函數(shù)就是HAL定義的一個(gè)系統(tǒng)滴答定時(shí)器初始化配置函數(shù),通過這個(gè)函數(shù)得到延時(shí)效果。
3. GPIO初始化函數(shù)
MX_GPIO_Init()函數(shù)用于LED的GPIO初始化,代碼如下:
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin : PB0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pins : PG6 PG7 */
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
}
以上就是LED的初始化函數(shù),初始化GPIO有個(gè)重要的結(jié)構(gòu)體GPIO_InitTypeDef,先看看該結(jié)構(gòu)體。
typedef struct
{
uint32_t Pin; /*!
- Pin:引腳編號選擇,一個(gè)GPIO有16個(gè)引腳可選,參數(shù)可選有:GPIO_PIN_0、….、GPIO_PIN_15和GPIO_PIN_ALL。很多時(shí)候我們可以使用或運(yùn)算進(jìn)行選擇:GPIO_PIN_0| GPIO_PIN_4。
- Mode:引腳模式選擇,在前文筆者已經(jīng)介紹了引腳有8中工作模式,根據(jù)外設(shè)選擇相應(yīng)模式。
- Pull:上下拉,用于輸入模式,可選:GPIO_NOPULL不上下拉;GPIO_PULLUP:使能下拉;GPIO_PULLDOWN使能下拉;
- Speed:引腳速度,可選:GPIO_SPEED_FREQ_LOW:低速(2MHz);中速(10MHz);高速(50MHz)。
在初始化代碼中,還有一個(gè)重要的函數(shù)HAL_GPIO_WritePin()。HAL_GPIO_WritePin()函數(shù)為3個(gè)LED燈時(shí)鐘初始化狀態(tài),這里設(shè)置為低電平,所以初始化狀態(tài)3個(gè)LED都是暗的。
4. 流水燈實(shí)現(xiàn)
在前文已經(jīng)貼出了流水燈的相應(yīng)代碼。
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_7);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_7);
}
HAL_GPIO_TogglePin函數(shù)用于GPIO電平反轉(zhuǎn),HAL_Delay用于延時(shí),單位是毫秒,以此不斷將LED1、LED2、LED3關(guān)滅,這就是實(shí)現(xiàn)流水燈的效果。
1.9實(shí)驗(yàn)現(xiàn)象
將編譯好的程序下載到板子中,可以看到三個(gè)LED燈不同地閃爍。
本章雖然用不同的方式實(shí)現(xiàn)了流水燈,也就是不斷控制GPIO的高低電平,我想大家會有很多疑問,寄存器、表混庫、HAL的區(qū)別是什么,流水燈是怎么一步步調(diào)用等等,后面的章節(jié)會一一解答,盡請期待!
小貼士:上拉電阻
1.當(dāng) TTL 電路驅(qū)動 CMOS 電路時(shí),如果 TTL 電路輸出的高電平低于 CMOS電路的最低高電平(一般為 3.5V),這時(shí)就需要在 TTL 的輸出端接上拉電阻,以提高輸出高電平的值。
2.OC(集電極開路)門電路必須加上拉電阻,才能使用。
3.為加大輸出引腳的驅(qū)動能力,有的單片機(jī)管腳上也常使用上拉電阻。
4.在 CMOS 芯片上,為了防止靜電造成損壞,不用的管腳不能懸空,一般接上拉電阻產(chǎn)生降低輸入阻抗,提供泄荷通路。
5.芯片的管腳加上拉電阻來提高輸出電平,從而提高芯片輸入信號的噪聲容限增強(qiáng)抗干擾能力。
6.提高總線的抗電磁干擾能力。管腳懸空就比較容易接受外界的電磁干擾。
7.長線傳輸中電阻不匹配容易引起反射波干擾,加上下拉電阻是電阻匹配,有效的抑制反射波干擾。
- 上拉電阻阻值的選擇原則包括:
- 從節(jié)約功耗及芯片的灌電流能力考慮應(yīng)當(dāng)足夠大;電阻大,電流小。
- 從確保足夠的驅(qū)動電流考慮應(yīng)當(dāng)足夠小;電阻小,電流大。
- 對于高速電路,過大的上拉電阻可能邊沿變平緩。綜合考慮
以上三點(diǎn),通常在 1k 到 10k 之間選取。對下拉電阻也有類似道理對上拉電阻和下拉電阻的選擇應(yīng)結(jié)合開關(guān)管特性和下級電路的輸入特性進(jìn)行設(shè)定,主要需要考慮以下幾個(gè)因素:
- 驅(qū)動能力與功耗的平衡。以上拉電阻為例,一般地說,上拉電阻越小,驅(qū)動能力越強(qiáng),但功耗越大,設(shè)計(jì)是應(yīng)注意兩者之間的均衡。
- 下級電路的驅(qū)動需求。同樣以上拉電阻為例,當(dāng)輸出高電平時(shí),開關(guān)管斷開,上拉電阻應(yīng)適當(dāng)選擇以能夠向下級電路提供足夠的電流。
- 高低電平的設(shè)定。不同電路的高低電平的門檻電平會有不同,電阻應(yīng)適當(dāng)設(shè)定以確保能輸出正確的電平。以上拉電阻為例,當(dāng)輸出低電平時(shí),開關(guān)管導(dǎo)通,上拉電阻和開關(guān)管導(dǎo)通電阻分壓值應(yīng)確保在零電平門檻之下。
- 頻率特性。以上拉電阻為例,上拉電阻和開關(guān)管漏源級之間的電容和下級電路之間的輸入電容會形成 RC 延遲,電阻越大,延遲越大。上拉電阻的設(shè)定應(yīng)考慮電路在這方面的需求。
- 下拉電阻的設(shè)定的原則和上拉電阻是一樣的
-
單片機(jī)
+關(guān)注
關(guān)注
6030文章
44499瀏覽量
632174 -
ARM
+關(guān)注
關(guān)注
134文章
9030瀏覽量
366539 -
STM32
+關(guān)注
關(guān)注
2264文章
10858瀏覽量
354391 -
流水燈
+關(guān)注
關(guān)注
21文章
432瀏覽量
59612 -
GPIO
+關(guān)注
關(guān)注
16文章
1189瀏覽量
51847
發(fā)布評論請先 登錄
相關(guān)推薦
評論