stm32f407按鍵檢測庫函式版
開始之前呢先祝大家聖誕節快樂,同時參加明天的研究生考試的同學們一戰成"碩",接下來我就直奔主題了。
今天我要通過庫函式操作stm32f407上的按鍵實現控制LED小燈以及蜂鳴器,實現的功能如下:
- KEY0鍵控制LED0的亮滅
- KEY1鍵控制LED1的亮滅
- KEY2鍵同時控制控制LED0和LED1的亮滅轉換
- WK_UP鍵控制蜂鳴器
這篇文章同時會涉及到LED和蜂鳴器,相當於是對前兩次的學習進行一個複習,可能在前兩篇文章中沒有考慮到的細節今天都會盡量考慮進去,希望這篇文章可以更好的幫助到大家對GPIO的理解,廢話不多說,下面正式開始。
LED的初始化配置
想要點亮LED,首先我們需要確定LED在stm32f407開發板上的硬體電路連線,如下圖所示:
從圖上可以看出,兩個LED屬於共陽極連線,也就是說,當GPIO口輸出高電平時,LED熄滅,當GPIO口輸出低電平時,LED點亮,那麼只需要配置好GPIO的輸出,即可實現LED的亮滅,所以,我們還得知道控制LED的GPIO口是哪一個,畢竟stm32f4有70個用於控制LED的GPIO口;
我們可以從從上圖中看到,控制LED0的是GPIOF_9,控制LED1的是GPIOF_10,同時我們還可以看到GPIOF_8是用來控制蜂鳴器(BEEP)的。知道了LED的硬體連線後,接下來就可以開始通過庫函式配置我們的GPIO了。
通過庫函式來配置GPIO,我們需要用到的庫函式是GPIO_Init,對於這個函式,有如下的說明:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
從GPIO_Init的函式原型可以看出,需要用到兩個引數,這兩個引數分別是:
- GPIO_TypeDef* GPIOx:用來指明配置的GPIO是7組GPIO中的哪一組,這裡控制LED用到的是GPIOF,所以該引數只需填入巨集定義GPIOF即可
- GPIO_InitTypeDef* GPIO_InitStruct:從引數名字就可以知道這個引數是一個結構體的地址,所以在這裡為了更好的說明,我們自定義一個結構體:
GPIO_InitTypeDef led_gpio;
該結構體的成員呢有以下幾個:
uint32_t GPIO_Pin; //該引數對應的是某組GPIO中的某一個,畢竟一組GPIO有16個GPIO引腳
//就如前面說到控制LED0的GPIO口是GPIOF_9;控制LED1的GPIO口是GPIOF_10;
/*下面這幾個引數我就放到正文裡細說吧*/
GPIOMode_TypeDef GPIO_Mode;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOOType_TypeDef GPIO_OType;
GPIOPuPd_TypeDef GPIO_PuPd;
GPIO_InitStruct的後面4個引數就是用於配置GPIO模式暫存器的,我在第一篇文章裡說到,配置GPIO的模式需要用到4個暫存器,如果你沒有看過我的第一篇文章的話,沒有關係,我再來詳細的說明一下:這裡說到的暫存器分別是:
- GPIOx_MODER:GPIO埠模式暫存器
- GPIOx_OSPEEDR:GPIO埠輸出速度暫存器
- GPIOx_OTYPER:GPIO埠輸出型別暫存器
- GPIOx_PUPDR:GPIO埠上拉/下拉暫存器
分別對應著GPIO_InitStruct的後四個成員
下面我就來具體說說要控制LED,這4個GPIOF分別需要怎麼配置:
>GPIOF_MODER
GPIOF_MODER暫存器可選的配置分別有:輸入模式(復位狀態),通用輸出模式,複用功能模式,模擬模式,這裡很容易想到,我們要向控制LED0,需要通過GPIOF_9輸出高電平或低電平,所以毫無疑問我們需要將GPIOF_MODER暫存器配置成通用輸出模式,對於該暫存器的四種模式在庫函式中有如下的說明:
GPIO_Mode_IN = 0x00, /*輸入模式*/
GPIO_Mode_OUT = 0x01, /*通用輸出模式*/
GPIO_Mode_AF = 0x02, /*複用功能模式*/
GPIO_Mode_AN = 0x03 /*模擬模式*/
在此我們只需選擇GPIO_Mode_OUT即可,一句話程式碼如下:
led_gpio.GPIO_Mode = GPIO_Mode_OUT;
>GPIOF_OSPEEDR
GPIOF_OSPEEDR暫存器可選的配置分別有:2MHz,25MHz,50MHz,100MHz,在此我們選擇100MHz的埠輸出速度(對於速度的選擇我也不太明白會有啥影響,或者說選擇的標準是啥,希望明白的大佬可以在評論區裡解答下,非常感謝),對於這4中選擇在庫函式中有如下定義:
#define GPIO_Speed_2MHz GPIO_Low_Speed
#define GPIO_Speed_25MHz GPIO_Medium_Speed
#define GPIO_Speed_50MHz GPIO_Fast_Speed
#define GPIO_Speed_100MHz GPIO_High_Speed
所以一句話程式碼如下:
led_gpio.GPIO_Speed = GPIO_Speed_100MHz;
>GPIOx_OTYPER
GPIOF_OSPEEDR暫存器可選的配置分別有:推輓輸出,開漏輸出,在此我們選擇推輓輸出模式(同樣不明白這兩種模式的區別以及選擇標準,希望有大佬可以在評論區解答下,感謝),對於這兩種輸出模式在庫函式中有如下的定義:
typedef enum
{
GPIO_OType_PP = 0x00, //推輓輸出
GPIO_OType_OD = 0x01 //開漏輸出
}GPIOOType_TypeDef;
所以一句話程式碼如下:
led_gpio.GPIO_OType = GPIO_OType_PP;
>GPIOx_PUPDR
GPIOF_OSPEEDR暫存器可選的配置分別有:浮空,上拉,下拉,GPIOF_OSPEEDR的配置就比較自由,在此我選擇上拉,以確保開發板上電時是熄滅的,對於這3種模式在庫函式中有如下定義:
GPIO_PuPd_NOPULL = 0x00,
GPIO_PuPd_UP = 0x01,
GPIO_PuPd_DOWN = 0x02
所以一句話程式碼如下
led_gpio.GPIO_PuPd = GPIO_PuPd_UP;
到此,LED的GPIO配置就完成了,注意LED0和LED1配置的GPIO_pin是有區別的,同時不要忘了在呼叫GPIO_Init之前,一定要先使能GPIOF的外設時鐘,這裡需要用到的函式為:
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)
對於該函式的第1個引數用來指明初始化的是哪一個外設時鐘,有如下定義(只擷取了一段):
#define RCC_AHB1Periph_GPIOA ((uint32_t)0x00000001)
#define RCC_AHB1Periph_GPIOB ((uint32_t)0x00000002)
#define RCC_AHB1Periph_GPIOC ((uint32_t)0x00000004)
#define RCC_AHB1Periph_GPIOD ((uint32_t)0x00000008)
#define RCC_AHB1Periph_GPIOE ((uint32_t)0x00000010)
#define RCC_AHB1Periph_GPIOF ((uint32_t)0x00000020)
#define RCC_AHB1Periph_GPIOG ((uint32_t)0x00000040)
在此我們選擇RCC_AHB1Periph_GPIOF即可,對於第2個引數是用於說明是否要初始化的,具體的定義如下:
#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
很明顯我們要選擇ENABLE
那麼到這裡第一個階段的工作就完成了,我們可以將以上的程式碼封裝成一個LED初始化的.c檔案,只需要在main函式中呼叫即可,led_init.c程式碼如下:
void led_init(void){
GPIO_InitTypeDef led_gpio; //定義的GPIO模式初始化結構體
/*使能GPIOF外設時鐘*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
/*配置控制LED的GPIOF_9和GPIOF_10*/
led_gpio.GPIO_Pin = GPIO_Pin_9||GPIO_Pin_10;//採用或運算可同時對LED1和LED0進行初始化
led_gpio.GPIO_Mode = GPIO_Mode_OUT;
led_gpio.GPIO_Speed = GPIO_Speed_100MHz;
led_gpio.GPIO_OType = GPIO_OType_PP;
led_gpio.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF,&led_gpio);
}
BEEP(蜂鳴器)的初始化配置
在瞭解了GPIO的配置過程後,後續的共組就容易的多了。同樣的,想要配置好BEEP,首先我們需要先知道BEEP的硬體是怎樣連線的,從而知道應該對哪一個GPIO口進行配置,從前面的LED的硬體連線圖上可以找的BEEP連線的是GPIOF_8,那麼下面我直接上程式碼,詳細的內容見程式碼的註釋,畢竟今天的重點是按鍵:
void beep_init(){
GPIO_InitTypeDef beep_gpio; //定義的GPIO模式初始化結構體
/*使能GPIOF外設時鐘*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
/*配置控制BEEP的GPIOF_8*/
beep_gpio.GPIO_Mode = GPIO_Mode_OUT; //配置GPIO模式為通用輸出模式
beep_gpio.GPIO_OType = GPIO_OType_PP; //配置GPIO輸出模式為推輓輸出
beep_gpio.GPIO_Pin = GPIO_Pin_8; //選擇配置8號引腳
beep_gpio.GPIO_PuPd = GPIO_PuPd_UP; //配置GPIO埠為上拉模式
beep_gpio.GPIO_Speed = GPIO_Speed_100MHz; //配置GPIO輸出速度為100MHz
GPIO_Init(GPIOF,&beep_gpio); //呼叫GPIO初始化函式
}
KEY的初始化配置與掃描
KEY的初始化配置
首先逃不掉的還是硬體連線圖:
從圖中可以看出,對於按鍵KEY0,KEY1,KEY2,當它們連線到的GPIO埠為上拉模式時,如按鍵按下,此時GPIO口讀到低電平,若按鍵沒有按下,則GPIO口讀到高電平,二對於KEY_UP來說就正好相反,當它對應的GPIO口為下拉模式時,沒有按鍵按下,GPIO口讀到第電平,按鍵按下則讀到高點配,也就意味著我們對KEY_num和KEY_UP的配置是不一樣的,同時,我們現在需要的不再是GPIO的通用輸出模式,而是輸入模式了,而且此處我們不需要再配置GPIO的輸出模式(即推輓輸出或者開漏輸出),有了這樣的認識後,我麼們再來確定需要配置哪寫GPIO口吧:
由上圖可以知道,對於KEY0,KEY1,KEY2需要配置的GPIO分別是GPIOE_4,GPIOE_3,GPIOE_2,而KEY_UP配置的GPIO是GPIOA_0。在此,模仿LED與BEEP的配置過程可以有一下程式碼:
void key_init(void){
GPIO_InitTypeDef key_gpio; //定義GPIO初始化的結構體
/*使能GPIOE和GPIOA的外設時鐘*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOA,ENABLE);
/*配置GPIOE_4 & GPIOE_3 & GPIOE_2*/
key_gpio.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_3|GPIO_Pin_2;//採用或運算可同時對KEY0,KEY1和KEY2進行初始化
key_gpio.GPIO_Mode = GPIO_Mode_IN; //配置GPIO模式為輸入模式
key_gpio.GPIO_Speed = GPIO_Speed_100MHz;//配置GPIO輸出速度為100MHz
key_gpio.GPIO_PuPd = GPIO_PuPd_UP; //配置GPIO埠為上拉模式
GPIO_Init(GPIOE,&key_gpio); //呼叫GPIO初始化函式
/*ÅäÖÃGPIOA_0*/
key_gpio.GPIO_Pin = GPIO_Pin_0; //對KEY_UP進行初始化
key_gpio.GPIO_PuPd = GPIO_PuPd_DOWN; //配置GPIO埠為下拉模式
GPIO_Init(GPIOA,&key_gpio); //呼叫GPIO初始化函式
}
KEY的掃描
想要按下不同的按鍵實現不同的功能,就需要知道按下的鍵是哪一個,然後通過按下的按鍵控制相應的裝置,就需要迴圈掃描按鍵以確定鍵值,下面使用虛擬碼說明一下按鍵掃描的過程:
//key_scan.c
void key_scan{
if(有按鍵按下){
延時10ms消抖;
if(確實有按鍵按下)
return 鍵值
else return 無效值;
}
}
//main.c
int main(){
while(1){
int key = key_scan();
switch(key){
case 1 : 點亮LED;break;
case 2 : 熄滅LED;break;
...
}
延時10ms
}
}
這樣便實現了通過按鍵控制不同裝置了,但是存在一個問題,當我按下某一個按鍵不放時,key_scan函式永遠檢測到有按鍵按下並且返回該鍵鍵值,再main函式中就會一直點亮或熄滅LED,如何避免呢,在此可以很巧妙的運用static定義一個靜態的變數作為按鍵按下與否的標誌位(簡單來說就是儲存一下上一次按鍵的結果),直接上虛擬碼解釋(只需再原來的按鍵掃描的基礎上修改即可)
//key_scan.c
void key_scan{
static KEY_STATE = 1;
if(KEY_STATE = 1 && 有按鍵按下){
延時10ms消抖;
KEY_STATE = 0 //按鍵有效從而更新狀態
if(確實有按鍵按下)
return 鍵值
else if(沒有按鍵按下) KEY_STATE= 1;//重新更新狀態
}
return 0;
}
可能不好理解,可以試著多迴圈幾次應該就會明朗許多,那麼具體的按鍵掃描函式如下所示:
u8 key_scan(u8 mode){
static u8 key_state = 1;
if(mode) key_state = 1;
if(key_state && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || WKUP == 1)){
delay_ms(10);
key_state = 0;
if(KEY0 == 0) return 1;
else if(KEY1 == 0) return 2;
else if(KEY2 == 0) return 3;
else if(WKUP == 1) return 4;
}else if(KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && WKUP == 0)
key_state = 1;
return 0;
}
在此需要說明的是:判斷是否有按鍵按下,我們需要呼叫函式實現
GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)
在以上的按鍵掃描函式中KEY0 == 0這樣的表示即為GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4),是在標頭檔案中進行了巨集定義而已,具體如下:
//key.h
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)
#define WKUP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
分別對應判讀KEY0,KEY1,KEY2以及KEY_UP是否按下。
至此所有的流程都已搞定,接下啦就是main函式的類容了:
int main(void){
u8 key;
delay_init(168);
led_init();
beep_init();
key_init();
LED0 = 0;
LED1 = 0;
while(1){
key = key_scan(0);
if(key){
switch(key){
case LED0_PRES: LED0 = !LED0; break;
case LED1_PRES: LED1 = !LED1; break;
case BEEP_PRES: BEEP = !BEEP; break;
case CHAG_PRES: {
LED0 = !LED0;
LED1 = !LED1;
}
break;
}
}else delay_ms(10);
}
}
同樣這裡需要注意的是對於LED和蜂鳴器的操作我採用了位操作的方式,位操作的相關內容可以參考我上一篇的文章,這裡也是採用的巨集定義方式,具體如下:
//led.h
#define LED0 PFout(9)
#define LED1 PFout(10)
//beep.h
#define BEEP PFout(8)
總結一下:經常會在CSDN上查詢大佬的文章來學習,但沒想過寫一篇文章這麼惱火,花了我兩個半小時才搞定,太難了,不過一定要堅持呀!!!
相關文章
- 檔案檢測函式 feof, ferror,clearerr C庫函式函式Error
- 按鍵多次檢測程式註釋
- STM32F407如何點亮一個呼吸燈(庫函式)函式
- 按鍵的檢測與去抖動
- 人臉檢測的harr檢測函式函式
- 特權FPGA 按鍵消抖檢測程式碼FPGA
- C++檢測異常assert()函式C++函式
- asp.net點選鍵盤enter鍵,呼叫按鈕點選事件函式ASP.NET事件函式
- VS安裝外掛,按CTRL+滑鼠左鍵進入函式函式
- 檢測真實的檔案型別函式型別函式
- 在檢視類裡新增左鍵響應函式OnLButtonDown函式
- android按鍵模擬測試Android
- opencv關鍵點檢測OpenCV
- PHP 函式庫 1 - 函式庫的分類PHP函式
- curl_easy_setopt-curl庫的關鍵函式之一函式
- 使用Instrument中的time profiler檢測耗時函式函式
- C# 檢測真實的檔案型別函式C#型別函式
- 預測:函式式未來與資料庫磨合,sql消失函式資料庫SQL
- 【6】Django檢視函式Django函式
- 前端函式式功能庫前端函式
- 資料庫主鍵、從鍵(易懂版)資料庫
- Mac按鍵Mac
- 檢測softice(delphi版),兼回答以前一位朋友函式createfile使用的提問 (1千字)函式
- iOS 人臉關鍵點檢測iOS
- fgetpos() - C庫函式函式
- PHP函式庫(other)PHP函式
- C 庫函式 - strstr()函式
- ysyx: 完善庫函式函式
- 三行程式碼按鍵消抖 獨立按鍵 矩陣按鍵 長按 短按 雙擊行程矩陣
- 小體積封裝單按鍵觸控檢測晶片VKD233HH,低功耗單鍵觸控晶片封裝晶片
- set容器幾個關鍵函式函式
- 函式的關鍵字引數函式
- REVERSE關鍵字之REVERSE函式函式
- SSD 目標檢測 Keras 版Keras
- 核心函式 系統呼叫 系統命令 庫函式函式
- tmux常用按鍵UX
- postgresql中怎麼檢視函式SQL函式
- 自編函式做t檢驗函式