40 矩阵键盘在linux内核里的驱动实现

本文介绍了一种基于Linux系统的矩阵键盘按键检测方法,通过行线和列线的电平变化来判断是否有按键按下及具体是哪个按键。文章详细阐述了通过中断处理按键按下与释放的过程,并加入了防抖动处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述

如图的矩阵键盘工作原理:
行线: 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");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值