我在高職教STM32——GPIO入門之按鍵輸入(1)

????????大家好,我是老耿,高職青椒一枚,一直從事單片機、嵌入式、物聯網等課程的教學。對于高職的學生層次,同行應該都懂的,老師在課堂上教學幾乎是沒什么成就感的。正因如此,才有了借助 CSDN 平臺尋求認同感和成就感的想法。在這里,我準備陸續把自己花了很多心思的教學設計分享出來,主要面向廣大師生朋友,單片機老鳥就略過吧。歡迎點贊+關注,各位的支持是本人持續輸出的動力,多謝多謝!

????????前面,我們介紹了STM32的IO口作為輸出的使用,這一章,我們將向大家介紹如何使用IO口作為輸入。在本章中,我們將利用開發板上的按鍵來控制LED的亮滅。通過本章的學習,我們將明白按鍵的電路原理,了解按鍵消抖是怎么回事,鞏固GPIO的初始化配置,學習GPIO端口輸入函數等知識。

【學習目標】

  1. 了解按鍵防抖、鎖存的方法
  2. 鞏固GPIO初始化的過程,獨立完成代碼編寫
  3. 理解按鍵單擊、雙擊、長按的程序算法

????????按鍵是初學嵌入式的第一類輸入器件,入門不難,但是一旦按法多樣化(單擊/雙擊/長按),或是結合其他被控器件,就需要用上中斷、定時器、狀態機等知識,難度也就上來了。本章還是基于GPIO輸入電平的傳統方法來按鍵,計劃分兩個部分,本文是第一部分。

一、認識按鍵開關

1.1 按鍵開關的形態和結構

????????按鍵開關主要是指輕觸式按鍵開關,廣泛用于各種電器和電子消費品中,有各種各樣不同的形態,如圖1所示,用圈標注的是我們開發板使用的類型。不管何種形態,它都是一種電子開關,使用時以滿足操作力的條件向開關操作方向施壓,開關可以閉合接通,當撤銷壓力時開關即斷開,其內部結構是靠金屬彈片受力變化來實現通斷的。

圖1 不同形態的按鍵開關

????????如圖2所示,我們的開發板上一共有4個按鍵開關,用KEYx來表示。這種開關雖然有四個引腳,但我們來看圖3,內部①腳和②腳是常閉的,③腳和④腳也是常閉的,這樣其實相當于只有兩個引腳了。

圖2 開發板上的四個按鍵
圖3 四腳按鍵開關的內部結構示意

1.2 按鍵的抖動和消抖

????????按鍵機械觸點斷開或閉合時,由于觸點的彈性作用,按鍵開關不會馬上穩定接通或一下子斷開,使用按鍵時會產生帶波紋信號,如圖4所示,所以需要對波紋信號進行消抖處理,否則可能會引起誤判。

圖4 按鍵接通或斷開瞬間的抖動紋波

?

????????顯然上圖中的紋波是我們不希望的,因此就需要想辦法消抖。消抖的辦法有兩種:硬件消抖和軟件消抖,硬件消抖是在按鍵兩端并聯一個0.1uF的電容,利用電容充放電的延時,消除波紋。硬件消抖很少采用,更普遍的做法是通過軟件消抖,通過延時10~20ms的方式避開抖動,也可以采用連續多次檢測電平的方式避開抖動。

二、按鍵控制LED編程實踐

2.1 任務描述

????????本實驗的任務是用一個按鍵實現對一個LED的控制,每按一次,LED的狀態就改變一次。在控制方式上,使用了無鎖存和有鎖存兩種方法,分別實現了按下有效和松開有效。

2.2 硬件電路

????????圖5是開發板上四個按鍵與STM32連接的電路原理圖,連接方式都是一樣的,這里就以SW1(KEY1)為例進行介紹。當SW1按下時,KEY1端(PC13)與GND接通,為低電平;當SW1松開時,KEY1端電平被上拉電阻R48拉高。這樣,我們通過檢測PC13的輸入電平就知道KEY1是按下還是松開了。同理,KEY2、KEY3、KEY4的狀態需要分別檢測PC11、PC12、PD2的輸入電平。

圖5 四個按鍵的原理圖

2.3 工程文件清單

????????本實驗的工程文件清單如圖6所示,在HARDWARE目錄中添加了一對按鍵的驅動文件 key.ckey.h。由于用到了LED,因此之前的 led.cled.h 需要保留。

