STM32CubeMX学习教程之三:GPIO输入之利用SysTick中断给按键去抖

2024-04-18 09:38

本文主要是介绍STM32CubeMX学习教程之三:GPIO输入之利用SysTick中断给按键去抖,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

完整源码下载:

https://github.com/simonliu009/STM32CubeMX-GPIO-Debounce

上一篇博文讲述了如何使用GPIO的外部中断检测按键控制LED。但是实际情况是,物理按键通常会有抖动,导致中断多次被触发。较好的设计,应该是在硬件上做去抖设计,比如设计RC电路,或者单端稳态电路、施密特触发器(比如NEC555)等来实现。硬件去抖的好处是可以避免抖动产生的负压对MCU的GPIO造成的可能损坏。坏处是增加成本。而软件实现的去抖则无法避免负压带来的可能损害。

本文主要讨论软件去抖。实现方法是通过SysTick中断每1ms对按键进行扫描,当检测到连续的稳定无抖动电平信号(长度可设置)之后,才进行相应的逻辑操作。

 

软件版本:
STM32CubeMX V4.25.0  
System Workbench V2.4

硬件:OneNet 麒麟座V2.3

 

 

在STM32CubeMX中新建项目,选择正确的MCU型号

然后设置RCC和SYS,然后根据板子实际情况设置时钟(麒麟座外部晶振是12M,STM32F103x的最高主频是72M)

 

然后设置GPIO_Output (连接到LED) 和GPIO_Input(连接到按键)。注意上一篇文章里面按键连接的引脚设置为外部中断模式,这里只需要设置为GPIO_Input就可以了。

GPIO_Output的具体设置如下:

GPIO_Input设置如下

这里按键我用了SW1/2/3/4。

 

同样修改

Project - setting ,ToolChain/IDE选择 SW4STM32

还要勾选这里

 

然后生成代码,打开项目。

编辑stm32f1xx_it.h,函数声明那里增加一行 :

 

void Key_Scan(void);

然后编辑stm32f1xx_it.c 增加如下代码:

/* USER CODE BEGIN 0 */
uint8_t sw1Count,sw2Count,sw3Count,sw4Count;
uint8_t pushFlag1,pushFlag2,pushFlag3,pushFlag4;
extern uint8_t swState1,swState2,swState3,swState4;void Key_Scan(void)
{/*检测SW1是否按下 */if(   HAL_GPIO_ReadPin(SW1_GPIO_Port,SW1_Pin) == GPIO_PIN_RESET ){sw1Count++;                         //SW1键按下,计数sw1Count加1if(sw1Count>=32)                    //1MS中断一次,sw1Count大于等于32,即按键已按下32ms{if(pushFlag1==0)                //判断有没有重按键,1为有,0为没有{swState1=1;                       //设置按键标志sw1Count=0;pushFlag1=1;                     //设置重按键标志}elsesw1Count=0;}elseswState1=0;}else                                            //无按键按下{sw1Count=0;                           //清零sw1CountswState1=0;                            //清除按键标志pushFlag1=0;                          //清除重按键标志}/*检测SW2是否按下 */if(   HAL_GPIO_ReadPin(SW2_GPIO_Port,SW2_Pin) == GPIO_PIN_RESET ){sw2Count++;                         //SW2键按下,计数sw2Count加1if(sw2Count>=32)                    //1MS中断一次,sw2Count大于等于32,即按键已按下32ms{if(pushFlag2==0)                //判断有没有重按键,1为有,0为没有{swState2=1;                       //设置按键标志sw2Count=0;pushFlag2=1;                     //设置重按键标志}elsesw2Count=0;}elseswState2=0;}else                                            //无按键按下{sw2Count=0;                           //清零sw2CountswState2=0;                            //清除按键标志pushFlag2=0;                          //清除重按键标志}/*检测SW3是否按下 */if(   HAL_GPIO_ReadPin(SW3_GPIO_Port,SW3_Pin) == GPIO_PIN_RESET ){sw3Count++;                         //SW3键按下,计数sw3Count加1if(sw3Count>=32)                    //1MS中断一次,sw3Count大于等于32,即按键已按下32ms{if(pushFlag3==0)                //判断有没有重按键,1为有,0为没有{swState3=1;                       //设置按键标志sw3Count=0;pushFlag3=1;                     //设置重按键标志}elsesw3Count=0;}elseswState3=0;}else                                            //无按键按下{sw3Count=0;                           //清零sw3CountswState3=0;                            //清除按键标志pushFlag3=0;                          //清除重按键标志}/*检测SW4是否按下 */if(   HAL_GPIO_ReadPin(SW4_GPIO_Port,SW4_Pin) == GPIO_PIN_RESET ){sw4Count++;                         //SW4键按下,计数sw4Count加1if(sw4Count>=32)                    //1MS中断一次,sw4Count大于等于32,即按键已按下32ms{if(pushFlag4==0)                //判断有没有重按键,1为有,0为没有{swState4=1;                       //设置按键标志sw4Count=0;pushFlag4=1;                     //设置重按键标志}elsesw4Count=0;}elseswState4=0;}else                                            //无按键按下{sw4Count=0;                           //清零sw4CountswState4=0;                            //清除按键标志pushFlag4=0;                          //清除重按键标志}}
/* USER CODE END 0 */

然后在SysTick中断处理函数增加一行 void Key_Scan(void);, 代码如下:

/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{/* USER CODE BEGIN SysTick_IRQn 0 */Key_Scan();/* USER CODE END SysTick_IRQn 0 */HAL_IncTick();HAL_SYSTICK_IRQHandler();/* USER CODE BEGIN SysTick_IRQn 1 *//* USER CODE END SysTick_IRQn 1 */
}

 在gpio.c 中增加如下两处代码:

/* USER CODE BEGIN 1 */
GPIO_TypeDef* GPIO_PORT[] = {LED1_GPIO_Port,LED2_GPIO_Port,LED3_GPIO_Port,LED4_GPIO_Port};const uint16_t GPIO_PIN[] = {LED1_Pin,LED2_Pin,LED3_Pin,LED4_Pin};/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
void LED_Toggle(Led_TypeDef Led)
{
HAL_GPIO_TogglePin(GPIO_PORT[Led], GPIO_PIN[Led]);
}/* USER CODE END 2 */

然后编辑main.c,增加如下两处代码:

/* USER CODE BEGIN 0 */
uint8_t swState1,swState2,swState3,swState4;
/* USER CODE END 0 */
 /* USER CODE BEGIN WHILE */while (1){if  ( swState1 == 1 ){LED_Toggle(LED1);
//		  HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);HAL_Delay(1);}if  ( swState2 == 1 ){LED_Toggle(LED2);
//		  HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);HAL_Delay(1);}if  ( swState3 == 1 ){LED_Toggle(LED3);
//		  HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);HAL_Delay(1);}if  ( swState4 == 1 ){LED_Toggle(LED4);
//		  HAL_GPIO_TogglePin(LED4_GPIO_Port,LED4_Pin);HAL_Delay(1);}/* USER CODE END WHILE */

注意 如下两个语句是等效的,我注释掉了HAL_GPIO_TogglePin()。因为我们使用枚举重新定义了LED状态切换的函数LED_Toggle()。

1.		  LED_Toggle(LED1);  
2.		  HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);

 

然后右键点击项目,选择Properties, Run-Debug Settings, 点击右侧的New,在弹出对话框中选择Ac6 STM32 Debugging。

然后任务栏上点击Run图,当然会报错的,原因请查看另一篇我的博客(https://blog.csdn.net/toopoo/article/details/79680323),所以需要右键点击  项目名Run.cfg ,给它改个名字,

 

然后右键点击项目树里面的项目名称,选择“Propeties”,然后在Run/Debug Settings-选择项目名-Edit-Main-C/C++Application那里点击“Search Project”,然后选择出现的默认的elf文件:

然后在Debugger-User Defined-Browse 那里选择你自己改名的配置文件:

然后右键点击那个新的cfg文件,选择"Open With - Text Editor", 进行如下更改:

source [find interface/stlink.cfg] 更改为 source [find interface/stlink-v2.cfg]

reset_config srst_only srst_nogate connect_assert_srst 这一行改为 reset_config none 

然后再Run一下,就可以了。

 

就实现四个按键分别控制LED的开关切换并实现了软件去抖,不会产生误动作了。

 

本文参考了如下文章:

http://www.waveshare.net/study/article-630-1.html

https://blog.csdn.net/yongyooh/article/details/21877227

这篇关于STM32CubeMX学习教程之三:GPIO输入之利用SysTick中断给按键去抖的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/914423

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss