蓝桥杯单片机按键进阶-基于柳离风模板
——基于柳离风老师模板及按键进阶教程
文章目录
说明
在使用特殊操作按键(如松手、长按)的过程中可能会触发普通按键,解决方法:1、在按键处理函数中严格控制生效条件,避免误触发,即题目中说的在某个界面下生效,其余界面无效;2、按键处理函数case中不考虑没用的按键返回值;3、改变普通按键检测返回键值代码的位置,把普通按键的触发改到特殊按键的互斥条件中,一般在松手检测处。推荐方法1、2,因为不需要改代码。
矩阵键盘在进行某些特殊按键操作时要判断具体的键值,因为有多个可能的值,处理操作可以放在得到具体的键值后,也可放在行列扫描的最后一步,本代码使用第二种。
1、按键测试-按下生效
key.c
#include "key.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
else if(P32 == 0) key = 5;
else if(P33 == 0) key = 4;
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
key_up = 1;
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0) key = key - 2;
else if(P33 == 0) key = key - 3;
return key;
}
else if(P44 == 1 && P42 == 1)
key_up = 1; //松开
return 0;
}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4: num++;
// break;
// case 5: num--;
// break;
// case 6: flag_L1 ^= 1;
// break;
// case 7: state_display = (state_display+1)%2;
// break;
/* 矩阵键盘 */
case 4: num++;
break;
case 5: num--;
break;
case 8: flag_L1 ^= 1;
break;
case 9: state_display = (state_display+1)%2;
break;
}
}
2、按键进阶-不同界面不同效果
key.c
这里不用改。
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4: num++;
// break;
// case 5: num--;
// break;
// case 6: flag_L1 ^= 1;
// break;
// case 7: state_display = (state_display+1)%2;
// break;
/* 矩阵键盘 */
case 4: //不同界面不同效果
if(state_display == 0)
{
num++;
}
else if(state_display == 1)
{
flag_L1 ^= 1;
}
//界面很多时用switch
break;
case 5: num--;
break;
case 8:
break;
case 9: state_display = (state_display+1)%2;
break;
}
}
3、按键进阶-某模式按键禁用/任意按键生效
key.c
这里不用改。
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// if(state_display == 0) return; //某模式按键禁用
// key_num = key_scan1();
key_num = key_scan2();
if((key_num > 0) && (state_display == 0)) //某模式任意按键生效
{
flag_L1 ^= 1;
return;
}
switch (key_num)
{
/* 独立按键 */
// case 4: num++;
// break;
// case 5: num--;
// break;
// case 6: flag_L1 ^= 1;
// break;
// case 7: state_display = (state_display+1)%2;
// break;
/* 矩阵键盘 */
case 4: num++;
break;
case 5: num--;
break;
case 8: flag_L1 ^= 1;
break;
case 9: state_display = (state_display+1)%2;
break;
}
}
4、按键进阶-松手生效
key.c
#include "key.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
else if(P32 == 0) key = 5;
else if(P33 == 0)
{
flag_s4 = 1; //标记按下
key = 4;
}
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
if(flag_s4 == 1)
{
flag_s4 = 0;
return 40; //松手生效
}
}
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0) key = key - 2;
else if(P33 == 0)
{
key = key - 3;
if(key == 4) flag_s4 = 1; //标记按下,这里判断键值是因为有两种可能s8,s4
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1; //松开
if(flag_s4 == 1)
{
flag_s4 = 0;
return 40; //松手生效
}
}
return 0;
}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4://按下生效
// break;
// case 5: num--;
// break;
// case 6:
// break;
// case 7: flag_L1 ^= 1;
// break;
// case 40:num++; //松手生效
// break;
/* 矩阵键盘 */
case 4://按下生效
break;
case 5: num--;
break;
case 8:
break;
case 9: flag_L1 ^= 1;
break;
case 40: num++; //松手生效
break;
}
}
5、按键进阶-长按
5-1、长按生效
按下后记录时间,然后判断已按下的时间是否到了。
key.c
#include "key.h"
#include "timer.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
static unsigned long key_time_s4 = 0;//按键4长按计时
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
else if(P32 == 0) key = 5;
else if(P33 == 0) //记录按下
{
flag_s4 = 1;
key_time_s4 = systick_ms;
key = 4;
}
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
flag_s4 = 0; //松手
}
if((flag_s4 == 1) && ((systick_ms - key_time_s4) > 1000)) //长按1s
{
flag_s4 = 0;
return 40;
}
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
static unsigned long key_time_s4 = 0;//按键4长按计时
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0) key = key - 2;
else if(P33 == 0)
{
key = key - 3;
if(key == 4) //记录按下
{
flag_s4 = 1;
key_time_s4 = systick_ms;
}
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1; //松开
flag_s4 = 0;
}
if((flag_s4 == 1) && ((systick_ms - key_time_s4) > 1000)) //长按1s
{
flag_s4 = 0;
return 40;
}
return 0;
}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4: //num++;
// break;
// case 5: num--;
// break;
// case 6:
// break;
// case 7: state_display = (state_display+1)%2;
// break;
// case 40: flag_L1 ^= 1; //长按生效
// break;
/* 矩阵键盘 */
case 4: //num++;
break;
case 5: num--;
break;
case 8:
break;
case 9: state_display = (state_display+1)%2;
break;
case 40: flag_L1 ^= 1; //长按生效
break;
}
}
5-2、长按一直生效
在生效后不把标志位置0就会一直生效。
key.c
#include "key.h"
#include "timer.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
static unsigned long key_time_s4 = 0;//按键4长按计时
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
else if(P32 == 0) key = 5;
else if(P33 == 0) //记录按下
{
flag_s4 = 1;
key_time_s4 = systick_ms;
key = 4;
}
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
flag_s4 = 0; //松手
}
if((flag_s4 == 1) && ((systick_ms - key_time_s4) > 1000)) //长按1s
{
// flag_s4 = 0; //这里不置0就一直生效
return 40;
}
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
static unsigned long key_time_s4 = 0;//按键4长按计时
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0) key = key - 2;
else if(P33 == 0)
{
key = key - 3;
if(key == 4) //记录按下
{
flag_s4 = 1;
key_time_s4 = systick_ms;
}
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1; //松开
flag_s4 = 0;
}
if((flag_s4 == 1) && ((systick_ms - key_time_s4) > 1000)) //长按1s
{
// flag_s4 = 0; //这里不置0就可以一直生效
return 40;
}
return 0;
}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4: //num++;
// break;
// case 5: num--;
// break;
// case 6:
// break;
// case 7: state_display = (state_display+1)%2;
// break;
// case 40: num = (num+1)%255; //长按一直生效
// break;
/* 矩阵键盘 */
case 4: //num++;
break;
case 5: num--;
break;
case 8:
break;
case 9: state_display = (state_display+1)%2;
break;
case 40: num = (num+1)%255; //长按一直生效,如果对速度有要求需要配合定时器
break;
}
}
5-3、长按松手生效
把长按检测放在松手检测里。
key.c
#include "key.h"
#include "timer.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
static unsigned long key_time_s4 = 0;//按键4长按计时
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
else if(P32 == 0) key = 5;
else if(P33 == 0) //记录按下
{
flag_s4 = 1;
key_time_s4 = systick_ms;
key = 4;
}
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
//把长按放在松手检测里
if((flag_s4 == 1) && ((systick_ms - key_time_s4) > 1000)) //长按1s以上松手
{
flag_s4 = 0;
return 40;
}
else
flag_s4 = 0; //短按松手
}
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
static unsigned long key_time_s4 = 0;//按键4长按计时
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0) key = key - 2;
else if(P33 == 0)
{
key = key - 3;
if(key == 4) //记录按下
{
flag_s4 = 1;
key_time_s4 = systick_ms;
}
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1; //松开
//把长按放在松手检测里
if((flag_s4 == 1) && ((systick_ms - key_time_s4) > 1000)) //长按1s
{
flag_s4 = 0; //这里不置0就可以一直生效
return 40;
}
else
flag_s4 = 0; //短按松手
}
return 0;
}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4: //num++;
// break;
// case 5: num--;
// break;
// case 6:
// break;
// case 7: state_display = (state_display+1)%2;
// break;
// case 40: flag_L1 ^= 1; //长按松手生效
// break;
/* 矩阵键盘 */
case 4: //num++;
break;
case 5: num--;
break;
case 8:
break;
case 9: state_display = (state_display+1)%2;
break;
case 40: flag_L1 ^= 1; //长按松手生效
break;
}
}
5-4、长按短按分别松手生效
原来的短按为按下生效,在长按松手检测里加一项时间判断,小于为短按松手生效。
key.c
#include "key.h"
#include "timer.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
static unsigned long key_time_s4 = 0;//按键4长按计时
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
else if(P32 == 0) key = 5;
else if(P33 == 0) //记录按下
{
flag_s4 = 1;
key_time_s4 = systick_ms;
key = 4;
}
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
//把长按放在松手检测里
if((flag_s4 == 1) && ((systick_ms - key_time_s4) > 1000)) //长按1s以上松手
{
flag_s4 = 0;
return 40;
}
else if((flag_s4 == 1) && ((systick_ms - key_time_s4) < 1000)) //短按松手生效
{
flag_s4 = 0;
return 41;
}
}
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志
static unsigned long key_time_s4 = 0;//按键4长按计时
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0) key = key - 2;
else if(P33 == 0)
{
key = key - 3;
if(key == 4) //记录按下
{
flag_s4 = 1;
key_time_s4 = systick_ms;
}
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1; //松开
//把长按放在松手检测里
if((flag_s4 == 1) && ((systick_ms - key_time_s4) > 1000)) //长按1s
{
flag_s4 = 0; //这里不置0就可以一直生效
return 40;
}
else if((flag_s4 == 1) && ((systick_ms - key_time_s4) < 1000))//短按松手生效
{
flag_s4 = 0;
return 41;
}
}
return 0;
}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4: //num++;//短按按下生效
// break;
// case 5: num--;
// break;
// case 6:
// break;
// case 7: state_display = (state_display+1)%2;
// break;
// case 40: flag_L1 ^= 1; //长按松手生效
// break;
// case 41: num++; //短按松手生效
// break;
/* 矩阵键盘 */
case 4: //num++;//短按按下生效
break;
case 5: num--;
break;
case 8:
break;
case 9: state_display = (state_display+1)%2;
break;
case 40: flag_L1 ^= 1; //长按松手生效
break;
case 41: num++; //短按松手生效
break;
}
}
6、按键进阶-按键双击
检测到按键按下后计数加1,并判断两次时间差,小于某个值即为双击。为避免双击单击同时触发,把单击改到松手检测中。
key.c
#include "key.h"
#include "timer.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static uchar s4_pres_count = 0; //按键4按下次数,用于双击
static unsigned long key_time = 0; //按键4按下计时
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
else if(P32 == 0) key = 5;
else if(P33 == 0)
{
s4_pres_count++;
if((s4_pres_count == 2) && ((systick_ms - key_time) <= 500)) //双击s4
{
s4_pres_count = 0;
return 40;
}
key_time = systick_ms; //先判断完双击再计时
// key = 4; //如果用这里作为单击,会导致双击过程中触发单击
}
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
if((s4_pres_count == 1) && ((systick_ms - key_time) > 500)) //单击s4松手
{
s4_pres_count = 0;
return 41;
}
}
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static uchar s4_pres_count = 0; //按键4按下次数,用于双击
static unsigned long key_time = 0; //按键4按下计时
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0) key = key - 2;
else if(P33 == 0) key = key - 3;
if(key == 4) //s4按下
{
s4_pres_count++;
if((s4_pres_count == 2) && ((systick_ms - key_time) <= 500)) //双击s4
{
s4_pres_count = 0;
return 40;
}
key_time = systick_ms; //先判断完双击再计时
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1; //松开
if((s4_pres_count == 1) && ((systick_ms - key_time) > 500)) //单击s4松手
{
s4_pres_count = 0;
return 41;
}
}
return 0;
}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4: //num++;//单击生效
// break;
// case 5:
// break;
// case 6:
// break;
// case 7: state_display = (state_display+1)%2;
// break;
// case 40: flag_L1 ^= 1; //双击生效
// break;
// case 41: num++; //单击松手生效
// break;
/* 矩阵键盘 */
case 4: //num++;//单击生效
break;
case 5:
break;
case 8:
break;
case 9: state_display = (state_display+1)%2;
break;
case 40: flag_L1 ^= 1; //双击生效
break;
case 41: num++; //单击松手生效
break;
}
}
7、按键进阶-双按键
双按键要在第一个按键检测到后将key_up置1,才能进行第二次检测,同时注意行列扫描法检测矩阵键盘的弊端,行列扫描的if-else结构会有优先级问题,满足当前if后,后面的都不会再判断了,类似短路,因此当一个按键持续按下时,会阻止检测其他按键,导致双按键需要两个按键按固定的顺序按下才会触发,这不是我们想要的。为避免这种现象,我们需要调整行扫描的优先级,大体思路是:当原先处于优先级靠前的行扫描的那一行如果是第一次按下,则保持原来的优先级不变,如果不是第一次按下(即第一个按键按下没松手,等待第二个按键按下),则把它的优先级放到原先优先级靠后的行扫描后面,优先扫描第二个按键。
具体代码如下:
if(P32 == 0 && flag_s5 == 0) key = key - 2; //s5第一次按下
else if(P33 == 0) key = key - 3; //s4按下,这一句一定要放在重复扫描前面,
//这是因为矩阵键盘行列扫描的局限性,依赖于代码执行顺序,否则s5一直按着后面的没法扫描
else if(P32 == 0 && flag_s5 == 1) key_up = 1; //如果s5一直按着就重复扫描,这句一定放在s4扫描之后,优先级最低
//注意这几句是else if,如果前面的条件满足,后面的就不执行了
或者:
if(P33 == 0 && flag_s8 == 0) key = key - 3;//第一次按下优先
else if(P32 == 0) key = key - 2;//比另一个按键第二次按下优先级要高
else if(P33 == 0) key = key - 3;//非第一次按下优先级靠后
如果两个按键有相同的行或列,可以对双按键检测进行简化,在行扫描前,先检测一下在同一列的两行是否同时按下了,再单独对每行检测,这样可以避免优先级问题,并且简化代码逻辑。
if(P32 == 0 && P33 == 0) //如果双按键有共同的行或列可简化扫描,注意后面跟的是else if
{
key_up = 0; //停止扫描
return 45; //双按键生效
}
else if(P32 == 0) key = key - 2;
else if(P33 == 0) key = key - 3;
if(key == 5)
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s5 = 1; //记录按下
}
if(key == 4)
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s4 = 1; //记录按下
}
key.c
#include "key.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志位
static bit flag_s5 = 0; //按键4按下标志位
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
if(P32 == 0) //注意这里是if,因为else分支只要满足一个就不再检查其他的了,这样会导致双按键有按下顺序要求
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s5 = 1; //记录按下
if(flag_s4 == 1) //如果另一个按键也按下了
{
key_up = 0; //停止扫描
flag_s5 = 0; //
flag_s4 = 0;
return 45; //双按键生效
}
// key = 5; //这里为避免双按键过程中触发单按键,将单按键改为松手触发
}
if(P33 == 0) //注意这里是if,因为else分支只要满足一个就不再检查其他的了,这样会导致双按键有按下顺序要求
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s4 = 1; //记录按下
if(flag_s5 == 1) //如果另一个按键也按下了
{
key_up = 0; //停止扫描
flag_s5 = 0; //
flag_s4 = 0;
return 45; //双按键生效
}
// key = 4; //这里为避免双按键过程中触发单按键,将单按键改为松手触发
}
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
if(flag_s4 == 1) //单按键松手生效
{
flag_s4 = 0;
return 40;
}
if(flag_s5 == 1)
{
flag_s5 = 0;
return 50;
}
}
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志位
static bit flag_s5 = 0; //按键4按下标志位
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0 && flag_s5 == 0) key = key - 2; //s5第一次按下
else if(P33 == 0) key = key - 3; //s4按下,这一句一定要放在重复扫描重复扫描前面,
//这是因为矩阵键盘行列扫描的局限性,依赖于代码执行顺序,否则s5一直按着后面的没法扫描
else if(P32 == 0 && flag_s5 == 1) key_up = 1; //如果s5一直按着就重复扫描,这句一定放在s4扫描之后,优先级最低
//注意这几句是else if,如果前面的条件满足,后面的就不执行了
if(key == 5)
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s5 = 1; //记录按下
if(flag_s4 == 1) //如果另一个按键也按下了
{
key_up = 0; //停止扫描
flag_s5 = 0; //
flag_s4 = 0;
return 45; //双按键生效
}
}
if(key == 4)
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s4 = 1; //记录按下
if(flag_s5 == 1) //如果另一个按键也按下了
{
key_up = 0; //停止扫描
flag_s5 = 0; //
flag_s4 = 0;
return 45; //双按键生效
}
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1; //松开
if(flag_s4 == 1) //单按键松手生效
{
flag_s4 = 0;
return 40;
}
if(flag_s5 == 1)
{
flag_s5 = 0;
return 50;
}
}
return 0;
}
//uchar key_scan2(void)
//{
// uchar key = 0; //按键值
// static bit key_up = 1; //按键松开标志,1松开0按下
// static bit flag_s4 = 0; //按键4按下标志位
// static bit flag_s5 = 0; //按键4按下标志位
//
// P44 = P42 = 1; //列扫描
// P32 = P33 = 0;
// if((key_up == 1)&&(P44 == 0 || P42 == 0))
// {
//
// if(P44 == 0) key = 7;
// else if(P42 == 0) key = 11;
// else return 0;
// key_up = 0; //标志按下
// P44 = P42 = 0;
// P32 = P33 = 1; //行扫描
//
// if(P32 == 0 && P33 == 0) //如果双按键有共同的行或列可简化扫描,注意后面跟的是else if
// {
// key_up = 0; //停止扫描
// return 45; //双按键生效
// }
//
// else if(P32 == 0) key = key - 2;
// else if(P33 == 0) key = key - 3;
//
// if(key == 5)
// {
// key_up = 1; //这里置1才能使第二个按键进入检测
// flag_s5 = 1; //记录按下
//// if(flag_s4 == 1) //如果另一个按键也按下了
//// {
//// key_up = 0; //停止扫描
//// flag_s5 = 0; //
//// flag_s4 = 0;
//// return 45; //双按键生效
//// }
// }
// if(key == 4)
// {
// key_up = 1; //这里置1才能使第二个按键进入检测
// flag_s4 = 1; //记录按下
//// if(flag_s5 == 1) //如果另一个按键也按下了
//// {
//// key_up = 0; //停止扫描
//// flag_s5 = 0; //
//// flag_s4 = 0;
//// return 45; //双按键生效
//// }
// }
// return key;
// }
// else if(P44 == 1 && P42 == 1)
// {
// key_up = 1; //松开
// if(flag_s4 == 1) //单按键松手生效
// {
// flag_s4 = 0;
// return 40;
// }
// if(flag_s5 == 1)
// {
// flag_s5 = 0;
// return 50;
// }
//
// }
// return 0;
//}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4:
// break;
// case 5:
// break;
// case 6:
// break;
// case 7: state_display = (state_display+1)%2;
// break;
// case 40: num++; //单按s4松手生效
// break;
// case 50: num--; //单按s5松手生效
// break;
// case 45: flag_L1 ^= 1; //双按键生效
// break;
/* 矩阵键盘 */
case 4: //单击生效
break;
case 5: //单击生效
break;
case 8:
break;
case 9: state_display = (state_display+1)%2;
break;
case 40: num++; //单按s4松手生效
break;
case 50: num--; //单按s5松手生效
break;
case 45: flag_L1 ^= 1; //双按键生效
break;
}
}
8、按键进阶-双按键长按生效
key.c
#include "key.h"
#include "timer.h"
/**
* @brief 独立按键扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan1(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志位
static bit flag_s5 = 0; //按键4按下标志位
static unsigned long key_time = 0; //双按键按下计时
if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) key = 7;
else if(P31 == 0) key = 6;
if(P32 == 0) //注意这里是if,因为else分支只要满足一个就不再检查其他的了,这样会导致双按键有按下顺序要求
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s5 = 1; //记录按下
if(flag_s4 == 1) //如果另一个按键也按下了
{
key_up = 0; //停止扫描
key_time = systick_ms;
// return 45; //双按键生效
}
// key = 5; //这里为避免双按键过程中触发单按键,将单按键改为松手触发
}
if(P33 == 0) //注意这里是if,因为else分支只要满足一个就不再检查其他的了,这样会导致双按键有按下顺序要求
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s4 = 1; //记录按下
if(flag_s5 == 1) //如果另一个按键也按下了
{
key_up = 0; //停止扫描
key_time = systick_ms;
// return 45; //双按键生效
}
// key = 4; //这里为避免双按键过程中触发单按键,将单按键改为松手触发
}
return key;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
if(flag_s4 == 1) //单按键松手生效
{
flag_s4 = 0;
return 40;
}
if(flag_s5 == 1)
{
flag_s5 = 0;
return 50;
}
}
else if(((systick_ms - key_time) > 2000) && (P33 == 0) && (P32 == 0) && (flag_s4 == 1) && (flag_s5 == 1))//双按键长按生效
{
flag_s4 = 0;
flag_s5 = 0;
return 45;
}
return 0;
}
/**
* @brief 矩阵键盘扫描函数
*
* @return uchar 键值对应的元件位号
*/
uchar key_scan2(void)
{
uchar key = 0; //按键值
static bit key_up = 1; //按键松开标志,1松开0按下
static bit flag_s4 = 0; //按键4按下标志位
static bit flag_s5 = 0; //按键4按下标志位
static unsigned long key_time = 0; //双按键按下计时
P44 = P42 = 1; //列扫描
P32 = P33 = 0;
if((key_up == 1)&&(P44 == 0 || P42 == 0))
{
if(P44 == 0) key = 7;
else if(P42 == 0) key = 11;
else return 0;
key_up = 0; //标志按下
P44 = P42 = 0;
P32 = P33 = 1; //行扫描
if(P32 == 0 && flag_s5 == 0) key = key - 2; //s5第一次按下
else if(P33 == 0) key = key - 3; //s4按下,这一句一定要放在重复扫描重复扫描前面,
//这是因为矩阵键盘行列扫描的局限性,依赖于代码执行顺序,否则s5一直按着后面的没法扫描
else if(P32 == 0 && flag_s5 == 1) key_up = 1; //如果s5一直按着就重复扫描,这句一定放在s4扫描之后,优先级最低
//注意这几句是else if,如果前面的条件满足,后面的就不执行了
if(key == 5)
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s5 = 1; //记录按下
if(flag_s4 == 1) //如果另一个按键也按下了
{
key_up = 0; //停止扫描
key_time = systick_ms;
// return 45; //双按键生效
}
}
if(key == 4)
{
key_up = 1; //这里置1才能使第二个按键进入检测
flag_s4 = 1; //记录按下
if(flag_s5 == 1) //如果另一个按键也按下了
{
key_up = 0; //停止扫描
key_time = systick_ms;
// return 45; //双按键生效
}
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1; //松开
if(flag_s4 == 1) //单按键松手生效
{
flag_s4 = 0;
return 40;
}
if(flag_s5 == 1)
{
flag_s5 = 0;
return 50;
}
}
else if(((systick_ms - key_time) > 2000) && (flag_s4 == 1) && (flag_s5 == 1))//双按键长按生效
{
flag_s4 = 0;
flag_s5 = 0;
return 45;
}
return 0;
}
//uchar key_scan2(void)
//{
// uchar key = 0; //按键值
// static bit key_up = 1; //按键松开标志,1松开0按下
// static bit flag_s4 = 0; //按键4按下标志位
// static bit flag_s5 = 0; //按键4按下标志位
// static unsigned long key_time = 0; //双按键按下计时
//
// P44 = P42 = 1; //列扫描
// P32 = P33 = 0;
// if((key_up == 1)&&(P44 == 0 || P42 == 0))
// {
//
// if(P44 == 0) key = 7;
// else if(P42 == 0) key = 11;
// else return 0;
// key_up = 0; //标志按下
// P44 = P42 = 0;
// P32 = P33 = 1; //行扫描
//
// if(P32 == 0 && P33 == 0) //如果双按键有共同的行或列可简化扫描,注意后面跟的是else if
// {
// key_up = 0; //停止扫描
// flag_s5 = 1; //
// flag_s4 = 1;
// key_time = systick_ms;
//// return 45; //双按键生效
// }
//
// else if(P32 == 0) key = key - 2;
// else if(P33 == 0) key = key - 3;
//
// if(key == 5)
// {
// key_up = 1; //这里置1才能使第二个按键进入检测
// flag_s5 = 1; //记录按下
//// if(flag_s4 == 1) //如果另一个按键也按下了
//// {
//// key_up = 0; //停止扫描
//// flag_s5 = 0; //
//// flag_s4 = 0;
//// return 45; //双按键生效
//// }
// }
// if(key == 4)
// {
// key_up = 1; //这里置1才能使第二个按键进入检测
// flag_s4 = 1; //记录按下
//// if(flag_s5 == 1) //如果另一个按键也按下了
//// {
//// key_up = 0; //停止扫描
//// flag_s5 = 0; //
//// flag_s4 = 0;
//// return 45; //双按键生效
//// }
// }
// return key;
// }
// else if(P44 == 1 && P42 == 1)
// {
// key_up = 1; //松开
// if(flag_s4 == 1) //单按键松手生效
// {
// flag_s4 = 0;
// return 40;
// }
// if(flag_s5 == 1)
// {
// flag_s5 = 0;
// return 50;
// }
//
// }
// else if(((systick_ms - key_time) > 2000) && (flag_s4 == 1) && (flag_s5 == 1))//双按键长按生效
// {
// flag_s4 = 0;
// flag_s5 = 0;
// return 45;
// }
// return 0;
//}
main.c
//按键处理任务
void key_task(void)
{
uchar key_num;
if(key_dly<10)return;
key_dly = 0;
// key_num = key_scan1();
key_num = key_scan2();
switch (key_num)
{
/* 独立按键 */
// case 4:
// break;
// case 5:
// break;
// case 6:
// break;
// case 7: state_display = (state_display+1)%2;
// break;
// case 40: num++; //单按s4松手生效
// break;
// case 50: num--; //单按s5松手生效
// break;
// case 45: flag_L1 ^= 1; //双按键长按生效
// break;
/* 矩阵键盘 */
case 4: //单击生效
break;
case 5: //单击生效
break;
case 8:
break;
case 9: state_display = (state_display+1)%2;
break;
case 40: num++; //单按s4松手生效
break;
case 50: num--; //单按s5松手生效
break;
case 45: flag_L1 ^= 1; //双按键长按生效
break;
}
}