普中STM32F103ZET6開發攻略(二)

接上文:普中STM32F103ZET6開發攻略(一)-CSDN博客

各位看官老爺們,點擊關注不迷路喲。你的點贊、收藏,一鍵三連,是我持續更新的動力喲!!!

目錄

接上文:普中STM32F103ZET6開發攻略(一)-CSDN博客

2.GPIO端口實驗_按鍵控制:

2.1 實驗目的:

2.2 實驗環境:

2.3 實驗原理

2.4 實驗思路

2.4.1 硬件電路

2.5 實驗代碼

2.5.1 LED頭、源

2.5.2 key頭、源

2.5.3 beep頭、源

2.6 實驗思考和拓展

2.6.1 如何改進按鍵消抖方法,使其更加可靠?

2.6.2 如何修改程序,實現不同按鍵組合的功能(如同時按下兩個按鍵)?

2.6.3 如何實現按鍵長按與短按的區分,并執行不同功能?

2.6.4 按鍵掃描可以用中斷方式替代輪詢方式嗎?兩種方式各有什么優缺點?

2.7 注意事項


2.GPIO端口實驗_按鍵控制

2.1 實驗目的:

  1. 熟悉STM32F10x微控制器的GPIO口結構和基本操作

  2. 掌握STM32標準庫函數對GPIO輸入和輸出的配置方法

  3. 學會使用按鍵輸入控制LED和蜂鳴器的工作狀態

  4. 理解按鍵消抖原理并實現穩定的按鍵檢測

2.2 實驗環境:

  • 開發板:STM32F103ZET6

  • IDE:Keil MDK 5 /Visual Studio

  • 調試工具:CMSIS-DAP

  • 電路連接:

    • LED0 接 PB5 引腳

    • LED1 接 PE5 引腳

    • KEY0 接 PE4 引腳

    • KEY1 接 PE3 引腳

    • KEY2 接 PE2 引腳

    • WK_UP 接 PA0 引腳

2.3 實驗原理

1. 按鍵輸入原理

STM32的GPIO可配置為輸入模式,用于檢測外部按鍵狀態。按鍵輸入模式包括: (1) 上拉輸入:默認高電平,按下按鍵后為低電平 (2) 下拉輸入:默認低電平,按下按鍵后為高電平 本實驗中KEY0、KEY1、KEY2采用上拉輸入方式,KEY_UP采用下拉輸入方式。

