STM32-自学笔记(10.使用GPIO和SysTick定时器实现按键扫描)

按键抖动的原因

通常按键所用的开关都是机械弹性开关。当按键触点闭合或者断开时,由于机械触点的弹性特点,一个按键开关在闭合时不会马上就稳定的接通。在断开时也不会一下子就彻底断开。而是在闭合和断开时伴随了一系列抖动。

按键消抖的方法:硬件消抖和软件消抖

硬件消抖

利用电容的充放电特性对抖动过程中产生的电压毛刺进行平滑处理。

软件消抖

通过延迟程序过滤,通过延迟来过滤掉抖动时间。

实验设计

4个按键控制4个发光二极管的亮灭状态。

硬件电路

  • 4个按键分别连接GPIOA.0~GPIOA.3
  • 4个LED灯分别连接GPIOA.4~GPIOA.7

软件设计(程序设计)

要点注意

  1. 配置RCC寄存器组,使用PLL输出72MHz时钟作为系统时钟。
  2. 配置GPIOA.0~GPIOA.3这4个引脚为上拉输入模式,并配置GPIOA.4~GPIOA.7这4个引脚为推挽输出模式。
  3. 配置SysTick使用系统时钟,并产生20ms时间间隔。现在结合图5.3.1分析按键扫描方法的流程。每20ms进行一次按键扫描。
  • 首先假设初始化状态没有按键按下,GPIO上也不存在抖动干扰电平,则此时的状态是“按键扫描状态0”。进入执行之后,因为扫描不到GPIO的电平变化,因此直接退出该状态,并且不更新状态标志。20ms之后仍然执行重复的过程。
  • 其次假设仍然没有按键按下,但GPIO上有干扰抖动电平,则程序首先仍然进入“按键扫描状态0”。但进入执行之后,扫描到了GPIO上的电平变化,更新状态标志至“按键扫描状态1”。20ms之后,进入“按键扫描状态1”,再次检测GPIO电平。因为是抖动电平,其持续的时间不会大于20ms,所以此处并不能再次检测到电平变化,因此确认为抖动电平,更新状态标志至“按键扫描状态0”。这样就避免了将抖动电平误识别为按键按下的情况。
  • 最后假设有按键按下。首先仍然是“按键扫描状态0”阶段,此状态阶段是判断按键对应的GPIO电平是否有跳变。此时以假设有按键按下,因此更新状态标志至“按键扫描状态1”。20ms以后,进入“按键扫描状态1”。检测得到GPIO电平还维持上一次的状态,确认为按键按下,更新状态标志为“按键扫描状态2”。再过20ms之后,进入“按键扫描状态2”,此时若还检测到电平维持上一次的状态,则显示表示按键尚未松开,则不更新按键标志。20ms后再次检测,直到按键松开之后才将状态标志更新为“按键扫描状态0”。检测下一次按键操作。

主函数 main.c

#include "stm32f10x_lib.h"              //头文件

typedef enum

{

KeyScanState_0=0X00,

KeyScanState_1=0X01,

KeyScanState_2=0X02,

}

KeyScanState_Typedef;

#define KEYPORT     GPIOA

#define KEY0PIN     GPIO_Pin_0

#define KEY1PIN     GPIO_Pin_1

#define KEY2PIN     GPIO_Pin_2

#define KEY3PIN     GPIO_Pin_3

#define LEDPORT     GPIOA

#define LED0PIN     GPIO_Pin_4

#define LED1PIN     GPIO_Pin_5

#define LED2PIN     GPIO_Pin_6

#define LED3PIN     GPIO_Pin_7

void RccInitialisation(void);

void GpioInitialisation(void);

void SystickInitialisation(void);

int main(void)

{

vu16 KeyPortStatus=0;

KeyPortStatus_Typedef KeyScanState;           //定义按键扫描状态枚举变量

RccInitialisation();                          //设置系统时钟

GpioInitialisation();                         //设置GPIO端口

SystickInitialisation();                      //设置Systick定时器

while(1)

{

if(SysTick_GetFlagStatus(SysTick_FLAG_COUNT)==SET)         //查询20ms是否到

{

KeyPortStatus=GPIO_ReadInputData(KEYPORT) &  0x000f;       //读取I/O电平

switch(KeyScanState)                                       //进入状态机流程

{

//状态1:判断是否有按键按下
case KeyScanState_0:

{

if(KeyScanStatus! = 0x000f)

{

KeyScanState=KeyScanState_1;                  //有按键按下,更新状态标志

}

break;

}

//状态2:判断是否抖动
case KeyScanState_1:

{

if(KeyPortStatus)

{

//非抖动,确认按键按下,执行相应操作
if(GPIO_ReadInputDataBit(KEYPORT,KEY0PIN)==0)

{

GPIO_WriteBit(LEDPORT,LED0PIN,(BitAction)(1-GPIO_ReadoutputDataBit(LEDPORT,LED0PIN)));

}

else if(GPIO_ReadInputDataBit(KEYPORT,KEY1PIN)==0)

{

GPIO_WriteBit(LEDPORT,LED1PIN,(BitAction)(1-GPIO_ReadoutputDataBit(LEDPORT,LED1PIN)));

}

else if(GPIO_ReadInputDataBit(KEYPORT,KEY2PIN)==0)

{

GPIO_WriteBit(LEDPORT,LED2PIN,(BitAction)(1-GPIO_ReadoutputDataBit(LEDPORT,LED2PIN)));

}

else if(GPIO_ReadInputDataBit(KEYPORT,KEY3PIN)==0)

{

GPIO_WriteBit(LEDPORT,LED3PIN,(BitAction)(1-GPIO_ReadoutputDataBit(LEDPORT,LED3PIN)));

}

KeyScanState=KeyScanState_2;                  //更新状态标志

}

else

{

KeyScanState=KeyScanState_0;                  //抖动,确认按键未按下,更新状态标志

}

break;

}

//状态3:松手检测
case KeyScanState_2:

{

if(KeyPortSrarus==0x000f)

{

KeyScanState = KeyScanState_0;                 //松手更新状态标志

}

break;

}

}

}

}

}  

设置系统各部分时钟  RccInitialisation

void  RccInitialisation(void)

{

ErrorStatus HSEStartUpStatus;      //定义枚举类型变量 HSEStartUpStatus

RCC_DeInit();                     //复位系统时钟设置

RCC_HSEConfig(RCC_HSE_ON);         //开启HSE

HSEStatrtUpStatus=RCC_WaitForHSEStartUp();   //等待HSE起振并稳定

if(HSEStatrtUpStatus==SUCCESS)     //判断HSE是否起振成功,是则进入if()内部

{

RCC_HCLKConfig(RCC_SYSCLK_Div1);   //选择HCLK(AHB)时钟源为SYSCLK分频

RCC_PCLK2Config(RCC_HCLK_Div1);    //选择PCLK2时钟源为HCLK(AHB)1分频

RCC_PCLK1Config(RCC_HCLK_Div2);    //选择PCLK1时钟源为HCLK(AHB)2分频

FLASH_SetLatency(FLASH_Latency_2);  //设置Flash延时周期数为2

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);   //使能Flash预取缓存

//选择PLL时钟源为 HSE 1 分频,倍频数为9,则PLL=8MHz *9=72MHz

RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);

RCC_PLLCmd(ENABLE);                  //使能PLL

while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);  //等待PLL输出稳定

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);    //选择SYSCLK时钟源为PLL

while(RCC_GetSYSCLKSource()!=0x08);      //等待PLL成为SYSCLK时钟源

}

}

设置各GPIO端口功能  GpioInitialisation

void GPIO_GpioInitialisation(void)

{

GPIO_InitTypeDef GPIO_InitStructure;          //定义GPIO初始化结构体GPIO_InitStructure

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  //打开APB2总线上的GPIOA时钟

//设置GPIOA.0~GPIOA.3为上拉输入

GPIO_InitStructure.GPIO_Pin=KEY0PIN | KEY1PIN | KEY2PIN | KEY3PIN ;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_IPU;

GPIO_Init(KEYPORT,&GPIO_InitStructure);

//设置GPIOA.4~GPIOA.7为推挽输出,最大翻转频率为50MHz

GPIO_InitStructure.GPIO_Pin=LED0PIN | LED1PIN | LED2PIN | LED3PIN ;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

GPIO_Init(LEDPORT,&GPIO_InitStructure);

}

