按键介绍
我们通常提到按键,一般是指按键开关,也称为轻触开关。轻触开关是最常用的几种电子元器件之一,被各种电子产品广泛使用。
轻触开关与普通开关类似,但又略有不同。普通开关有闭合与断开两种状态,切换后状态会锁定,直到下次操作前不改变;而轻触开关内部有弹簧,弹起时为断开状态,在施加一定压力后会闭合短路,而松开后又会自动弹起,重新恢复断开状态。
所以轻触开关除了可以检测按下与抬起外,还可以检测长按与短按,从而单按键开关可实现多种复合功能。
最常见的轻触开关为四脚轻触开关,如下图
四脚轻触开关实际内部是两两相连,设计为四脚一方面是为了稳定性(四脚固定在按动时会比两脚更稳),另一方面也是为了硬件布线可以更方便。
根据规格书的描述,内部1与2,3与4直接相连,在按下时,1/2 才会和 3/4闭合,接线时需要注意,不要弄错引脚,导致按键为常闭状态。
按键抖动
按键开关一般是由弹性金属制成,在按键按下后会经历波动过程,然后再稳定;弹起时也会产生类似情况。通常我们称此现象为按键抖动,也被称为接触弹跳(Contant Bounce)。
由于按键检测芯片频率都比较高,而接触弹跳的存在,会导致软件在检测按键按下/抬起时,会发生多次误检测的异常现象。
所以在实际使用按键扫描时,需要进行消抖(Debounce)才能正常使用。
按键消抖分为两种:硬件消抖,软件消抖。
硬件消抖
硬件的滤波电路或者其他电路实现,此处不做讨论
软件消抖
根据抖动的特征,软件进行消除。
参考按键规格书中的描述,抖动分为"按下抖动"和"抬起抖动",一般都在 10ms以下。简单的消抖方法为延时消抖,即检测到状态变化后,延时10ms,再次确认状态,如果相同才识别为有效。
// 示例伪代码
// 判断按键为按下状态
if scan_status == KEY_PRESSED
// 延时 10ms
delay_10ms
// 再次检测按键状态如果为按下
if scan_status == KEY_PRESSED
// 确认当前按键状态为按下
key_status = scan_status
// 结束
按键扫描
按键扫描按照接线方式的不同,可分为独立按键接线与矩阵按键接线。
独立按键
独立按键,即单个按键独立的接到单片机的 IO口上,读取对应IO状态,例如高电平为按键抬起,低电平为按键按下。
此种方式结构简单,软件简单。实际开发中,单片机IO资源有限,除非按键很少,否则不会使用。
示例流程如下
- 按键单独连接到IO口,另一端接地
- 配置对应IO口为输入状态,且为内部上拉
- 读取IO口电平,为高时按键抬起,为低时为按键按下
- 消抖后判定状态是否有效,确认按键状态
// 独立按键检测
void stand_alone_scan(void)
{
// 检测 P2.0 IO口, 按键状态变化
if (stand_alone_io != stand_alone_last)
{
// 延时 10ms 消抖
delay_10us(1000);
// 再次检测 IO 口确认按键状态
if (stand_alone_io != stand_alone_last)
{
printf("stand alone - %s\r\n",
stand_alone_io == KEY_UNPRESSED ? "unpressed" : "pressed");
stand_alone_last = stand_alone_io;
}
}
}
矩阵按键
矩阵键盘,顾名思义采用行列式连线进行布局,按键的两端分别接到矩阵的行线与列线。按键按下时,对应按键的行和列短路,检测确定行和列,即可确认按键位置。
以 4x4 矩阵键盘为例,共16个按键,仅需要8个IO口,4个IO口连接按键矩阵行,4个IO口连接按键矩阵列。
矩阵键盘识别比独立按键略微复杂,但是相比独立按键可节省大量IO口,实际开发中应用较多。
常用的矩阵扫描方法有三种:逐行扫描,逐列扫描,行列反转扫描。
逐行扫描
按键以矩阵方式连接,P1.0~P1.3 为行线,P1.4~P1.7为列线
设置全部行线为输出线,行线全部设置为高电平,全部列线为输入线,检测列线全部为高时,则代表无按键按下
逐行设置行线为低电平,检测每行所有列线,如果识别到低电平,则可判定对应行列按键被按下
如下图所示,行1设置为低电平,检测到列2为低电平,则判定行1列2对应按键被按下
// 逐行检测代码示例
void matrix_scan(void)
{
unsigned char row, col;
for (row = 0; row < MATRIX_ROW_MAX; row++)
{
// 逐行选中
P1 &= ~(1 << row);
for (col = 0; col < MATRIX_COL_MAX; col++)
{
// 在选中行检查每一列
if (((P1 >> (MATRIX_ROW_MAX + col)) & 0x01) == KEY_PRESSED)
{
// 消抖
delay_10us(1000);
// 再次检测 IO 口确认按键状态
if (((P1 >> (MATRIX_ROW_MAX + col)) & 0x01) == KEY_PRESSED)
{
if (matrix_status == KEY_UNPRESSED)
{
printf("matrix - row %d col %d pressed\r\n", (int)row, (int)col);
pressed_row = row;
pressed_col = col;
matrix_status = KEY_PRESSED;
}
break;
}
}
if (((row == pressed_row) && (col == pressed_col)))
{
printf("matrix - row %d col %d unpressed\r\n", (int)row, (int)col);
pressed_row = MATRIX_UNKNOWN;
pressed_col = MATRIX_UNKNOWN;
matrix_status = KEY_UNPRESSED;
}
}
// 清除选中行
P1 |= (1 << row);
}
}
逐列扫描
与逐行扫描类似,变为逐列设置列线为0,检测每列对应行线,确定按键位置。
行列反转扫描
行先全部设置为高,列全部设置为低,如有按键按下,检测到对应行线变低,确认行;
反向设置,列为高,行为低,检测对应变低的列,确认列;
根据对应行列,即可确认到对应被按下按键位置。文章来源:https://www.toymoban.com/news/detail-444620.html
总结
实际按键使用中,使用独立按键与矩阵按键,可覆盖大部分应用场景,针对一些特殊场景,例如多按键按下,会在后续继续讨论优化。文章来源地址https://www.toymoban.com/news/detail-444620.html
到了这里,关于【按键扫描】独立按键与矩阵按键的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!