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. 题目介绍 设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...