设置Systick定时器,重装载时间为20ms  SystickInitialisation

void SystickInitialisation (void)

{

SysTick_CounterCmd(SysTick_Counter_Disable);    //失能Systick定时器

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);  //选择HCLK为SysTick时钟源

SysTick_CounterCmd(SysTick_Counter_Clear);      //清除Systick计数器

SysTick_SetReload(72000*20);;     //主频为72MHz,配置计数值为72000*20可以得到20ms定时间隔

SysTick_CounterCmd(SysTick_Counter_Enable);      //启动Systick定时器

}

注意事项:

  • 有的按键扫描程序将按键对应的GPIO引脚设置为浮空输入模式,是因为其外部电路已经加入了上拉电阻。如果没有事先加上上拉电阻,那么必须将相应的GPIO引脚设置为上拉输入模式。
  • STM32控制器开发的一个通用原则:在配置某个外设之前一定要保证其时钟是打开状态的,否则配置无效。

 

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在STM32开发中,使用定时器可以方便地实现按键单击、双击和长按功能。具体实现方式如下: 1. 按键单击: 当按键被按下时,启动定时器计时,若在规定的时间内松开键,则视为单击操作。可以设置定时器1为中断模式,启动定时器后等待中断响应即可。 2. 按键双击: 按键双击功能需要在单击结束后一定时间内再次单击才能触发,一般设置为200ms-300ms。实现方式与单击类似,只需在单击结束后启动定时器再次等待中断响应即可。 3. 按键长按: 按键长按功能一般指按住按键不动一定时间后触发,可以设置一个长按时间阈值,一般为1s-2s。启动定时器开始计时,若在规定时间内键状态一直为高电平,则视为长按操作。 总之,在STM32的开发中,使用定时器可以方便地实现按键单击、双击和长按功能,只需设置好相应的时间参数和中断响应即可。 ### 回答2: 在STM32使用定时器实现按键单击、双击和长按的实现方法比较简单。 按键单击可以通过检测按键是否按下并保持一段时间来实现。在STM32中,可以通过读取GPIO口的状态来检测按键是否按下,并使用定时器来延时判断按键是否被松开。当按键被按下并且被松开时,即可判断为单击事件。 按键双击可以通过在单击事件的基础上增加一个短暂的时间间隔来实现。当第一次按下按键时,开启定时器计数并在计数完毕后进行单击判断,当第二次按下按键时,再次开启定时器计数并在计数完毕后进行单击判断,如果两次单击之间的时间间隔较短,则可以判断为双击事件。 按键长按可以通过读取GPIO口状态并在一段时间内判断按键是否一直为按下状态来实现。当按键被按下时,开启定时器计数,并在计数完成后判断GPIO口是否一直为按下状态,如果是,则判断为长按事件。 总之,在STM32使用定时器实现按键单击、双击和长按的方法非常简单,只需要结合GPIO口状态来进行相应的判断即可。 ### 回答3: STM32系列的微处理器内置了多种定时器功能,可用于实现按键的单击、双击和长按等功能。下面是使用定时器实现按键的单击、双击和长按的具体方法: 1、按键单击:当按键按下时,开启一个短时间的定时器,在定时器时间内检测按键是否释放,如果释放则判断为单击事件。 2、按键双击:当检测到按键第一次单击事件时,开启一个短时间的定时器,在定时器时间内检测是否有第二次单击事件,如果有,则判断为双击事件。 3、按键长按:当按键按下一段时间后,开启一个长时间定时器,在定时器时间内不断检测按键是否释放,如果一直按下不放,则判断为长按事件。 需要注意的是,定时器的时间要适当设置,避免误判或判断不准确。此外,还需要进行防抖处理,避免按键抖动导致误判。 在STM32中,定时器的具体使用可以参考官方提供的开发文档和代码示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值