按键输入的硬件连接(枭龙)

独立键盘:

stm32cubeMX配置模拟键盘 stm32f103键盘_stm32cubeMX配置模拟键盘


其中KEY1按键连接在PA0上,可以作普通按键,也可以做待机唤醒输入,KEY2,KEY3,KEY4分别连接到STM32的PC3,PC2,PC1;

这四个按键都可以作为普通IO输入,这四个按键都是低电平有效。(貌似没有矩形键盘)。

读取输入电平函数

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_ GPIO_Pin);

作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。

实例: HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);

寄存器代码

test.c

#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "beep.h"
#include "key.h"

int main()
{
	   u8 key = 0; 
	Stm32_Clock_Init(9);
	 
	 delay_init(72);
	 
	   LED_Init();
	
     BEEP_Init();
	
	   KEY_Init();
	   
	   LED1 = 0;
	   LED2 = 0;
	   LED3 = 0;
	
	while(1)
	{
		key = KEY_Scan(0);
		if( key )
		{
			switch(key)
			{
				case KEY1_PRES:
					LED1 = 1;
				break;
				
				case KEY2_PRES:
					LED2 = 1;
				break;
				
				case KEY3_PRES:
				  LED3 = 1;
				break;
				
				case KEY4_PRES:
					LED1 = 0;
	        LED2 = 0;
	        LED3 = 0;
				break;
			}
		}
	}
	 
	
}

key.h

#ifndef __KEY_H
#define __KEY_H

#include "sys.h"

#define KEY1 PAin(0)
//KEY1 PA0 输入
#define KEY2 PCin(3)
#define KEY3 PCin(2)
#define KEY4 PCin(1)

#define KEY1_PRES 1
//按键返回值
#define KEY2_PRES 2
#define KEY3_PRES 3
#define KEY4_PRES 4

void KEY_Init(void);
u8 KEY_Scan(u8);

#endif

key.c

#include "key.h"
#include "delay.h"

void KEY_Init(void)
{
	 RCC->APB2ENR |= 1<<2; //GPIOA时钟使能
	 RCC->APB2ENR |= 1<<4; //GPIOC时钟使能
	 C
	 GPIOA->CRL &= 0xFFFFFFF0;
	 GPIOA->CRL |= 0x00000008;   //PA0 普通下拉输入
	 GPIOA->ODR |= 1;            //PA0 拉高
	 
	 GPIOC->CRL &= 0xFFFF000F;
     GPIOC->CRL |= 0x0000888F;   //PC 2 3 4 普通下拉输入
     GPIOC->ODR |= 7<<1;         //PC 2 3 4 拉高
}


u8 KEY_Scan(u8 mode)
{
	 static u8 key_up = 1;//按下状态标志
	 if(mode)
		 key_up = 1;//支持连按
	 if(key_up&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))
	 {
		 delay_ms(10);
		 //延迟消抖
		 key_up = 0;
		 //松开标志,为下次做准备
		 if(KEY1 == 0) return 1;
		 else if(KEY2 == 0) return 2;
		 else if(KEY3 == 0) return 3;
		 else if(KEY4 == 0) return 4;
	 }
	 else if(KEY1 == 1 && KEY2 == 1 && KEY3 == 1 && KEY4 ==1)
		 key_up = 1;
	 return 0;
}

HAL库代码

main.c

#include "MyIncludes.h"

int main()
{
  System_Init();
  LED_Init();
  SysTick_Init(NULL);
  //NULL为空,相当于关闭systick滴答定时器把
  Key_Init();//按键初始化

  while(1)
  {
    Key_Read();
    if(Key_Info.Num_Last != Key_Info.Num)
    //如果过去的按键值不等于现在的,说明新的按键输入
    {
       Key_Info.Num_Last = Key_Info.Num;
       //让过去的等于现在的,作用:判断是否有按键按下
       if(Key_Info.Num != 0)
       {
         switch(Key_Info.Num)
         {
           case KEY_ONE:
           LED_Open(LED1);
           break;

           case KEY_TWO:
           LED_Close(LED1);
           break;

            case KEY_THREE:
            LED_Open(LED2);
            break;

            case KEY_FOUR:
            LED_Close(LED2);
            break;

            default:break;
         }
       }
    }
  }
}

key.h

#ifndef __KEY_H_
#define __KEY_H_

#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"

#define KEY_SHAKE_DELAY 10
//按键去抖延时 

#define UP_TRIGGER 1
//按键抬起触发
#define DOWN_TRIGGER 2
//按键按下触发
 
 #define KEY_TRIGGER_MODE  DOWN_TRIGGER
 //按键触发方式,按键按下触发
enum key_num
{
  KEY_ONE = 1,
  KEY_TWO = 2,
  KEY_THREE = 3,
  KEY_FOUR = 4,
  KEY_FIVE = 5,
  KEY_SIX = 6,
  KEY_SEVEN = 7,
  KEY_EIGHT = 8,
  KEY_NINE = 9,
};
//注意枚举里面是,(逗号),枚举括号后有个;(分号);
enum key_state
{
  Key_UP = 1,
  Key_DOWN = 2,
  Key_KEEP = 3,
};

typedef struct
{
  u8 Num; //按键
  u8 State; //按键状态
  u8 Num_Last; // 上次按键值
  u8 Shake_LastNum; //上次 去抖键值
  u32 Key_Delay_Cnt; // 按键去抖计数器
}_KEY_;

extern _KEY_ Key_Info;

void Key_Init(void);
//按键初始化
void Key_Read(void);
//按键读取
#endif

key.c

#include "key.h"
#include "delay.h"

_KEY_ Key_Info;
//结构体变量声明

void Key_Info_Init(void)
//按键结构体信息初始化
{
  Key_Info.Num = 0; 
  //按键值为零
  Key_Info.State = Key_UP;
  //按键的状态为按键抬起  
  Key_Info.Num_Last = 0;
  //上次按键的值为0
  Key_Info.Key_Delay_Cnt = 0;
  //去抖计数器清零
}

void Key_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStruct;
 
  __GPIOA_CLK_ENABLE();
  //使能GPIOA时钟
  __GPIOC_CLK_ENABLE();
  //使能GPIOC时钟,因为按键分别为
  //PA0 PC1 PC2 PC3
   
   GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;//输入模式
   GPIO_InitStruct.Pull = GPIO_NOPULL;  // 上拉
   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;//高速
   HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);//管脚初始化

   GPIO_InitStruct.Pin =  GPIO_PIN_0;
   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
   GPIO_InitStruct.Pull = GPIO_NOPULL;
   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
   HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);
   Key_Info_Init();//按键初始化
}

__INLINE u8 Independent_Key_Scan(void)
{
  if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET )
  {
     return KEY_ONE;//KEY1按键按下(WKUP)
  }
  if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_3) == GPIO_PIN_RESET )
  {
    return KEY_TWO;
  }
  if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_2) == GPIO_PIN_RESET )
  {
    return KEY_THREE;
  }
  if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_1) == GPIO_PIN_RESET )
  {
    return KEY_FOUR;
  }
  return 0;
}

