资讯详情

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

状态机是软件编程中的重要概念,比这个概念更重要的是对它的灵活应用。在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。例如,一个按键命令解析程序就可以被看做状态机:本来在A状态下,触发一个按键后切换到了B状态;再触发另一个键后切换到C状态,或者返回到A状态。这就是最简单的按键状态机的例子。实际的按键解析程序会比这更复杂,但这并不影响我们对状态机的认识。 进一步看,击键动作本身可以看做一个状态机。一个击键动作包含按下、抖动、释放等状态。其实状态机的思想不单只是用在按键方面,显示动态扫描、LED灯亮灭都存在状态机的思想。使用状态机思想进行单片机编程,比较通用的方法就是使用switch的选择性分支语句来进行状态跳转。 通过这个实验向大家展示状态机的思想。 上图是proteus仿真图,时间每过1s计数器值自动加1,K1启动和停止计数器,K2选择要修改的位,K3当前位加1,K4当前位减1。 完整代码如下: #include<reg51.h>

typedef unsigned char UI8; typedef unsigned int UINT16; typedef unsigned long UINT32; typedef char INT8; typedef int INT16; typedef long INT32;

#define MER0_INITIAL_LUE 5000 //5ms定时 #define SEG_PORT P0 //数码管占用的IO口 #define KEY_PORT P1 //按键占用的IO口 #define KEY_MASK 0x0F //按键掩码 #define KEY_SEARCH_ 0 //查询按键状态 #define KEY_ACK_ATUS 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 1 #define OFF 0

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

UINT8 Timer0QEvent = 0; //定时器0中断事件 UINT8 Time1SecEvent = 0; //1s定时事件 UINT8 TimeCount = 0; //定时器0计数器,用于计数产生1s定时事件 UINT8 SegCurPosMark = 0; //被选中的数码管 UINT16 CounterValue = 0; //计数器 UINT8 SegCurSel = 0; //当前选中的数码管 UINT8 SegBuf[4] = {0}; code UINT8 SegCode[10] = {~0x3F,~0x06,~0x5B,~0x4F,~0x66,~0x6D,~0x7D,~0x07,~0x7F,~0x6F}; code UINT8 SegSelTbl[4] = {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 *输 入:无 *输 出:无 *功 能:数码管刷新显示缓存 ***********************************************************/ void SegRefreshDisplayBuf(void) { SegBuf[0] = CounterValue%10; SegBuf[1] = CounterValue/10%10; SegBuf[2] = CounterValue/100%10; SegBuf[3] = CounterValue/1000%10; } /********************************************************** *函数名称:SegDisplay *输 入:无 *输 出:无 *功 能:数码管显示数据 ***********************************************************/ void SegDisplay(void) { UINT8 t; SEG_PORT = 0x0F; //熄灭所有数码管 if(bSetTime) //检查是否设置计数值 { if(SegCurSel == SegCurPosMark) { t = SegCode[SegBuf[SegCurSel]] & 0x7F; //加上小数点 } else { t = SegCode[SegBuf[SegCurSel]]; //正常显示当前数值 } } else { t = SegCode[SegBuf[SegCurSel]]; //正常显示当前数值 } LS164Send(t); SEG_PORT = SegSelTbl[SegCurSel]; //点亮当前要显示的数码管 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

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台