单片机学习笔记-51单片机实现独立按键的短按及长按触发

发布者:心若澄明最新更新时间:2020-06-19 来源: eefocus关键字:单片机  51单片机  独立按键  短按  长按触发 手机看文章 扫描二维码
随时随地手机看文章

一、使用proteus绘制简单的电路图,用于后续仿真

二、编写程序


/********************************************************************************************************************

---- @Project: Independent-KEY

---- @File: main.c

---- @Edit: ZHQ

---- @Version: V1.0

---- @CreationTime: 20200506

---- @ModifiedTime: 20200506

---- @Description: 两个独立按键S1和S2,按住其中一个按键,在短时间内松手,则认为是短按,触发蜂鸣器短鸣一声。如果一直按住这个按键不松手,那么超过规定的长时间内,则认为是长按,触发蜂鸣器长鸣一声。

---- 单片机:AT89C52

********************************************************************************************************************/

#include "reg52.h"

/*——————宏定义——————*/

#define FOSC 11059200L

#define T1MS (65536-FOSC/12/1000)   /*1ms timer calculation method in 12Tmode*/

 

#define const_voice_short  80   /*蜂鸣器短叫的持续时间*/

#define const_voice_long  600   /*蜂鸣器长叫的持续时间*/

 

#define const_key_time_short1  60    /*短按的按键去抖动延时的时间*/

#define const_key_time_long1   1000     /*长按的按键去抖动延时的时间*/

 

#define const_key_time_short2  60    /*短按的按键去抖动延时的时间*/

#define const_key_time_long2   1000     /*长按的按键去抖动延时的时间*/

 

/*——————变量函数定义及声明——————*/

/*定义按键S1*/

sbit Key_S1 = P0^0;

/*定义按键S2*/

sbit Key_S2 = P0^1;

/*定义蜂鸣器*/

sbit BUZZER = P2^7;

 

unsigned char ucKeySec = 0;   /*被触发的按键编号*/

 

unsigned int  uiKeyTimeCnt1 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock1 = 0; /*按键触发后自锁的变量标志*/

unsigned char ucShortTouchFlag1=0; /*短按的触发标志*/

 

unsigned int  uiKeyTimeCnt2 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock2 = 0; /*按键触发后自锁的变量标志*/

unsigned char ucShortTouchFlag2=0; /*短按的触发标志*/

 

unsigned int  uiVoiceCnt = 0;  /*蜂鸣器鸣叫的持续时间计数器*/

 

 

/**

* @brief  定时器0初始化函数

* @param  无

* @retval 初始化T0

**/

void Init_T0(void)

{

TMOD = 0x01;                    /*set timer0 as mode1 (16-bit)*/

TL0 = T1MS;                     /*initial timer0 low byte*/

TH0 = T1MS >> 8;                /*initial timer0 high byte*/

}

/**

* @brief  外围初始化函数

* @param  无

* @retval 初始化外围

**/

void Init_Peripheral(void)

{

ET0 = 1;/*允许定时中断*/

TR0 = 1;/*启动定时中断*/

EA = 1;/*开总中断*/

 

}

 

/**

* @brief  初始化函数

* @param  无

* @retval 初始化单片机

**/

void Init(void)

{

Init_T0();

BUZZER = 1;

}

/**

* @brief  扫描按键函数

* @param  无

* @retval 长按与短按的按键扫描的详细过程: 

* 第一步:平时只要按键没有被按下时,按键的自锁标志,去抖动延时计数器一直被清零。

* 第二步:一旦按键被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到

*         阀值const_key_time_short1或者const_key_time_long1时,如果在这期间由于受外界干扰或者按键抖动,而使

*         IO口突然瞬间触发成高电平,这个时候马上把延时计数器uiKeyTimeCnt1

*         清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。

*         以后凡是用到开关感应器的时候,都可以用类似这样的方法去干扰。

* 第三步:如果按键按下的时间超过了短按阀值const_key_time_short1,则马上把短按标志ucShortTouchFlag1=1;

*         如果还没有松手,一旦发现按下的时间超过长按阀值const_key_time_long1时,

*         先把短按标志ucShortTouchFlag1清零,然后触发长按。在这段程序里,把自锁标志ucKeyLock1置位,

*         是为了防止按住按键不松手后一直触发。

* 第四步:等按键松开后,自锁标志ucKeyLock12及时清零,为下一次自锁做准备。如果发现ucShortTouchFlag1等于1,

*         说明短按有效,这时触发一次短按。

* 第五步:以上整个过程,就是识别按键IO口下降沿触发的过程。

**/

