蓝桥杯单片机——按键、LED等

写在前面

本篇博客是几年前参赛留下的一些记录,仅供参考
参加蓝桥杯如果想进国赛,按键部分至少掌握以下内容(注意是至少)

  1. 按键每按一次,蜂鸣器状态取反
  2. 按键按下的时候蜂鸣器响起,松开后关闭
  3. 按键按下超过5秒,松开后蜂鸣器状态取反

按键的基本原理

按下按键之后电路接通,然后单片机引脚电平会随之发生改变,根据蓝桥杯比赛官方给的板子和原理图,我们知道,只需要检测输入引脚电平的上升沿即可。
在这里插入图片描述
在这里插入图片描述

独立按键延时消抖

void Delay10ms()                //@11.0592MHz
{
    unsigned char i, j;

    i = 108;
    j = 145;
    do
    {
            while (--j);
    } while (--i);
}

unsigned char key_read()
{
    P3 = 0xff;
    if(P3 != 0xff)
    {
        Delay10ms();
        if(P3 != 0xff)
        {
            // 判断按下的是哪个按键
        }
    }
}

按键状态机消抖
状态机消抖的基本程序框架
状态机消抖是蓝桥杯中常用的消抖方法,相比于其他方法,状态机消抖较容易理解且使用起来较为方便,因此建议大家采用状态机消抖的方式。如果实在学不会也可以采用上述延时消抖的方法。
以下是状态机消抖的假代码框架:

unsigned char KBD_Scan()
{
    switch(状态)                                
    {
        case 状态0:
            if(检测到按键按下)
            {
                下一次扫描按键时跳到状态1              
            }
            break;

        case 状态1:
            if(没有按键按下) 则证明状态0中所检测到的按下为抖动
            {
                下一次扫描按键时跳回状态0
            }
            else
            {
                确认按键按下
                并检测按下的是哪个按键
                (如果是矩阵键盘,这里可以使用switch或者if;独立按键则可以直接赋值)
                下一次扫描按键时跳到状态2
            }
            break;
 
        case 状态2:
            if(检测到没有按键按下) 证明已经松开
            {
                下一次扫描按键时跳回状态0
            }
            break;
    }
    return key_value;
}

矩阵键盘状态机消抖程序

#define NO_KEY 0xFF
#define KEY_STATE0 0
#define KEY_STATE1 1
#define KEY_STATE2 2
static unsigned char key_state=KEY_STATE0;
unsigned char KBD_Scan()
{
    u8 key_value=0,key_temp;
    u8 key1,key2;

    P30=0;P31=0;P32=0;P33=0;P34=1;P35=1;P42=1;P44=1;
    if(P44==0)        key1=0x70;
    if(P42==0)        key1=0xb0;
    if(P35==0)        key1=0xd0;
    if(P34==0)        key1=0xe0;
    if((P34==1)&&(P35==1)&&(P42==1)&&(P44==1))        key1=0xf0;

    P30=1;P31=1;P32=1;P33=1;P34=0;P35=0;P42=0;P44=0;
    if(P30==0)        key2=0x0e;
    if(P31==0)        key2=0x0d;
    if(P32==0)        key2=0x0b;
    if(P33==0)        key2=0x07;
    if((P30==1)&&(P31==1)&&(P32==1)&&(P33==1))        key2=0x0f;
    key_temp=key1|key2;

    switch(key_state)                                
    {
        case KEY_STATE0:
            if(key_temp!=NO_KEY)
            {
                key_state=KEY_STATE1;               
            }
            break;

        case KEY_STATE1:
            if(key_temp==NO_KEY)
            {
                key_state=KEY_STATE0;
            }
            else
            {
                switch(key_temp)                             
                {
                    case 0x77: key_value=4;break;
                    case 0x7b: key_value=5;break;
                    case 0x7d: key_value=6;break;
                    case 0x7e: key_value=7;break;

                    case 0xb7: key_value=8;break;
                    case 0xbb: key_value=9;break;
                    case 0xbd: key_value=10;break;
                    case 0xbe: key_value=11;break;

                    case 0xd7: key_value=12;break;
                    case 0xdb: key_value=13;break;
                    case 0xdd: key_value=14;break;
                    case 0xde: key_value=15;break;

                    case 0xe7: key_value=16;break;
                    case 0xeb: key_value=17;break;
                    case 0xed: key_value=18;break;
                    case 0xee: key_value=19;break;        
                }
                key_state=KEY_STATE2;
            }
            break;
 
        case KEY_STATE2:
            if(key_temp==NO_KEY)
            {
                    key_state=KEY_STATE0;
            }
            break;
    }
    return key_value;
}

补充

LED补充

利用与、或、取反等位运算进行操作,在定时器中对数码管赋值时,往往影响P0的值,从而导致在对单个LED操作时发生错误。
解决方案:

  1. 在对所有LED灯操作之前关闭中断,并且将P0赋值为0xff
  2. 在每次操作完LED后用一个变量保存下P0的值,下次操作LED前将保存的变量赋值给P0

点亮单个LED而不影响其他灯的状态:

u8 LED_P0; // 开为全局变量
void led_on(u8 ul)    // 从外部传入参数ul 1~8
{
    P0 = LED_P0;    // 将P0赋值为上次操作后P0的值,防止两次操作之间P0的赋值受到干扰
    ul --;
    P2 = 0x80; P0 &= ~(0x01<<ul); P2 = 0x00;
    LED_P0 = P0;    // 保存下本次操作后P0的值
}

熄灭单个LED而不影响其他灯的状态:

u8 LED_P0; // 开为全局变量
void led_off(u8 ul)    // 从外部传入参数ul 1~8
{
    P0 = LED_P0;
    ul --;
    P2 = 0x80; P0 |= (0x01<<ul); P2 = 0x00;
    LED_P0 = P0;
}

数码管补充
(P.S.本部分作为拓展,不准备冲刺国一国二的同学可以不用学)
利用指针读取整个数组,以方便定时器中数组的操作

u8 (*show)[8];
void main()
{    
    Init_timer();                  // 此处定时器代码省略
    while(1)
    {
        temp_display[0] = 0x39;temp_display[1] = 0x00;temp_display[2] = 0x00;temp_display[3] = 0x00;
        temp_display[4] = t_display[2];temp_display[5] = t_display[4]|0x80;
        temp_display[6] = t_display[2];temp_display[7] = t_display[5];
        
        show = &temp_display;                            // 将数组赋值给指针
    }
}

void timer0() 
{
    static int smg_cnt = 0, i;
    smg_cnt ++;
    if (smg_cnt == 2)
    {
        smg_cnt = 0;
        P2=0xc0;P0=0x00;P2=0x00;
        // C语言中, *show+i 指向的是 *show位置的下一个位置
        P2=0xe0;P0=~(*(*show+i));P2=0x00;                // 读取数组的每一位
        P2=0xc0;P0=T_COM[i];P2=0x00;
        i++;
        if (i == 8) i = 0 ;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值