蓝桥杯——单片机学习(5(1)——按键(独立按键))

注:此单片机型号为 STC15F2K60S2.

独立按键

原理

来源:我自己:)
这是一个矩阵按键的电路图,要使得其进入独立按键模式,就要把跳线帽连接2、3.
来源:我自己:)
(注:跳线帽是外部可移动的元件。)

此时,即为独立按键模式。
来源:我自己:)

按键原理

以 S7 按键为例,其左端连接接地,右端连接 P30,由于一开始所有 P 口都默认为高电平,则只要检测到 P30 为低电平,则说明按键 S7 被按下。

消抖

在这里插入图片描述

当按键被按下的时候,电路导通接地,I/O口为低电平;
当按键未被按下时,电路断开,I/O口保持高电平。

但一般的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,假如不加以处理,会导致按键被识别为按下多次。为了不产生这种现象而作的措施就是按键消抖。

消抖有硬件消抖和软件消抖两种方法,但我们不可能去更换硬件,所以主要采用软件消抖 —— 写一个延迟 5ms 左右的函数。

参考资料:@我要糖 —— 单片机之按键消抖

代码

入门代码

//key.c

/**************************************************************************** 
* Copyright (C), 2022,Moqim
* 文件名: main.c 
* 内容简述:检测独立按键被按下的键值 
* 
* 文件历史: 
* 版本号      日期       作者     说明 
*   01a    2022-05-18   Moqim   创建该文件
*/

#include "key.h"

//为方便使用,重新定义管脚名
sbit S7 = P3^0;
sbit S6 = P3^1;
sbit S5 = P3^2;
sbit S4 = P3^3;

/**************************************************************************** 
* 函数名: Key_Scan() 
* 功 能: 按键检测
* 输 入: 无        
* 输 出: unsigned char类型的数值,表示键值
*/
unsigned char Key_Scan(void)
{
	if (S4 == 0)
	{
		Key_Delay(10);
		if (S4 == 0)
			return 4;
	}
	
	else if (S5 == 0)
	{
		Key_Delay(10);
		if (S5 == 0)
			return 5;
	}
	
	else if (S6 == 0)
	{
		Key_Delay(10);
		if (S6 == 0)
			return 6;
	}
	
	else if (S7 == 0)
	{
		Key_Delay(10);
		if (S7 == 0)
			return 7;
	}
	
	return 0;
}

/**************************************************************************** 
* 函数名: Key_Delay() 
* 功 能: 延时
* 输 入: unsigned int num:延时num毫秒        
* 输 出: 无 
*/
void Key_Delay(unsigned int num)
{
	int i, j;
	for (i = 0; i<num; i++)
	  for(j = 0; j<625; j++);
}

//key.h

#ifndef __KEY_H
#define __KEY_H

#include "stc15f2k60s2.h"

unsigned char Key_Scan(void);
void Key_Delay(unsigned int num);

#endif /*__KEY_H*/

//main.c

/**************************************************************************** 
* Copyright (C), 2022,Moqim
* 文件名: main.c 
* 内容简述:实现LED和SEG显示按键对应数值 
* 
* 文件历史: 
* 版本号      日期       作者     说明 
*   01a    2022-05-18   Moqim   创建该文件
*/

//头文件调用区
#include "stc15f2k60s2.h"
#include "led.h"
#include "seg.h"
#include "key.h"

//变量定义区


//函数声明区
void Delay_ms(unsigned int num);
void Key_Disp(void);

//Main Body
int main(void)
{
	LED_Init();                      //关闭LED
	SEG_Init();                      //关闭数码管
	
	while (1)
	{
		Key_Disp();                    //执行按键函数
	}
	
}

/**************************************************************************** 
* 函数名: Key_Disp() 
* 功 能: 按下按键,LED和SEG显示对应数值
* 输 入: 无       
* 输 出: 无 
*/
void Key_Disp(void)
{
	static unsigned char Key_Val = 0;           //定义无符号字符型的静态变量,表示当前键值
	static int i = 0;                                  //一个用于调用LED和SEG的参数
	
	Key_Val = Key_Scan();                       //获取当前键值
	
	switch (Key_Val)                            //判断键值,执行内容
	{
		case 4:i = 4;break;
		case 5:i = 5;break;
		case 6:i = 6;break;
		case 7:i = 7;break;
		default:i = 0;break;
	}
	
	if (i > 0)                     
	{
		LED_Ctrl(0x01 << i - 1);                  //点亮对应序号的LED
		smg1 = i;                                 //数码管显示对应键值
	}
	else
	{
		LED_Ctrl(0x00);                          //LED全灭
		smg1 = 0;                                //数码管显示0
	}
	SEG_Scan();                                //数码管显示
	Delay_ms(200);                             //增加显示时间
}

