系列文章目录
前言
【方法来源】江协科技编程技巧第二期
按住:一直按着
按下:按下瞬间
松开:松手瞬间
单击:按一次
双击:短时间内按两次
长按:按住按键超过一定的时间
重复:长按后每隔一段时间将重复的标志位置1
如果能检测多种按键的事件,可以实现更加多的功能。
本代码使用的是普中A2开发板。
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】LCD1602、按键
本文代码为矩阵按键的全功能演示,下载链接中也有独立按键的全功能演示的工程文件。
效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有工程文件下载链接。
一、效果展示
二、原理分析
1、方法来源
不懂的建议看江协科技的视频。
[编程技巧] 第2期 全功能按键非阻塞式实现 按键单击 双击 长按
江协科技共享的PPT。
2、我的移植
(1)我按照江协科技的方法,扩展到了矩阵按键的全功能非阻塞式的检测。
跟独立按键的检测类似,每次调用函数KeyTick后,都要检测每个按键是否已经按下。
如上图所示,如果想要检测S11是否已按下,可以设置P15=0,即拉低第三行的线,然后检测P11的电平,如果是低电平,则说明S11已按下,如果是高电平,则说明S11未按下。
要注意,每次检测前或检测后,都要释放所有引脚,即将P10~P17都设置为高电平。
3、移植出现的问题
/**
* 函 数:按键检测驱动函数,在定时器中断函数中使用
* 参 数:无
* 返 回 值:无
* 说 明:最后的else的目的是防止S[i]的初值在0~4之外导致检测不了单击、双击、长按、重复
*/
void Key_Tick(void)
{
static unsigned char xdata Count,i;
static unsigned char xdata NowState[16];
static unsigned char xdata LastState[16];
static unsigned char xdata S[16];
static unsigned char xdata KeyTime[16];
for(i=0;i<16;i++)
{
if(KeyTime[i]>0)
{
KeyTime[i]--;
}
}
Count++;
if(Count>=2) //本案例中每隔20ms检测一次按键
{
Count=0;
for(i=0;i<16;i++)
{
LastState[i]=NowState[i];
NowState[i]=Key_GetState(i);
if(NowState[i] == KEY_PRESSED)
{
Key_Flag[i] |= HOLD;
}
else
{
Key_Flag[i] &= ~HOLD;
}
if(NowState[i] == KEY_PRESSED && LastState[i] == KEY_UNPRESSED)
{
Key_Flag[i] |= DOWN;
}
if(NowState[i] == KEY_UNPRESSED && LastState[i] == KEY_PRESSED)
{
Key_Flag[i] |= UP;
}
if(S[i] == 0)
{
if(NowState[i] == KEY_PRESSED)
{
KeyTime[i]=KEY_TIME_LONG;
S[i]=1;
}
}
else if(S[i] == 1)
{
if(NowState[i] == KEY_UNPRESSED)
{
KeyTime[i]=KEY_TIME_DOUBLE;
S[i]=2;
}
else if(KeyTime[i] == 0)
{
KeyTime[i]=KEY_TIME_REPEAT;
Key_Flag[i] |= LONG;
S[i]=4;
}
}
else if(S[i]==2)
{
if(NowState[i] == KEY_PRESSED)
{
Key_Flag[i] |= DOUBLE;
S[i]=3;
}
else if(KeyTime[i] == 0)
{
Key_Flag[i] |= SINGLE;
S[i]=0;
}
}
else if(S[i] == 3)
{
if(NowState[i] == KEY_UNPRESSED)
{
S[i]=0;
}
}
else if(S[i]==4)
{
if(NowState[i] == KEY_UNPRESSED)
{
S[i]=0;
}
else if(KeyTime[i] == 0)
{
KeyTime[i]=KEY_TIME_REPEAT;
Key_Flag[i] |= REPEAT;
S[i]=4;
}
}
else //我加的
{ //我加的
S[i]=0; //我加的
} //我加的
}
}
}
如上所示,由于是实现矩阵按键的全功能,每个数组的变量都是16个,所以我将变量存储在片外RAM区,即用xdata修饰,然后发现一个问题,按住、按下、松开是能检测的,单击、双击、长按、重复检测不到,经过思考,发现可能是这些变量的初值是不确定的,如果S[i]大于4,就没办法检测单击、双击、长按、重复了,所以后面加多一个else的情况,确保S[i]的范围是0~4的范围。
三、各模块代码
1、LCD1602
h文件
#ifndef __LCD1602_H__
#define __LCD1602_H__
void LCD_WriteCommand(unsigned char Command);
void LCD_WriteData(unsigned char Data);
void LCD_SetCursor(unsigned char Line,unsigned char Column);
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_MakeChar(void);
void LCD_Clear(void);
#endif
c文件
#include <REGX52.H>
//引脚定义
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//自定义字符,最多8个
unsigned char code CGRAMData[]={
0x07,0x05,0x07,0x00,0x00,0x00,0x00,0x00, //0:摄氏度的圆圈
0x04,0x0C,0x04,0x04,0x0E,0x00,0x00,0x00, //1:小1
0x0E,0x02,0x0E,0x08,0x0E,0x00,0x00,0x00, //2:小2
0x00,0x0A,0x1F,0x1F,0x0E,0x04,0x00,0x00, //3:小心形
0x0E,0x0A,0x0E,0x04,0x0E,0x15,0x0A,0x11, //4:小人
0x00,0x0A,0x00,0x00,0x11,0x0E,0x00,0x00, //5:小笑脸
0x1F,0x15,0x1F,0x15,0x1F,0x1F,0x1F,0x1F, //6:小门
0x00,0x00,0x01,0x02,0x14,0x08,0x00,0x00, //7:小勾
};
/**
* 函 数:LCD1602私有延时函数,延时约100us
* 参 数:无
* 返 回 值:无
*/
void LCD_Delay100us(void) //12T@11.0592MHz
{
unsigned char i;
i=44;
while(--i);
}
/**
* 函 数:LCD1602私有延时函数,延时2ms
* 参 数:无
* 返 回 值:无
*/
void LCD_Delay2ms(void) //12T@11.0592MHz
{
unsigned char i, j;
i=4;
j=146;
do
{
while(--j);
}while(--i);
}
/**
* 函 数:LCD1602写指令
* 参 数:Command 要写入的指令
* 返 回 值:无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay100us();
LCD_EN=0;
LCD_Delay100us();
}
/**
* 函 数:LCD1602写数据
* 参 数:Data 要写入的数据
* 返 回 值:无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay100us();
LCD_EN=0;
LCD_Delay100us();
}
/**
* 函 数:LCD1602设置光标位置
* 参 数:Line 行位置,范围:1~2
* 参 数:Column 列位置,范围:1~16
* 返 回 值:无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* 函 数:LCD1602初始化函数
* 参 数:无
* 返 回 值:无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38); //八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0C); //显示开,光标关,闪烁关
LCD_WriteCommand(0x06); //数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01); //光标复位,清屏
LCD_Delay2ms(); //清屏指令执行需要较长时间,需要较长的延时
}
/**
* 函 数:在LCD1602指定位置上显示一个字符
* 参 数:Line 行位置,范围:1~2
* 参 数:Column 列位置,范围:1~16
* 参 数:Char 要显示的字符
* 返 回 值:无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* 函 数:在LCD1602指定位置开始显示所给字符串
* 参 数:Line 起始行位置,范围:1~2
* 参 数:Column 起始列位置,范围:1~16
* 参 数:String 要显示的字符串
* 返 回 值:无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* 函 数:指数函数/幂函数
* 参 数:X 底
* 参 数:Y 幂
* 返 回 值:X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* 函 数:在LCD1602指定位置开始显示所给数字
* 参 数:Line 起始行位置,范围:1~2
* 参 数:Column 起始列位置,范围:1~16
* 参 数:Number 要显示的数字,范围:0~65535
* 参 数:Length 要显示数字的长度,范围:1~5
* 返 回 值:无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* 函 数:在LCD1602指定位置开始以有符号十进制显示所给数字
* 参 数:Line 起始行位置,范围:1~2
* 参 数:Column 起始列位置,范围:1~16
* 参 数:Number 要显示的数字,范围:-32768~32767
* 参 数:Length 要显示数字的长度,范围:1~5
* 返 回 值:无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* 函 数:在LCD1602指定位置开始以十六进制显示所给数字
* 参 数:Line 起始行位置,范围:1~2
* 参 数:Column 起始列位置,范围:1~16
* 参 数:Number 要显示的数字,范围:0~0xFFFF
* 参 数:Length 要显示数字的长度,范围:1~4
* 返 回 值:无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* 函 数:在LCD1602指定位置开始以二进制显示所给数字
* 参 数:Line 起始行位置,范围:1~2
* 参 数:Column 起始列位置,范围:1~16
* 参 数:Number 要显示的数字,范围:0~1111 1111 1111 1111
* 参 数:Length 要显示数字的长度,范围:1~16
* 返 回 值:无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
/**
* 函 数:在CGRAM中写入8个自定义字符的64个字节,每个5*8点阵横向取模,使用每个字节的低5位
* 参 数:无
* 返 回 值:无
*/
void LCD_MakeChar(void)
{
unsigned char i;
LCD_WriteCommand(0x40); //CGRAM起始地址为0x40
for(i=0;i<64;i++) //01XX XXXX,最多可以在CGRAM中写入64个数据,一个自定义字符对应8个数据
{
LCD_WriteData(CGRAMData[i]);
}
}
/**
* 函 数:LCD1602的光标复位,清屏
* 参 数:无
* 返 回 值:无
*/
void LCD_Clear(void)
{
LCD_WriteCommand(0x01);
LCD_Delay2ms();
}
2、矩阵按键
h文件
/*方法来源:B站江协科技的编程技巧(第二期)*/
#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__
//各按键对应数组的索引
#define S1 0
#define S2 1
#define S3 2
#define S4 3
#define S5 4
#define S6 5
#define S7 6
#define S8 7
#define S9 8
#define S10 9
#define S11 10
#define S12 11
#define S13 12
#define S14 13
#define S15 14
#define S16 15
//标志位掩码
#define HOLD 0x01 //按住
#define DOWN 0x02 //按下
#define UP 0x04 //松开
#define SINGLE 0x08 //单击
#define DOUBLE 0x10 //双击
#define LONG 0x20 //长按
#define REPEAT 0x40 //重复
void Key_Clear(void);
unsigned char Key(unsigned char n,unsigned char Flag);
void Key_Tick(void);
#endif
c文件
/*方法来源:B站江协科技的编程技巧(第二期)*/
#include <REGX52.H>
#include "MatrixKey.h"
#define KEY_PRESSED 1 //按键已按下
#define KEY_UNPRESSED 0 //按键未按下
//数值单位:定时器中断函数中相邻两次调用函数Key_Tick的时间间隔
#define KEY_TIME_DOUBLE 20 //双击判定的等待时长
#define KEY_TIME_LONG 100 //长按判定的等待时长
#define KEY_TIME_REPEAT 6 //长按后,重复标志位再次置1的时长
//本案例中,以上数值的单位是:10ms
//按住按键,从将Down标志位置1开始计时,经过1000ms将长按的标志位置1
//长按的标志位置1后,每隔60ms将重复标志位置1
//引脚配置
#define Port P1
sbit Row1=P1^7;
sbit Row2=P1^6;
sbit Row3=P1^5;
sbit Row4=P1^4;
sbit Column1=P1^3;
sbit Column2=P1^2;
sbit Column3=P1^1;
sbit Column4=P1^0;
/** 按键标志数组
*
* 一个字节对应8位,分别为:B7 B6 B5 B4 B3 B2 B1 B0
* 【B0】1:一直按住,0:未按下
* 【B1】1:按下瞬间,0:不是按下瞬间
* 【B2】1:松开瞬间,0:不是松开瞬间
* 【B3】1:单击,0:不是单击
* 【B4】1:双击,0:不是双击
* 【B5】1:长按,0:不是长按
* 【B6】1:重复,0:未重复
* 【B7】保留位
* 其中单击、双击、长按互斥(即这三个对应的标志位最多只能有一个是1)
* 除B0外,其他标志位在读取后置0
* xdata:变量保存在片外RAM
*/
unsigned char xdata Key_Flag[16];
/**
* 函 数:获取按键状态(检测按键是否按下)
* 参 数:n 按键索引,范围:0~15
* 返 回 值:按下返回1,未按下返回0
*/
unsigned char Key_GetState(unsigned char n)
{
if(n==S1)
{
Row1=0;
if(Column1==0)
{
return KEY_PRESSED;
}
}
else if(n==S2)
{
Row1=0;
if(Column2==0)
{
return KEY_PRESSED;
}
}
else if(n==S3)
{
Row1=0;
if(Column3==0)
{
return KEY_PRESSED;
}
}
else if(n==S4)
{
Row1=0;
if(Column4==0)
{
return KEY_PRESSED;
}
}
else if(n==S5)
{
Row2=0;
if(Column1==0)
{
return KEY_PRESSED;
}
}
else if(n==S6)
{
Row2=0;
if(Column2==0)
{
return KEY_PRESSED;
}
}
else if(n==S7)
{
Row2=0;
if(Column3==0)
{
return KEY_PRESSED;
}
}
else if(n==S8)
{
Row2=0;
if(Column4==0)
{
return KEY_PRESSED;
}
}
else if(n==S9)
{
Row3=0;
if(Column1==0)
{
return KEY_PRESSED;
}
}
else if(n==S10)
{
Row3=0;
if(Column2==0)
{
return KEY_PRESSED;
}
}
else if(n==S11)
{
Row3=0;
if(Column3==0)
{
return KEY_PRESSED;
}
}
else if(n==S12)
{
Row3=0;
if(Column4==0)
{
return KEY_PRESSED;
}
}
else if(n==S13)
{
Row4=0;
if(Column1==0)
{
return KEY_PRESSED;
}
}
else if(n==S14)
{
Row4=0;
if(Column2==0)
{
return KEY_PRESSED;
}
}
else if(n==S15)
{
Row4=0;
if(Column3==0)
{
return KEY_PRESSED;
}
}
else if(n==S16)
{
Row4=0;
if(Column4==0)
{
return KEY_PRESSED;
}
}
Port=0xFF;
return KEY_UNPRESSED;
}
/**
* 函 数:获取按键标志位的值
* 参 数:n 按键索引,范围:0~15
* 参 数:Flag 标志位掩码,用来获取标志的某一位的值为1还是0
* 返 回 值:1或者0
*/
unsigned char Key(unsigned char n, unsigned char Flag)
{
if(Key_Flag[n] & Flag)
{
if(Flag != HOLD)
{
Key_Flag[n] &= ~Flag;
}
return 1;
}
return 0;
}
/**
* 函 数:按键检测驱动函数,在定时器中断函数中使用
* 参 数:无
* 返 回 值:无
* 说 明:最后的else的目的是防止S[i]的初值在0~4之外导致检测不了单击、双击、长按、重复
*/
void Key_Tick(void)
{
static unsigned char xdata Count,i;
static unsigned char xdata NowState[16];
static unsigned char xdata LastState[16];
static unsigned char xdata S[16];
static unsigned char xdata KeyTime[16];
for(i=0;i<16;i++)
{
if(KeyTime[i]>0)
{
KeyTime[i]--;
}
}
Count++;
if(Count>=2) //本案例中每隔20ms检测一次按键
{
Count=0;
for(i=0;i<16;i++)
{
LastState[i]=NowState[i];
NowState[i]=Key_GetState(i);
if(NowState[i] == KEY_PRESSED)
{
Key_Flag[i] |= HOLD;
}
else
{
Key_Flag[i] &= ~HOLD;
}
if(NowState[i] == KEY_PRESSED && LastState[i] == KEY_UNPRESSED)
{
Key_Flag[i] |= DOWN;
}
if(NowState[i] == KEY_UNPRESSED && LastState[i] == KEY_PRESSED)
{
Key_Flag[i] |= UP;
}
if(S[i] == 0)
{
if(NowState[i] == KEY_PRESSED)
{
KeyTime[i]=KEY_TIME_LONG;
S[i]=1;
}
}
else if(S[i] == 1)
{
if(NowState[i] == KEY_UNPRESSED)
{
KeyTime[i]=KEY_TIME_DOUBLE;
S[i]=2;
}
else if(KeyTime[i] == 0)
{
KeyTime[i]=KEY_TIME_REPEAT;
Key_Flag[i] |= LONG;
S[i]=4;
}
}
else if(S[i]==2)
{
if(NowState[i] == KEY_PRESSED)
{
Key_Flag[i] |= DOUBLE;
S[i]=3;
}
else if(KeyTime[i] == 0)
{
Key_Flag[i] |= SINGLE;
S[i]=0;
}
}
else if(S[i] == 3)
{
if(NowState[i] == KEY_UNPRESSED)
{
S[i]=0;
}
}
else if(S[i]==4)
{
if(NowState[i] == KEY_UNPRESSED)
{
S[i]=0;
}
else if(KeyTime[i] == 0)
{
KeyTime[i]=KEY_TIME_REPEAT;
Key_Flag[i] |= REPEAT;
S[i]=4;
}
}
else
{
S[i]=0;
}
}
}
}
/**
* 函 数:清空所有按键的所有标志位
* 参 数:无
* 返 回 值:无
* 说 明:防止切换模式的时候受上一模式所按按键的影响
*/
void Key_Clear(void)
{
unsigned char i;
for(i=0;i<16;i++)
{
Key_Flag[i]=0;
}
}
3、定时器0
h文件
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0_Init(void);
#endif
c文件
#include <REGX52.H>
/**
* 函 数:定时器0初始化
* 参 数:无
* 返 回 值:无
*/
void Timer0_Init(void)
{
TMOD&=0xF0; //设置定时器模式为16位不自动重装模式
TMOD|=0x01; //设置定时器模式为16位不自动重装模式
TL0=0x66; //设置定时初值,定时1ms,12T@11.0592MHz
TH0=0xFC; //设置定时初值,定时1ms,12T@11.0592MHz
TF0=0; //清除TF0标志
TR0=1; //定时器0开始计时
ET0=1; //打开定时器0中断允许
EA=1; //打开总中断
PT0=0; //设置定时器0的优先级
}
/*定时器中断函数模板
void Timer0_Routine() interrupt 1 //定时器0中断函数
{
static unsigned int T0Count; //定义静态变量
TL0=0x66; //设置定时初值,定时1ms,12T@11.0592MHz
TH0=0xFC; //设置定时初值,定时1ms,12T@11.0592MHz
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
*/
四、主函数
main.c
/*by甘腾胜@20250608
【效果查看/操作演示】B站搜索“甘腾胜”或“gantengsheng”查看
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】LCD1602、矩阵按键
【接线】
LCD1602模块:RS接P26,RW接P25,EN接P27,并口接P0(开发板上已接好)
矩阵按键:P17接第一行,P16接第二行,P15接第三行,P14接第四行,P13接第一列,P12接第二列,P11接第三列,P10接第四列(开发板上已接好)
【简单的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【注意】点阵屏旁边的跳线帽需要接右边两个排针
【操作说明】无
*/
#include <REGX52.H> //51单片机头文件
#include "LCD1602.h" //液晶显示屏
#include "MatrixKey.h" //矩阵按键
#include "Timer0.h" //定时器0
unsigned int Num1,Num2;
/**
* 函 数:主函数(有且仅有一个)
* 参 数:无
* 返 回 值:无
* 说 明:主函数是程序执行的起点,负责执行整个程序的主要逻辑
*/
void main()
{
LCD_Init(); //LCD1602初始化
Timer0_Init(); //定时器0初始化
LCD_ShowNum(1,1,Num1,5); //在LCD1602的1行1列开始用五位数显示变量Num1
LCD_ShowNum(2,1,Num2,5); //在LCD1602的2行1列开始用五位数显示变量Num2
Key_Clear(); //进主循环之前先清空所有按键的标志位,防止读出错误的标志位
while(1)
{
//S1
if(Key(S1,DOWN)) //如果S1按下
{
Num1--; //变量Num1减1
LCD_ShowNum(1,1,Num1,5);
}
if(Key(S1,REPEAT)) //如果S1重复
{
Num1--; //变量Num1减1
LCD_ShowNum(1,1,Num1,5);
}
//S2
if(Key(S2,UP)) //如果S2松开
{
Num1++; //变量Num1加1
LCD_ShowNum(1,1,Num1,5);
}
if(Key(S2,REPEAT)) //如果S2重复
{
Num1++; //变量Num1加1
LCD_ShowNum(1,1,Num1,5);
}
//S3
if(Key(S3,SINGLE)) //如果S3单击
{
Num1-=10; //变量Num1减10
LCD_ShowNum(1,1,Num1,5);
}
if(Key(S3,DOUBLE)) //如果S3双击
{
Num1-=100; //变量Num1减100
LCD_ShowNum(1,1,Num1,5);
}
if(Key(S3,LONG)) //如果S3长按
{
Num1=0; //变量Num1清零
LCD_ShowNum(1,1,Num1,5);
}
//S4
if(Key(S4,SINGLE)) //如果S4单击
{
Num1+=10; //变量Num1加10
LCD_ShowNum(1,1,Num1,5);
}
if(Key(S4,DOUBLE)) //如果S4双击
{
Num1+=100; //变量Num1加100
LCD_ShowNum(1,1,Num1,5);
}
if(Key(S4,LONG)) //如果S4长按
{
Num1=0; //变量Num1清零
LCD_ShowNum(1,1,Num1,5);
}
//组合键
if(Key(S16,HOLD) && Key(S5,DOWN)) //按住S16的同时,按下S5
{
Num2-=1000; //变量Num2减1000
LCD_ShowNum(2,1,Num2,5);
}
if(Key(S16,HOLD) && Key(S6,UP)) //按住S16的同时,松开S6
{
Num2+=1000; //变量Num2加1000
LCD_ShowNum(2,1,Num2,5);
}
}
}
/**
* 函 数:定时器0中断函数
* 参 数:无
* 返 回 值:无
*/
void Timer0_Routine() interrupt 1
{
TL0=0x00; //设置定时初值,定时10ms,12T@11.0592Hz
TH0=0xDC; //设置定时初值,定时10ms,12T@11.0592Hz
Key_Tick(); //每隔10ms调用一次按键驱动函数Key_Tick
}
总结
我觉得江协科技的这个检测按键的方法非常巧妙,非常好用。只要硬件没问题,检测就不会出错。