void Key_Scan(void)

{

/*扫描S1*/

if(Key_S1 == 1) /*如果按键没有被按下(高电平),将一些标志位及时清零*/

{

ucKeyLock1 = 0;/*自锁标志位清0*/

uiKeyTimeCnt1 = 0;/*按键去抖动延时计数器清零*/

if(ucShortTouchFlag1 == 1)/*短按*/

{

ucShortTouchFlag1 = 0;

ucKeySec = 1; /*触发S1短按*/

}

}

else if(ucKeyLock1 == 0) /*如果有按键按下,且是第一次按下*/

{

uiKeyTimeCnt1 ++;

if(uiKeyTimeCnt1 > const_key_time_short1)/*判定短按*/

{

ucShortTouchFlag1 = 1;/*激活短按的有效标志位*/

}

if(uiKeyTimeCnt1 > const_key_time_long1)/*判定长按*/

{

ucShortTouchFlag1 = 0;/*清除短按的有效标志位*/

uiKeyTimeCnt1 = 0;

ucKeyLock1 = 1;/*自锁按键置位,避免一直触发*/

ucKeySec = 2; /*触发S1长按*/

}

}

/*扫描S2*/

if(Key_S2 == 1) /*如果按键没有被按下(高电平),将一些标志位及时清零*/

{

ucKeyLock2 = 0;/*自锁标志位清0*/

uiKeyTimeCnt2 = 0;/*按键去抖动延时计数器清零*/

if(ucShortTouchFlag2 == 1)/*短按*/

{

ucShortTouchFlag2 = 0;

ucKeySec = 3; /*触发S1短按*/

}

}

else if(ucKeyLock2 == 0) /*如果有按键按下,且是第一次按下*/

{

uiKeyTimeCnt2 ++;

if(uiKeyTimeCnt2 > const_key_time_short2)/*判定短按*/

{

ucShortTouchFlag2 = 1;/*激活短按的有效标志位*/

}

if(uiKeyTimeCnt2 > const_key_time_long2)/*判定长按*/

{

ucShortTouchFlag2 = 0;/*清除短按的有效标志位*/

uiKeyTimeCnt2 = 0;

ucKeyLock2 = 1;/*自锁按键置位,避免一直触发*/

ucKeySec = 4; /*触发S1长按*/

}

}

}

/**

* @brief  按键服务函数

* @param  无

* @retval 根据扫描得到的值,进行数据处理

**/

void key_Service(void)

{

switch(ucKeySec)

{

case 1: /*S1短按*/

uiVoiceCnt = const_voice_short;  /*蜂鸣器短叫*/

ucKeySec = 0; /*响应按键服务处理程序后,按键编号清零,避免一致触发*/

break;

case 2: /*S1长按*/

uiVoiceCnt = const_voice_long;  /*蜂鸣器长叫*/

ucKeySec = 0; /*响应按键服务处理程序后,按键编号清零,避免一致触发*/

break;

case 3: /*S2短按*/

uiVoiceCnt = const_voice_short;  /*蜂鸣器短叫*/

ucKeySec = 0; /*响应按键服务处理程序后,按键编号清零,避免一致触发*/

break;

case 4: /*S2长按*/

uiVoiceCnt = const_voice_long;  /*蜂鸣器长叫*/

ucKeySec = 0; /*响应按键服务处理程序后,按键编号清零,避免一致触发*/

break;

}

}

/**

* @brief  定时器0中断函数

* @param  无

* @retval 无

**/

void ISR_T0(void) interrupt 1