模式分類具體模式核心特點典型應用
輸入模式浮空輸入(GPIO_Mode_IN_FLOATING無內部上下拉,電平由外部決定外部信號采集(需外部上下拉)
上拉輸入GPIO_Mode_IPU內部上拉,默認高電平按鍵輸入(低電平有效)
下拉輸入GPIO_Mode_IPD內部下拉,默認低電平按鍵輸入(高電平有效)
模擬輸入(GPIO_Mode_AIN連接 ADC,禁用數字輸入功能ADC 模數轉換
輸出模式開漏輸出(GPIO_Mode_Out_OD需外部上拉,支持線與邏輯I2C 總線、電平轉換
推挽輸出(GPIO_Mode_Out_PP直接輸出高低電平,驅動能力強LED 控制、普通數字信號
開漏復用功能()(GPIO_Mode_AF_OD外設驅動,需外部上拉SPI/I2C 外設輸出
推挽復用功能(GPIO_Mode_AF_PP外設驅動,直接輸出高低電平USART/CAN 外設輸出

2. 按鍵消抖技術 機械按鍵在按下或釋放時會產生抖動,導致一次按鍵操作被多次檢測。消抖的常用方法有: (1) 延時消抖:檢測到按鍵狀態變化后,延時一段時間再次檢測確認 (2) 連續采樣消抖:連續多次采樣按鍵狀態,確認狀態穩定才視為有效

3. LED和蜂鳴器控制原理

本實驗板上的LED采用共陽極連接方式,GPIO輸出低電平時LED點亮;蜂鳴器則是GPIO輸出高電平時發聲。

2.4 實驗思路

2.4.1 硬件電路

本實驗使用STM32F10x開發板上的按鍵、LED燈和蜂鳴器進行測試。LED1連接到PB5,LED2連接到PE5,KEY_UP連接到PA0(下拉輸入),KEY0連接到PE4,KEY1連接到PE3,KEY2連接到PE2(上拉輸入),蜂鳴器連接到PB8,接線圖如下:

由上圖可知:KEY0、KEY1、KEY2采用上拉輸入方式,KEY_UP采用下拉輸入方式。

為了完成實驗目的:

我們需要在項目工程中,在實驗一的基礎上新建兩個“庫函數”:key.c+beep.c

由于實驗二的實驗目的與實驗一的不一樣所以Led頭、源文件需要重新寫。

2.5 實驗代碼

2.5.1 LED頭、源

led.h

#ifndef __LED_H
#define __LED_H
?
#include "stm32f10x.h"
?
#define LED0_GPIO_PORT GPIOB
#define LED0_GPIO_PIN  GPIO_Pin_5 //LED0
?
#define LED1_GPIO_PORT GPIOE
#define LED1_GPIO_PIN  GPIO_Pin_5 //LED1
?
void LED_Init(void);
void LED0_TOGGLE(void);
void LED1_ON(void);
void LED1_OFF(void);
?
#endif
?

led.c

#include "led.h"
?
void LED_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 開啟 GPIOB 和 GPIOE 的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE);
?//配置PB5(LED0)GPIO_InitStructure.GPIO_Pin = LED0_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(LED0_GPIO_PORT, &GPIO_InitStructure);
?//配置PE5(LED1)GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
?//低電平輸出GPIO_ResetBits(LED0_GPIO_PORT, LED0_GPIO_PIN);GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
?
void LED0_TOGGLE(void)
{LED0_GPIO_PORT->ODR ^= LED0_GPIO_PIN;
}
?
void LED1_ON(void)
{GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
?
void LED1_OFF(void)
{GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
?

2.5.2 key頭、源

key.h

#ifndef __KEY_H
#define __KEY_H
?
#include "stm32f10x.h"
?
// GPIO 輸入宏
#define PAin(n)  (GPIO_ReadInputDataBit(GPIOA, (1 << (n))))
#define PEin(n)  (GPIO_ReadInputDataBit(GPIOE, (1 << (n))))
?
// 按鍵 GPIO 宏定義
#define KEY0_PIN ? ?  GPIO_Pin_4
#define KEY1_PIN ? ?  GPIO_Pin_3
#define KEY2_PIN ? ?  GPIO_Pin_2
#define KEY_UP_PIN ?  GPIO_Pin_0
?
#define KEY_PORT ? ?  GPIOE
#define KEY_UP_PORT ? GPIOA
?
#define KEY_UP ? ? ?  PAin(0)
#define KEY0 ? ? ? ?  PEin(4)
#define KEY1 ? ? ? ?  PEin(3)
#define KEY2 ? ? ? ?  PEin(2)
?
// 返回值宏定義
#define KEY_UP_PRESS  1
#define KEY0_PRESS ?  2
#define KEY1_PRESS ?  3
#define KEY2_PRESS ?  4
?
void KEY_Init(void);
u8 KEY_Scan(u8 mode);
?
#endif
?

key.c

#include "key.h"
#include "delay.h"
?
void KEY_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE, ENABLE);
?// KEY_UP: 下拉輸入GPIO_InitStructure.GPIO_Pin = KEY_UP_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_Init(KEY_UP_PORT, &GPIO_InitStructure);
?// KEY0, KEY1, KEY2: 上拉輸入GPIO_InitStructure.GPIO_Pin = KEY0_PIN | KEY1_PIN | KEY2_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(KEY_PORT, &GPIO_InitStructure);
}
?
u8 KEY_Scan(u8 mode)
{static u8 key_up = 1;
?if (mode) key_up = 1;
?if (key_up && (KEY_UP || !KEY0 || !KEY1 || !KEY2)){Delay_ms(10);  // 消抖key_up = 0;if (KEY_UP) ? return KEY_UP_PRESS;if (!KEY0) ?  return KEY0_PRESS;if (!KEY1) ?  return KEY1_PRESS;if (!KEY2) ?  return KEY2_PRESS;}else if (!KEY_UP && KEY0 && KEY1 && KEY2){key_up = 1;}
?return 0;
}
?

2.5.3 beep頭、源

bepp.h

#ifndef __BEEP_H
#define __BEEP_H
?
#include "stm32f10x.h"
?
#define BEEP_GPIO_PORT GPIOB
#define BEEP_GPIO_PIN  GPIO_Pin_8
?
void BEEP_Init(void);
void BEEP_ON(void);
void BEEP_OFF(void);
?
#endif
?

beep.c

#include "beep.h"
?
void BEEP_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//初始化各項參數:GPIO_InitStructure.GPIO_Pin = BEEP_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(BEEP_GPIO_PORT, &GPIO_InitStructure);
?GPIO_ResetBits(BEEP_GPIO_PORT, BEEP_GPIO_PIN);  // 默認關閉蜂鳴器
}
?
void BEEP_OFF(void)
{GPIO_ResetBits(BEEP_GPIO_PORT, BEEP_GPIO_PIN);
}
?
void BEEP_ON(void)
{GPIO_SetBits(BEEP_GPIO_PORT, BEEP_GPIO_PIN);
}
?

實驗現象:

DS0指示燈會不斷閃爍(200ms翻轉一次)

按下KEY_UP鍵,DS1指示燈點亮

按下KEY2鍵,DS1指示燈熄滅

按下KEY1鍵,蜂鳴器發聲

按下KEY0鍵,蜂鳴器停止發聲

2.6 實驗思考和拓展

2.6.1 如何改進按鍵消抖方法,使其更加可靠?

傳統的延時消抖存在阻塞問題,可通過以下方式優化:

改進方案

  1. 雙閾值檢測:同時設置按下閾值和釋放閾值(如按下 > 20ms,釋放 > 15ms)

  2. 狀態機設計:將按鍵狀態分為 "未按下 - 抖動 - 已按下 - 釋放抖動" 等狀態

  3. 定時器掃描:使用硬件定時器進行定時檢測,避免阻塞主程序

  4. 濾波算法:采用一階 RC 濾波或滑動平均濾波處理按鍵信號

2.6.2 如何修改程序,實現不同按鍵組合的功能(如同時按下兩個按鍵)?

實現思路

  1. 定義組合鍵映射表:將按鍵組合與功能函數關聯

  2. 掃描所有按鍵狀態:記錄每個按鍵的獨立狀態

  3. 狀態比對:檢測是否符合預定義的組合模式

例如:
// 定義按鍵掩碼
#define KEY1_MASK (1 << 0)
#define KEY2_MASK (1 << 1)
#define KEY3_MASK (1 << 2)
?
// 組合鍵處理函數
void HandleKeyCombination(uint8_t key_mask) {switch(key_mask) {case KEY1_MASK: ? ? ? ? ?// 單按KEY1Function1(); break;case KEY2_MASK: ? ? ? ? ?// 單按KEY2Function2(); break;case KEY1_MASK|KEY2_MASK: // 同時按KEY1+KEY2Function3(); break;case KEY1_MASK|KEY3_MASK: // 同時按KEY1+KEY3Function4(); break;// 其他組合...}
}
?
// 主循環中調用
void MainLoop(void) {uint8_t key_mask = 0;if(IS_KEY1_PRESSED) key_mask |= KEY1_MASK;if(IS_KEY2_PRESSED) key_mask |= KEY2_MASK;if(IS_KEY3_PRESSED) key_mask |= KEY3_MASK;HandleKeyCombination(key_mask);
}
2.6.3 如何實現按鍵長按與短按的區分,并執行不同功能?

實現方案

  1. 定時器計時:按下按鍵時啟動定時器,釋放時停止

  2. 時間閾值判斷:超過長按閾值(如 1 秒)為長按,否則為短按

  3. 狀態標志:使用標志位區分長按 / 短按回調

2.6.4 按鍵掃描可以用中斷方式替代輪詢方式嗎?兩種方式各有什么優缺點?

中斷方式

  • 優點:實時響應、不占用 CPU 資源、適合低功耗設計

  • 缺點:需處理中斷優先級、可能受噪聲干擾、抖動處理復雜

  • 適用場景:需要立即響應的場合(如緊急停止按鍵)

輪詢方式

  • 優點:實現簡單、可靠性高、可批量處理多個按鍵

  • 缺點:占用 CPU 資源、響應不及時(取決于掃描周期)

  • 適用場景:按鍵數量較多、對響應時間要求不高的場合

推薦方案

  • 使用中斷檢測按鍵動作,進入中斷后啟動定時器消抖

  • 消抖完成后在定時器回調函數中處理按鍵事件

  • 既保證實時性,又避免抖動干擾

2.7 注意事項

(1) 按鍵檢測時需要注意輸入模式的選擇(上拉或下拉)和實際電平變化方向

(2) 按鍵消抖是保證按鍵檢測可靠性的關鍵

(3) 使用標準庫函數時,需要注意頭文件的包含和依賴關系

(4) GPIO操作前必須先使能對應的外設時鐘

(5) 避免在主循環中設置過長的延時,以免影響按鍵響應速度

文章有寫的不當的地方,歡迎在評論區中指正修改。如果感覺文章實用對你有幫助,歡迎點贊收藏和關注,你的點贊關注就是我動力,大家一起學習進步。

有不懂的可以在評論區里提出來喲,博主看見后會及時回答的。

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

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

相關文章

用提示詞寫程序(3),VSCODE+Claude3.5+deepseek開發edge擴展插件V2

edge擴展插件;篩選書簽,跳轉搜索,設置背景 鏈接: https://pan.baidu.com/s/1nfnwQXCkePRnRh5ltFyfag?pwd86se 提取碼: 86se 導入解壓的擴展文件夾: 導入擴展成功: edge擴展插件;篩選書簽,跳轉搜索,設置背景

電腦桌面便簽軟件哪個好?桌面好用便簽備忘錄推薦

在日常辦公中&#xff0c;一款優秀的桌面便簽工具能顯著提升工作效率。面對市面上琳瑯滿目的選擇&#xff0c;不少用戶都難以抉擇。如果你正在尋找一款兼具輕量化與多功能性的便簽軟件&#xff0c;那么集實用性與便捷性于一身的"好用便簽"&#xff0c;或許就是你的理…

性能優化 - 工具篇:基準測試 JMH

文章目錄 Pre引言1. JMH 簡介2. JMH 執行流程詳解3. 關鍵注解詳解3.1 Warmup3.2 Measurement3.3 BenchmarkMode3.4 OutputTimeUnit3.5 Fork3.6 Threads3.7 Group 與 GroupThreads3.8 State3.9 Setup 與 TearDown3.10 Param3.11 CompilerControl 4. 示例代碼與分析4.1 關鍵點解讀…

2025年十大AI幻燈片工具深度評測與推薦

我來告訴你一個好消息。 我們已經親自測試和對比了市面上最優秀的AI幻燈片工具&#xff0c;讓你無需再為選擇而煩惱。 得益于AI技術的飛速發展&#xff0c;如今你可以快速制作出美觀、專業的幻燈片。 這些智能平臺的功能遠不止于配色美化——它們能幫你頭腦風暴、梳理思路、…

雪花算法:分布式ID生成的優雅解決方案

一、雪花算法的核心機制與設計思想 雪花算法&#xff08;Snowflake&#xff09;是由Twitter開源的分布式ID生成算法&#xff0c;它通過巧妙的位運算設計&#xff0c;能夠在分布式系統中快速生成全局唯一且趨勢遞增的ID。 1. 基本結構 雪花算法生成的是一個64位&#xff08;lo…

第1章:走進Golang

第1章&#xff1a;走進Golang 一、Golang簡介 Go語言&#xff08;又稱Golang&#xff09;是由Google的Robert Griesemer、Rob Pike及Ken Thompson開發的一種開源編程語言。它誕生于2007年&#xff0c;2009年11月正式開源。Go語言的設計初衷是為了在不損失應用程序性能的情況下…

Higress項目解析(二):Proxy-Wasm Go SDK

3、Proxy-Wasm Go SDK Proxy-Wasm Go SDK 依賴于 tinygo&#xff0c;同時 Proxy - Wasm Go SDK 是基于 Proxy-Wasm ABI 規范使用 Go 編程語言擴展網絡代理&#xff08;例如 Envoy&#xff09;的 SDK&#xff0c;而 Proxy-Wasm ABI 定義了網絡代理和在網絡代理內部運行的 Wasm …

NVMe IP現狀掃盲

SSD優勢 與機械硬盤&#xff08;Hard Disk Driver, HDD&#xff09;相比&#xff0c;基于Flash的SSD具有更快的數據隨機訪問速度、更快的傳輸速率和更低的功耗優勢&#xff0c;已經被廣泛應用于各種計算領域和存儲系統。SSD最初遵循為HDD設計的現有主機接口協議&#xff0c;例…

`docker commit` 和 `docker save`區別

理解 docker commit 和 docker save 之間的區別對于正確管理 Docker 鏡像非常重要。讓我們詳細解釋一下這兩個命令的作用及其區別。 1. docker commit 作用&#xff1a; docker commit roop-builder roop:v1 命令的作用是基于一個正在運行的容器 roop-builder 創建一個新的鏡…

Linux內核體系結構簡析

1.Linux內核 1.1 Linux內核的任務 從技術層面講&#xff0c;內核是硬件和軟件之間的一個中間層&#xff0c;作用是將應用層序的請求傳遞給硬件&#xff0c;并充當底層驅動程序&#xff0c;對系統中的各種設備和組件進行尋址。從應用程序的角度講&#xff0c;應用程序與硬件沒有…

python爬蟲:Ruia的詳細使用(一個基于asyncio和aiohttp的異步爬蟲框架)

更多內容請見: 爬蟲和逆向教程-專欄介紹和目錄 文章目錄 一、Ruia概述1.1 Ruia介紹1.2 Ruia特點1.3 安裝Ruia1.4 使用案例二、基本使用2.1 Request 請求2.2 Response - 響應2.3 Item - 數據提取2.4 Field 提取數據2.5 Spider - 爬蟲類2.6 Middleware - 中間件三、高級功能3.1 …

網絡攻防技術二:密碼學分析

文章目錄 一、傳統密碼分析方法1、根據明文、密文等信息的掌握情況分類 2、從密碼分析途徑分類二、密碼旁路分析1、概念2、旁路分析方法三、現代密碼系統1、對稱密碼&#xff08;單密鑰&#xff09;2、公開密碼&#xff08;成對密鑰&#xff09; 四、典型對稱密碼&#xff08;單…

Linux --TCP協議實現簡單的網絡通信(中英翻譯)

一、什么是TCP協議 1.1 、TCP是傳輸層的協議&#xff0c;TCP需要連接&#xff0c;TCP是一種可靠性傳輸協議&#xff0c;TCP是面向字節流的傳輸協議&#xff1b; 二、TCPserver端的搭建 2.1、我們最終好實現的效果是 客戶端在任何時候都能連接到服務端&#xff0c;然后向服務…

pc端小卡片功能-原生JavaScript金融信息與節日日歷

代碼如下 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>金融信息與節日日歷</title><…

C語言——獲取變量所在地址(uint8和uint32的區別)

前言&#xff1a; 1.使用uint8 *的原因 在C語言中&#xff0c;獲取或操作一個4字節地址&#xff08;指針&#xff09;時使用uint8_t*&#xff08;即unsigned char*&#xff09;而不是uint32_t*&#xff0c;主要基于以下關鍵原因&#xff1a; 1.1. 避免違反嚴格別名規則&…

Python----目標檢測(《YOLOv3:AnIncrementalImprovement》和YOLO-V3的原理與網絡結構)

一、《YOLOv3:AnIncrementalImprovement》 1.1、基本信息 標題&#xff1a;YOLOv3: An Incremental Improvement 作者&#xff1a;Joseph Redmon, Ali Farhadi 機構&#xff1a;華盛頓大學&#xff08;University of Washington&#xff09; 發表時間&#xff1a;2018年 代…

50天50個小項目 (Vue3 + Tailwindcss V4) ? | Form Wave(表單label波動效果)

&#x1f4c5; 我們繼續 50 個小項目挑戰&#xff01;—— FormWave組件 倉庫地址&#xff1a;https://github.com/SunACong/50-vue-projects 項目預覽地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 組件目標 構建一個美觀、動態的登錄表單&#xff0…

【數據結構】--二叉樹--堆(上)

一、樹的概念和結構 概念&#xff1a; 樹是一種非線性的數據結構&#xff0c;他是由n(n>0)個有限結點組成一個具有層次關系的集合。其叫做樹&#xff0c;是因為他倒過來看就和一棵樹差不多&#xff0c;其實際上是根在上&#xff0c;樹枝在下的。 樹的特點&#xff1a; 1…

linux有效裁剪視頻的方式(基于ffmpeg,不改變分辨率,幀率,視頻質量,不需要三方軟件)

就是在Linux上使用OBS Studio錄制一個講座或者其他視頻&#xff0c;可能總有些時候會多錄制一段時間&#xff0c;但是如果使用剪映或者PR這樣的工具在導出的時候總需要煩惱導出的格式和參數&#xff0c;比如剪映就不支持mkv格式的導出&#xff0c;導出成mp4格式的視頻就會變得很…

SystemVerilog—Interface語法(一)

SystemVerilog中的接口&#xff08;interface&#xff09;是一種用于封裝多模塊間通信信號和協議的復合結構&#xff0c;可顯著提升代碼復用性和維護效率。其核心語法和功能如下&#xff1a; 一、接口的基本定義 1. 聲明語法 接口通過interface關鍵字定義&#xff0c;支持信…