ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

STM32-按键输入

2021-09-19 11:05:09  阅读:145  来源: 互联网

标签:KEY0 up STM32 按下 key 按键 GPIO 输入


1  硬件连接

1.1 mini

1.2 战舰

1.3 探索者

        KEY0->PE4  上拉输入        KEY1->PE3  上拉输入        KEY2->PE2  上拉输入        WK_UP->PA0  下拉输入

        按键输入最关键的是对按键的初始化和输入判断。这里按键的初始化与LED的初始化不同的是GPIO的模式不一样了,LED是推挽输出,按键则相反,是输入,这个时候要考虑是哪一种输入方式,这里当我的板子上按键是共阴极的,当按键按下的时候IO口输入的低电平,所以我需要在IO口接上拉电阻,使用上拉输入模式;这里当我的板子上按键是共阳极的,当按键按下的时候IO口输入的高电平,所以我需要在IO口接下拉电阻,使用下拉输入模式。

2  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口电平

3  按键输入实验

        按键消抖:我们采用延时消抖的思想,因为按键是机械开关,所以在按下的时候会产生电平的抖动,这在51单片机中经常使用。我个人认为延时消抖应该是一种思想。

按键消抖 的图像结果

        第一步先检查按键是否按下,如果按下了,那么就延时20毫秒,再次判断按键是否按下,确保其不是因为机械抖动造成的误判断。两次判断以后,其结果基本确定,如果按键按下了,就点亮LED灯,然后再加入一个死循环,等待按键被松开。就是通过一个等待按键松开的函数来跳出循环,达到按键锁存的目的。

        对于按键输入来说,怎么判断是连续按,还是不连续按,这是一个重点。以前是将两种情况分开考虑,写成了两个函数,然后判断,其实,这个可以写成一个函数,只需添加一个选择形参,然后对形参进行判断操作即可。

        按键扫描(支持连续按)的一般思路:如果我要实现:按键按下,没有松开,只能算按下一次,这个函数无法实现。

u8 KEY_Scan(void)                              u8 key_Scan(u8 mode)
{                                              {
   if(KEY按下)                                   if(KEY0 == 0 || KEY1 == 0|| WK_UP == 1)   //key按键按下
  {                                               {
    delay_ms(10);/*延时10-20ms,防抖。*/           delay_ms(10);
    if(KEY确实按下)                                 if(KEY0 == 0)             //    if(key 确实按下)
     {                                                 return KEY0_PRES;     //   返回按键值
      return KEY_Value;                            else if(KEY1 == 0)
     }                                                 return KEY1_PRES;
    return 无效值;                                 else if(WK_UP == 1)
  }                                                    return WKUP_PRES;
}                                                 }
                                                  else if(KEY1 == 1 && KEY0 == 1 && WK_UP == 0)
                                                       return 0;                       //没有按键按下 返回无效值
                                               }

        按键扫描(不支持连续按)的一般思路:不支持连续按:就是说,按键按下了,没有松开,只能算一次。

u8 KEY_Scan(void)                             u8 key_Scan(u8 mode)
{                                             {
  static u8 key_up=1;                            static u8 key_up = 1; //按键松开标志
  if(key_up &&  KEY按下)                       if(key_up && (KEY0 == 0 || KEY1 == 0|| WK_UP == 1))
  {                                              {
    delay_ms(10);//延时,防抖                       delay_ms(10);
    key_up=0;//标记这次key已经按下                  key_up = 0;
    if(KEY确实按下)                                 if(KEY0 == 0)              //if(key确实按下)
     {                                                  return KEY0_PRES;      // return 键值
      return KEY_VALUE;                             else if(KEY1 == 0)
     }                                                  return KEY1_PRES;
  }else if(KEY没有按下)  key_up=1;                  else if(WK_UP == 1)
  return 没有按下                                       return WKUP_PRES;
}                                                }
                                                 else if(KEY1 == 1 && KEY0 == 1 && WK_UP == 0)
                                                    key_up = 1;
                                                 return 0;
                                              }

        按键扫描(两种模式合二为一)的一般思路:

u8 KEY_Scan(u8 mode)                            u8 key_Scan(u8 mode)
{                                               {
  static u8 key_up=1;                             static u8 key_up = 1; //按键松开标志
  if(mode==1) key_up=1;//支持连续按               if(mode)
  if(key_up &&  KEY按下)                            key_up = 1;
  {                                               if(key_up && (KEY0 == 0 || KEY1 == 0|| WK_UP == 1))
    delay_ms(10);//延时,防抖                     {
    key_up=0;//标记这次key已经按下                    delay_ms(10);
    if(KEY确实按下)                                   key_up = 0;
    {                                                 if(KEY0 == 0)
      return KEY_VALUE;                                   return KEY0_PRES;
    }                                                 else if(KEY1 == 0)
   }else if(KEY没有按下)  key_up=1;                       return KEY1_PRES;
   return 没有按下                                    else if(WK_UP == 1)
}                                                         return WKUP_PRES;
                                                  }
                                                  else if(KEY1 == 1 && KEY0 == 1 && WK_UP == 0)
                                                      key_up = 1;
                                                  return 0;
                                                }

          在主函数中 执行到这条 key=KEY_Scan(0)命令 ,也就是mode=0,那么开始执行该函数。定义一个关键字static 变量 key_up,并给其赋值为1。if(mode)key_up=1; 不执行。执行if(key_up&&(KEY0==0||KEY1==0||WK_UP==1)) ,假设此时按下KEY1且不松开,则执行该if后面的语句。delay_ms(10);目的是去抖动。将key_up赋值为0,然后函数返回值为1(KEY0_PRES在宏定义中表示为1),跳出函数,继续执行主函数,使得LED1灯亮。程序继续走,再一次走到key=KEY_Scan(0),调用KEY_Scan函数,这时候由于key_up在上一次被赋值为0了,故此时if(key_up&&(KEY0==0||KEY1==0||WK_UP==1)) 也不执行,直接执行else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==0)key_up=1;此时key_up才重新变为1,然后return 0;那么主函数中key=0,则不执行if(key);故此时LED1灯状态不变,依然亮。这样就实现了第一种模式,即按键连续按的时候,灯/蜂鸣器的状态不改变。(其他的形式类似)

        下面贴一个正点原子论坛中一位老哥的解释:key_up只是作为一个标志,if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1))这里边判断按键按下是要跟key_up相与的,也就是当你有键按下时还需要key_up为1才能让程序读取到按键值。key_up作为静态变量只会被初始化一次(也就是static u8 key_up=1只会执行一次),每一次读取到一个按键值后key_up就会被置0,让它重新变为1只有两种情况:
        (1)else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==0)key_up=1;这里就并不是连续按了,只要你按键松开了,下次继续读取没问题。
        (2)你如果是想连续按着不动又希望程序能多次读取到的话,必须通过if(mode)key_up=1; 这一句就能让key_up恢复1,这样就实现了不需要松开按键就让它恢复1,即可实现连续按键并且程序连续读取。

        (1)使能按键对应IO口时钟。调用函数:RCC_APB2PeriphClockCmd();
        (2)初始化IO模式:上拉/下拉输入。调用函数:GPIO_Init();
        (3)扫描IO口电平(库函数/寄存器/位操作)。

/************mian.c************/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
 
