计数器(状态机按键检测)程序
进一步看,击键动作本身可以看做一个状态机。一个击键动作包含按下、抖动、释放等状态。其实状态机的思想不单只是用在按键方面,数码管显示动态扫描、LED灯亮灭都存在状态机的思想。使用状态机思想进行单片机编程,比较通用的方法就是使用switch的选择性分支语句来进行状态跳转。
通过计数器这个实验向大家展示状态机的思想。
上图是proteus仿真图,时间每过1s计数器值自动加1,K1启动和停止计数器,K2选择要修改的位,K3当前位加1,K4当前位减1。
完整代码如下:
#include
typedef unsigned char UINT8;
typedef unsigned int UINT16;
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[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;
计数器状态机按键检 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)