void Key_Read(void)
{
  u8 Key_CurrNum; //当前按键值
  delay_ms(1);  
  //每隔1ms扫描一次按键
  Key_CurrNum = 0;
  //每次读数之前将上次读到的值清除
  Key_CurrNum = Independent_Key_Scan();
  //获取按键

  if((Key_Info.Shake_LastNum == Key_CurrNum)&&(Key_CurrNum != 0))
  //如果上次去抖的键值等于当前的键值
  {
    Key_Info.Key_Delay_Cnt++; //去抖计数
    #if KEY_TRIGGER_MODE == DOWN_TRIGGER //按键按下触发
    if( Key_Info.Key_Delay_Cnt > KEY_SHAKE_DELAY )
    {
      Key_Info.Key_Delay_Cnt = KEY_SHAKE_DELAY + 1;
      //如果延时计数大于去抖说明有效,然后让延时计数一直等于11
      Key_Info.State = Key_KEEP; //按键保持按下
    }
   
    if(Key_Info.Key_Delay_Cnt == KEY_SHAKE_DELAY )
    {
      Key_Info.Num = Key_Info.Shake_LastNum;
      //去抖后的按键值赋给Key_Info.Num;
      Key_Info.State = Key_DOWN;
      //按键按下
    }
    #endif
  }
  else 
  //说明按下值与上次去抖的值不同
  {
      #if KEY_TRIGGER_MODE == DOWN_TRIGGER
      //如果模式是按键按下触发
      Key_Info.Key_Delay_Cnt = 0;
      //计数器清零
      Key_Info.Num = 0;
      //按键值清零
      Key_Info.State = Key_UP;
      //按键状态抬起,因为不足去抖时间
      goto KEY_END;
      //就是将当前按键值赋予上次去抖值
      
      #elif KEY_TRIGGER_MODE == UP_TRIGGER
      //如果模式是抬起触发
      if( Key_Info.Key_Delay_Cnt < KEY_SHAKE_DELAY )
      //如果计数器小于按键去抖延时,说明按键无效
      {
        Key_Info.Key_Delay_Cnt = 0;
        //计数器清零
        Key_Info.Num = 0;
        //按键值清零
        Key_Info.State = Key_UP;
        //按键状态抬起
        goto KEY_END;
      }
      else
      //如果计数器大于等于按键去抖延时,说明抬起的时间够
      {
       Key_Info.Key_Delay_Cnt = 0;
       //清零计数器
       Key_Info.Num = Key_Info.Shake_LastNum;
       //记录按键值
       Key_Info.State = Key_UP;
       //按键状态抬起
       goto KEY_END;
      }
      #endif
  }
  KEY_END:
  Key_Info.Shake_LastNum = Key_CurrNum;
}

总结补充

按键实验main.c和key.h都很好理解,为什么
#define UP_TRIGGER 1
#define DOWN_TRIGGER 2
这个没有也可以,只是标记一下触发方式 1 2 其实没什么用
这个也很好理解,因为在后面有配置按键触发方式
#define KEY_TRIGGER_MODE DOWN_TRIGGER

主要的是key.c 有按键结构体信息初始化,按键IO初始化,按键扫描 按键读取,前三个中规中矩,按键读取感觉就有点绕,我的理解是这样的:

void Key_Read(void)思路:

首先声明一个当前按键值Key_CurrNum用来存取读取的按键值,

按键按下触发:

刚开始当前的当前按键值Key_CUrrNum不等于上次去抖按键值Shake_LastNum于是进入

stm32cubeMX配置模拟键盘 stm32f103键盘_键值_02


计数器为零,按键为零,因为未达到按键去抖延时,所以,按键状态还是抬起,然后进入 KEY_END;让当前按键值Key_CUrrNum等于上次去抖按键值Shake_LastNum,如果没有继续,那么下次还执行上面的函数,如果继续按下那么第二次扫描当前按键值仍然等于上次去抖按键值Shake_LastNum,则进入

stm32cubeMX配置模拟键盘 stm32f103键盘_#define_03


每进入一次,去抖计时加一,等去抖计时等于10的时候,说明按键按下,如果一直按,就是去抖计时大于10,则让去抖计时就等于按键去抖延时+1,应该是防止去抖计时过大按键抬起触发

首先要将

stm32cubeMX配置模拟键盘 stm32f103键盘_键值_04


GPIO_PIN_RESET 改成 GPIO_PIN_RESET;

也是刚开始当前的当前按键值Key_CUrrNum不等于上次去抖按键值Shake_LastNum且去抖计时小于按键去抖延时于是进入

stm32cubeMX配置模拟键盘 stm32f103键盘_stm32cubeMX配置模拟键盘_05


清零计数器,按键值清零,按键状态为抬起,并且将让当前按键值Key_CUrrNum等于上次去抖按键值Shake_LastNum,如果没有继续则下次还是进行上面图片中的函数,如果继续抬起则进入

stm32cubeMX配置模拟键盘 stm32f103键盘_#include_06


开始去抖计时,直到去抖计时大于等于按键抖动延时,然后开始执行

stm32cubeMX配置模拟键盘 stm32f103键盘_stm32cubeMX配置模拟键盘_07


清零计数器,(这里我考虑了一下如果不清零计数器,和按下触发一样,也就是将去抖计时等于按键抖动延时+1,会怎么样?但是感觉按键抖动延时时间很短,应该都差不多)

记录有效按键值,按键状态为抬起,如果一直抬起,则 经过按键抖动延时后再次记录有效按键值