{

TF0 = 0;  /*清除中断标志*/

  TR0 = 0; /*关中断*/

/*扫描按键*/

Key_Scan();

if(0 != uiVoiceCnt)

{

uiVoiceCnt --;

BUZZER = 0;

}

else

{

BUZZER = 1;

}

TL0 = T1MS;                     /*initial timer0 low byte*/

TH0 = T1MS >> 8;                /*initial timer0 high byte*/

  TR0 = 1; /*开中断*/

}

/**

* @brief  延时函数

* @param  无

* @retval 无

**/

void Delay_Long(unsigned int uiDelayLong)

{

   unsigned int i;

   unsigned int j;

   for(i=0;i   {

      for(j=0;j<500;j++)  /*内嵌循环的空指令数量*/

          {

             ; /*一个分号相当于执行一条空语句*/

          }

   }

}

/*——————主函数——————*/

/**

* @brief  主函数

* @param  无

* @retval 实现LED灯闪烁

**/

void main()

{

/*单片机初始化*/

Init();

/*延时,延时时间一般是0.3秒到2秒之间,等待外围芯片和模块上电稳定*/

Delay_Long(100);

/*单片机外围初始化*/

Init_Peripheral();

while(1)

{

 

/*按键服务函数*/

key_Service();

}

}

 

三、仿真实现

关键字:单片机  51单片机  独立按键  短按  长按触发 引用地址:单片机学习笔记-51单片机实现独立按键的短按及长按触发

上一篇:单片机开发中多功能按键详细解析设计
下一篇:【单片机笔记】单个按键实现单击、双击、长按的实现

推荐阅读最新更新时间:2024-11-03 17:50

单片机一个月能入门么?单片机工程师能干到多少岁?
初我在学习单片机的时候,和很多新手一样,很迷茫,很焦虑,不确定自己能不能学会,如果可以大概需要多久。 不过最终我还是成功了,不然也没有今天这篇文章的出现。 今天来分享下我是怎么做到,希望能为和我当初一样迷茫的小伙伴们指引方向。 网上有很多类似话题;“1天征服单片机”“10天征服单片机”,“一个月学会单片机”! 作为资深的工程师来说,我不贩卖焦虑,也不捏造事实,一个月掌握单片机确实有点不现实,太难了! 除非你是天才!!!华为的少年天才,月薪几百万,让他一个月搞定,应该有希望,毕竟他们不是一般人。 而我们呢,我们就是一个普普通通的屌丝,要一个月搞定,不是很现实。 不过呢!如果大家有足够的时间,有一个正确的学习方法和途径,一个月的时间学
[单片机]
I2C总线在单片机上的实现
    摘要: 本文介绍了I2C总线的性能、特点以及数据传输和结构,并详细分析了它的应用示例,给出了它在单片机上的具体实现和相应的汇编程序。     关键词: I2C总线 单片机 随着大规模集成电路技术的发展,把CPU和一个单独工作系统所必需的ROM、RAM、I/O端口、A/D、D/A等外围电路集成在一个单片内而制成的单片机或微控制器愈来愈方便。目前,世界上许多公司生产单片机,品种很多。其中包括各种字长的CPU,各种容量的ROM、RAM以及功能各异的I/O接口电路等等,但是,单片机的品种规格仍然有限,所以只能选用某种单片机来进行扩展。扩展的方法有两种:一种是并行总线,另一种是串行总线。由于串行总线的连线少,结
[工业控制]
基于单片机的低成本高精度A/D转换方案设计
  目前单片机在电子产品中已经得到了广泛的应用,许多类型的单片机内部已带有A/D转换电路,但此类单片机会比无A/D转换功能的单片机在价格上高几元甚至很多!本文给大家提供一种实用的用普通单片机实现的A/D转换电路,它只需要使用普通单片机的2个I/O脚与1个运算放大器即可实现,而且它可以很容易地扩展成带有4通道A/D转换功能,由于它占用资源很少,成本很低,其A/D转换精度可达到8位或更高,因此很具有实用价值。   其电路如图一所示:      图一   其工作原理说明如下:   1、硬件说明:   图一中“RA0”和“RA1”为单片机的两个I/O脚,分别将其设置为输出与输入状态,在进行A/D转换时,在程序中通过软件产生PWM,
[单片机]
基于<font color='red'>单片机</font>的低成本高精度A/D转换方案设计
WS51F7340 系列物联网 MCU的特点和概括
1T 8051 内核,16KB Flash,2KB 可编程 Boot Loader,1KB SRAM,128B EEPROM,25通道低功耗 Touch Key,1 个 26 通道 12 位 SAR-ADC,4 个通用定时器,4 对 8 通道16 位互补输出 PWM,1 路蜂鸣器输出,2 个 UART,1 个 I2C,1 个 SPI,1 个 WDT, 1 个 WKT,硬件和软件 LED 驱动,软件 LCD 驱动。 1 WS51F7340 特性 n 工作电压: 2.0V-5.5V n 工作温度: -40-+105℃ n 封装类型: SOP28/TSSOP28/SOP24/TSSOP24/SOP20/TSSOP20 n 内核:
[单片机]
一种嵌入式时钟管理器的设计与实现
目前,在嵌入式产品的研发中,低档微处理器软件多采用裸机开发模式实现。在这种开发模式中,常有如下需求: (1)在经历特定的时间段后,执行特定操作; (2)根据给定周期执行特定操作。 传统的作法是利用前后台方式:设定硬件定时器,使其在后台以特定周期对各相关操作的标志变量作计数操作;前台则不断对各标志变量巡回查询,若发现标志变量达到预定值,则执行特定操作。可见,上述需求需直接操作硬件定时器实现,其过程繁琐,且需要用户对相关硬件有深入了解。因此,本文设计、实现了一种使用方便的低端系统时钟管理器。 本时钟管理器适用于可提供至少一个硬件定时器的处理器。其为用户提供了有益、友好的裁剪途径,以满足不同目标系统的实际需要。通过裁剪,
[单片机]
一种嵌入式时钟管理器的设计与实现
PIC单片机广告灯程序
程序介绍:灯为左右移动加闪烁,程序中采用了调用子程序功能 //** Date: Wednesday, November 21, 2012 21:51:18 #define MX_PIC //Defines for microcontroller #define P16F690 #define MX_EE #define MX_EE_SIZE 256 #define MX_SPI #define MX_SPI_BCB #define MX_SPI_SDI 4 #define MX_SPI_SDO 7 #define MX_SPI_SCK 6 #define MX_UART #define MX_UART_B #d
[单片机]
PIC<font color='red'>单片机</font>广告灯程序
单片机8位LED数码管的静态显示课程设计说明书与仿真
设计单片机控制8位LED数码管的动态驱动电路,并编写程序实现在8位LED数码管上静态信息的显示。 电路方面主要包括以下3部分。 (a)设计单片机的最小系统(包括复位电路和外接的晶振电路),并确定相关元器件参数。 (b)采用动态驱动的方式,设计单片机并行端口与LED数码管的动态显示电路,包括LED数码管位选线和段选线的连线。 (c)设计LED数码管位选端(线)给电流电路。 编写单片机控制(驱动)8位LED数码管显示程序,实现数字(0—9)或简单的英文字符(A—F)的静态信息显示。显示方式和内容自定,如每隔一段时间循环
[单片机]
<font color='red'>单片机</font>8位LED数码管的静态显示课程设计说明书与仿真
GD32 MCU ISP失败的原因
玩过GD32的小伙伴们都知道,GD32 MCU支持ISP,即在系统编程,前面的常见问题也给大家讲过什么是ISP,什么是IAP? 那有没有小伙伴遇到过ISP失败的情况,失败的原因是什么呢? 我们就以GD32F30x系列为例,来看下用户手册: 通过将BOOT0拉高,BOOT1拉低,上电后,MCU则工作在ISP模式,此时可以通过ALL-IN-ONE软件(或用户自行开发的上位机软件)对MCU进行编程、操作选项字节、flash数据回读等操作,使用的是以下口: 如果用的是容量不超过512K的GD32F303系列(HD产品),则使用USART0(PA9 PA10)进行ISP交互,如果是超过512K的GD32F303系列(XD产
[单片机]
GD32 <font color='red'>MCU</font> ISP失败的原因
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved