14 在H5上实现的矩阵键盘驱动

本文介绍了一个2x2矩阵键盘驱动的设计与实现过程,包括设备树配置、驱动源码解析及工作原理说明。该驱动能够通过中断处理实现按键的识别与报告。

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

矩阵键盘工作原理参考: http://blog.youkuaiyun.com/jklinux/article/details/73649292

实现一个2x2的矩阵键盘驱动, 在设备树里的描述:

    mykeypad  {
        compatible = "mykeypad";
        /* 行线的io口, 以数组的形式列出 */
        row-gpios = <&pio 0  12 GPIO_ACTIVE_HIGH>, <&pio 0 11 GPIO_ACTIVE_HIGH>;

        /* 列线的io口, 以数组的形式列出 */
        col-gpios = <&pio 0  19 GPIO_ACTIVE_HIGH>, <&pio 0 18 GPIO_ACTIVE_HIGH>;

    /* 按键顺序对应的键码 */
    keycodes = <KEY_L, KEY_S, KEY_ENTER, KEY_UP>;
    }; 


驱动源码:

/* mydrv.c */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/input.h>


typedef struct {
    struct gpio_descs *rows; //存放行线io口信息
    struct gpio_descs *cols; //存放列线io口信息

    struct input_dev *idev; 
    int    *keycodes; //存放设备树提供的键码
    int    key_pressed; //记录第几个按钮已按下,用于在松手中断时汇报键松手
}mypdata;


irqreturn_t irq_func(int irqno, void *arg)
{
    mypdata *pdata = (mypdata *)arg;
    int r = -1, i, c = -1;

    //找出哪一行发生中断
    for (i = 0; i < pdata->rows->ndescs; i++)
    {
        if (irqno == gpiod_to_irq(pdata->rows->desc[i]))
        {
            r = i;
            break;
        }
    }
    if (r < 0)
        return IRQ_HANDLED;

    //先把行线的中断暂时关闭
    disable_irq_nosync(irqno);

    if (gpiod_get_value(pdata->rows->desc[r])) //松手
    {
        //如果有记录键已按下,则汇报键松手
        if (pdata->key_pressed >= 0)
        {
            input_report_key(pdata->idev, pdata->keycodes[pdata->key_pressed], 0);
            input_sync(pdata->idev);

            pdata->key_pressed = -1;
        }
        goto out;
    }   
    else
    {
        //轮流让每一个列线输出高电平
        for (i  = 0; i < pdata->cols->ndescs; i++)
        {
            gpiod_set_value(pdata->cols->desc[i], 1);
            if (gpiod_get_value(pdata->rows->desc[r]))
            {
                gpiod_set_value(pdata->cols->desc[i], 0);
                c = i;
                break;
            }
            gpiod_set_value(pdata->cols->desc[i], 0);
        }

        if (c < 0)
            goto out;

        //汇报键按下
        pdata->key_pressed = r*pdata->cols->ndescs+c;
        input_report_key(pdata->idev, pdata->keycodes[pdata->key_pressed], 1);
        input_sync(pdata->idev);
    }

out:
    msleep(100); //防止抖动
    enable_irq(irqno); //恢复行线的中断功能

    return IRQ_HANDLED;
}

int myprobe(struct platform_device *pdev)
{
    mypdata *pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
    int ret = -ENODEV;
    int i;

    //获取列线col-gpios的io口信息 */
    pdata->cols = devm_gpiod_get_array(&pdev->dev, "col", GPIOD_OUT_LOW);
    if (IS_ERR(pdata->cols))
    {
        printk("rows gpio failed\n");
        goto err0;
    }

    //获取行线row-gpios的io口信息 */
    pdata->rows = devm_gpiod_get_array(&pdev->dev, "row", GPIOD_IN);
    if (IS_ERR(pdata->rows))
    {
        printk("cols gpio failed\n");
        goto err1;
    }
    //准备存放键码的空间
    pdata->keycodes = devm_kzalloc(&pdev->dev, sizeof(int)*pdata->rows->ndescs*pdata->cols->ndescs, GFP_KERNEL);

    //获取设备树里的键码信息
    device_property_read_u32_array(&pdev->dev, "keycodes", pdata->keycodes, pdata->rows->ndescs*pdata->cols->ndescs);
    pdata->key_pressed = -1;

     input dev 
    pdata->idev = input_allocate_device();
    pdata->idev->name = pdev->name;

    set_bit(EV_KEY, pdata->idev->evbit);
    set_bit(EV_REP, pdata->idev->evbit);
    for (i = 0; i < pdata->rows->ndescs * pdata->cols->ndescs; i++)
        set_bit(pdata->keycodes[i], pdata->idev->keybit);

    ret = input_register_device(pdata->idev);
    if (ret < 0)
        goto err2;
        

    //请求中断
    for (i  = 0; i < pdata->rows->ndescs; i++)
    {
        ret = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(pdata->rows->desc[i]),
        NULL, irq_func, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING|IRQF_ONESHOT, pdev->name, pdata);
        if (ret < 0)
            goto err3;
    }

    platform_set_drvdata(pdev, pdata);
    printk("in myprobe\n");
    return 0;
err3:
    while (i)
        devm_free_irq(&pdev->dev, gpiod_to_irq(pdata->rows->desc[--i]), pdata);
err2:
    input_unregister_device(pdata->idev);
    devm_kfree(&pdev->dev, pdata->keycodes);    

    devm_gpiod_put_array(&pdev->dev, pdata->rows);
err1:
    devm_gpiod_put_array(&pdev->dev, pdata->cols);
err0:
    return ret;
}

int myremove(struct platform_device *pdev)
{
    mypdata *pdata = platform_get_drvdata(pdev);
    int i;

    input_unregister_device(pdata->idev);
    devm_kfree(&pdev->dev, pdata->keycodes);
    for (i = 0; i < pdata->rows->ndescs; i++)
        devm_free_irq(&pdev->dev, gpiod_to_irq(pdata->rows->desc[i]), pdata);

    devm_gpiod_put_array(&pdev->dev, pdata->rows);
    devm_gpiod_put_array(&pdev->dev, pdata->cols);
    printk("in myremove\n");
    return 0;
}

struct of_device_id ids[] = {
    {.compatible = "mykeypad"},
    {},
};

struct platform_driver mydrv = {
    .probe = myprobe,
    .remove = myremove,

    .driver = {
        .owner = THIS_MODULE,
        .name = "mydrv" ,

        .of_match_table = ids,
    },
};

module_platform_driver(mydrv);
MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值