蓝桥杯单片机————基于状态机按键扫描

概要

        实验名称:基于状态机的按键扫描器

        实验环境:IAP15F2K61S2国信长天实验板

        实验配置:J13跳线配置为I/O模式,J5配置为BTN模式,J2配置为1-1,2-4模式

        实验内容:通过8个LED代表8个二进制位,R45为最高位,R32 为最低位,二进制数为按键RX的编号X(十进制)转化二进制的体现

备注:实验过程中蜂鸣器应处于关闭状态 

技术实现 

1.硬件环境 

        IAP15F2K61S2单片机部分

74HC138 译码器

 

74HC573锁存器

 ULN2003

2.原理图 

3.代码实现

#include <STC15F2K60S2.h>
#include <intrins.h>

/*宏函数*/
#define LED(X) {P0=X;P2=((P2&0X1F)|0X80);P2 &= 0X1F;}
#define BUZ(X) {P0=X;P2=((P2&0X1F)|0XAF);P2 &= 0X1F;}

sbit S7 = P3^0;
sbit S6 = P3^1;
sbit S5 = P3^2;
sbit S4 = P3^3;

/*按键状态*/
typedef enum {
    WAIT_FOR_PRESS,
    KEY_PRESSED,
    KEY_RELEASED,
    LONG_PRESS
} Status;

int key_press_time = 0;                 /*注意此处的数据类型*/ /*key_press_time用来按键长按计时*/
Status key_status = WAIT_FOR_PRESS;     /*按键状态变量*/
unsigned char key_value = 0x00;         /*按键数值变量*/
unsigned char key_routine = 0;          /*定时器辅助计数器*/
unsigned char key_mid = 0x00;
bit key_flag = 0;                       /*按键扫描标志位*/
bit key_long = 0;                       /*长按标志位*/

/*状态机按键扫描*/
void SCAN_KEY_BTN(void)
{
    switch(key_status)
    {
        case WAIT_FOR_PRESS:
            key_long = 0;                               /*按键进入待检测状态,切换为短按*/
            if((S7==0) || (S6==0) || (S5==0) || (S4==0))
                key_status = KEY_PRESSED;
            break;
        case KEY_PRESSED:
            if(S7==0){
                key_status = KEY_RELEASED;
                key_value = 7;
                key_mid = 7;
            }else if(S6==0){
                key_status = KEY_RELEASED;
                key_value = 6;
                key_mid = 6;
            }else if(S5==0){
                key_status = KEY_RELEASED;
                key_value = 5;
                key_mid = 5;
            }else if(S4==0){
                key_status = KEY_RELEASED;
                key_value = 4;
                key_mid = 4;
            }else{
                key_status = WAIT_FOR_PRESS;
            }
            break;
        case KEY_RELEASED:
            if(S7 && S6 && S5 && S4){               /*按键按下后释放*/   
                    key_status = WAIT_FOR_PRESS;
                    key_value = key_mid;
            }else{                                  
                if(key_press_time > 1000){          /*按键未释放持续时间大于1s*/
                    key_status = LONG_PRESS;        /*进入长按状态*/
                    key_press_time = 0;             //长按计数器清零
                }
            }
            break;
        case LONG_PRESS:
            if(S7&& S6 && S5 && S4){                //按键释放后进入待检测状态
                key_status = WAIT_FOR_PRESS;
            }else
                key_long = 1;                   //设置长按标志
            break;      
        default :
            break;    
    } 
}

/*定时器0初始化*/
void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	AUXR |= 0x80;			//定时器时钟1T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0xCD;				//设置定时初始值
	TH0 = 0xD4;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
    ET0 = 1;
    EA = 1;
}

/*定时器中断*/
void T0timer_ROUTINE(void) interrupt 1
{
    key_routine++;
    
    if(!(S7 && S6 && S5 && S4) && (key_status != LONG_PRESS)){  /*按键按下且未进入长按状态就开始计时*/
        key_press_time++;
    }
    else{                                                       /*按键释放就将计数器清零*/
        key_press_time = 0;
    }
    if(key_routine == 10)                                       /*每10ms执行一次按键扫描*/
    {
        key_routine = 0;
        key_flag = 1;
    }
}

/*主函数*/
void main(void)
{
    BUZ(0X00);
    LED(0XFF);
    Timer0_Init();
    while(1)
    {   
        LED(key_long ? 0x00 : ~key_value);            
        /*
            此处用长按标志位来决定LED显示,
            达到长按全部点亮LED,释放后回归普通点亮效果
        */
        if(key_flag)
        {
            SCAN_KEY_BTN();
            key_flag = 0;
        }
    }
}

4.内容要点

        1.状态机

状态机编程是一种将系统行为分解为有限状态的设计模式,通过定义状态、事件、转换条件和动作来管理程序流程。每个状态封装特定行为,事件触发状态间转换,使复杂逻辑清晰可控,常用于处理异步事件、协议解析或UI交互等场景,具有结构清晰、易维护、可扩展性强的特点。

        WAIT_FOR_PRESS为状态机初始状态,按键状态机处于待检测状态,等待按键按下,key_long为按键长按标志位,默认状态下为0,即短按触发。当检测到按键按下,发生状态转换,状态机将转换至KEY_PRESSED状态,获取按键的值.

case WAIT_FOR_PRESS:
            key_long = 0;                               /*按键进入待检测状态,切换为短按*/
            if((S7==0) || (S6==0) || (S5==0) || (S4==0))
                key_status = KEY_PRESSED;
            break;

        KEY_PRESSED为状态机检测到按键按下后进入的状态,在该状态下确定是哪一个按键被按下,然后发生状态转换,将状态机的状态更新至KEY_RELEASED状态,获取按键的值,由主副数值存储变量存储。若无按键按下,则将状态机的状态更新至WAIT)FOR_PRESS状态,状态机进入待检测状态。

case KEY_PRESSED:
            if(S7==0){
                key_status = KEY_RELEASED;
                key_value = 7;
                key_mid = 7;
            }else if(S6==0){
                key_status = KEY_RELEASED;
                key_value = 6;
                key_mid = 6;
            }else if(S5==0){
                key_status = KEY_RELEASED;
                key_value = 5;
                key_mid = 5;
            }else if(S4==0){
                key_status = KEY_RELEASED;
                key_value = 4;
                key_mid = 4;
            }else{
                key_status = WAIT_FOR_PRESS;
            }
            break;

        KEY_RELEASED状态为状态机检测按键是否释放的状态,状态机将检测按键的释放情况,若按键释放,则将状态机更新至待检测状态WAIT_FOR_PRESS,主数值存储变量与副数值存储变量保持一致。否则判断按键的保持时间是否满足条件,保持时间达到1s,则认为按键为长按。将状态机的状态更新至长按状态LONG_PRESS。按键保持按下的时间通过变量key_press_time来计时。

case KEY_RELEASED:
            if(S7 && S6 && S5 && S4){               /*按键按下后释放*/   
                    key_status = WAIT_FOR_PRESS;
                    key_value = key_mid;
            }else{                                  
                if(key_press_time > 1000){          /*按键未释放持续时间大于1s*/
                    key_status = LONG_PRESS;        /*进入长按状态*/
                    key_press_time = 0;             //长按计数器清零
                }
            }
            break;

定时器0中断中,每1ms进行一次中断,检测是否有按键按下并且状态机的状态不是LONG_PRESS状态,按键计时加一,否则按键计时清零

/*定时器中断*/
void T0timer_ROUTINE(void) interrupt 1
{
    key_routine++;
    
    if(!(S7 && S6 && S5 && S4) && (key_status != LONG_PRESS)){  /*按键按下且未进入长按状态就开始计时*/
        key_press_time++;
    }
    else{                                                       /*按键释放就将计数器清零*/
        key_press_time = 0;
    }
    if(key_routine == 10)                                       /*每10ms执行一次按键扫描*/
    {
        key_routine = 0;
        key_flag = 1;
    }
}

LONG_PRESS状态机,检测按键是否释放,未释放的长按状态下将长按标志位置1,释放后将状态机更新至WAIT_FOR_PRESS待检测状态。

case LONG_PRESS:
            if(S7&& S6 && S5 && S4){                //按键释放后进入待检测状态
                key_status = WAIT_FOR_PRESS;
            }else
                key_long = 1;                   //设置长按标志
            break;      

实验结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值