/**************************************************************************** 
* 函数名: Delay_ms() 
* 功 能: 延时
* 输 入: unsigned int num:延时num毫秒        
* 输 出: 无 
*/
void Delay_ms(unsigned int num)              //软件延时不精确,凑合用吧
{
	int i, j;
	for(i = 0; i<num; i++)
	{
		for(j = 0; j<625; j++);
	}
}

LED 和 SEG 代码请参考之前博客。

进阶代码

void key_press(int* p)   //设定一个变量,可返回至数码管以检查代码是否正确执行;
{                     
	if (P30 == 0) {      //当检测到P30为低电平时;
		delay(5);        //消抖;
		if (P30 == 0)    //若仍为低电平,说明按键被按下;
			(*p)++;      //执行需要的程序;
		while (P30 == 0);//一直执行程序,直到P30!=0,即按键被松开;
	}
}

但上述代码有一个缺点,即长按按键会一直执行程序,
所以,改进代码如下——

void key_press(int* p)                //设定一个变量,可返回至数码管以检查代码是否正确执行;
{   
	bit S7 = 0;                       //设定一个位标量,令其为0;
	if ((P30 == 0)&&(S7 == 0)) {      //当检测到P30为低电平且S7为0时;
		delay(5);                     //消抖
		if ((P30 == 0) && (S7 == 0))  //若仍为低电平且S7为0,说明按键被按下;
			S7 = 1;                   //令S7为1;
	}

	if ((P30 == 1) && (S7 == 1)) {    //当检测到P30为高电平且S7为1时,说明已被按下过,且现在处于抬起状态;
		(*p)++;                       //执行需要的程序;
		S7 = 0;                       //复位S7;
	}
}

这样,每次按完一次才会执行一次,长按则不会影响。

参考资料:bilibili@一蓑烟雨任平生 a—— 基于蓝桥杯单片机的 STC15 学习教程。

消抖改进(按一下只执行一次,长按也一样)

来源:上课认真听讲。

//前提条件:按下一个键(S4为例),该变量读到的是4(已经通过key_read()函数获取按键数值了)
//按下为4,抬起为0

//way 1,设置一个参考变量
u8 flag = 0;
if(key_read == 4 && flag == 0){//读取S4
  flag = 1;
  //按键按一下执行的代码
}
if(flag == 1 && key_read() != 4){
  flag = 0;
}

//way 2
//比较当前值和前一次值,

u8 last_key_value = 0;//上一次的值
u8 current_key_value = key_read();//当前值

if(current_key_value > last_key_value && current_key_value == 4){//如果当前值大于上一次的值,且当前值为4
   //执行语句
}
last_key_value = current_key_value;//把当前值赋给上一次的值


//way 3
//用异或

u8 key_val = 0;//结果值
u8 last_key_value = 0;//上一次的值
u8 current_key_value = 0;//当前值

current_key_value = key_read();//当前值赋值
key_val = current_key_value ^ last_key_value  & current_key_value;
//异或完后(相异为1),若按下(当前值为4),与上当前值后不变;若抬起(当前值为0),与上后为0;
//若长按,则异或结果为0,0与上任何数都是0;
last_key_value = current_key_value;

switch(key_val){
   case 4:if(++ work_state == 4)work_state = 1;break;
   case 5:
   case 6:
   case 7:
}

补充

2022.04.04
我现在常用的:

void Key_Proc(void){
  static unsigned char ucKey_Val = 0;
  static unsigned char ucKey_Down = 0;  
  static unsigned char ucKey_Old = 0;

  ucKey_Val = Key_Scan();//从按键判断函数中获取键值
  ucKey_Down = ucKey_Val & (ucKey_Val ^ ucKey_Old);//当前键值与上一次键值异或,若相同则为0,则ucKey_Down为0;相异则反之。
  ucKey_Old = ucKey_Val;
    
  switch(ucKey_Down){//短按用ucKey_Down,长按用ucKey_Old
      case 4:break;
      default:break;   
  }
}
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Moqim Flourite.

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值