当前位置: 首页 > news >正文

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&#xff08;n<50000) 数组 arr&#xff0c;进行Q次查询&#xff08;Q<200000&#xff09;&#xff0c;每次查询的内容为数组arr在 [L , R] 的切片的极差&#xff08;最大元素 - 最小元素&#xff09; 二、解题思路 1、线段树 区间极差…...

element-plus自动引入组件报错,例如collapse、loading

element-plus自动引入组件&#xff0c;例如collapse、loading&#xff0c;使用时报错&#xff0c;报错信息如下图所示&#xff1a; 解决办法&#xff1a;vite-config.ts改变vue的引入顺序&#xff0c;将vue放在第一个...

ChainForge:衡量Prompt性能和模型稳健性的GUI工具包

ChainForge是一个用于构建评估逻辑来衡量模型选择&#xff0c;提示模板和执行生成过程的GUI工具包。ChainForge可以安装在本地&#xff0c;也可以从chrome浏览器运行。 ChainForge可以通过聊天节点对多个对话可以使用不同的llm并行运行。可以对聊天消息进行模板化&#xff0c;并…...

队列--二叉树层序遍历

/*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*/ 代码&#xff1a; class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List&l…...

Ceph入门到精通-Linux内核网络参数优化小结

tcp建连优化 1 tcp建连&#xff0c;降低客户端超时时间 net.ipv4.tcp_syn_retries 6 2 tcp建连&#xff0c;服务端避免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提供了适量的内置函数库&#xff0c;如 length、sub、substr、printf 等其他十来个&#xff1b;在A.2.1节的参考手册中都有列出。你可以自己创建更多函数&#xff0c;以便有需要时引入到Awk程序中。比如内置库函数 sub 和 gsub 都只能返回替换的次数&#xff0c…...

8年经验之谈 —— Web ui自动化测试框架总结!

实施过了web系统的UI自动化&#xff0c;回顾梳理下&#xff0c;想到什么写什么&#xff0c;随时补充。 首先&#xff0c;自动化测试不是手动测试的替代品&#xff0c;是比较好的补充&#xff0c;而且不是占大比重的补充。 70%的测试工作集中在底层接口测试和单元测试&#xff0…...

Kafka在企业级应用中的实践

前言 前面说了很多Kafka的性能优点&#xff0c;有些童鞋要说了&#xff0c;这Kafka在企业开发或者企业级应用中要怎么用呢&#xff1f;今天咱们就来简单探究一下。 1、 使用 Kafka 进行消息的异步处理 Kafka 提供了一个可靠的消息传递机制&#xff0c;使得企业能够将不同组件…...

使用企业订货系统后的效果|软件定制开发|APP小程序搭建

使用企业订货系统后的效果|软件定制开发|APP小程序搭建 企业订货系统是一种高效的采购管理系统&#xff0c;它可以帮助企业更好地管理采购流程&#xff0c;降低采购成本&#xff0c;提高采购效率。 可以帮助企业提高销售效率和降低成本的软件工具。使用该系统后&#xff0c;企业…...

STL关联式容器set,multiset,pair,map

set容器是一个集合容器。包含元素是唯一的。集合元素按照一点顺序排列&#xff0c;元素插入过程是顺序插入&#xff0c;所有不能插入指定位置。 set采用红黑树变体的数据结构实现。红黑树属于平衡二叉树。再插入和删除上比vector快。 set不能直接存取元素&#xff08;不能用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 数据分析与挖掘&#xff08;数据探索&#xff09; 数据探索 1.1 需要掌握的工具&#xff08;库&#xff09; 1.1.1 Nump库 Numpy 提供多维数组对象和各种派生对象&#xff08;类矩阵&#xff09;&#xff0c;利用应用程序接口可以实现大量且繁琐的数据运算。可以构建…...

【问题证明】矩阵方程化为特征值方程求得的特征值为什么是全部特征值?不会丢解吗?

问题 这个问题困扰了我好久&#xff0c;一直感觉如果有其他的特征值没法证伪&#xff0c;不过一直存在思想的层面&#xff0c;没有实际解决&#xff0c;今天突然想到动笔来解决&#xff0c;遂得解&#xff0c;证明如下。 证明 总结 这个证明看似证明过后很直观&#xff0c;但…...

虹科干货 | 不是吧,Redis Enterprise也能当向量数据库来用?

什么是向量相似性搜索啊&#xff1f; 例如&#xff0c;你需要搜索一棵发财树的图片&#xff0c;如果用传统数据库来检索&#xff0c;你大概率会在茫茫树丛中错失心仪的发财树。但是&#xff0c;向量相似性搜索能用向量来表示所有树的特征&#xff0c;这样就能够通过计算向量之间…...

汽车驾驶 - 四梁六柱是什么

汽车的四梁六柱指的是车辆的两个前纵梁&#xff0c;两个后纵梁和ABC柱。虽然不像车辆上的发动机变速箱这些部件出镜率那么高&#xff0c;但这几个部位的重要作用可一点都不含糊。一辆车在碰撞时能够受力起到保护左右的就是四梁六柱&#xff0c;对我们汽车的安全性起到至关重要的…...

CI522 13.56MHZ电动车NFC测试资料

Ci522是一颗工作在13.56MHz频率下的非接触式读写芯片&#xff0c;支持读A卡&#xff08;CI523支持读A/B卡&#xff09;&#xff0c;可做智能门锁、电动车NFC一键启动、玩具NFC开锁等应用。为部分要求低成本&#xff0c;PCB小体积的产品提供了可靠的选择。 Ci522与Si522/MFRC52…...

【微信小程序开发】一文学会使用CSS样式布局与美化

引言 在微信小程序开发中&#xff0c;CSS样式布局和美化是非常重要的一部分&#xff0c;它能够为小程序增添美感&#xff0c;提升用户体验。本文将介绍如何学习使用CSS进行样式布局和美化&#xff0c;同时给出代码示例&#xff0c;帮助开发者更好地掌握这一技巧。 一、CSS样式布…...

漏刻有时物联网环境态势感知大数据(设备列表、动态折线图)

物联网环境下的态势感知是指对物联网环境中的各种要素进行全面、实时、准确的监测、分析和预测,以实现网络态势的全面掌握和安全威胁的及时响应和处理。具体而言,态势感知以物联网环境为基础,利用各类传感器、数据采集设备和其他相关工具,对物联网设备、资产、数据流等进行…...

【力扣】单调栈:901. 股票价格跨度

【力扣】单调栈&#xff1a;901. 股票价格跨度 文章目录 【力扣】单调栈&#xff1a;901. 股票价格跨度1. 题目介绍2. 思路3. 解题代码参考 1. 题目介绍 设计一个算法收集某些股票的每日报价&#xff0c;并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究

摘要&#xff1a;在消费市场竞争日益激烈的当下&#xff0c;传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序&#xff0c;探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式&#xff0c;分析沉浸式体验的优势与价值…...

Tauri2学习笔记

教程地址&#xff1a;https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引&#xff1a;https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多&#xff0c;我按照Tauri1的教程来学习&…...

智能体革命:企业如何构建自主决策的AI代理?

OpenAI智能代理构建实用指南详解 随着大型语言模型&#xff08;LLM&#xff09;在推理、多模态理解和工具调用能力上的进步&#xff0c;智能代理&#xff08;Agents&#xff09;成为自动化领域的新突破。与传统软件仅帮助用户自动化流程不同&#xff0c;智能代理能够自主执行工…...