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

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

《offer来了》第二章学习笔记

1.集合 Java四种集合:List、Queue、Set和Map 1.1.List:可重复 有序的Collection ArrayList: 基于数组实现,增删慢,查询快,线程不安全 Vector: 基于数组实现,增删慢,查询快,线程安全 LinkedList: 基于双向链实现,增删快,查询慢,线程不安全 1.2.Queue:队列 ArrayBlockingQueue:

硬件基础知识——自学习梳理

计算机存储分为闪存和永久性存储。 硬盘(永久存储)主要分为机械磁盘和固态硬盘。 机械磁盘主要靠磁颗粒的正负极方向来存储0或1,且机械磁盘没有使用寿命。 固态硬盘就有使用寿命了,大概支持30w次的读写操作。 闪存使用的是电容进行存储,断电数据就没了。 器件之间传输bit数据在总线上是一个一个传输的,因为通过电压传输(电流不稳定),但是电压属于电势能,所以可以叠加互相干扰,这也就是硬盘,U盘

人工智能机器学习算法总结神经网络算法(前向及反向传播)

1.定义,意义和优缺点 定义: 神经网络算法是一种模仿人类大脑神经元之间连接方式的机器学习算法。通过多层神经元的组合和激活函数的非线性转换,神经网络能够学习数据的特征和模式,实现对复杂数据的建模和预测。(我们可以借助人类的神经元模型来更好的帮助我们理解该算法的本质,不过这里需要说明的是,虽然名字是神经网络,并且结构等等也是借鉴了神经网络,但其原型以及算法本质上还和生物层面的神经网络运行原理存在

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa