STM32复习笔记(二):GPIO
目录
(一)Demo流程
(二)工程配置
(三)代码部分
(四)外部中断(EXTI)
(一)Demo流程
首先,板子上有4个按键,两颗灯,一个beep,所以设计一个demo如下:
1、按下KEY0,LED0输出翻转;
2、按下KEY1,LED1输出翻转;
3、按下KEY2,LED0和LED1输出翻转;
4、按下WK_UP,蜂鸣器输出翻转;
相关部分电路schematic如下:



此外,WK_UP接到PA0;
简单分析一下电路:两个LED为0点亮;按键三个为0有效,一个为1有效;蜂鸣器处有一个BJT放大电路,BEEP给1就导通,给0就截止。
(二)工程配置
按老样子配置好SYS,RCC以及时钟频率之后,开始配置引脚;首先找到PF9和PF10,配置为输出,并修改label为LED0和LED1;然后找到PE4、PE3、PE2、PA0,配置为输入,并修改label为KEY0、KEY1、KEY2、WK_UP;最后找到PF8,配置为输出,并修改label为BEEP:

接下来,给每个引脚配置初始化如下:

首先对于两个灯为0点亮,所以首先给1且上拉,先不点亮,待需要时再点亮;然后对于beep,因为给1为响,所以先给0,而电路图中已经默认帮我下拉了,所以我直接不需要下拉;最后对于按键,因为WK_UP是1有效,所以默认下拉,检测到1则表明按下,而KEY0~2是0有效,所以默认上拉,检测到0则表明按下;然后设置好相关路径等配置直接generate code即可。
(三)代码部分
首先进入到main.h,就会发现刚刚给引脚起的label名都被define在里面了:

接下来新建文件,勾选下图第一个选项,直接帮你创建相关头文件,无需勾选第二个,后面再在cmake中手动加入:

接下来,需要在cmake中使用include_directories()包含头文件路径,以及使用file()包含源文件路径(此外,如果再用cubemx生成代码的话,CmakeLists.txt文件是会被重新覆盖掉的,所以需要写入CmakeLists_template.txt中,就不会被覆盖):


再点击右键,选择重新加载cmake即可:

经过漫长的代码编写之后......终于写完了:
keyled.h:
#ifndef DEMO_GPIO_KEYLED_H
#define DEMO_GPIO_KEYLED_H
#ifdef __cplusplus
extern "C" {
#endif#include "main.h"//表示4个按键的枚举类型
typedef enum {KEY_NONE = 0, //没有按键按下KEY0,KEY1,KEY2,WK_UP
}KEYS;#define KEY_WAIT_ALWAYS 0 //作为函数ScanPressedKey()的一种参数,表示一直等待按键输入#ifdef LED0_Pin //LED0
#define LED0_ON() HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET)
#define LED0_OFF() HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET)
#define LED0_TOGGLE() HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin)
#endif#ifdef LED1_Pin //LED1
#define LED1_ON() HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET)
#define LED1_OFF() HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET)
#define LED1_TOGGLE() HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin)
#endif#ifdef BEEP_Pin //Beep
#define BEEP_ON() HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_SET)
#define BEEP_OFF() HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET)
#define BEEP_TOGGLE() HAL_GPIO_TogglePin(BEEP_GPIO_Port, BEEP_Pin)
#endifKEYS ScanPressedKey(uint32_t timeout);#ifdef __cplusplus
}
#endif
#endif //DEMO_GPIO_KEYLED_H
keyled.cpp:
#include "keyled.h"//轮询方式扫米奥4个按键,并返回按键值
//轮询方式扫描4个按键,返回按键值
//timeout单位ms,若timeout=0表示一直扫描,直到有键按下
KEYS ScanPressedKey(uint32_t timeout)
{KEYS key = KEY_NONE;uint32_t tickstart = HAL_GetTick(); //当前计数值const uint32_t btnDelay = 20; //按键按下阶段的抖动,延时再采样时间GPIO_PinState keyState;while(true){
#ifdef KEY0_Pin //如果定义了KEY0,就可以检测KEY0keyState = HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin); //低输入有效if (keyState == GPIO_PIN_RESET){HAL_Delay(btnDelay); //前抖动期keyState = HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin); //再采样if (keyState == GPIO_PIN_RESET)return KEY0;}
#endif#ifdef KEY1_Pin //如果定义了KEY1,就可以检测KEY1keyState = HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin); //低输入有效if (keyState == GPIO_PIN_RESET){HAL_Delay(btnDelay); //前抖动期keyState = HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin); //再采样if (keyState == GPIO_PIN_RESET)return KEY1;}
#endif#ifdef KEY2_Pin //如果定义了KEY2,就可以检测KEY2keyState = HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin); //低输入有效if (keyState == GPIO_PIN_RESET){HAL_Delay(btnDelay); //前抖动期keyState = HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin); //再采样if (keyState == GPIO_PIN_RESET)return KEY2;}
#endif#ifdef WK_UP_Pin //如果定义了WK_UP,就可以检测WK_UPkeyState = HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin); //PE4=KeyLeft,低输入有效if (keyState == GPIO_PIN_SET)//注意这里默认是下拉{HAL_Delay(btnDelay); //前抖动期keyState = HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin); //再采样if (keyState == GPIO_PIN_SET)return WK_UP;}
#endifif (timeout != KEY_WAIT_ALWAYS) //没有按键按下时,会计算超时,timeout时退出{if ((HAL_GetTick() - tickstart) > timeout)break;}}return key;
}
主函数:
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();while (1){KEYS curkey = ScanPressedKey(KEY_WAIT_ALWAYS); //一直等待按键输入switch (curkey) {case KEY0:LED0_TOGGLE();break;case KEY1:LED1_TOGGLE();break;case KEY2:LED0_TOGGLE();LED1_TOGGLE();break;case WK_UP:BEEP_TOGGLE();break;}HAL_Delay(200); //跳过后抖动}
}
以上,实现了轮询检测按键,从而控制LED及Beep。
工程链接:https://pan.baidu.com/s/11xCxqty3KRJD6cx0S65jWQ
提取码:0xFF
(四)外部中断(EXTI)
但是,总所周知,轮询查询GPIO口是非常非常浪费cpu资源的,所以可以利用外部中断(EXTI,External Interrupt)来检测按键输入;设计一个demo如下:
1、按下KEY0,触发EXTI4,LED0输出翻转;
2、按下KEY1,触发EXTI3,LED1输出翻转;
3、按下WK_UP,触发EXTI0,LED0和LED1输出翻转;
4、按下KEY2,产生EXTI0软中断(SWIT),模拟按下WK_UP;
配置如下(因为KEY0~2为0有效,所以设置为falling edge触发且上拉;而WK_UP为1有效,所以设置为raising edge触发且下拉):

接下来配置NVIC。设置为2bit抢占优先级 & 2bit次优先级;抢占优先级:谁大可以立即抢占小的中断;次优先级:当抢占优先级一样时,优先执行次优先级大的,但是不能抢占同抢占级的,只能排队;当抢占优先级和次优先级都一样时,则FCFS(First Come First Serve);设置EXTI0,EXTI2,EXTI3,EXTI4的抢占优先级为1,2,1,1,次优先级为0,0,2,1(注意0为最高优先级,3为最低优先级),主要是为了观察同时发生中断时,高抢占优先级的中断能否如理论般正常抢占低抢占优先级的中断,还有就是抢占优先级相同时,次优先级高的是否先执行;如下图所示:

还有一点,设置外部中断的抢占优先级时不能设置为0,因为外部中断的回调函数中会用到HAL_Delay()函数来延时消抖,该函数实际上用的是SysTick嘀嗒计时器的中断,其抢占优先级为0,如果外部中断的抢占优先级也设置为0,那么SysTick嘀嗒计时器的中断就无法抢占外部中断(相同抢占优先级),这将会导致HAL_Delay()函数死循环,系统卡死。
接下来生成代码,可以观察到在stm32f4xx_it.c中,cubemx已经生成了外部中断的函数,切记函数名不能改,因为在启动的汇编文件.s中已经将它们定义好了,要保持二者一致(除非去改汇编,但是没必要):

观察每个EXTI_IRQHandler()函数,发现它们都调用了HAL_GPIO_EXTI_IRQHandler()函数,跳到定义会发现,该函数首先判断是否为中断触发,然后传入中断触发源,再调用HAL_GPIO_EXTI_Callback()外部中断回调函数(callback means 回调):

继续跟进代码可以看到,回调函数是一个__weak修饰的函数,而__weak是一个宏定义,表示为属性:__attribute__((weak)),也就是弱函数;弱函数需要用户自己重新实现,编译时编译器就会自动编译重新实现的函数而忽略弱函数,如果没有重新实现,则自动编译原来的弱函数;其中的UNUSER()是为了避免gcc编译警告:

重新实现的回调函数如下(随便写在哪个文件都行,反正编译器会找得到,不过最好写在相关文件中):

编译下载到板子中会发现结果有一点点不如预期,比如说按下WK_UP时,两个LED会翻转两次,这很明显就是触发了两次中断,但是代码里不是用了1s这么长的延时来消抖么?为什么还会有问题?问题就出在cubemx生成的代码中;观察下图可以发现,HAL_GPIO_EXTI_IRQHandler()函数先判断是否为中断,然后清除标志位,再调用中断回调函数,一般的中断流程这样处理没有问题,主要是为了硬件能及时响应下一次中断;但是对于检测按键输入EXTI就有问题了,因为按键的抖动会导致产生不止一次的外部中断,而先清除了第一次的中断标志位,再执行回调时,后面还有几个抖动的相同外部中断又来了,同样会产生中断标志位,而此时系统正在中断的回调中延时消抖,执行完第一次回调函数之后,cpu出来又发现还有一个中断标志位,将会再进行一次同样的外部中断。

当然理解了原理修改起来就不难,只需要将两行函数互换,当检测到外部中断时,立马执行中断回调,不在管外界还有多少个相同的外部中断均不理会,只有当回调函数执行完毕后,再清除中断标志,这样就避免了多次中断。如下图:

值得注意的是,当重新用cubemx生成代码后,这两行又默认变回原来的位置了,还要手动修改,,,这也是不算bug的bug吧。。。
完~
工程链接:https://pan.baidu.com/s/1Svj7bh_sRzLUYvGjNr4Q3A
提取码:0xFF
以上均为个人学习心得,如有错误,请不吝赐教~
THE END
相关文章:
STM32复习笔记(二):GPIO
目录 (一)Demo流程 (二)工程配置 (三)代码部分 (四)外部中断(EXTI) (一)Demo流程 首先,板子上有4个按键,…...
POJ 3264 Balanced Lineup 线段树 / 平方分割
一、题目大意 给出一个长度为 n(n<50000) 数组 arr,进行Q次查询(Q<200000),每次查询的内容为数组arr在 [L , R] 的切片的极差(最大元素 - 最小元素) 二、解题思路 1、线段树 区间极差…...
element-plus自动引入组件报错,例如collapse、loading
element-plus自动引入组件,例如collapse、loading,使用时报错,报错信息如下图所示: 解决办法:vite-config.ts改变vue的引入顺序,将vue放在第一个...
ChainForge:衡量Prompt性能和模型稳健性的GUI工具包
ChainForge是一个用于构建评估逻辑来衡量模型选择,提示模板和执行生成过程的GUI工具包。ChainForge可以安装在本地,也可以从chrome浏览器运行。 ChainForge可以通过聊天节点对多个对话可以使用不同的llm并行运行。可以对聊天消息进行模板化,并…...
队列--二叉树层序遍历
/*1/ \2 3/\ /\4 5 6 7利用LinkedListQueue1. 头 [1] 尾12.头 [2 3] 尾1 23.头 [3 4 5] 尾1 24.头 [4 5 6 7] 尾1 2 35.头 [] 尾1 2 3 4 5 6 7*/ 代码: class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List&l…...
Ceph入门到精通-Linux内核网络参数优化小结
tcp建连优化 1 tcp建连,降低客户端超时时间 net.ipv4.tcp_syn_retries 6 2 tcp建连,服务端避免syn攻击 netstat -s | grep "SYNs to LISTEN" 1192450 SYNs to LISTEN sockets dropped 可以考虑增大syn队列 net.ipv4.tcp_max_syn_backlo…...
AWK语言第二版 2.6个人库 2.7小结
2.6 个人库 Awk提供了适量的内置函数库,如 length、sub、substr、printf 等其他十来个;在A.2.1节的参考手册中都有列出。你可以自己创建更多函数,以便有需要时引入到Awk程序中。比如内置库函数 sub 和 gsub 都只能返回替换的次数,…...
8年经验之谈 —— Web ui自动化测试框架总结!
实施过了web系统的UI自动化,回顾梳理下,想到什么写什么,随时补充。 首先,自动化测试不是手动测试的替代品,是比较好的补充,而且不是占大比重的补充。 70%的测试工作集中在底层接口测试和单元测试࿰…...
Kafka在企业级应用中的实践
前言 前面说了很多Kafka的性能优点,有些童鞋要说了,这Kafka在企业开发或者企业级应用中要怎么用呢?今天咱们就来简单探究一下。 1、 使用 Kafka 进行消息的异步处理 Kafka 提供了一个可靠的消息传递机制,使得企业能够将不同组件…...
使用企业订货系统后的效果|软件定制开发|APP小程序搭建
使用企业订货系统后的效果|软件定制开发|APP小程序搭建 企业订货系统是一种高效的采购管理系统,它可以帮助企业更好地管理采购流程,降低采购成本,提高采购效率。 可以帮助企业提高销售效率和降低成本的软件工具。使用该系统后,企业…...
STL关联式容器set,multiset,pair,map
set容器是一个集合容器。包含元素是唯一的。集合元素按照一点顺序排列,元素插入过程是顺序插入,所有不能插入指定位置。 set采用红黑树变体的数据结构实现。红黑树属于平衡二叉树。再插入和删除上比vector快。 set不能直接存取元素(不能用a…...
MFC文本输出学习
void CTxttstView::OnDraw(CDC* pDC) {CTxttstDoc* pDoc GetDocument();ASSERT_VALID(pDoc);// TODO: add draw code for native data hereCString str1;pDC->SetBkColor(RGB(0,0,0));pDC->TextOut(50, 50, "一段文字");pDC->SetBkColor(RGB(255,255,255))…...
Python 数据分析与挖掘(一)
Python 数据分析与挖掘(数据探索) 数据探索 1.1 需要掌握的工具(库) 1.1.1 Nump库 Numpy 提供多维数组对象和各种派生对象(类矩阵),利用应用程序接口可以实现大量且繁琐的数据运算。可以构建…...
【问题证明】矩阵方程化为特征值方程求得的特征值为什么是全部特征值?不会丢解吗?
问题 这个问题困扰了我好久,一直感觉如果有其他的特征值没法证伪,不过一直存在思想的层面,没有实际解决,今天突然想到动笔来解决,遂得解,证明如下。 证明 总结 这个证明看似证明过后很直观,但…...
虹科干货 | 不是吧,Redis Enterprise也能当向量数据库来用?
什么是向量相似性搜索啊? 例如,你需要搜索一棵发财树的图片,如果用传统数据库来检索,你大概率会在茫茫树丛中错失心仪的发财树。但是,向量相似性搜索能用向量来表示所有树的特征,这样就能够通过计算向量之间…...
汽车驾驶 - 四梁六柱是什么
汽车的四梁六柱指的是车辆的两个前纵梁,两个后纵梁和ABC柱。虽然不像车辆上的发动机变速箱这些部件出镜率那么高,但这几个部位的重要作用可一点都不含糊。一辆车在碰撞时能够受力起到保护左右的就是四梁六柱,对我们汽车的安全性起到至关重要的…...
CI522 13.56MHZ电动车NFC测试资料
Ci522是一颗工作在13.56MHz频率下的非接触式读写芯片,支持读A卡(CI523支持读A/B卡),可做智能门锁、电动车NFC一键启动、玩具NFC开锁等应用。为部分要求低成本,PCB小体积的产品提供了可靠的选择。 Ci522与Si522/MFRC52…...
【微信小程序开发】一文学会使用CSS样式布局与美化
引言 在微信小程序开发中,CSS样式布局和美化是非常重要的一部分,它能够为小程序增添美感,提升用户体验。本文将介绍如何学习使用CSS进行样式布局和美化,同时给出代码示例,帮助开发者更好地掌握这一技巧。 一、CSS样式布…...
漏刻有时物联网环境态势感知大数据(设备列表、动态折线图)
物联网环境下的态势感知是指对物联网环境中的各种要素进行全面、实时、准确的监测、分析和预测,以实现网络态势的全面掌握和安全威胁的及时响应和处理。具体而言,态势感知以物联网环境为基础,利用各类传感器、数据采集设备和其他相关工具,对物联网设备、资产、数据流等进行…...
【力扣】单调栈:901. 股票价格跨度
【力扣】单调栈:901. 股票价格跨度 文章目录 【力扣】单调栈:901. 股票价格跨度1. 题目介绍2. 思路3. 解题代码参考 1. 题目介绍 设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