圖6 按鍵工程文件清單

2.4 工程代碼剖析

1. key.h文件源碼

????????頭文件里依然是與IO有關的宏和函數聲明,如代碼清單1所示。

?

//-------------------------------------------------------
// 代碼清單1:key.h
//-------------------------------------------------------#ifndef  _KEY_H_
#define  _KEY_H_#include "stm32f10x.h"//-------------------------------------------------------
// 必要的宏定義
//-------------------------------------------------------
#define  KEY1_PIN     GPIO_Pin_13
#define  KEY2_PIN     GPIO_Pin_11
#define  KEY3_PIN     GPIO_Pin_12
#define  KEY4_PIN     GPIO_Pin_2//-------------------------------------------------------
// 庫函數操作宏定義
//-------------------------------------------------------
#define  READ_KEY1	GPIO_ReadInputDataBit(GPIOC, KEY1_PIN)
#define  READ_KEY2	GPIO_ReadInputDataBit(GPIOC, KEY2_PIN)
#define  READ_KEY3	GPIO_ReadInputDataBit(GPIOC, KEY3_PIN)
#define  READ_KEY4	GPIO_ReadInputDataBit(GPIOD, KEY4_PIN)//--------------------------------------------------------
// 函數聲明
//--------------------------------------------------------
void Key_Init(void);	//按鍵初始化函數#endif

????????這里,我們用 GPIO_ReadInputDataBit() 這個庫函數來讀取一個IO口的電平,函數名雖然長了點,但確實見名就能知意。它有兩個參數:GPIOx和GPIO_Pin_x,返回值就是讀到的電平(1或0),確實也很直觀。

2. key.c文件源碼

????????該文件里就一個函數 Key_Init(),用來初始化按鍵的IO口,如代碼清單2所示。

/************************************************************************** 代碼清單2:key.c* 描    述:按鍵的初始化、驅動* 平    臺:麒麟座V3.2* 作    者:老耿* 日    期:yyyy-mm-dd* 固 件 庫:ST3.5.0* 版    本:V1.0* 修改記錄:無************************************************************************
*///必要的頭文件
#include "key.h"
#include "delay.h"/*************************************************************************** 函 數 名:Key_Init* 功    能:按鍵端口初始化* 入口參數:無* 出口參數:無* 說    明:將按鍵端口設置成輸入模式************************************************************************
**/
void Key_Init(void)
{//定義一個GPIO初始化對象(結構體)GPIO_InitTypeDef  gpio_initstruct;//打開必要的外設時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);//填充初始化結構體,上拉輸入模式,并執行生效gpio_initstruct.GPIO_Pin = KEY1_PIN | KEY2_PIN | KEY3_PIN;gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;//輸入模式不用配置Speed參數//gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &gpio_initstruct);//KEY4與KEY1/2/3不是一組端口,單獨再初始化gpio_initstruct.GPIO_Pin = KEY4_PIN;GPIO_Init(GPIOD, &gpio_initstruct);
}

????????雖然本實驗只使用到了KEY1,但在代碼中,我們將KEY2、KEY3、KEY4的IO口也一并進行了初始化。需要注意的是,GPIO要配置為輸入模式。由于按鍵松開時對應的引腳為高電平,所以我們將其配置為上拉輸入模式。

????????那為什么沒有配置為下拉輸入模式呢?那是因為下拉模式,引腳默認為低電平,這相當于按鍵被按下的狀態。這樣的話,程序將無法檢測按鍵是否被按下,因為無論按鍵是否被按下,引腳電平都為低電平了。當然,配置成浮空輸入模式當然也是可以的。

????????還有一點大家是否發現,上面代碼中,沒有配置引腳的速度,那是因為引腳處于輸入模式時,并不需要配置引腳速度。引腳速度僅是指單片機向引腳刷新電平的頻率,所以只有在引腳處于輸出模式時才有效。

3. main.c文件源碼

????????主程序里編寫了無鎖存和有鎖存兩種按鍵控燈的方法,如代碼清單3所示,大家測試的時候請保留一種方式的代碼,注釋掉另一種,來體會這兩種方式的差異。

/********************************************************* 代碼清單3:main.c* 項    目:按鍵控制LED* 任務描述:按一次KEY1,變一次LED* 實驗平臺:OneNET STM32開發板V3.2* 作    者:老耿* 日    期:yyyy/mm/dd******************************************************
**///-----------------------------------------------------
// 必要的頭文件
//-----------------------------------------------------
#include "delay.h"
#include "led.h"
#include "key.h"//-----------------------------------------------------
// 主函數
//-----------------------------------------------------
int main()
{delay_init();	//延時初始化Led_Init();		//LED初始化Key_Init();		//按鍵初始化while(1)    //以下兩種方式保留一種,注釋掉另一種{/*-------------- 無鎖存方式 -------------*/
//      if(!READ_KEY1)  //按住紅燈亮
//          RED_ON;
//      else
//          RED_OFF;    //松開紅燈滅/*-------------- 有鎖存方式 -------------*/if(!READ_KEY1)      //按下KEY1{delay_ms(10);   //延時消抖if(!READ_KEY1)  //確認按下{while(!READ_KEY1);  //等待松開RED_TOG;    //紅燈變化}}}
}

2.5 驗證與測試

????????我們對兩種方式分別下載和測試后,大家應該可以發現,無鎖存方式下需要按住不放,LED才亮著,一旦松開,LED就滅了。而有鎖存的方式下,需要完成按下和松開這一組動作(即單擊)才能改變LED的狀態,實現這個效果的就是代碼清單3中的第36行,當按鍵被按下時,while的條件將永遠成立,這樣將導致程序一直停留在此處,只有按鍵松開時,后續代碼才可以得到執行,也就達到了通過按鍵鎖存程序狀態的目的。

????????此外,在有鎖存方式中,當按鍵被按下后,并不是馬上進行后續動作,而是延時了10ms,再次判斷按鍵是否被按下,這樣就避免了按鍵因電平抖動造成被誤判的可能。大家也可以嘗試去掉消抖這條語句,看每次按鍵按下的操作是否都能有效。

(第一部分完,共兩部分)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/36075.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/36075.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/36075.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux下創建虛擬磁盤

參考文檔 https://blog.csdn.net/lujun9972/article/details/115762407 https://blog.csdn.net/Kiritow/article/details/118076034 1,創建掛載點 sudo mkdir /mnt/z //方式一,內存盤方式 2,創建內存盤 sudo mount -t ramfs -o size1G z /…

Java中的內存泄漏及其排查方法

Java中的內存泄漏及其排查方法 大家好,我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編,也是冬天不穿秋褲,天冷也要風度的程序猿! 在Java開發中,內存管理通常由Java虛擬機(JVM&#xff0…

【觀察】戴爾科技+AMD:釋放技術創新“乘數效應”,助力制造業打造“新質生產力”...

在今年的政府工作報告中,“人工智能”首次被寫入報告,同時“大力推進現代化產業體系建設,加快發展新質生產力”也被列為2024年的首項政府工作任務,其重要性不言而喻。 尤其是最近幾年,以人工智能、大模型、大數據、云計…

如何在Spring Boot中使用RabbitMQ實現消息隊列

如何在Spring Boot中使用RabbitMQ實現消息隊列 大家好,我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編,也是冬天不穿秋褲,天冷也要風度的程序猿!在分布式系統中,消息隊列是實現解耦、異步通信和削峰…

java設計模式(六)代理模式(Proxy Pattern)

1、模式介紹: 代理模式(Proxy Pattern)是一種結構型設計模式,它允許你在不改變客戶端代碼的情況下,向某個對象提供一個代理,以控制對該對象的訪問。代理對象通常會在實際對象的方法調用前后添加一些附加邏…

【LeetCode】每日一題:三數之和

解題思路 最開始是打算沿著二數之和的思路做,即固定了最大的,然后小的開始遍歷,因為這種遍歷方式只需要遍歷一輪就能完成,所以復雜度應該是O(n2),但是最后幾個示例還是超時了,可能進…

《UDS協議從入門到精通》系列——圖解0x35:請求上傳

《UDS協議從入門到精通》系列——圖解0x35:請求上傳 一、簡介二、數據包格式2.1 服務請求格式2.2 服務響應格式2.2.1 肯定響應2.2.2 否定響應 三、通信示例 Tip📌:本文描述中但凡涉及到其他UDS服務的,將陸續提供鏈接跳轉方式以便快…

解決Java中的NoSuchElementException異常的常見方法

解決Java中的NoSuchElementException異常的常見方法 大家好,我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編,也是冬天不穿秋褲,天冷也要風度的程序猿!在Java編程中,NoSuchElementException異常是一個…

AMSR-E/Aqua 第 3 級全球地表土壤水分月平均值 V005 (AMSRE_AVRMO)

AMSR-E/Aqua level 3 global monthly Surface Soil Moisture Averages V005 (AMSRE_AVRMO) at GES DISC AMSR-E/Aqua level 3 global monthly Surface Soil Moisture Standard Deviation V005 (AMSRE_STDMO) at GES DISC 簡介 GES DISC 的 AMSR-E/Aqua 第 3 級全球地表土壤水…

操作系統入門 -- 內存管理

操作系統入門 – 內存管理 1.內存種類 1.1 虛擬內存(VIRT) 進程需要的虛擬內存大小,包括進程使用的庫、代碼、數據以及malloc、new分配的堆空間和棧空間等。若進程申請了10MB內存但實際使用了1MB,則物理空間會增長10MB。 1.2 …

Resource punkt not found.的解決方法

這個問題本來不想記錄,但是在好幾個機子上都碰到了(用到了LangChain讀Word文檔)。簡單記錄一下。看到報錯以后運行: import nltk # nltk.set_proxy(http://192.168.1.68:10811) nltk.download() 中間這句我注釋掉了,…

接軌國際安全標準:等保認證在提升企業全球競爭力中的核心作用

隨著全球化進程的加速和數字經濟的蓬勃發展,信息安全已成為企業拓展國際市場、參與國際競爭的重要基石。網絡安全等級保護(簡稱“等保”)認證,作為衡量企業信息安全管理水平的重要標尺,不僅體現了企業的技術實力和合規…

速盾:ddos攻擊類型有哪些?

DDoS攻擊(分布式拒絕服務攻擊)是一種通過利用多個被感染的計算機或網絡設備,以大量的請求或數據包來占用目標系統資源,導致其無法正常提供服務的攻擊方式。DDoS攻擊常常被黑客用來影響目標的可用性,造成經濟損失或打擊…

如何以智能方式安裝 Python

Python易于使用,對初學者友好,功能強大,幾乎可以為任何應用程序創建強大的軟件。 但與任何其他軟件一樣,Python 的設置和管理可能很復雜。 在本文中,我們將介紹如何正確設置 Python。 您將學習如何選擇合適的版本、…

學習筆記——動態路由——RIP(附加度量值配置)

六、附加度量值配置 RIP協議cost開銷值:默認值為0,路由信息每傳遞一次,值增加1,最大15,(路由器不能超過15臺)16代表不可達。 入接口附加度量值 rip metricin 5 //可以修改開銷改變路徑。只能增加,不能減小 …

count(*) over (partition by ……)用法詳解

select id,count(*) over(partition by pro_id) from sal; 以pro_id分組,統計分組后每個pro_id的記錄總數及對應的id; 類似還有count(*) over(order by ……)、sum(amount) over(partition by ……)等,略有區別

降低企業運營成本的API服務有哪些?

通過API服務,企業可以實現許多功能和服務的自動化和優化,從而有效降低企業的運營成本。API服務可以幫助企業簡化流程、減少人工操作、提高效率,并提供數據支持和決策依據,從而實現成本的有效控制和降低。無論是人力資源管理、客戶…

【D3.js in Action 3 精譯】1.2.2 可縮放矢量圖形(一)

譯注 由于 1.2.2 小節介紹 SVG 的篇幅過多,為了方便查閱,后續將分多個小節依次進行翻譯。為了確保整個 1.2.2 小節的完整性,特意將上一篇包含的 SVG 小節的內容整理出來重新編排。敬請留意。 1.2.2 SVG - 可縮放矢量圖形 可伸縮矢量圖形&…

kaoYan-English

英語的提高是個日積月累,可以花一個月時間突擊政治。但英語不可。關鍵在于單詞和閱讀理解 提高英語成績的捷徑,多做閱讀題。閱讀理解的分值高,閱讀理解在鞏固詞匯,培養語感有不可替代作用。 選資料,貼合考研難度的&a…

x264 編碼器 i_intra_cost 計算過程

介紹 是uint16_t類型指針變量,用來存儲每個宏塊的幀內代價值,在 frame.h 文件中x264_frame_t結構體中聲明。在*frame_new 函數中將lowres_costs[0][0]指向給i_intra_cost,并 memset 為-1;//代碼有刪減 frame->i_intra_cost = frame->lowres_costs[0][0]; memset( fra…