独立按键和矩阵键盘

原理图:

capture_20240303182545985

由原理图可以知道,独立按键和矩阵键盘有些线是接在一起的。

我们看到板子实物,会看到可更改的跳线帽。当2,3跳线帽接在一起时,启用的是独立按键。

独立按键

接下来我们简化独立按键原理图:

capture_20240303182953055

从上到下依次是S7、S6、S5、S4,所有按键可以同时输入与输出(区别于stm32)。

输入读取

按键按下为低电平,程序选择表达为:

1
2
3
if(s7 == 0)
{
}

当按键没有按下时,为高电平。(此处涉及到单片机IO口的结构和电路分析基础,此处不做过多研究。PS:我也研究不明白。)

另外,单片机按键会出现抖动,这是由于单片机按键的机械原因,其按键具有弹性,断开与闭合时均会因为弹性作用,不会马上稳定的接通,最大抖动时常为(5-10ms)+(5-10ms)。

此外,单片机的输入高电平为5v,输出为0v,但输出的高低电平均为一个范围。

capture_20240303184405576

用示波器检测,我们即可发现其抖动的状态。

如果我们要消除抖动,在判断为按键按下时候加一个10ms的延时函数即可。

程序设计

定义端口;

1
2
3
4
sbit S7 = P3^0;
sbit S6 = P3^1;
sbit S5 = P3^2;
sbit S4 = P3^3;

定义函数:

1
2
3
4
5
6
7
8
void keyscan()//按键扫描函数
{ hc573(4);//开LED锁存器通道
if (S7 == 0);
{
P0 = 0xfe;//亮第一个灯
}

}

主函数:

1
2
3
4
5
6
7
8
void main()
{
init();
while(1)
{
keyscan();
}
}

记得改跳线帽哦。

练习:

1.按下S7亮灯,松开熄灭

  void keyscan()//按键扫描函数
{   
    hc573(4);//开LED锁存器通道
    if (S7 == 0);
    {
       P0 = 0xfe;//亮第一个灯
       while(S7==0);//不松手的话就会一直卡在循环里,即一直亮第一个灯。
       P0 = 0xff;
    } 
}  

2.按下S7,L1亮,再按下S7,L1灭

这里我们使用一个计分板:state

当state为0,led亮,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
uchar state;//keil5初始值默认为0

void led()
{
hc573(4);
if(state == 0)//按第一次state为0,第二次为1
{
P0 = 0xff;
}
else
{
P0 = 0xfe;
}
}

void keyscan()//按键扫描函数
{
if (S7 == 0);
{
delay_ms(10);等待10ms再次确认
if (S7 == 0);//消抖
{
state = 1 - state;
while(S7==0);
delay_ms(10);
while(S7==0);
}
}
}

void main()
{
init();
while(1)
{
keyscan();
led();
}
}

3.第一次按S7,L1亮,第二次按,L1以500ms闪烁,第三次熄灭。

整体思路不变,依然是用state,不过这次需要三个数,我们可以这样改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void keyscan()//按键扫描函数
{
if (S7 == 0);
{
if (S7 == 0);//消抖
{
delay_ms(10);
// state = 1 - state;
if (state == 0)
{
state = 1;
}
else if (state == 1)
{
state = 2;
}
else if (state ==2)
{
state = 0;
}
while(S7==0);
delay_ms(10);
while(S7==0);
}
}
}

或者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void keyscan()//按键扫描函数
{
if (S7 == 0);
{
delay_ms(10);
if (S7 == 0);//消抖
{
state = 1 + state;
if(state == 3)//第四次state值为3,初始化为0继续循环
{
state = 0;
}
while(S7==0);
delay_ms(10);
while(S7==0);
}
}
}

两个改法核心思路都是:

第一次state值为0,执行亮L1操作,第二次按下state值为执行闪烁操作,第三次state为2,执行熄灭,第四次自动把state值改为0,再重新进入循环。

led部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void led()
{
hc573(4);
if(state == 0)
{
P0 = 0xfe;
}
else if (state == 1)
{
P0 = 0xff;
delay_ms(500);
P0 = 0xfe;
delay_ms(500);
}
else if (state == 2)
{
P0 = 0xff;
}
}

然后我们写入单片机,我们发现,第三次无法实现。

这里,我们需要定时器中断

解释原因:

我们的程序执行过程是这样的:

1.keyscan

2.led

​ P0 = 0xff;
​ delay_ms(500);
​ P0 = 0xfe;
​ delay_ms(500); //这个过程是每次执行完才去检测按键函数。

所以说,在这每次循环完闪烁之后,只有极短的时间去检测按键,但这个时间不足以让人的手去按下按键。

这样,我们的函数state==1就会一直循环下去。除非某次它刚好执行完,你刚好按下按键并且被它检测到,才会停止循环,执行state==2.

那怎么样才能加大我们被检测到的概率呢?

别忘了,delay_ms(500)函数是1ms的函数循环500次,只要我们把按键检测塞到这个循环里去,那我们被检测的概率将大大提高。

延时函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
void Delay_ms(uint xms)		//@12MHz
{
for ( ; xms > 0; xms--)
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}

不过,我们仅仅是在按键时候检测,所以不破坏原本延时函数,复制后改个名整个新的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void Delay_msl(uint xms)		
{
for ( ; xms > 0; xms--)
{
unsigned char i, j;
i = 12;
j = 169;
do
{ keyscan();//插入按键检测函数
while (--j);
} while (--i);

}
}

记得把keyscan放在新的延时函数之前。否则编译会出错。