我是靠谱客的博主 顺心流沙,最近开发中收集的这篇文章主要介绍嵌入式单片机基础篇(四)之stm32以及51单片机按键输入详解嵌入式单片机基础篇(四),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

嵌入式单片机基础篇(四)

stm32以及51单片机按键输入详解

第一部分 stm32 按键输入

1、问题:简述按键输入的基本原理?
回答:按键输入就是将GPIO口配置成输入模式,当芯片检测到IO口的电平状态改变了就执行一些其他功能,比如让led灯发光或者蜂鸣器发声等
2、按键开关的硬件电路图分析
在这里插入图片描述
如上图所示可以分析出当按键按下时,该引脚的电平变为低电平,也就是低电平有效
3、实现程序分析
(1)首先要配置按键的IO口状态

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_3|GPIO_Pin_4;//GPIOE.3~4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化 GPIOE3,4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //初始化 WK_UP-->GPIOA.0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 设置成输入,下拉 
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0
}

注意:既然是按键输入,那么IO口的状态要配置成输入模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
(2)IO口的状态配置好了之后,如果电平已经改变了,那么芯片如何接收该引脚的电平状态呢?
回答:调用下面的函数
GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //读取PE4的引脚电平状态
(3)芯片收到了引脚的电平状态后执行其他函数功能,比如使LED发光,那么需要配置led函数,led函数如下:

void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|
RCC_APB2Periph_GPIOE, ENABLE); //使能 PB,PE 端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE,GPIO_Pin_5); /PE.5 输出高
}

4、问题:至此按键输入的总体思路就讲完了,那么具体如何实现按键输入呢?
回答:消抖以及程序优化
(1)消抖:(以下部分内容来源网络)
机械弹性开关:

当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上就稳定的接通,在断开时也不会一下子彻底断开,而是在闭合和断开的瞬间伴随了一连串的抖动;

按键时间:按键稳定闭合时间长短是由操作人员决定的,通常都会在 100ms 以上,刻意快速按的话能达到 40-50ms 左右,很难再低了;大致时间可以通过在1s时间正常可按多少次来计算;
在这里插入图片描述
当检测到按键状态变化时,不是立即去响应动作,而是先等待闭合或断开稳定后再进行处理。即为按键消抖
按键消抖:可分为硬件消抖和软件消抖。
硬件消抖就是在按键上并联一个电容,如图 8-11 所示,利用电容的充放电特性来对抖动过程中产生的电压毛刺进行平滑处理,从而实现消抖。

  • 但实际应用中,这种方式的效果往往不是很好,而且还增加了成本和电路复杂度,所以实际中使用的并不多。绝大多数情况下,我们是用软件即程序来实现消抖的
    在这里插入图片描述
    软件消抖

(1)延时消抖

最简单的消抖原理,就是当检测到按键状态变化后,先等待一个 10ms 左右的延时时间,让抖动消失后再进行一次按键状态检测,如果与刚才检测到的状态相同,就可以确认按键已经稳定的动作了

//按键处理函数 //输入模式选择:mode:0,不支持连续按;1,支持连续按

//返回按键值===================== ;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下
//4,WKUP按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按,如果模式1的时候,key_up在未松开的时候依然依然可烧描更新
if(key_up&&(KEY00||KEY10||KEY20||WK_UP1))
{
key_up=0; //如果模式是0,key_up在此处变为0之后,在未松开的时候无法扫描更新
delay_ms(10);//去抖动 :通过延时实现
if(KEY00)
return 1;
else if(KEY1
0)
return 2;
else if(KEY20)
return 3;
else if(WK_UP
1)
return 4;
}else if(KEY01&&KEY11&&KEY21&&WK_UP0)
key_up=1;
return 0;// 无按键按下
}
(2):利用定时器消抖

举个例子:我们启用一个定时中断,每 2ms 进一次中断,扫描一次按键状态并且存储起来,连续扫描 8 次后,看看这连续 8 次的按键状态是否是一致的。8 次按键的时间大概是 16ms,这 16ms 内如果按键状态一直保持一致,那就可以确定现在按键处于稳定的阶段,而非处于抖动的阶段,如图 8-12:

/* T0 中断服务函数,用于按键状态的扫描并消抖 */
void InterruptTimer0() interrupt 1
{
static unsigned char keybuf = 0xFF; //扫描缓冲区,保存一段时间内的扫描值
TH0 = 0xF8; //重新加载初值
TL0 = 0xCD;
keybuf = (keybuf<<1) | KEY4; //缓冲区左移一位,并将当前扫描值移入最低位
if (keybuf == 0x00)
{ //连续 8 次扫描值都为 0,即 16ms 内都只检测到按下状态时,可认为按键已按下
KeySta = 0;
}
else if (keybuf == 0xFF)
{ //连续 8 次扫描值都为 1,即 16ms 内都只检测到弹起状态时,可认为按键已弹起
KeySta = 1;
}
else
{} //其它情况则说明按键状态尚未稳定,则不对 KeySta 变量值进行更新
}
利用之前的例子修改

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_go=1;
	key_up=0;
	//delay_ms(10);//去抖动 
	if(flag_key_delay)
	{ 
		flag_key_delay=0;
		if(KEY0==0)
			return 1;
		else if(KEY1==0)
			return 2;
		else if(KEY2==0)
			return 3;
		else if(WK_UP==1)
			return 4;
	}
	
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)
		key_up=1; 	    
return 0;// 无按键按下

}
在这里插入图片描述
注意:一般时候都是采用的软件消抖,软件的消抖方法简而言之就是在判断电平状态之后加上延时
(2)程序优化
既然采用软件消抖的方法是加上延时,那么还需要考虑其他的按键情况吗?
回答:还要考虑按键是否支持连续按键,比如一直按着按键不放,信号量就一直增加或者信号量只加一次就是支持连续按或者不支持连续按键的例子
知识点补充:
C语言关键字 :static
Static申明的局部变量,存储在静态存储区。
它在函数调用结束之后,不会被释放。它的值会一直保留下来。
所以可以说static申明的局部变量,具有记忆功能。
关于static的问题:每次调用getValue函数之后,返回值是多少?
int getValue(void)
{
int flag=0;
flag++;
return flag;
}
回答:1,1,1,1……
int getValue(void)
{
static int flag=0;
flag++;
return flag;
}
回答:1,2,3,4……
举例:

#include <stdio.h>

void fn(void)
{
    int n = 10;

    printf("n=%dn", n);
    n++;
    printf("n++=%dn", n);
}

void fn_static(void)
{
    static int n = 10;

    printf("static n=%dn", n);
    n++;
    printf("n++=%dn", n);
}

int main(void)
{
    fn();
    printf("--------------------n");
    fn_static();
    printf("--------------------n");
    fn();
    printf("--------------------n");
    fn_static();

    return 0;
}

执行结果:

-> % ./a.out 
n=10
n++=11
--------------------
static n=10
n++=11
--------------------
n=10
n++=11
--------------------
static n=11
n++=12

static关键字讲完了下面来分析是否支持连续按键程序

u8 KEY_Scan(u8 mode)
{  
 static u8 key_up=1;
 if(mode)key_up=1; //支持连续按
 if(key_up&&(key1==0||key2==0))
 {
  delay_ms(10);
  key_up=0;
  if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)
   return 1;
  else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==0)
   return 2;
  }
 else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==1&&GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==1)
  key_up=1;      
  return 0;
}

程序分析:
我们先分析当mode为1的时候并且按住按键不放,那么KEY_Scan每次调用key_up都为1,所以类似于信号量一直变化
当mode为0时,那么调用KEY_Scan时,key_up由1变为0,如果不松开按键,那么key_up一直为0,所以类似于信号量只变化一次
接下来我们分析一下主函数,主函数如下:

#include "stm32f10x.h"
#include "sys.h" 
#include "key.h"
#include "led.h"
#include "beep.h"
#include "delay.h"
 void Delay(u32 count)
 {
   u32 i=0;
   for(;i<count;i++);
 }
 int main(void)
 { 
  vu8 key=0; 
 delay_init();         
 LED_Init();     
 BEEP_Init();         
 KEY_Init();         
 while(1)
 {
   key=KEY_Scan(0); 
     if(key)
  {         
   switch(key)
   {     
    
     break; 
    case 2: 
      LED1=!LED1; BEEP=!BEEP;
     break;
    case 1: 
       LED2=!LED2;  
     break;
   }
  }else delay_ms(10); 
 }  
 }

程序分析:
主函数并不复杂,主函数里while循环一直扫描按键,如果按键按下,key变量发生变化,从而执行不同的程序
程序源码:
按键扫描程序

第二部分 51单片机之按键输入

51单片机程序:

#include "reg52.h"//此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

sbit k1=P3^1;	 //定义P31口是k1
sbit led=P2^0;	 //定义P20口是led
void delay(u16 i)
{
	while(i--);	
}

void key()
{
	if(k1==0)		  //检测按键K1是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(k1==0)	 //再次判断按键是否按下
		{
			led=~led;	  //led状态取反
		}
		while(!k1);	 //检测按键是否松开
	}		
}

void main()
{	
	led=1;
	while(1)
	{	
		key();  //按键处理函数	
	}		
}

程序分析:51单片机按键扫描程序比较简单,不再赘述。

最后

以上就是顺心流沙为你收集整理的嵌入式单片机基础篇(四)之stm32以及51单片机按键输入详解嵌入式单片机基础篇(四)的全部内容,希望文章能够帮你解决嵌入式单片机基础篇(四)之stm32以及51单片机按键输入详解嵌入式单片机基础篇(四)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部