仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
#ifndef _KEY_H
#define _KEY_H
//#include"reg51.h"
sbit KEY_INPUT = P1^2; // 按键IO
#define KEY_STATE_0 0 // 按键状态
#define KEY_STATE_1 1
#define KEY_STATE_2 2
#define KEY_STATE_3 3
#define KEY_STATE_4 4
#define KEY_STATE_5 5
#define KEY_STATE_6 6
#define KEY_STATE_7 7
#define KEY_STATE_8 8
#define KEY_STATE_9 9
#define SINGLE_KEY_TIME 3 // SINGLE_KEY_TIME*10MS = 30MS 判定单击的时间长度,软件消抖
#define KEY_INTERVAL 70 // KEY_INTERVAL*10MS = 300MS 判定双击的时间间隔(仿真调慢了700MS)实际30-40
#define LONG_KEY_TIME 300 // LONG_KEY_TIME*10MS = 3S 判定长按的时间长度
#define N_KEY 0 // no click
#define S_KEY 1 // single click
#define D_KEY 2 // double click
#define T_KEY 3 // Triple click
#define F_KEY 4 //4击
#define FIVE_KEY 5 //5击
#define SIX_KEY 6 //6击
#define SEVEN_KEY 7 //7击
#define EIGHT_KEY 8 //8击
#define NINE_KEY 9 //9击
#define TEN_KEY 10 //10击
#define L_KEY 15 // long press
//unsigned char key_driver(void);
//unsigned char key_read(void);
#endif
#include "key.h"
// =========================== key.c ======================
// ----------------------------------- key_driver --------------------------
unsigned char key_driver(void)
{
static unsigned char key_state = 0;
static unsigned int key_time = 0;
unsigned char key_press, key_return;
key_return = N_KEY; // 清除 返回按键值
key_press = KEY_INPUT; // 读取当前键值
switch (key_state)
{
case KEY_STATE_0: // 按键状态0:判断有无按键按下
if (!key_press) // 有按键按下
{
key_time = 0; // 清零时间间隔计数
key_state = KEY_STATE_1; // 然后进入 按键状态1
}
break;
case KEY_STATE_1: // 按键状态1:软件消抖(确定按键是否有效,而不是误触)。按键有效的定义:按键持续按下超过设定的消抖时间。
if (!key_press)
{
key_time++; // 一次10ms
if(key_time>=SINGLE_KEY_TIME) // 消抖时间为:SINGLE_KEY_TIME*10ms = 30ms;
{
key_state = KEY_STATE_2; // 如果按键时间超过 消抖时间,即判定为按下的按键有效。按键有效包括两种:单击或者长按,进入 按键状态2, 继续判定到底是那种有效按键
}
}
else key_state = KEY_STATE_0; // 如果按键时间没有超过,判定为误触,按键无效,返回 按键状态0,继续等待按键
break;
case KEY_STATE_2: // 按键状态2:判定按键有效的种类:是单击,还是长按
if(key_press) // 如果按键在 设定的长按时间 内释放,则判定为单击
{
key_return = S_KEY; // 返回 有效按键值:单击
key_state = KEY_STATE_0; // 返回 按键状态0,继续等待按键
}
else
{
key_time++;
if(key_time >= LONG_KEY_TIME) // 如果按键时间超过 设定的长按时间(LONG_KEY_TIME*10ms=300*10ms=3000ms), 则判定为 长按
{
key_return = L_KEY; // 返回 有效键值值:长按
key_state = KEY_STATE_3; // 去状态3,等待按键释放
}
}
break;
case KEY_STATE_3: // 等待按键释放
if (key_press)
{
key_state = KEY_STATE_0; // 按键释放后,进入 按键状态0 ,进行下一次按键的判定
}
break;
default: // 特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候
key_state = KEY_STATE_0;
break;
}
return key_return; // 返回 按键值
}
// ----------------------------------- key_read --------------------------------
unsigned char key_read(void)
{
static unsigned char key_state1=0, key_time1=0;
unsigned char key_return,key_temp;
key_return = N_KEY; // 清零 返回按键值
key_temp = key_driver(); // 读取键值
switch(key_state1)
{
case KEY_STATE_0: // 按键状态0:等待有效按键(通过 key_driver 返回的有效按键值)
if (key_temp == S_KEY ) // 如果是[单击],不马上返回单击按键值,先进入 按键状态1,判断是否有[双击]的可能
{
key_time1 = 0; // 清零计时
key_state1 = KEY_STATE_1;
}
else // 如果不是[单击],直接返回按键值。这里的按键值可能是:[长按],[无效按键]
{
key_return = key_temp; // 返回 按键值
}
break;
case KEY_STATE_1: // 按键状态1:判定是否有[双击]
if (key_temp == S_KEY) // 有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[双击],但是不马上返回 有效按键值为[双击],先进入 按键状态2,判断是否有[三击]
{
key_time1 = 0; // 清零 时间间隔
key_state1 = KEY_STATE_2; // 改变 按键状态值
// key_return = D_KEY; // 返回 有效按键:[双击]
// key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
else // 有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击]出现,则判定为 [单击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = S_KEY; // 返回 有效按键:[单击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
case KEY_STATE_2: // 按键状态2:判定是否有[三击]
if (key_temp == S_KEY) // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[三击],由于这里只扩展到[三击],所以马上返回 有效按键值为[三击]
{
// key_return = T_KEY;
key_time1 = 0; // 返回 有效按键:[三击]
key_state1 = KEY_STATE_3; // 返回 按键状态0,等待新的有效按键
}
else // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击],则判定为 [双击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = D_KEY; // 返回 有效按键 返回 有效按键:[2击】
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
case KEY_STATE_3: // 按键状态3:判定是否有[4击]
if (key_temp == S_KEY) // 有[3击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[三击]
{
// key_return = F_KEY; // 返回 有效按键:[4击]
key_time1 = 0;
key_state1 = KEY_STATE_4; // 返回 按键状态0,等待新的有效按键
}
else // 有[3击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[4击],则判定为 [3击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = T_KEY; // 返回 有效按键:[3击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
case KEY_STATE_4: // 按键状态4:判定是否有[5击]
if (key_temp == S_KEY) // 有[4击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[5击],由于这里只扩展到[5击],
{
//key_return = FIVE_KEY; // 返回 有效按键:[5击]
key_time1 = 0;
key_state1 = KEY_STATE_5; // 返回 按键状态0,等待新的有效按键
}
else // 有[4击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[5击],则判定为 [4击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = F_KEY; // 返回 有效按键:[4击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
case KEY_STATE_5: // 按键状态5:判定是否有[5击]
if (key_temp == S_KEY) // 有[5击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[6击]
{
// key_return = SIX_KEY; // 返回 有效按键:[6击]
// key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
key_time1 = 0;
key_state1 = KEY_STATE_6; // 返回 按键状态0,等待新的有效按键
}
else // 有[5击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[6击],则判定为 [5击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = FIVE_KEY; // 返回 有效按键:[5击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
case KEY_STATE_6: // 按键状态6:判定是否有[6击]
if (key_temp == S_KEY) // 有[6击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[7击]
{
// key_return = SEVEN_KEY; // 返回 有效按键:[7击]
// key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
key_time1 = 0;
key_state1 = KEY_STATE_7; // 返回 按键状态0,等待新的有效按键
}
else // 有[6击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[7击],则判定为 [6击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = SIX_KEY; // 返回 有效按键:[6击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
case KEY_STATE_7: // 按键状态7:判定是否有[7击]
if (key_temp == S_KEY) // 有[7击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[8击]
{
// key_return = EIGHT_KEY; // 返回 有效按键:[8击]
// key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
key_time1 = 0;
key_state1 = KEY_STATE_8; // 返回 按键状态0,等待新的有效按键
}
else // 有[7击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[8击],则判定为 [7击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = SEVEN_KEY; // 返回 有效按键:[7击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
case KEY_STATE_8: // 按键状态7:判定是否有[8击]
if (key_temp == S_KEY) // 有[8击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[9击]
{
// key_return = NINE_KEY; // 返回 有效按键:[9击]
// key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
key_time1 = 0;
key_state1 = KEY_STATE_9; // 返回 按键状态0,等待新的有效按键
}
else // 有[8击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[9击],则判定为 [8击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = EIGHT_KEY; // 返回 有效按键:[8击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
case KEY_STATE_9: // 按键状态7:判定是否有[9击]
if (key_temp == S_KEY) // 有[9击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[9击]
{
key_return = TEN_KEY; // 返回 有效按键:[10击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
else // 有[9击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[10击],则判定为 [9击]
{
key_time1++; // 计数 时间间隔
if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔
{
key_return = NINE_KEY; // 返回 有效按键:[9击]
key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键
}
}
break;
//=============================================================================================
default: // 特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候
key_state1 = KEY_STATE_0;
break;
}
return key_return; // 返回 按键值
}
// =========================== main.c ======================
#include "reg51.h"
#include "key.c"
#define FOSC 11059200L
#define T1MS (65536-FOSC/12/100) //1ms timer calculation method in 12T mode
sbit LED1 = P2^0; // 定义LEDIO口
sbit LED2 = P2^1;
sbit LED3 = P2^2; // 定义LEDIO口
sbit LED4 = P2^3;
sbit LED5 = P2^4; // 定义LEDIO口
sbit LED6 = P2^5;
sbit LED7 = P2^6;
sbit LED8 = P2^7;
unsigned char g_u8_KeyValue; // 按键值
unsigned char g_flag_10ms_key; // 10ms 计时标志
unsigned char key_read(); // 声明读取按键函数
void T0_Init_10ms(void) // timer0,初始化函数 ,定时时间为 10ms
{
TMOD = 0x01; //set timer0 as mode1 (16-bit)
TL0 = T1MS; //initial timer0 low byte
TH0 = T1MS >> 8; //initial timer0 high byte
TR0 = 1; //timer0 start running
ET0 = 1; //enable timer0 interrupt
EA = 1;
}
// 主函数
void main(void)
{
// P1^2=1; // P1.0 拉高
T0_Init_10ms(); // 定时器0,初始化,定时10ms
while(1)
{
if(g_flag_10ms_key) // 等待10ms,定时完成
{
g_flag_10ms_key = 0; // 清零10ms定时标志
g_u8_KeyValue = key_read(); // 读取按键值
switch(g_u8_KeyValue)
{
case S_KEY: LED1=0;LED2=1;LED3=1;LED4=1;LED5=1;LED6=1;LED7=1;LED8=1; break; // 单击 点亮LED1
case D_KEY: LED1=1;LED2=0;LED3=1;LED4=1;LED5=1;LED6=1;LED7=1;LED8=1; break; // 双击
case T_KEY: LED1=1;LED2=1;LED3=0;LED4=1;LED5=1;LED6=1;LED7=1;LED8=1; break; // 三击
case F_KEY: LED1=1;LED2=1;LED3=1;LED4=0;LED5=1;LED6=1;LED7=1;LED8=1; break; // 四击
case FIVE_KEY: LED1=1;LED2=1;LED3=1; LED4=1; LED5=0;LED6=1;LED7=1;LED8=1;break; //五击
case SIX_KEY: LED1=1;LED2=1;LED3=1; LED4=1; LED5=1;LED6=0;LED7=1;LED8=1;break; //六击
case SEVEN_KEY: LED1=1;LED2=1;LED3=1; LED4=1; LED5=1;LED6=1;LED7=0;LED8=1;break; //7击
case EIGHT_KEY: LED1=1;LED2=1;LED3=1; LED4=1; LED5=1;LED6=1;LED7=1;LED8=0;break; //8击
case NINE_KEY: LED1=0;LED2=0;LED3=0; LED4=0; LED5=1;LED6=1;LED7=1;LED8=1;break; //9击
case TEN_KEY: LED1=1;LED2=1;LED3=1; LED4=1; LED5=0;LED6=0;LED7=0;LED8=0;break; //10击
case L_KEY: LED1=1;LED2=1;LED3=1; LED4=1; LED5=1;LED6=1;LED7=1;break; // 长按 熄灭所有LED
}
}
}
}
// timer0 中断服务程序
void IRQ_T0(void) interrupt 1
{
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
g_flag_10ms_key = 1; // 置位 10ms 定时标志
}
仿真程序:
按键单击多击长按扫描演示 -状态机.7z
(135.29 KB, 下载次数: 54)
|