• STM32-按键检测


    做按键检测时,GPIO为输入操作

    读取IO口输入电平调用的库函数为:

    uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

    读取IO口输入电平操作的寄存器为:

    GPIOx_IDR:端口输入寄存器

    使用位带操作读取IO口输入电平:

    PEin(4) -读取GPIOE.4口电平
    PEin(n) -读取 GPIOE.n口电平

    按键输入实验

    1.使能按键对应IO口时钟。调用函数:RCC_APB2PeriphClockCmd();

    2.初始化IO模式:上拉/下拉输入。调用函数:GPIO_lnit();

    3.扫描IO口电平(库函数/寄存器/位操作)。

    按键扫描(支持连续按)的实现思路

    这个就是学51时最开始用的那个按键检测方法,简单

    连续按意思是只要一直按住按键,按键就一直生效,比如增大音量键,按住不放,则音量会一直增加

    如果要实现:按键按下,没有松开,只能算按下一次,这个函数无法实现。

    u8 KEY_Scan(void)
    {
    	if(KEY被按下)
    	{
    		delay_ms(10);		//消抖
    		if(KEY确实被按下)
    		{
    			return KEY_Value;
    		}
    		return 无效值;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    按键扫描(不支持连续按)的实现思路

    不支持连续按意思就是,按下按键了,就算不松手,也只算按键被按下一次,必须松手后,再按下,才能算第二次按下,比如开启空调,按住电源键不放,也只是当作按下一次按键,不会说是连续按,电源开了又关,关了又开,不会这样设计

    增加一个按下标志位key_up,按下后置0,只有当松手时才置1,要key_up和引脚状态同时为真才返回按键值

    u8 KEY_Scan(void)
    {
    	static u8 key_up = 1;
    	if(key_up && KEY被按下)
    	{
    		delay_ms(10);		//消抖
    		if(KEY确实被按下)
    		{
    			key_up = 0;
    			return KEY_Value;
    		}
    	}else if(KEY没有被按下){
    		key_up = 1;
    	}
    	return 没有按下
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    支持连续按 和 不支持连续按 两者结合

    在不支持连续按的代码上添加一句 if(mode == 1) key_up = 1;即可,可通过对传入的参数mode进行判断来实现两种按键方式的切换

    如果mode == 1,则key_up = 1,则支持连续按;因为key_up被初始化为1,第一次按下时,if(key_up && KEY被按下)判断为真,检测KEY确实被按下后,key_up被置为0,返回 KEY_Value 然后return退出,假如是一直按着按键的,那下一次进行按键检测时,if(mode == 1) 为真,key_up = 1,所以 if(key_up && KEY被按下)判断又为真,继续进入里面执行,继续返回KEY_Value,达到连续按效果

    如果mode != 1,则判断为假,key_up = 1不会执行,则这条 if 判断等于没有,代码就是上面不支持连续按的效果

    u8 KEY_Scan(u8 mode)
    {
    	static u8 key_up = 1;
    	if(mode == 1) key_up = 1;	//mode == 1,则支持连续按
    	if(key_up && KEY被按下)
    	{
    		delay_ms(10);		//消抖
    		if(KEY确实被按下)
    		{
    			key_up = 0;
    			return KEY_Value;
    		}
    	}else if(KEY没有被按下)
    	{
    		key_up = 1;
    	}
    	return 没有被按下
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    所以要切换按键方式,在main函数while循环中调用KEY_Scan函数时,如果想支持连续按,则传入参数1,如果想不支持连续按,则传入参数不是1即可

    void main()
    {
    	while(1)
    	{
    		KEY = KEY_Scan(1);	//支持连续按
    		KEY = KEY_Scan(0);	//不支持连续按
            …………
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    程序

    KEY.h

    定义两个按键状态的宏定义,声明函数

    #ifndef _KEY_H_
    #define _KEY_H_
    
    #include "stm32f10x.h"
    
    //按键状态宏定义
    #define KEY_DOWN    0
    #define KEY_UP      1
    
    void KEY_Init(void);		//按键引脚初始化函数
    u8   KEY_Scan(u8 mode);		//按键检测函数
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    KEY.c

    要先对按键所接到的引脚进行初始化,配置为上拉输入,因为按键另一端接地,按键没按下时,引脚内部上拉电阻作用将引脚拉高,当按键被按下时,接GND,CPU读取引脚输入为低电平

    按键扫描函数通过参数mode切换支持长按或不支持长按

    #include "KEY.h"
    #include "delay.h"
    
    /**
      * @name   KEY_Init
      * @brief  按键初始化
      * @param  None
      * @retval None
      */
    void KEY_Init(void)
    {
        GPIO_InitTypeDef GPIO_Init_Structure;
        
        //先初始化GPIOA的时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
        //配置GPIOA_Pin_8
        GPIO_Init_Structure.GPIO_Pin = GPIO_Pin_8;          //按键接到了PA8引脚
        GPIO_Init_Structure.GPIO_Mode = GPIO_Mode_IPU;      //设置为上拉输入
    
        //初始化PA8
        GPIO_Init(GPIOA,&GPIO_Init_Structure);
    }
    
    /**
      * @name   KEY_Scan
      * @brief  按键扫描
      * @param  mode:1:支持按键长按;其他:不支持按键长按
      * @retval u8
      */
    u8 KEY_Scan(u8 mode)
    {
        static u8 key_up = 1;
        //读取PA8引脚的值
        u8 KEY1 = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8);
        if(mode == 1) key_up = 1;
        //按键检测
        if(key_up && (KEY1 == 0))
        {
            delay_ms(10);           //消抖
            //确定按键按下
            if(KEY1 == 0)
            {
                key_up = 0;         //记录这次按下
                return KEY_DOWN;    //返回按下标志,0
            }
        }else if(KEY1 == 1){        //按键没按下或者松开
            key_up = 1;    
        }
        return KEY_UP;              //返回没按下标志,1
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    main.c

    主函数while循环不断检测按键扫描函数,检测到函数返回按键按下标志后,执行亮灯操作

    这里使用预编译指令,#ifndef #endif 和 #ifdef #endif,用宏定义KEY_MODE来进行支持长按和不支持长按的切换,这样就不用注释一大块的函数功能,只需注释宏定义语句就行了

    当宏定义了KEY_MODE时,编译支持长按的语句;当注释掉宏定义了KEY_MODE时,编译不支持长按的语句

    #include "LED.h"
    #include "delay.h"
    #include "KEY.h"
    
    #define KEY_MODE 1
    
    
    int main()
    {
    	LED_Init();		//LED初始化
    	delay_init();	//延时初始化
    	KEY_Init();		//按键初始化
    
    	while(1)
    	{
    		#ifndef KEY_MODE
    			//不支持长按
    			if(KEY_Scan(0) == KEY_DOWN)
    			{
    				//GPIO_ResetBits(GPIOA,GPIO_Pin_1);	//点亮LED
    				GPIOA->ODR ^= (0x01<<1);	//对ODR1进行异或取反操作,按一次点亮,再按一次熄灭
    			}
    		#endif
    
    		#ifdef KEY_MODE
    			//支持长按
    			if(KEY_Scan(1) == KEY_DOWN)
    			{
    				GPIOA->ODR ^= (0x01<<1);	//对ODR1进行异或取反操作,按一次点亮,再按一次熄灭
    				delay_ms(1000);
    			}
    		#endif
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    想实现按一次按键亮灯,再按一次就灭灯,这个实现简单,只要把LED的引脚电平翻转即可,但查看了GPIO标准库相关函数发现好像没有让引脚电平翻转的函数,下面是各个函数的功能

    在这里插入图片描述

    然后通过对寄存器进行操作,采用异或的功能,取反某一位,达到按一次亮,再按一次熄灭的效果

    GPIOA->ODR ^= (0x01<<1);	//对ODR1进行异或取反操作,按一次点亮,再按一次熄灭
    
    • 1

    复习状态机用法

    KEY.h

    在KEY.h头文件中添加状态机的枚举类型,定义枚举类型变量STA_KEY,并用extern声明为外部变量

    #ifndef _KEY_H_
    #define _KEY_H_
    
    #include "stm32f10x.h"
    
    //按键状态宏定义
    #define KEY_DOWN    0
    #define KEY_UP      1
    
    //状态机按键枚举类型
    typedef enum
    {
        KEY_Statues1,
        KEY_Statues2
    }STA_KEY_t;
    
    //定义变量
    extern STA_KEY_t STA_KEY;
    
    void KEY_Init(void);		//按键引脚初始化函数
    u8   KEY_Scan(u8 mode);		//按键检测函数
    
    void STA_KEY1(void);        //状态机,按键状态1
    void STA_KEY2(void);        //状态机,按键状态2
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    KEY.c

    源文件中添加不同状态要执行的函数,状态1按一次按键LED灯亮1秒再熄灭,然后切换到状态2,状态2中按一次按键LED灯亮1秒再熄灭,然后再亮1秒再熄灭,最后再把状态切回到状态1

    /**
      * @name   STA_KEY1
      * @brief  状态机-按键状态1
      * @param  None
      * @retval None
      */
     void STA_KEY1()
     {
        GPIO_ResetBits(GPIOA,GPIO_Pin_1);   //PA1置0,LED点亮
        delay_ms(1000);
        GPIO_SetBits(GPIOA,GPIO_Pin_1);     //延时1秒后熄灭LED灯
        delay_ms(1000);
    
        //切换状态
        STA_KEY = KEY_Statues2;
     } 
    
     /**
      * @name   STA_KEY2
      * @brief  状态机-按键状态2
      * @param  None
      * @retval None
      */
     void STA_KEY2()
     {
        GPIO_ResetBits(GPIOA,GPIO_Pin_1);   //PA1置0,LED点亮
        delay_ms(1000);
        GPIO_SetBits(GPIOA,GPIO_Pin_1);     //延时1秒后熄灭LED灯、
        delay_ms(1000);
    
        GPIO_ResetBits(GPIOA,GPIO_Pin_1);   //PA1置0,LED点亮
        delay_ms(1000);
        GPIO_SetBits(GPIOA,GPIO_Pin_1);     //延时1秒后熄灭LED灯
        delay_ms(1000);
    
        //切换状态
        STA_KEY = KEY_Statues1;
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    main.c

    主函数中当按键按下后,再用switch case 语句判断状态,并调用不同状态下的函数

    #include "LED.h"
    #include "delay.h"
    #include "KEY.h"
    
    #define KEY_MODE 1
    STA_KEY_t STA_KEY;	//定义状态变量
    
    int main()
    {
    	LED_Init();		//LED初始化
    	delay_init();	//延时初始化
    	KEY_Init();		//按键初始化
    
    	while(1)
    	{
    		#ifndef KEY_MODE
    			//不支持长按
    			if(KEY_Scan(0) == KEY_DOWN)
    			{
    				//GPIO_ResetBits(GPIOA,GPIO_Pin_1);	//点亮LED
    				//GPIOA->ODR ^= (0x01<<1);	//对ODR1进行异或取反操作,按一次点亮,再按一次熄灭
    
    				//状态机点灯
    				switch (STA_KEY)
    				{
    					case KEY_Statues1:STA_KEY1();break;
    					case KEY_Statues2:STA_KEY2();break;
    					default:STA_KEY = KEY_Statues1;break;
    				}
    			}
    		#endif
    
    		#ifdef KEY_MODE
    			//支持长按
    			if(KEY_Scan(1) == KEY_DOWN)
    			{
    				GPIOA->ODR ^= (0x01<<1);	//对ODR1进行异或取反操作,按一次点亮,再按一次熄灭
    				delay_ms(1000);
    			}
    		#endif
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
  • 相关阅读:
    RocketMQ多机集群配置和部署
    5.合宙Air32F103_LCD_key
    数字信号处理、语音信号处理、现代信号处理
    PPT+Visio复现顶刊三维流程图
    常用数据库的最大并发和实际并发
    zabbix 监控--报警平台与分布式
    【Transform3D】转换详解(看完就会)
    数组的子集能否累加出K
    如何计算连续区间,字母分段
    IDEA实现命令行传参快捷方法传参
  • 原文地址:https://blog.csdn.net/weixin_46251230/article/details/126657330