状态机cpu时间·

Nebula Pi 开发板应用(一)—利用状态机的思想实现按键检测

ii11nnocent

ii11nnocent

1866 0

在使用按键时,常见的使用方法是识别按键是否按下—消抖(delay延时)—再次确认按键是否按下,但是这样进行判断时会经常使用delay延时函数,大大降低CPU的效率。利用状态机的思想可以很好的解决CPU等待时间过长的缺陷,提高CPU的效率。 状态机是在进行程序编写时的一个重要概念,比如在进行按键按下与否的判断时,可以看成一个状态机,其中共有四个要素:现在的状态、触发的条件、触发后的动作及新的状态。 现在的状态指的是目前的状态,通常为按键未按下的初始状态。 触发的条件指的是当有满足的条件后,将会触发一个新的动作,或者进行一次状态的迁移。 触发后的动作指的是当条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态、保持现在的状态或者回到原来的状态。 新的状态指的是条件满足后要迁移的新状态。 以按键为例,可以将识别按键的过程设定为三个状态: (1)按键未按下的初始状态,设定为S1 (2)确认有按键按下的状态,设定为S2 (3)按键按下后释放的状态,设定为S3 以Nebula Pi开发板上的按键为例(按下时IO口的电平为0,释放时IO口的电平为1),采用状态机的方法实现识别按键的过程如下: 当开发板上电时,按键处于初始状态S1,当检测到IO口的电平值为1时,表明按键没有被按下,保持S1状态。当检测到IO口的电平值为0时,表明有按键按下,此时立即转入状态S2。 在状态S2时,如果检测到IO口的电平为1时,表明刚才的按键按下为干扰,此时状态立即转回S1;如果检测到IO口的电平为0时,表明确实有按键被按下,此时可以保存按键的键值,也可以转入新的状态S3,也可以同时进行。 在状态S3时,如果检测到IO口的电平值为0,表明按键一直被按下,此时可以进行按键按下的时长计算,也可以进行其他的操作;如果检测到IO口的电平值为1,表明按键被释放了,此时可以保存按键的键值,也可以转回状态S0。具体的过程可以用下面的状态迁移图来表示:

具体的代码实现如下(按下K1时,D7点亮;释放K1时,K1熄灭。长按K1 1秒时,D6点亮,再次长按K1 1秒时,D6熄灭): KeyState.h:

#ifndef _KEYSTATE_H
#define _KEYSTATE_H

#include "reg52.h"

unsigned char ReadKeyState(void);
void Timer0_Init(void);
extern unsigned char Times;             //按下的次数
extern bit Flag;                    //1s长按标志位

#endif

KeyState.c:

#include "KeyState.h"

// 位定义 Key1 按键
sbit Key1 = P1 ^0;
// 保存按键的返回值
unsigned char KeyReturn;
unsigned char Times;
bit Flag;

/****宏定义按键的初始、按下和抬起状态****/
#define Key_State_Init    0
#define Key_State_Down    1
#define Key_State_Up    2


unsigned char ReadKeyState(void) {
    // 按键的状态值,初始值为0
    static char KeyState = 0;
    // 保存按键的电平值
    unsigned char KeyPress;
    // 保存P1.0口电平
    KeyPress = Key1;

    switch (KeyState) {
        // 初始状态
        case Key_State_Init:
            // 读取 IO 电平
            if (!KeyPress) {
                // 按键返回值为0
                KeyReturn = 0;
                // 按键的状态转为按键按下的状态
                KeyState = Key_State_Down;
            }
            break;
        // 初始状态按下后转为按下状态
        case Key_State_Down:
            // 读取IO电平确认按键是否真的被按下
            if (!KeyPress) {
                // 按键返回值为1
                KeyReturn = 1;
                // 按键的状态转为按键释放的状态
                KeyState = Key_State_Up;
            } else {
                // 如果IO电平为0,证明按键已抬起,回到初始状态
                KeyState = Key_State_Init;
            }
            break;
        // 按下以后转为抬起状态
        case Key_State_Up:
            // 如果按键一直按下,即没有抬起
            if (!KeyPress) {
                // 只要一直按下按键,Times就会10ms增加1
                Times++;
                // Times=100时,1s定时时间到,标志位取反并且Times值清零
                if (Times == 100) {
                    Flag = ~Flag;
                    Times = 0;
                }
            }
            // 如果按键抬起了
            if (KeyPress) {
                // 按键返回值为 2
                KeyReturn = 2;
                // 读取 IO 电平,如果是 1,证明按键已释放,回到初始状态
                KeyState = Key_State_Init;
            }
            break;
    }
    return KeyReturn;
}

void Timer0_Init(void) {
    TMOD = 0x01;
    TH0 = 0xDC;
    // 10ms 定时初值
    TL0 = 0x00;
    // 开定时器 0 中断
    ET0 = 1;
    // 开总中断
    EA = 1;
    // 打开定时器 0
    TR0 = 1;
}

/****在中断服务函数中调用按键读取函数,然后主函数中判断键值****/

main.c:

#include "reg52.h"
#include "KeyState.h"

sbit Led = P1^7;        
sbit Led2 = P1^6;

unsigned char KeyNumber = 0;    //按键值保存变量

void main(void)
{
    Timer0_Init();
    while(1)
    {
        if( KeyNumber == 1 )
        {
            Led = 0;
        }
        if( KeyNumber == 2 )
        {
            Led = 1;
        }
        if( Flag == 1 )             
        {
            Led2 = 0;
        }
        else    Led2 = 1;
    }
}

void Timer0(void) interrupt 1
{
    TH0 = 0xDC;
    TL0 = 0x00;     //重新赋初值
    
    KeyNumber = ReadKeyState();     //读取按键值
}

相关文章

优先推荐同专题、同标签和同作者内容,补足热门文章。

评论 0

登录 后参与评论

评论

成为第一个评论的人