蓝桥杯单片机---按键

独立按键

基础知识

红方框框出来的按键(S4-S7)就是独立按键,蓝色框框出来的4x4键盘就是矩阵键盘,下方红色圈圈圈出来的就是进行独立按键和矩阵键盘切换的跳帽,BIN就是独立按键,KBD就是矩阵键盘。

ps:应该注意到了这一堆按键右边有一个孤零零的按键,那个被孤立的RESET按键是单片机的复位键,按下程序就会重头开始跑。

独立按键电路原理

可以从上图看出来,独立按键S4、S5、S6、S7一端分别连在P30、P31、P32、P33上,一端连在GND上,所以当按键被按下时读取P3口,相应的位会变成低电平,所以只要判断P30-P33口的电平,就能知道那个按键被按下了。

消抖处理

通常按键所用的开关都是机械弹性开关,当机械触电断开、闭合时,由于机械触电的弹性作用,一个按键开关在闭合时不会马上就稳定的连通,在断开时也不是一下子就彻底断开,而是在闭合和断开的瞬间伴随了一连串的抖动。抖动时间是由按键的机械特性决定的 ,一般都会在10ms以内,为了确保程序对按键的一次闭合或者一次断开只响应一次,就必须进行按键的消抖处理!!!

编程注意事项

  1. 每次按下按键需要单次触发

  1. 需要有松手检测

  1. 不要阻塞程序,不要有delay,更不要有类似while(!P30);等待按键弹起的操作,这样子会耗费大量的CPU资源

  1. 最好要有长按功能

以下就是一种不太好的编程方式,虽然咱们尊敬的单片机大师郭天祥老师写的书里面是这么教的,但是总归是有更好的方法的。

while(1)
{
    if(S4==0)
    {
        Delay_Ms(10);//代码中最好不要出现delay,尽可能的用中断来处理
        if(S4==0)
        {
            number++;
        }
    }
    while(S4==0);//用while来卡时间,来等待按键弹起,如果按键一直被按下,
                 //那么CPU就会一直在这里等待,会一直耗费CPU资源,此时CPU不能干任何事情
}

三行按键法

以下就是上面提出问题的完美解决方案,不仅不会耗费CPU资源,还能实现单次触发以及长按触发。

此外还有一种状态机的方法也能够完美解决以上问题,并且很多很多地方都可以用状态机的思想,例如红外线的编解码等,但是代码量相对较多,所以在此不做介绍

u8 trg;     //单次触发
u8 cont;    //连续触发

void btn_read()
{
    u8 read_data=P3^0xff;        //将P3每一位按位取反
    trg=read_data & (read_data^cont);
    cont=read_data;
}

三次按键法应用

/*
每按下一次S4,数码管的数字+1 ;(默认数字是0)
每按下一次S5,数码管的数字-1 ;
每按下一次S6,数码管的数字+10 ;
每按下一次S7,数码管的数字-10 ;
长按S7,数码管的数字清0;
*/

#include <STC15F2K60S2.H>

typedef unsigned char u8;
typedef unsigned int u16;

u8 code t_display[]={                       //标准字库
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

u8 code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};      //位码

void Timer2Init(void)        //1毫秒@12.000MHz
{
    AUXR &= 0xFB;        //定时器时钟12T模式
    T2L = 0x18;        //设置定时初始值
    T2H = 0xFC;        //设置定时初始值
    AUXR |= 0x10;        //定时器2开始计时
    IE2 |= 0x04;                    //开定时器2中断
    EA = 1;
}

void device_ctrl(u8 p2data,u8 p0data)
{
    P0=p0data;
    P2=p2data;
    P2=0;
}

void system_init()
{
    device_ctrl(0xa0,0);
    device_ctrl(0X80,0XFF);

}




u8 trg;     //单次触发
u8 cont;    //连续触发

void btn_read()
{
    u8 read_data=P3^0xff;        //将P3每一位按位取反
    trg=read_data & (read_data^cont);
    cont=read_data;
}

u8 key_cnt;
u8 key_number;
u16 cont_number;
void btn_process()
{
    if(key_cnt==10)
    {
        key_cnt=0;
        btn_read();
        if(trg&0x08)
        {
            key_number=key_number+1;
        }
        if(trg&0x04)
        {
            key_number=key_number-1;
        }
        if(trg&0x02)
        {
            key_number=key_number+10;
        }
        if(trg&0x01)
        {
            key_number=key_number-10;
        }
        if(cont&0x01)
        {
            cont_number++;
        }
        if(cont_number>=300)
        {
            cont_number=0;
            key_number=0;
        }
    }

}

u8 smg_buf[8];
void smg_process()
{
    smg_buf[0]=t_display[key_number/100];
    smg_buf[1]=t_display[key_number/10%10];
    smg_buf[2]=t_display[key_number%10];
    smg_buf[3]=0x00;
    smg_buf[4]=0x00;
    smg_buf[5]=0x00;
    smg_buf[6]=0x00;
    smg_buf[7]=0x00;

}

void smg_display()
{
    static u8 i;
    device_ctrl(0xc0,0);
    device_ctrl(0xe0,~smg_buf[i]);
    device_ctrl(0xc0,T_COM[i]);
    i++;
    if(i==8)
    {i=0;}

}

void main()
{
    system_init();
    Timer2Init();
    while(1)
    {
        smg_process();
        btn_process();
    }

}


void t2int() interrupt 12           //中断入口
{
    smg_display();
    key_cnt++;
    
}

矩阵键盘

注意J5跳帽要连接在KBD上

矩阵键盘电路原理

P30-P35、P42、P44低四位是矩阵键盘的行,高四位是矩阵键盘的列,

编程思路:

  1. 给高四位赋值0,检测低四位哪一位是0,确定按键所在行

  1. 给低四位赋值0,检测高四位哪一位是0,确定按键所在列

  1. 根据行和列,确定哪个按键按下

编程需要的注意事项与独立按键一样。

程序控制

与独立按键一样,使用三行按键法,只不过需要在使用之前获取到矩阵键盘对应的IO状态

/*
每按下一次S4,数码管的前三位数字+1 ;(默认数字是0)
每按下一次S9,数码管的前三位数数字-1 ;
每按下一次S14,数码管的前三位数数字+10 ;
每按下一次S19,数码管的前三位数数字-10 ;
数码管后三位显示当前按键对应16进制取反
*/
#include <STC15F2K60S2.H>
 typedef unsigned char u8;
 typedef unsigned int u16;
 
 u8 code t_display[]={                       //标准字库
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

u8 code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};      //位码
 
 void device_ctrl(u8 p2data,u8 p0data)
 {
     P0=p0data;
     P2=p2data;
     P2=0;
 }
 
 void system_init()
 {
     device_ctrl(0xa0,0);
     device_ctrl(0x80,0xff);
 }
 
 void Timer2Init(void)        //1毫秒@12.000MHz
{
    AUXR &= 0xFB;        //定时器时钟12T模式
    T2L = 0x18;        //设置定时初始值
    T2H = 0xFC;        //设置定时初始值
    AUXR |= 0x10;        //定时器2开始计时
    IE2 |= 0x04;                    //开定时器2中断
    EA = 1;
}
u8 key_io()
{
    u8 key_io=0xff;
    P3=0xf0;P4=0xff;//列扫描
    if(P44==0) key_io=0x70;
    if(P42==0) key_io=0xb0;
    if(P35==0) key_io=0xd0;
    if(P34==0) key_io=0xe0;
    
    P3=0x0f;P4=0x00;//行扫描
    if(P33==0) key_io=key_io|0x07;
    if(P32==0) key_io=key_io|0x0b;
    if(P31==0) key_io=key_io|0x0d;
    if(P30==0) key_io=key_io|0x0e;
    
    return key_io;
}

u8 trg;
u8 cont;
void key_read()
{
    u8 readData=key_io()^0xff;
    trg=readData & (readData ^ cont);
    cont=readData;
}

u8 key_cnt;
u8 key_val;
u8 key_number;
u16 cont_number;
void key_process()
{
    if(key_cnt==10)
    {
        key_cnt=0;
        key_read();
        key_val=cont;
        if(trg==0x88)    key_number=key_number+1;
        if(trg==0x44)    key_number=key_number-1;
        if(trg==0x22)    key_number=key_number+10;
        if(trg==0x11)    key_number=key_number-10; 
    }
}

u8 smg_buf[8];
void smg_process()
{
    smg_buf[0]=t_display[key_number/100];
    smg_buf[1]=t_display[key_number/10%10];
    smg_buf[2]=t_display[key_number%10];
    smg_buf[3]=0x00;
    smg_buf[4]=0x00;
    smg_buf[5]=0x00;
    smg_buf[6]=t_display[key_val/16];
    smg_buf[7]=t_display[key_val%16];
}

void smg_display()
{
    static u8 i=0;
    device_ctrl(0xc0,0);
    device_ctrl(0xe0,~smg_buf[i]);
    device_ctrl(0xc0,T_COM[i]);
    i++;
    if(i==8)    i=0;
}

 void main()
 {
    system_init();
    Timer2Init();
    while(1)
    {              
        smg_process();
        key_process();   
    }
 }

 
 void t2int() interrupt 12           //中断入口
{
    smg_display();
    key_cnt++;
}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值