标题:
单片机单个按键状态机编程控制5种不同LED闪法程序与仿真
[打印本页]
作者:
nxw_toyosz
时间:
2022-9-17 10:22
标题:
单片机单个按键状态机编程控制5种不同LED闪法程序与仿真
LED流水灯堆叠设计,且按键不占用CPU时间,上电显示第1功能,单个按键的单击显示第2种功能,单按第5次返回第1功能,任意功能长按2S以上返回第2功能.程序注释全。附件包含程序源码和PROTUSE仿真图。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
51hei.gif
(66.59 KB, 下载次数: 20)
下载附件
2022-9-17 15:57 上传
单片机源程序如下:
/*****************************************************************************
*名称:头文件,预定义。
*功能:包含头文件,预定义,端口定义
******************************************************************************/
#include<reg51.h> //包含 51 单片机寄存器定义的头文件
#define uchar unsigned char //定义uchar可在函数中当unsign char使用,取值不超过255。
#define uint unsigned int //定义uint可在函数中当unsign int使用,取值不超过65535。
sbit KEY=P1^0; //定义按键KEY为P1.0
#define LED P2 //定义LED为P2口
#define KEY_STATE_0 0 //按键初始状态
#define KEY_STATE_1 1 //按键消抖
#define KEY_STATE_2 2 //按键按下功能种类是单击,双击还是长按
#define KEY_STATE_3 3 //按键弹起
#define SINGLE_KEY_TIME 3 //SINGLE_KEY_TIME*10MS = 30MS?判定单击的时间长度,软件消抖
#define KEY_INTERVAL 30 //KEY_INTERVAL*10MS?= 300MS 判定双击的时间间隔
#define LONG_KEY_TIME 200 //LONG_KEY_TIME*10MS? ?= 2S? ?判定长按的时间长度
#define N_KEY 0 //按键没动作
#define S_KEY 1 //单击
#define L_KEY 2 //长按
#define D_KEY 3 //双击
uchar code Zuoyi[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //左流水数组,低电平导通
uchar code Youyi[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe}; //右流水数组,低电平导通
uchar keySu=0; // 定义按键功能状态变量
uchar key_val; // 按键值
uchar time_10ms_ok=0; //10MS定时标志
//define LongCount 100; //延时长按变量100X10MS=1S
/******************************************************************************
***函数名称: void delay_ms(uint x)
***函数功能: MS延时(按下按键时,可以跳出循环体)
***输入: 无
***输出: 无
******************************************************************************/
/*******/
void delay_ms(uint x) // 延时函数,延时x MS,
{
uint i; //定义无符号字符型变量j值域为0-65536.
uchar j; //定义无符号字符型变量i值域为0-255.
if(KEY) //如果按键为1时,则执行延时循环
{
for(i=0;i<x;i++) // 循环x遍
{
for(j=0;j<112;j++)
; //空语句等待一个机器周期
}
}
else //如果按键按下则跳出循环
return;
}
/******************************************************************************
***函数名称: void move_l(void)
***函数功能: 左流水(P2口低位向高位流动跑马)
***输入: 无
***输出: 无
******************************************************************************/
/******/
void move_l(void) //1.左流水函数
{
uchar b; //循环变量b
while(1) //死循环
{
for(b=0;b<8;b++) // 循环8遍
{
LED=Zuoyi[b]; //调用左流水数组
delay_ms(500); //延时500MS
if(KEY==0) //如果有按键按下
return; //退出循环
}
//if(KEY==0) //此两语句可以不用
//return;
}
}
/******************************************************************************
***函数名称: void move_r(void)
***函数功能: 右流水(P2口高位向低位流动跑马)
***输入: 无
***输出: 无
******************************************************************************/
/******/
void move_r(void) //2.右流水函数
{
uchar b; //循环变量b
while(1) //无限循环
{
for(b=0;b<8;b++) // 循环8遍
{
LED=Youyi[b]; //调用右流水数组
delay_ms(500); //延时500MS
if(KEY==0) //如果有按键按下则退出循环
return; //退出循环
}
//if(KEY==0) //此两语句可以不用
//return;
}
}
/******************************************************************************
***函数名称: void flash(void)
***函数功能: 闪烁
***输入: 无
***输出: 无
******************************************************************************/
/******/
void flash(void) //3.闪烁
{
while(1) //无限循环
{
LED=0x00; //亮
delay_ms(500); //延时500MS
LED=0xff; //灭
delay_ms(500); //延时500MS(毫秒)
if(KEY==0) //如果有按键按下则退出循环
return; //退出循环
}
}
/******************************************************************************
***函数名称: void Di_zeng1 (void)
***函数功能:.左递增右递减,,右递增左递减
***输入: 无
***输出: 无
******************************************************************************/
/******/
void Di_zeng1 (void) //4.左递增右递减,,右递增左递减
{
while(1)
{
uchar m; //循环变量
uchar n=0; //中间变量
uchar n1=0; //中间变量
uchar n2=0; //中间变量
uchar n3=0; //中间变量
for(m=0;m<8;m++) //左递增,循环8位
{
n +=(0x01<<m); // 1左移m位再赋值给n
LED=~n; //n取反后点亮LED灯
delay_ms(500); //延时500毫秒
if(KEY==0) //如果按键按下
return; //退出循环
}
for(m=0;m<8;m++) //右递减,循环8位
{
n1 +=(0x80>>m); // 1右移m位再赋值给n1
LED=n1; //n1取反后点亮LED灯
delay_ms(500); //延时500毫秒
if(KEY==0) //如果按键按下
return; //退出循环
}
for(m=0;m<8;m++) //右递增
{
n2 +=(0x80>>m);
LED=~n2;
delay_ms(500);
if(KEY==0)
return;
}
for(m=0;m<8;m++) //左递减
{
n3=n3+(0x01<<m);
LED=n3;
delay_ms(500);
if(KEY==0)
return;
}
//if(KEY==0) //此两语句可以不用
//return;
}
}
/******************************************************************************
***函数名称: void Di_zeng2 (void)
***函数功能:左递增左递减,,右递增右递减
***输入: 无
***输出: 无
******************************************************************************/
/******/
void Di_zeng2 (void) //5.左递增左递减,,右递增右递减
{
while(1)
{
uchar m,n=0;
uchar n1=0;
uchar n2=0;
uchar n3=0;
for(m=0;m<8;m++) //左递增
{
n=n+(0x01<<m);
LED=~n;
delay_ms(500);
if(KEY==0)
return;
}
for(m=0;m<8;m++)//左递减
{
n1 +=(0x01<<m);
LED=n1;
delay_ms(500);
if(KEY==0)
return;
}
for(m=0;m<8;m++) //右递增
{
n2 +=(0x80>>m);
LED=~n2;
delay_ms(500);
if(KEY==0)
return;
}
for(m=0;m<8;m++) //右递减
{
n3=n3+(0x80>>m);
LED=n3;
delay_ms(500);
if(KEY==0)
return;
}
//if(KEY==0) //此两语句可以不用
//return;
}
}
/******************************************************************************
***函数名称: uchar key_read(void)
***函数功能: 键盘扫描函数(按键状态机方式)
***输入: 无
***输出: 返回值。单击,长按,双击
******************************************************************************/
/******/
uchar key_read(void)
{
static uchar key_state = 0; //按键初始状态
static uint key_time =0; //按键时间间隔计时变量
uchar key_press; //按键是按下还是抬起变量
uchar key_return; //按键函数返回
key_return=N_KEY; //清除返回按键值
key_press=KEY; //读取当前键值
switch(key_state)
{
case KEY_STATE_0: //按键初始状态0:判断有无按键按下
if(!key_press) //有按键按下
{
key_time=0; //一次10ms,时间间隔计数器清0
key_state=KEY_STATE_1;//然后进入转到按键确认态1
}
break;
case KEY_STATE_1:
//按键确认状态1:软件消抖(确定按键是否有效,而不是误触)
//按键有效的定义:按键持续按下超过设定的消抖时间
if(!key_press) //按键仍然按下
{
key_time++; //一次10ms,时间间隔变量加1
//消抖时间为SINGLE_KEY_TIME*10MS=30MS
if(key_time>=SINGLE_KEY_TIME) //如果大于消抖时间
{
key_state = KEY_STATE_2;//按键仍然处于按下状态
}
//如果按键时间超过消抖时间,即判定为按下的按键有效。
//按键有效包括两种:单击或长按,继续判定为那种有效按键
}
else //按键时间没有超过,判定为误触,按键无效,返回状态0
{
key_state = KEY_STATE_0; //返回初始状态
}
break; //跳出
case KEY_STATE_2: //按键状态2:判定按键有效的种类:是单击,还是长按
if(key_press) //如果按键在设定的长按时间内释放,则判定为单击
{
key_time++; //一次10ms,时间间隔变量加1
//按键弹起(高电平)后计时,计时大于双击30*10MS时间小于长按时间则为单击
if((key_time >=KEY_INTERVAL)&&( key_time < LONG_KEY_TIME))
{
key_return = S_KEY; //返回有效按键值: 单击
key_time=0; //时间间隔变量清0
key_state = KEY_STATE_0; //返回按键状态0继续等待按键
}
else //否则在此时间段为低电平则为双击
{
key_return = D_KEY; //返回有效按键值: 双击
key_time=0; //时间间隔变量清0
key_state = KEY_STATE_0; //返回按键状态0继续等待按键
}
}
else // 如果一直按下,则计算按下的时间
{
key_time++;
//如果按键继续按下时间超过设定的长按时间
//(LONG_KEY_TIME*10ms=300*10ms=3000ms)则判定为长按
if(key_time >= LONG_KEY_TIME)
{
key_return = L_KEY; //返回有效键值:长按
key_time=0; //时间间隔变量清0
key_state = KEY_STATE_3; //去状态3,等待按键释放
}
}
break;
case KEY_STATE_3: //状态3为长按按键释放抬起
if(key_press) //如果按键为1,释放状态
{
key_state = KEY_STATE_0; //按键弹起,进行下一次按键的判定
}
break;
//特殊情况:key_state 是其他值的情况,清0key_state ,这种情况一般出现
//在没有初始key_state,第一次执行这个函数的时候
default: key_state = KEY_STATE_0;
break;
}
return key_return; //返回按键值
}
/******************************************************************************
***函数名称: void Timer0Init()
***函数功能: 定时器0初始化
***输入: 无
***输出: 无
******************************************************************************/
/******/
void Timer0Init() //10毫秒@12.00MHZ
{
TMOD=0x01; //设置定时器工作方式1
TH0=(65536-10000)/256;
TL0=(65536-10000)%256; //定时器0预设初值10000US=10MS
TR0=0; //关闭定时器0,当按键按下才开启定时器0,计算按下的时长
EA=1; //开总中断
ET0=1; //允许定时器0中断
TR0=1; //启动定时器0
}
/******************************************************************************
***函数名称: void Timer0_Isr() interrupt 1
***函数功能: 定时器0中断函数
***输入: 无
***输出: 无
******************************************************************************/
/******/
void Timer0_Isr() interrupt 1 //定时器0中断服务函数
{
TH0=(65536-10000)/256; //预设初值50MS
TL0=(65536-10000)%256; //预设初值50MS
time_10ms_ok=1; //10毫秒到标志
}
/**********主程序*******/
void main(void)
{
KEY=0x01; //P1.0作为输入口前必须输出高电平
Timer0Init(); //调用定时器0初始化函数
while(1)
{
if(time_10ms_ok) //10MS定时时间到
{
time_10ms_ok=0; //赋值0,10MS查询一次按键状态
key_val=key_read(); //读取按键的返回值
if(key_val==S_KEY) //如果是单击
{
keySu++; //变量按键数加1
if(keySu==5) //如果加到5
keySu=0; //重新赋值0
}
if(key_val==L_KEY) //如果是长按
{
keySu=1; //按键数为1
}
if(key_val==D_KEY) //如果是双击
{
keySu=2; //按键数为2
}
switch(keySu) //5路分支
{
case 0: move_l();break; //当按键数为0,执行左流水功能
case 1: move_r();break; //当按键数为1,执行右流水功能
case 2: flash(); break; //当按键数为2,执行闪烁功能
case 3: Di_zeng1(); break; //当按键数为3执行左递增右递减,右递增左递减功能
case 4: Di_zeng2(); break;//当按键数为4执行左递增左递减,右递增右递减功能
default : break; //默认跳出
}
}
}
}
复制代码
Keil代码与Proteus8.8仿真下载:
单个按键状态机编程控制5种功能.7z
(75.63 KB, 下载次数: 38)
2022-9-17 15:57 上传
点击文件名下载附件
下载积分: 黑币 -5
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1