int main(void)
 {
 	vu8 key=0;	
	delay_init();	    	 //延时函数初始化	  
 	LED_Init();			     //LED端口初始化
	KEY_Init();          //初始化与按键连接的硬件接口
	BEEP_Init();         	//初始化蜂鸣器端口
	LED0=0;					//先点亮红灯
	while(1)
	{
 		key=KEY_Scan(0);	//得到键值
	   	if(key)
		{						   
			switch(key)
			{				 
				case WKUP_PRES:	//控制蜂鸣器
					BEEP=!BEEP;
					break;
				case KEY2_PRES:	//控制LED0翻转
					LED0=!LED0;
					break;
				case KEY1_PRES:	//控制LED1翻转	 
					LED1=!LED1;
					break;
				case KEY0_PRES:	//同时控制LED0,LED1翻转 
					LED0=!LED0;
					LED1=!LED1;
					break;
			}
		}else delay_ms(10); 
	}	 
}
 
/************key.h************/
#ifndef __KEY_H
#define __KEY_H	 
#include "sys.h"	 

//#define KEY0 PEin(4)    //PE4
//#define KEY1 PEin(3)	  //PE3 
//#define KEY2 PEin(2)	  //PE2
//#define WK_UP PAin(0)	  //PA0  WK_UP

#define KEY0  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)    //读取按键0
#define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)    //读取按键1
#define KEY2  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)    //读取按键2 
#define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)  //读取按键3(WK_UP) 

#define KEY0_PRES 	1	//KEY0按下
#define KEY1_PRES	2	//KEY1按下
#define KEY2_PRES	3	//KEY2按下
#define WKUP_PRES   4	//KEY_UP按下(即WK_UP/KEY_UP)

void KEY_Init(void);    //IO初始化
u8 KEY_Scan(u8);  	    //按键扫描函数					    
#endif
 
/************key.c************/
#include "stm32f10x.h"
#include "key.h"
#include "sys.h" 
#include "delay.h" 
								    
//按键初始化函数
void KEY_Init(void) //IO初始化
{ 
 	GPIO_InitTypeDef GPIO_InitStructure;
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//KEY0-KEY2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
 	GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,3,4

	//初始化 WK_UP-->GPIOA.0	  下拉输入
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉	  
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0

}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下 
//4,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!!
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(KEY0==0)return KEY0_PRES;
		else if(KEY1==0)return KEY1_PRES;
		else if(KEY2==0)return KEY2_PRES;
		else if(WK_UP==1)return WKUP_PRES;
	}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1; 	    
 	return 0;// 无按键按下
}

        注意此函数有响应优先级:KEY0>KEY1>KEY2>KEY_UP !!

4  C语言关键字 :static

4.1 局部变量

        普通局部变量是再熟悉不过的变量了,在任何一个函数内部定义的变量(不加static修饰符)都属于这个范畴。编译器一般不对普通局部变量进行初始化,也就是说它的值在初始时是不确定的,除非对其显式赋值。普通局部变量存储于进程栈空间,使用完毕会立即释放。静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。变量在全局数据区分配内存空间;编译器自动对其初始化;其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束。静态局部变量的效果跟全局变量有一拼,但是位于函数体内部,就极有利于程序的模块化了。

4.2 全局变量

        全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。

4.3 Static变量

        Static的用途主要有两个:
                一是用于修饰存储类型使之成为静态存储类型
                二是用于修饰链接属性使之成为内部链接属性。
        1)静态存储类型:
                在函数内定义的静态局部变量,该变量存在内存的静态区,所以即使该函数运行结束,静态变量的值不会被销毁,函数下次运行时能仍用到这个值。
                在函数外定义的静态变量——静态全局变量,该变量的作用域只能在定义该变量的文件中,不能被其他文件通过extern引用。
        2) 内部链接属性
                静态函数只能在声明它的源文件中使用。

        下例中,每次调用getValue函数之后,返回值是多少?

int getValue(void)                      int getValue(void)
{                                       {
  int  flag=0;                             static int  flag=0;
  flag++;                                  flag++;
  return flag;                             return flag;
}                                       }

标签:KEY0,up,STM32,按下,key,按键,GPIO,输入
来源: https://blog.csdn.net/qq_39792063/article/details/120377015

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有