【独立按键鼠标式的单击与双击】

(1)按键与蜂鸣器的非阻塞驱动
消抖的两种境界:第一种判断两次电平状态,中间加入“固定延时时间”,不足之处是“固定时间”靠经验值,软件抗干扰能力也弱了很多,“密码等级”不够高,第二种是“清零式滤波”。

#include "reg52.h"
#define KEY_VOICE_TIME 50//按键触发后发出的声音长度
#define KEY_FILTER_TIME 25//按键滤波的“稳定时间”25ms
sbit beep=P2^3;
sbit KEY_INPUT1=P3^4;
sbit KEY_INPUT2=P3^5;//按键识别输入口
volatile unsigned char vGu8BeepTimerFlag=0;
volatile unsigned int vGu16BeepTimerCnt=0;
volatile unsigned char vGu8KeySec=0;//按键的触发序号
void SystemInitial()
{
    TMOD=0x01;
    TH0=0xfc;
    TL0=0x66;
    EA=1;
    ET0=1;
    TR0=1;
}
void Delay(unsigned long u32DelayTime)
{
    for(;u32DelayTime>0;u32DelayTime--);
}
void PeripheralInitial()
{

}
void BeepOpen()
{
    beep=0;
}
void BeepClose()
{
    beep=1;
}
void VoiceScan()//蜂鸣器的非阻塞驱动."驱动层"的驱动函数,放在定时中断函数里每1ms扫描一次,保证声音长度的一致性
{
    static unsigned char Su8Lock=0;
    if(1==vGu8BeepTimerFlag && vGu16BeepTimerCnt>0)
    {
        if(0==Su8Lock)
        {
            Su8Lock=1;//触发声音后自锁起来,避免BeepOpen()被重复扫描影响执行效率,发声时只执行一次BeepOpen()函数即可
            BeepOpen();//蜂鸣器发声,封装成函数为了代码的移植性
        }
        else//借用else结构实现先发声下一次中断再开始计时
        {
            vGu16BeepTimerCnt--;
            if(0==vGu16BeepTimerCnt)
            {
                Su8Lock=0;//关闭声音后及时解锁为下一次触发做准备
                BeepClose();
            }
        }
    }
}
void KeyScan()//非阻塞,"清零式滤波"
{
    static unsigned char Su8KeyLock1;//1号按键的自锁
    static unsigned int Su16KeyCnt1;//1号按键的计时器
    static unsigned char Su8KeyLock2;//2号按键的自锁
    static unsigned int Su16KeyCnt2;//2号按键的计时器

    if(0!=KEY_INPUT1)//IO口是高电平,没有按键按下,”去抖动延时计数器“一直被清零,如果受到外界或抖动干扰就可以去除瞬间的杂波干扰
    {
        Su8KeyLock1=0;//按键解锁
        Su16KeyCnt1=0;//”去抖动延时计数器“,按键去抖动的重要语句,在抖动过程中一旦有高电平立刻置0直到稳定
    }
    else if(0 == Su8KeyLock1)//说明按键被按下,else设计很巧妙相当于KEY_INPUT1==0
    {
        Su16KeyCnt1++;//累加的过程就是在滤波,若在累加过程中受到干扰IO口触发高电平,这时马上将Su16KeyCnt1置0,即去除瞬间杂波
        if(Su16KeyCnt1>=KEY_FILTER_TIME)//滤波的“稳定时间”
        {
            Su8KeyLock1=1;//按键自锁标志位,一旦触发按键标志位置1,防止按键按键按住不松手时不断触发按键
            vGu8KeySec=1;//触发1号键
        }
    }

    if(0!=KEY_INPUT2)
    {
        Su8KeyLock2=0;
        Su16KeyCnt2=0;
    }
    else if(0 == Su8KeyLock2)
    {
        Su16KeyCnt2++;
        if(Su16KeyCnt2>=KEY_FILTER_TIME)
        {
            Su8KeyLock2=1;
            vGu8KeySec=2;//触发2号键
        }
    }
}
void KeyTask()
{
    if(0==vGu8KeySec)
    {
        return;//序号为0无按键触发,退出当前函数.因为当按键多达几十个时就能避免主函数每次进入KeyTask函数时进入switch多次判断,可能会更高效
    }
    switch(vGu8KeySec)
    {
        case 1:
            vGu8BeepTimerFlag=0;
            vGu16BeepTimerCnt=KEY_VOICE_TIME;//"应用层"只需赋值,就发出固定时间长度的声音
            vGu8BeepTimerFlag=1;
            vGu8KeySec=0;//响应按键服务程序后,按键编号清零避免一直触发
            break;
        case 2:
            vGu8BeepTimerFlag=0;
            vGu16BeepTimerCnt=KEY_VOICE_TIME;
            vGu8BeepTimerFlag=1;
            vGu8KeySec=0;
            break;
    }
}
void main()
{
    SystemInitial();
    Delay(10000);
    PeripheralInitial();
    while(1)
    {
        KeyTask();
    }
}
void T0_time() interrupt 1
{
    VoiceScan();
    KeyScan();//因为KeyScan是特殊函数放在中断函数里,涉及到IO口输入信号的滤波,滤波就涉及到时间的及时性和均匀性,放在中断中更能保证时间的一致性
    TH0=0xfc;//如蜂鸣器驱动,动态数码管驱动,按键扫描驱动
    TL0=0x66;
}

