目的:记录自己学习过程
大神按键处理程序:原著
https://blog.csdn.net/qq_26697807/article/details/78196263
另一位大神详细对上面的博文发表的个人看法,并加入防抖
https://blog.csdn.net/tianxuechao/article/details/51675033
程序
1、未加防抖
unsigned char Trg;
unsigned char Cont;
void KeyRead( void )
{
unsigned char ReadData = PINB^0xff; // 1
Trg = ReadData & (ReadData ^ Cont); //按键按下,trg置一只保持一次
Cont = ReadData; // Cont代表的是长按键,如果按着不放,那么Cont的值就为 1;
}
#define KEY_MODE 0x01 // 模式按键
#define KEY_PLUS 0x02 // 加
void KeyProc(void)
{
if (Trg & KEY_MODE) // 如果按下的是KEY_MODE,而且你常按这按键也没有用,
{ //它是不会执行第二次的哦 , 必须先松开再按下
Mode++; // 模式寄存器加1,当然,这里只是演示,你可以执行你想
// 执行的任何代码
}
if (Cont & KEY_PLUS) // 如果“加”按键被按着不放
{
cnt_plus++; // 计时
if (cnt_plus > 100) // 20ms*100 = 2S 如果时间到
{
Func(); // 你需要的执行的程序
}
}
}
2、加入防抖
//按键变量
unsigned char KeyPressDown=0x00;
unsigned char KeyRelease=0x00;
unsigned char LastKey=0x00;
//按键扫描,定时10ms执行一次
void KeyScan(void)
{
static unsigned char LastReadKey=0x00; //记录上次KeyScan()读取的IO口键值
unsigned char CurrReadKey; //记录本次KeyScan()读取的IO口键值
unsigned char CurrKey; //记录本次经过消抖处理后的有效按键值
P1|=KEYMASK; //将按键对应的IO设置为输入状态
CurrReadKey=(~P1)&KEYMASK; //取反
//消抖原理很简单:
//如果上次LastReadKey和当前CurReadKey读取的键值都为1,那么当前有效键值CurrKey一定为1
//如果上次和当前读取的键值不一样的话,则与上次有效键值LastKey保持一致。
//通过这种方式来进行消抖,抖动时间必须小于keyscan()定时扫描周期,否则会出现错误。
CurrKey=(CurrReadKey&LastReadKey)|LastKey&(CurrReadKey^LastReadKey);
//记录按键按下及释放
KeyPressDown=(~LastKey)&CurrKey;
KeyRelease=LastKey&(~CurrKey);
LastReadKey=CurrReadKey;
LastKey=CurrKey;
}
我是新手,看了半天才领会算法防抖的奇妙
我对程序加注释
void KEY_Check(void)
{
static unsigned int LastReadKey=0x00; //记录上次KeyScan()读取的IO口键值
unsigned int CurrReadKey; //记录本次KeyScan()读取的IO口键值
unsigned int CurrKey; //记录本次经过消抖处理后的有效按键值
/********************KEY1_IN引脚运行代码示例*******************************
CurrReadKey CurrKey KeyTrg KeyRel LastKey LastReadKey
初始值: 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
按下消抖: 0x0001 0x0000 0x0000 0x0000 0x0000 0x0001
确认按下: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001
长按按键: 0x0001 0x0001 0x0000 0x0000 0x0001 0x0001
释放消抖: 0x0000 0x0001 0x0000 0x0000 0x0001 0x0000
确认释放: 0x0000 0x0000 0x0000 0x0001 0x0000 0x0000
***************************************************************************/
CurrReadKey = (~KEY_Signal.iBuf)^0xFFFF; //读当前IO口键值
CurrKey=(CurrReadKey&LastReadKey)|(LastKey&(CurrReadKey^LastReadKey)); //消抖算法,当前读取键值与上次读取键值相同才说明无已消抖
KeyTrg = CurrKey & (CurrKey ^ LastKey); //按键按下,KeyTrg置一只保持一次
KeyRel = LastKey & (LastKey ^ CurrKey); //按键释放,KeyRel置一只保持一次
LastKey = CurrKey; // LastKey代表的是长按键,如果按着不放,那么LastKey值的对应位为 1
LastReadKey=CurrReadKey; //存储当前读取的IO口键值
}
void IO_Read(void)
{
KEY_Signal.onebits.bKEY1 = KEY1_IN;
KEY_Signal.onebits.bKEY2 = KEY2_IN;
KEY_Signal.onebits.bKEY3 = KEY3_IN;
KEY_Signal.onebits.bKEY4 = KEY4_IN;
KEY_Signal.onebits.bKEY5 = KEY5_IN;
KEY_Signal.onebits.bKEY6 = KEY6_IN;
KEY_Signal.onebits.bKEY7 = KEY7_IN;
KEY_Signal.onebits.bKnob1 = Knob_IN1;
KEY_Signal.onebits.bKnob2 = Knob_IN2;
KEY_Signal.onebits.bKnob4 = Knob_IN4;
KEY_Signal.onebits.bKnob3 = Knob_IN3;
KEY_Signal.onebits.bKnob5 = Knob_IN5;
KEY_Signal.onebits.bKnob6 = Knob_IN6;
}
typedef union
{
unsigned int iBuf;
struct
{
unsigned bKEY1 :1;
unsigned bKEY2 :1;
unsigned bKEY3 :1;
unsigned bKEY4 :1;
unsigned bKEY5 :1;
unsigned bKEY6 :1;
unsigned bKEY7 :1;
unsigned :1;
unsigned bKnob1 :1;
unsigned bKnob2 :1;
unsigned bKnob3 :1;
unsigned bKnob4 :1;
unsigned bKnob5 :1;
unsigned bKnob6 :1;
unsigned :1;
unsigned :1;
}onebits;
}KEY_TYPE;
CurrReadKey CurrKey KeyTrg KeyRel LastKey LastReadKey
初始值: 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
按下消抖: 0x0001 0x0000 0x0000 0x0000 0x0000 0x0001
确认按下: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001
长按按键: 0x0001 0x0001 0x0000 0x0000 0x0001 0x0001
释放消抖: 0x0000 0x0001 0x0000 0x0000 0x0001 0x0000
确认释放: 0x0000 0x0000 0x0000 0x0001 0x0000 0x0000
这样看是不是更能理解了?我只要判断LastKey、KeyRel、KeyTrg的值就能判断按键所处的状态,而且通过创建一个共同体就能不受限制的使用任何一个引脚,只要 #define KEY1_IN _RA8,宏定义一下。这里只需要注意共同体的值就可以了。