如图的矩阵键盘工作原理:
行线: MCU_SPICLK0, MCU_SPIFRM0
列线: MCU_SPITXD0, MCU_SPIRXD0
当按键按下时,它的1脚和2脚接通,行线与列线就会接通.
1) 判断是否有接键按下或松手:
首先,行线有接上拉电阻 ,默认高电平。列线全部输出低电平. 如K14按下时, 列线SPITXD0与行线SPICLK0接通, 行线的电平就会被列线输出的电平影响,变成低电平(有下降沿). 当K14松手时, 行线SPICLK0与列线SPITXD0断开, 行线会被上拉恢复高电平(有上升沿).
也就是可通过行线的上升沿,下降沿中断可得知有键按下和松手.
2) 当有按键按下时怎样判断具体是哪个按键按下:
如行线SPICLK0发生下降沿中断,表示有键按下,有可能是K14或K20按下. K14和K20是接不同的列线,如果键按下,则相应的列线会与行线SPICLK0接通,改变列线的电平,行线的电平也会随着列线变化。
也就是有按键下后,逐一改变列线的电平,判断行线的电平是否跟着变化,如行线跟着变化则表示此列的按键按下了.
///
现板上接4行4列的矩阵键盘模块:
行线分别接: PA7, PA8, PA9, PA10
列线分别接: PA20, PA21, PC4, PC7
因模块的行线没有外接上接,需在script.fex里设置行线使用上接功能.
script.fex
...
gpio_pin_12 = port:PA07<0><1><default><0>
gpio_pin_13 = port:PA08<0><1><default><0>
gpio_pin_14 = port:PG08<1><1><default><0>
gpio_pin_15 = port:PA09<0><1><default><0>
gpio_pin_16 = port:PA10<0><1><default><0>
...
判断是否有按键按下或松手,并加入防抖动的代码:
test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <linux/timer.h>
int rows[] = {GPIOA(7), GPIOA(8), GPIOA(9), GPIOA(10)}; //行线
int cols[] = {GPIOA(20), GPIOA(21), GPIOC(4), GPIOC(7)}; //列线
struct timer_list mytimer;
void timer_func(unsigned long data)
{
enable_irq(data);
}
irqreturn_t irq_func(int irqno, void *arg)
{
int r = (int )arg;
printk("row : %d, %s\n", r, gpio_get_value(rows[r])?"released":"pressed");
disable_irq_nosync(irqno);
mytimer.data = irqno;
mod_timer(&mytimer, jiffies+HZ*20/1000); // 20ms后重新打开中断
return IRQ_HANDLED;
}
static int __init test_init(void)
{
int ret, i, j, k;
init_timer(&mytimer);
mytimer.function = timer_func;
//申请行线的中断
for (j = 0, k = 0; j < ARRAY_SIZE(rows); k++, j++)
{
ret = gpio_request(rows[j], "mykeypad");
if (ret < 0)
goto err1;
ret = request_irq(gpio_to_irq(rows[k]), irq_func, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "mykeypad", (void *)k);
if (ret < 0)
goto err1;
}
//把列全部输出低电平
for (i = 0; i < ARRAY_SIZE(cols); i++)
{
ret = gpio_request(cols[i], "mykeypad");
if (ret < 0)
goto err0;
gpio_direction_output(cols[i], 0);
}
return 0;
err1:
while (k > 0)
{
k--;
free_irq(gpio_to_irq(rows[k]), (void *)k);
}
while (j > 0)
gpio_free(rows[--j]);
err0:
while (i > 0)
gpio_free(cols[--i]);
return ret;
}
static void __exit test_exit(void)
{
int i, j;
del_timer(&mytimer);
for (i = 0; i < ARRAY_SIZE(cols); i++)
gpio_free(cols[i]);
for (j = 0; j < ARRAY_SIZE(rows); j++)
{
gpio_free(rows[j]);
free_irq(gpio_to_irq(rows[j]), (void *)j);
}
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
加入检查具体按键按下和松手的判断:
test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <linux/timer.h>
int rows[] = {GPIOA(7), GPIOA(8), GPIOA(9), GPIOA(10)};
int cols[] = {GPIOA(20), GPIOA(21), GPIOC(4), GPIOC(7)};
struct timer_list mytimer;
//用一个数组记录哪个按键按下,当松手中断发生时,就用此数组里记录的按键作松手的按键
char keys[4][4];
void all_cols_output_level(int level)
{
int i;
for (i = 0; i < ARRAY_SIZE(cols); i++)
gpio_set_value(cols[i], level);
}
void timer_func(unsigned long data)
{
int r = data;
enable_irq(gpio_to_irq(rows[r]));
}
irqreturn_t irq_func(int irqno, void *arg)
{
int r = (int )arg; //r表示哪一行,由request_irq时指定
int i;
int stat = gpio_get_value(rows[r]);
disable_irq_nosync(irqno);
/检查列
if (stat) //行线为高电平, 则是有按键松手
{
for (i = 0; i < ARRAY_SIZE(cols); i++)
{
if (keys[r][i]) //根据数据里存放的记录来判断是哪个按键松手
{
printk("key[%d][%d] released\n", r, i);
keys[r][i] = 0;
break;
}
}
}
else //行线为低电平,则是有按键按下
{
for (i = 0; i < ARRAY_SIZE(cols); i++)
{
all_cols_output_level(1); //所有的列输出高电平
gpio_set_value(cols[i], 0); //逐一改变一列线的电平
if (!(gpio_get_value(rows[r])) && !(gpio_get_value(rows[r]))) //判断行线电平是否随着列线改变, 如改变则是按下
{
keys[r][i] = 1;
printk("key[%d][%d] pressed\n", r, i);
break;
}
}
all_cols_output_level(0); //所有的列输出低电平
}
//
mytimer.data = r;
mod_timer(&mytimer, jiffies+HZ*50/1000); // 50ms后重新打开中断
return IRQ_HANDLED;
}
static int __init test_init(void)
{
int ret, i, j, k;
init_timer(&mytimer);
mytimer.function = timer_func;
//申请行线的中断
for (j = 0, k = 0; j < ARRAY_SIZE(rows); k++, j++)
{
ret = gpio_request(rows[j], "mykeypad");
if (ret < 0)
goto err1;
ret = request_irq(gpio_to_irq(rows[k]), irq_func, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "mykeypad", (void *)k);
if (ret < 0)
goto err1;
}
//把列全部输出低电平
for (i = 0; i < ARRAY_SIZE(cols); i++)
{
ret = gpio_request(cols[i], "mykeypad");
if (ret < 0)
goto err0;
gpio_direction_output(cols[i], 0);
}
return 0;
err1:
while (k-- > 0)
free_irq(gpio_to_irq(rows[k]), (void *)k);
while (j > 0)
gpio_free(rows[--j]);
err0:
while (i > 0)
gpio_free(cols[--i]);
return ret;
}
static void __exit test_exit(void)
{
int i, j;
del_timer(&mytimer);
for (i = 0; i < ARRAY_SIZE(cols); i++)
gpio_free(cols[i]);
for (j = 0; j < ARRAY_SIZE(rows); j++)
{
gpio_free(rows[j]);
free_irq(gpio_to_irq(rows[j]), (void *)j);
}
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");