(2)独立按键的单击和双击

#include "reg52.h"
#define KEY_VOICE_TIME 50//按键触发后发出的声音长度
#define KEY_FILTER_TIME 25//按键滤波的“稳定时间”25ms
#define KEY_INTERVAL_TIME 400//连续两次单击之间的最大有效时间
sbit beep=P2^3;//蜂鸣器
sbit P1_4=P1^4;//LED
sbit KEY_INPUT1=P3^4;//按键识别输入口
volatile unsigned char vGu8BeepTimerFlag=0;
volatile unsigned int vGu16BeepTimerCnt=0;
unsigned char Gu8LedStatus=0;//LED灯的状态,0代表灭,1代表亮
volatile unsigned char vGu8SingleKeySec=0;//单击按键的触发序号
volatile unsigned char vGu8DoubleKeySec=0;//双击按键的触发序号
void SystemInitial()
{
    TMOD=0x01;
    TH0=0xfc;
    TL0=0x66;
    EA=1;
    ET0=1;
    TR0=1;
}
void Delay(unsigned long u32DelayTime)
{
    for(;u32DelayTime>0;u32DelayTime--);
}
void LedOpen()
{
    P1_4=0;
}
void LedClose()
{
    P1_4=1;
}
void PeripheralInitial()
{//led初始化没有放在SystemInitial中是因为led显示内容对上电瞬间要求不高,如果是控制继电器则应该放在SystemInitial中
    if(0 == Gu8LedStatus)
    {
        LedClose();
    }
    else
    {
        LedOpen();
    }
}
void BeepOpen()
{
    beep=0;
}
void BeepClose()
{
    beep=1;
}
void VoiceScan()//蜂鸣器的非阻塞驱动."驱动层"的驱动函数,放在定时中断函数里每1ms扫描一次,保证声音长度的一致性
{
    static unsigned char Su8Lock=0;
    if(1==vGu8BeepTimerFlag && vGu16BeepTimerCnt>0)
    {
        if(0==Su8Lock)
        {
            Su8Lock=1;//触发声音后自锁起来,避免BeepOpen()被重复扫描影响执行效率,发声时只执行一次BeepOpen()函数即可
            BeepOpen();//蜂鸣器发声,封装成函数为了代码的移植性
        }
        else//借用else结构实现先发声下一次中断再开始计时
        {
            vGu16BeepTimerCnt--;
            if(0==vGu16BeepTimerCnt)
            {
                Su8Lock=0;//关闭声音后及时解锁为下一次触发做准备
                BeepClose();
            }
        }
    }
}
void KeyScan()//非阻塞,"清零式滤波"
{
    static unsigned char Su8KeyLock1;//1号按键的自锁
    static unsigned int Su16KeyCnt1;//1号按键的计时器
    static unsigned char Su8KeyTouchCnt1;//按键次数记录
    static unsigned int Su16KeyIntervalCnt1;//按键时间间隔计数器

    if(0!=KEY_INPUT1)//IO口是高电平,没有按键按下,”去抖动延时计数器“一直被清零,如果受到外界或抖动干扰就可以去除瞬间的杂波干扰
    {
        Su8KeyLock1=0;//按键解锁
        Su16KeyCnt1=0;//”去抖动延时计数器“,按键去抖动的重要语句,在抖动过程中一旦有高电平立刻置0直到稳定
        if(Su8KeyTouchCnt1>=1)//当按键触发一次后启动间隔时间计数器
        {
            Su16KeyIntervalCnt1++;
            if(Su16KeyIntervalCnt1>=KEY_INTERVAL_TIME)
            {
                Su16KeyIntervalCnt1=0;
                Su8KeyTouchCnt1=0;//间隔时间到达将按下的次数清零
            }
        }
    }
    else if(0 == Su8KeyLock1)//说明按键被按下,else设计很巧妙相当于KEY_INPUT1==0
    {
        Su16KeyCnt1++;//累加的过程就是在滤波,若在累加过程中受到干扰IO口触发高电平,这时马上将Su16KeyCnt1置0,即去除瞬间杂波
        if(Su16KeyCnt1>=KEY_FILTER_TIME)//滤波的“稳定时间”
        {
            Su8KeyLock1=1;//按键自锁标志位,一旦触发按键标志位置1,防止按键按键按住不松手时不断触发按键
            Su16KeyIntervalCnt1=0;//按键按下一次有效间隔时间就清零
            Su8KeyTouchCnt1++;//记录按下次数
            if(1==Su8KeyTouchCnt1)
            {
                vGu8SingleKeySec=1;//单击触发
            }
            else if(Su8KeyTouchCnt1>=2)
            {
                Su8KeyTouchCnt1=0;//按下次数清零
                vGu8SingleKeySec=1;//单击任务
                vGu8DoubleKeySec=1;//双击触发
            }
        }
    }
}
void SingleKeyTask()
{
    if(0==vGu8SingleKeySec)
    {
        return;//序号为0无按键触发,退出当前函数.因为当按键多达几十个时就能避免主函数每次进入KeyTask函数时进入switch多次判断,可能会更高效
    }
    switch(vGu8SingleKeySec)
    {
        case 1:
            if(0==Gu8LedStatus)
            {
                Gu8LedStatus=1;
                LedOpen();
            }
            else
            {
                Gu8LedStatus=0;
                LedClose();
            }
            vGu8SingleKeySec=0;//响应按键服务程序后按键编号清零避免一直触发
            break;
    }
}
void DoubleKeyTask()
{
    if(0==vGu8DoubleKeySec)
    {
        return;//序号为0无按键触发,退出当前函数.因为当按键多达几十个时就能避免主函数每次进入KeyTask函数时进入switch多次判断,可能会更高效
    }
    switch(vGu8DoubleKeySec)
    {
        case 1:
            vGu8BeepTimerFlag=0;
            vGu16BeepTimerCnt=KEY_VOICE_TIME;//"应用层"只需赋值,就发出固定时间长度的声音
            vGu8BeepTimerFlag=1;
            vGu8DoubleKeySec=0;//响应按键服务程序后,按键编号清零避免一直触发
            break;
    }
}
void main()
{
    SystemInitial();
    Delay(10000);
    PeripheralInitial();
    while(1)
    {
        SingleKeyTask();
        DoubleKeyTask();
    }
}
void T0_time() interrupt 1
{
    VoiceScan();
    KeyScan();//因为KeyScan是特殊函数放在中断函数里,涉及到IO口输入信号的滤波,滤波就涉及到时间的及时性和均匀性,放在中断中更能保证时间的一致性
    TH0=0xfc;//如蜂鸣器驱动,动态数码管驱动,按键扫描驱动
    TL0=0x66;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值