重庆-风雪 发表于 2014-8-3 23:27:47

计数器(状态机按键检测)

计数器(状态机按键检测)作者:王庆雷       状态机是软件编程中的重要概念,比这个概念更重要的是对它的灵活应用。在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。例如,一个按键命令解析程序就可以被看做状态机:本来在A状态下,触发一个按键后切换到了B状态;再触发另一个键后切换到C状态,或者返回到A状态。这就是最简单的按键状态机的例子。实际的按键解析程序会比这更复杂,但这并不影响我们对状态机的认识。
       进一步看,击键动作本身可以看做一个状态机。一个击键动作包含按下、抖动、释放等状态。其实状态机的思想不单只是用在按键方面,数码管显示动态扫描、LED灯亮灭都存在状态机的思想。使用状态机思想进行单片机编程,比较通用的方法就是使用switch的选择性分支语句来进行状态跳转。
      通过计数器这个实验向大家展示状态机的思想。
http://b169.photo.store.qq.com/psb?/V1138dzR2xmS5E/80nZdl0e3bRanmYDe6BvYT0SC04b7evt7iVRRp00u18!/b/dA0ZyWSBGgAA&bo=JgMIAgAAAAABAAg!

上图是proteus仿真图,时间每过1s计数器值自动加1,K1启动和停止计数器,K2选择要修改的位,K3当前位加1,K4当前位减1。
完整代码如下:
#include<reg51.h>

typedef unsigned char UINT8;
typedef unsigned intUINT16;
typedef unsigned long UINT32;
typedef char          INT8;
typedef int            INT16;
typedef long          INT32;

#define TIMER0_INITIAL_VALUE 5000    //5ms定时
#define SEG_PORT             P0      //数码管占用的IO口
#define KEY_PORT             P1      //按键占用的IO口
#define KEY_MASK             0x0F    //按键掩码
#define KEY_SEARCH_STATUS   0      //查询按键状态
#define KEY_ACK_STATUS         1      //确认按键状态
#define    KEY_REALEASE_STATUS   2      //释放按键状态
#define KEY1               1      //按键1键值
#define KEY2               2      //按键2键值
#define KEY3               3      //按键3键值
#define KEY4               4      //按键4键值

#define HIGH                1
#define LOW                  0
#define ON                  1
#define OFF                  0

sbit DATA = P0^4;
sbit CLK = P0^5;

UINT8 Timer0IRQEvent = 0;    //定时器0中断事件
UINT8 Time1SecEvent = 0;    //1s定时事件
UINT8 TimeCount = 0;      //定时器0计数器,用于计数产生1s定时事件
UINT8 SegCurPosMark = 0;    //被选中的数码管
UINT16 CounterValue = 0;    //计数器
UINT8 SegCurSel = 0;      //当前选中的数码管
UINT8 SegBuf = {0};
code UINT8 SegCode = {~0x3F,~0x06,~0x5B,~0x4F,~0x66,~0x6D,~0x7D,~0x07,~0x7F,~0x6F};
code UINT8 SegSelTbl = {0xFE,0xFD,0xFB,0xF7};
UINT8 bSetTime = 0;            //标志位:是否设置计数值

void LS164_DATA(unsigned char x)
{
    if(x)
    {
      DATA = 1;
    }
    else
    {
      DATA = 0;
    }
}
void LS164_CLK(unsigned char x)
{
    if(x)
    {
      CLK = 1;
    }
    else
    {
      CLK = 0;
    }
}
/**********************************************************
*函数名称:LS164Send
*输    入:byte单个字节
*输    出:无
*功    能:74LS164发送单个字节
***********************************************************/
void LS164Send(UINT8 byte)
{
    UINT8 j;
    for(j=0;j<=7;j++)
    {
      if(byte&(1<<(7-j)))
      {
            LS164_DATA(HIGH);
      }
      else
      {
            LS164_DATA(LOW);
      }
      LS164_CLK(LOW);
      LS164_CLK(HIGH);
    }
}
/**********************************************************
*函数名称:SegRefreshDisplayBuf
*输    入:无
*输    出:无
*功    能:数码管刷新显示缓存
***********************************************************/
voidSegRefreshDisplayBuf(void)
{
   SegBuf = CounterValue%10;
   SegBuf = CounterValue/10%10;
   SegBuf = CounterValue/100%10;
   SegBuf = CounterValue/1000%10;         
}
/**********************************************************
*函数名称:SegDisplay
*输    入:无
*输    出:无
*功    能:数码管显示数据
***********************************************************/
void SegDisplay(void)
{
    UINT8 t;
    SEG_PORT = 0x0F;                                    //熄灭所有数码管
   
    if(bSetTime)                                          //检查是否设置计数值
    {
      if(SegCurSel == SegCurPosMark)
      {
            t = SegCode] & 0x7F;      //加上小数点
      }
      else
      {
            t = SegCode];               //正常显示当前数值
      }
    }
    else
    {
      t = SegCode];                     //正常显示当前数值
    }
   
    LS164Send(t);
    SEG_PORT = SegSelTbl;                     //点亮当前要显示的数码管
    if(++SegCurSel >= 4)
    {
      SegCurSel = 0;
    }   
}
/**********************************************************
*函数名称:TimerInit
*输    入:无
*输    出:无
*功    能:定时器初始化
***********************************************************/
void TimerInit(void)
{
    TH0 = (65536 - TIMER0_INITIAL_VALUE)/256;
    TL0 = (65536 - TIMER0_INITIAL_VALUE)%256;
    TMOD = 0x01;
}
/**********************************************************
*函数名称:Timer0Start
*输    入:无
*输    出:无
*功    能:定时器启动
***********************************************************/
void Timer0Start(void)
{
    TR0 = 1;
    ET0 = 1;
}
/**********************************************************
*函数名称:Timer0Stop
*输    入:无
*输    出:无
*功    能:定时器停止
***********************************************************/
void Timer0Stop(void)
{
    TR0 = 0;
    ET0 = 0;
}
/**********************************************************
*函数名称:PortInit
*输    入:无
*输    出:无
*功    能:I/O初始化
***********************************************************/
void PortInit(void)
{
    P0 = P1 = P2 = P3 = 0xFF;   
}
/**********************************************************
*函数名称:KeyRead
*输    入:无
*输    出:当前按下的按键
*功    能:读取按键值
***********************************************************/
UINT8 KeyRead(void)
{
    //KeyStatus:静态变量,保存按键状态
    //keyCurPress:静态变量,保存当前按键的键值
    static UINT8 KeyStatus = KEY_SEARCH_STATUS,KeyCurPress = 0;
    UINT8 KeyValue;
    UINT8 i = 0;

    KeyValue = (~KEY_PORT) & KEY_MASK;

    switch(KeyStatus)
    {
      case KEY_SEARCH_STATUS:                     //按键查询状态
      {
            if(KeyValue)
            {
                KeyStatus = KEY_ACK_STATUS;      //按键下一个状态为确认状态   
            }
            return 0;
      }
      break;
      
      case KEY_ACK_STATUS:                  //按键确认状态
      {
            if(!KeyValue)
            {
                KeyStatus = KEY_SEARCH_STATUS;
            }
            else
            {
                for(i=0;i<4;i++)
                {
                  if(KeyValue&(1<<i))
                  {
                        KeyCurPress = KEY1 + i;
                        break;
                  }
                }
                KeyStatus = KEY_REALEASE_STATUS;
            }
            return 0;
      }
      break;

      case KEY_REALEASE_STATUS:               //按键释放状态
      {
            if(!KeyValue)
            {
                KeyStatus = KEY_SEARCH_STATUS;
                return KeyCurPress;
            }
            return 0;
      }
      default:
            return 0;
      break;
    }
}
/**********************************************************
*函数名称:main
*输    入:无
*输    出:无
*功    能:函数主题
***********************************************************/
void main(void)
{
    PortInit();
    TimerInit();
    Timer0Start();
    SegRefreshDisplayBuf();
    EA = 1;
    while(1)
    {
      SegRefreshDisplayBuf();
      if(Timer0IRQEvent)
      {
            Timer0IRQEvent = 0;
            switch(KeyRead())
            {
                case KEY1:
                {
                  bSetTime = ~bSetTime;
                  SegCurPosMark = 0;   
                }
                break;

                case KEY2:
                {
                  if(++SegCurPosMark>=4)
                  {
                        SegCurPosMark = 0;
                  }
                }
                break;

                case KEY3:
                {
                  if(!bSetTime)
                        break;
                  if(CounterValue>=9999)
                        CounterValue = 0;
                  if   (SegCurPosMark == 0)
                        CounterValue += 1;
                  else if(SegCurPosMark == 1)
                        CounterValue += 10;
                  else if(SegCurPosMark == 2)
                        CounterValue += 100;
                  else
                        CounterValue += 1000;
                }
                break;

                case KEY4:
                {
                  if(!bSetTime)
                         break;
                  if(CounterValue<=0)
                  CounterValue = 9999;
                  if   (SegCurPosMark == 0)
                        CounterValue -= 1;
                  else if(SegCurPosMark == 1)
                        CounterValue -= 10;
                  else if(SegCurPosMark == 2)
                        CounterValue -= 100;
                  else
                        CounterValue -= 1000;
                }
                break;

                default:
                break;
            }
      }
      else if(Time1SecEvent)
      {
            Time1SecEvent = 0;
            if(!bSetTime)
            {
               if(++CounterValue>=9999)
                {
                  CounterValue = 0;
                }
            }
      }   
    }
}
/**********************************************************
*函数名称:Timer0IRQ
*输    入:无
*输    出:无
*功    能:定时器中断函数
***********************************************************/
void Timer0IRQ(void) interrupt 1
{
    TH0 = (65536 - TIMER0_INITIAL_VALUE)/256;
    TL0 = (65536 - TIMER0_INITIAL_VALUE)%256;
    Timer0IRQEvent = 1;

    SegDisplay();

    if(++TimeCount >= 200)
    {
      TimeCount = 0;
      Time1SecEvent = 1;
    }
}


页: [1]
查看完整版本: 计数器(状态机按键检测)