文章目录
前言
本文介绍一下蓝桥杯单片机按键考点,以总结的角度去讲解,对底层代码不做过多介绍,对底层有疑问可以看底层代码讲解文章或b站up柳离风视频。
提示:以下是本篇文章正文内容,下面案例可供参考
一、按键底层代码
按键底层一共有两种,独立按键和矩阵键盘。(这里按键消抖使用定时器任务调度来实现)
bit key_up = 1;
//独立按键
uchar key_scan1()
{
if(key_up == 1 && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) return 1;
else if(P31 == 0) return 2;
else if(P32 == 0) return 3;
else if(P33 == 0) return 4;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
key_up = 1;
return 0;
}
//矩阵键盘
uchar key_scan2()
{
uchar key;
P44 = P42 = P35 = P34 = 1;
P30 = P31 = P32 = P33 = 0;
if(key_up == 1 &&(P44 == 0 || P42 == 0 || P35 == 0 || P34 == 0))
{
if(P44 == 0) key = 1;
else if(P42 == 0) key = 2;
else if(P35 == 0) key = 3;
else if(P34 == 0) key = 4;
else return 0;
P44 = P42 = P35 = P34 = 0;
P30 = P31 = P32 = P33 = 1;
key_up = 0;
if(P30 == 0) key = key;
else if(P31 == 0) key = key + 4;
else if(P32 == 0) key = key + 8;
else if(P33 == 0) key = key + 12;
return key;
}
else if(P44 == 1 && P42 == 1 && P35 == 1 && P34 == 1)
key_up = 1;
//如果题目考到第一二行按键并且使用串口收发,在这行需要加一句P30 = 1;P31 = 1;让通信时序恢复
return 0;
}
//矩阵键盘如果题目没有用到全部按键一定要删除多余的引脚,否则会干扰别的功能,例如P34引脚和NE555模块互相干扰。
//比如题目用到S4、S5、S8、S9,你会发现P30、P31、P34、P35未使用,可以删除
uchar key_scan2()
{
uchar key;
P44 = P42 = 1;
P32 = P33 = 0;
if(key_up == 1 &&(P44 == 0 || P42 == 0 ))
{
if(P44 == 0) key = 1;
else if(P42 == 0) key = 2;
else return 0;
P44 = P42 = 0;
P32 = P33 = 1;
key_up = 0;
if(P32 == 0) key = key ;
else if(P33 == 0) key = key + 2;
return key;
}
else if(P44 == 1 && P42 == 1)
key_up = 1;
return 0;
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S5
break;
case 2: //S9
break;
case 3: //S4
break;
case 4: //S8
break;
}
}
二、按键触发方式(近几年S4、S5、S8、S9考频很高,就以这四个按键为例)
1.按下生效、松手生效
第八届省赛
uchar key_scan2()
{
static bit S4_flag;
uchar key;
P44 = P42 = 1;
P32 = P33 = 0;
if(key_up == 1 &&(P44 == 0 || P42 == 0 ))
{
if(P44 == 0) key = 1;
else if(P42 == 0) key = 2;
else return 0;
P44 = P42 = 0;
P32 = P33 = 1;
key_up = 0;
if(P32 == 0) key = key ;
else if(P33 == 0) key = key + 2;
if(key == 3)//S4按下
S4_flag = 1;
return key;//S4按下后返回3
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1;
if(S4_flag == 1)
{
S4_flag = 0;
return 40; //S4按下后的松手
}
}
return 0;
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S5
break;
case 2: //S9
break;
case 3: //S4按下
break;
case 4: //S8
break;
case 40: //S4按下后松手
break;
}
}
2.短按、长按生效
题目一:
//长按短按功能互不影响,所以短按必须放松手判断
uchar key_scan2()//S8、S9、S12、S13
{
static bit S13_flag;
static bit S12_flag;
static unsigned long key_time;
uchar key;
P42 = P35 = 1;
P32 = P33 = 0;
if(key_up == 1 &&(P42 == 0 || P35 == 0 ))
{
if(P42 == 0) key = 1;
else if(P35 == 0) key = 2;
else return 0;
P42 = P35 = 0;
P32 = P33 = 1;
key_up = 0;
if(P32 == 0) key = key ;
else if(P33 == 0) key = key + 2;
if(key == 2)//S13按下
{
S13_flag = 1;
key_time = systick_ms;
}
else if(key == 4)//S14按下
{
S12_flag = 1;
key_time = systick_ms;
}
return key;//S4按下后返回3
}
else if(P42 == 1 && P35 == 1)
{
key_up = 1;
if(S13_flag == 1 && systick_ms - key_time <= 1000) //按下时间小于1s为短按
{
S13_flag = 0;
return 130; //S13短按
}
else if(S12_flag == 1 && systick_ms - key_time <= 1000) //按下时间小于1s为短按
{
S12_flag = 0;
return 120; //S13短按
}
}
if(S13_flag == 1 && systick_ms - key_time > 1000)
{
S13_flag = 0;
return 131; //S13长按
}
if(S12_flag == 1 && systick_ms - key_time > 1000)
{
S12_flag = 0;
return 121; //S12长按
}
return 0;
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S9
break;
case 2:
break;
case 3: //S8
break;
case 4:
break;
case 120: //S12短按
break;
case 121: //S12长按
break;
case 130: //S13短按
break;
case 131: //S13长按
break;
}
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S5
break;
case 2: //S9
break;
case 3: //S4按下
break;
case 4: //S8
break;
case 40: //S4按下后松手
break;
}
}
题目二:
//这个题目并没有规定长按快速加的速度,所以我们规定100ms加0.1V
bit flag_add;
void timer1() interrupt 3
{
systick_ms++;
key_dly++;
display_dly++;
collect_dly++;
smg(SMG,dot,pos);
if(flag_add == 1)
{
if(++count_100ms == 100)
{
count_100ms = 0;
if(++vol_par == 50)
vol_par = 0;
}
}
led(LED,pos);
if(++pos == 8) pos = 0;
relay(state_relay);
}
uchar key_scan1()
{
static unsigned long key_time;
if(key_up == 1 && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0) return 7;
else if(P31 == 0)
{
S6_flag = 1;
key_time = systick_ms;
}
else if(P32 == 0) return 5;
else if(P33 == 0) return 4;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
if(S6_flag == 1 && systick_ms - key_time <= 800) //S6短按
{
S6_flag = 0;
return 6;
}
if(S6_flag == 1 && systick_ms - key_time > 800)//S6长按松手
{
S6_flag = 0;
return 61;
}
}
if(S6_flag == 1 && systick_ms - key_time > 800) //S6长按
{
return 60;
}
return 0;
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan1();
switch (key)
{
case 7: //S7
break;
case 6: vol_par++; //S6短按
break;
case 5: //S5
break;
case 4: //S4
break;
case 60: flag_add = 1; //S6长按
break;
case 61: flag_add = 0; //S6长按后松手
break;
}
}
3.双按键
代码如下(示例):
uchar key_scan2() //以S8、S9为例
{
static bit S8_flag;
static bit S9_flag;
uchar key;
P44 = P42 = 1;
P32 = P33 = 0;
if(key_up == 1 &&(P44 == 0 || P42 == 0 ))
{
if(P44 == 0) key = 1;
else if(P42 == 0) key = 2;
else return 0;
P44 = P42 = 0;
P32 = P33 = 1;
key_up = 0;
if(P32 == 0) key = key ;
else if(P33 == 0) key = key + 2;
if(key == 2)//S9按下
{
key_up = 1;
S9_flag = 1;
if(S8_flag == 1) //S8、S9同时按下
{
key_up = 0;
S8_flag = 0;
S9_flag = 0;
return 89;
}
}
if(key == 4)//S8按下
{
key_up = 1;
S8_flag = 1;
if(S9_flag == 1)//S8、S9同时按下
{
key_up = 0;
S8_flag = 0;
S9_flag = 0;
return 89;
}
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1;
if(S8_flag == 1)
{
S8_flag = 0;
return 80; //S8按下后的松手
}
else if(S9_flag == 1)
{
S9_flag = 0;
return 90; //S9按下后的松手
}
}
return 0;
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S5按下
break;
case 3: //S4按下
break;
case 80: //S8按下
break;
case 90: //S9按下
break;
case 89: //S9、S9都按下
break;
}
}
4.双击
双击按键在第十五届蓝桥杯之前还未考过,但十五届考纲出现双击,可能国赛会考,双击与单击的区别就是第一次按下后一定时间内有无二次按下,这里取时间间隔为500ms
uchar key_scan2() //S4双击为例
{
uchar key;
P44 = P42 = 1;
P32 = P33 = 0;
if(key_up == 1 &&(P44 == 0 || P42 == 0 ))
{
if(P44 == 0) key = 1;
else if(P42 == 0) key = 2;
else return 0;
P44 = P42 = 0;
P32 = P33 = 1;
key_up = 0;
if(P32 == 0) key = key ;
else if(P33 == 0) key = key + 2;
if(key == 3)
{
S4_state++;
if(S4_state == 2 &&(systick_ms - key_time <= 500)) //双击S4
{
S4_state = 0;
return 40;
}
key_time = systick_ms;
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1;
if(S4_state == 1 && (systick_ms - key_time) > 500) //单击S4
{
S4_state = 0;
return 41;
}
}
return 0;
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S5
break;
case 2: //S9
break;
case 4: //S8
break;
case 40: //S4双击
break;
case 41: //S4单击
break;
}
}
5.组合按键(根据排列组合,大概有二十一种组合方式,一定要灵活使用)
1.短按和长按松手
uchar key_scan1()
{
static bit S7_state;
if(key_up == 1 && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0))
{
key_up = 0;
if(P30 == 0)
{
S7_flag = 1;
key_time = systick_ms;
}
else if(P31 == 0) return 2;
else if(P32 == 0) return 3;
else if(P33 == 0) return 4;
}
else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)
{
key_up = 1;
if(S7_flag == 1 && systick_ms - key_time <= 1000) //短按S7
{
S7_flag = 0;
return 70;
}
else if(S7_flag == 1 && systick_ms - key_time > 1000) //长按S7 超过1秒后松手
{
S7_flag = 0;
return 71;
}
}
return 0;
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 2: //S6
break;
case 3: //S5
break;
case 4: //S4
break;
case 70: //S7短按
break;
case 71: //S7长按超过1s后松手
break;
}
}
2.双按键长按
第十四届国赛
uchar key_scan2() //以S8、S9为例
{
static bit S8_flag;
static bit S9_flag;
uchar key;
P44 = P42 = 1;
P32 = P33 = 0;
if(key_up == 1 &&(P44 == 0 || P42 == 0 ))
{
if(P44 == 0) key = 1;
else if(P42 == 0) key = 2;
else return 0;
P44 = P42 = 0;
P32 = P33 = 1;
key_up = 0;
if(P32 == 0) key = key ;
else if(P33 == 0) key = key + 2;
if(key == 2)//S9按下
{
key_up = 1;
S9_flag = 1;
if(S8_flag == 1) //S8、S9同时按下
{
key_up = 0;
key_time = systick_ms; //开始计时
}
}
if(key == 4)//S8按下
{
key_up = 1;
S8_flag = 1;
if(S9_flag == 1)//S8、S9同时按下
{
key_up = 0;
key_time = systick_ms; //开始计时
}
}
return key;
}
else if(P44 == 1 && P42 == 1)
{
key_up = 1;
if(S8_flag == 1)
{
S8_flag = 0;
return 80; //S8按下后的松手
}
else if(S9_flag == 1)
{
S9_flag = 0;
return 90; //S9按下后的松手
}
}
else if((systick_ms - key_time > 2000) && (P32 == 0 && P33 == 0) && (S8_flag == 1 && S9_flag == 1)) //S8、S9都按下并按下持续时间超过2秒
{
S8_flag = 0;
S9_flag = 0;
return 89
}
return 0;
}
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S5按下
break;
case 3: //S4按下
break;
case 80: //S8按下
break;
case 90: //S9按下
break;
case 89: //S9、S9都按下并持续时间超过2秒
break;
}
}
三、按键功能
1.状态切换
第十四届省赛
uchar state_display1,state_display2;
//主函数中的按键处理任务
//按键处理任务
void key_task() //切记注意界面约束和参数边界值
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan();
switch (key)
{
case 1: //S5
if(display_state1 == 1) //回显界面
display_state2 = (display_state2 + 1)%3;
break;
case 3: //S4 //这里不用界面约束,因为下面加了切换到子界面时为第一个子界面
display_state1 = (display_state1 + 1)%3;
if(display_state1 == 1) display_state2 = 0;
break;
}
}
2.参数调整
1.是否退出参数界面生效(题目一定会说明,不说明就默认设置时生效)
第十三届省赛第二场
uchar state_display;//0:电压界面 1:测距界面 2:参数界面
bit state_par; //0:上限参数 1:下限参数
uchar vol_par_max,vol_display1; //参数上限和显示参数上限
uchar vol_par_min,vol_display2; //参数下限和显示参数下限
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S7按下 减0.5
if(state_display == 2 && state_par == 0) //参数界面且上限参数
{
vol_display1 = (vovl_display1 - 5);
if(vol_display1 == 0) vol_display1 = 50;
}
else if(state_display == 2 && state_par == 1) //参数界面且上限参数
{
vol_display2 = (vovl_display2 - 5);
if(vol_display2 == 0) vol_display2 = 50;
}
break;
case 2: //S6按下 加0.5
if(state_display == 2 && state_par == 0) //参数界面且上限参数
{
vol_display1 = (vovl_display1 + 5);
if(vol_display1 == 55) vol_display1 = 5;
}
else if(state_display == 2 && state_par == 1) //参数界面且上限参数
{
vol_display2 = (vovl_display2 + 5);
if(vol_display2 == 55) vol_display2 = 5
}
break;
case 3: //S5按下
if(state_display == 2)
state_par ^= 1;
break;
case 4: state_display = (state_display + 1)%3; //S4按下
if(state_display == 0) //从参数界面退出时参数生效
{
vol_par_max = vol_display1;
vol_par_min = vol_display2;
}
break;
}
}
2.边界值限制(题目一定会说明,不说明就默认参数加、减到最值时不会再加、减)
第十一届省赛第二场
uchar state_display;//0:数据界面 1:参数界面 2:计数界面
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: count_tri = 0;//S13按下
break;
case 2: //S17按下
if(statet_display == 1)
{
if(vol_par == 0)
vol_par = 55;
vol_par = (vol_par - 5);
}
break;
case 3: state_display = (state_display + 1)%3 //S12按下
break;
case 4: //S16按下
if(statet_display == 1)
{
vol_par = (vol_par + 5);
if(vol_par == 55)
vol_par = 0;
}
break;
}
}
3.检查参数合理性(等一会更新)
第十一届省赛第一场
uchar T_max,T_min;
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S7按下
break;
case 2: //S6按下
break;
case 3: //S5按下
break;
case 4: //S4按下
break;
}
}
4.参数非线性调节(等一会更新)
第十二届国赛
code uchar col_time_display[] ={2,3,5,7,9};
code uchar dis_par_display[] = {10,20,30,40,50,60,70,80};
uchar col_time;
uchar dis_par;
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S5按下
break;
case 3: //S4按下
break;
case 80: //S8按下
break;
case 90: //S9按下
break;
case 89: //S9、S9都按下并持续时间超过2秒
break;
}
}
3.多界面(等一会更新)
第十四届国赛
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S5按下
break;
case 2: //S4按下
break;
case 3: //S8按下
switch(state_display)
{
}
break;
case 4: //S9按下
break;
}
}
4.切换选中单元
第八届省赛
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1: //S7按下
index = (index + 1)%4; //然后在显示中判断哪个数码管闪烁
break;
case 2: //S6按下
break;
case 3: //S5按下
break;
case 4: //S4按下
break;
}
}
5.键盘输入(等一会更新)
第六届省赛
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
switch (key)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
case 9:
break;
case 10:
break;
case 11:
break;
case 12:
break;
}
}
6.按键禁用
第十四届省赛
//主函数中的按键处理任务
void key_task() //切记注意界面约束和参数边界值
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
if(col_flag == 1) //温湿度界面禁用所有按键
return ;
key = key_scan();
switch (key)
{
case 1: //S5
if(display_state1 == 1) //回显界面
display_state2 = (display_state2 + 1)%3;
break;
case 20: //S9
if(display_state1 == 2) //参数界面
{
if(temp_par > 0)
temp_par --;
}
break;
case 3: //S4 //这里不用界面约束,因为下面加了切换到子界面时为第一个子界面
display_state1 = (display_state1 + 1)%3;
if(display_state1 == 1) display_state2 = 0;
break;
case 4: //S8
if(display_state1 == 2)
{ //参数界面
temp_par ++;
if(temp_par == 100)
temp_par = 99;
}
break;
case 21:
if(display_state2 == 2)
{
count_tri = 0;
Time_tri[0] = 0;
Time_tri[1] = 0;
temp_max = 0;
hum_max = 0;
temp_sum = 0;
hum_sum = 0;
temp = 0;
hum = 0;
}
break;
}
}
7.任意按键生效
第八届省赛
//主函数中的按键处理任务
void key_task()
{
uchar key;
if(key_dly<10)return;
key_dly = 0;
key = key_scan2();
if(key > 0 && state_alarm == 1) //在闹钟提示状态,所有按键生效,且不执行按键原有功能
{
state_alarm = 0;
return;
}
switch (key)
{
case 1: //S7按下
break;
case 2: //S6按下
break;
case 3: //S5按下
break;
case 4: //S4按下
break;
}
}
8.记录无效按键
更多资料关注B站UP柳离风
学习交流群
总结
以上就是蓝桥杯按键的所有考点了,希望大家都可以掌握。