49 使用linux内核源码里的矩阵键盘驱动<GPIO driven matrix keypad support>

49 使用linux内核源码里的矩阵键盘驱动<GPIO driven matrix keypad support>这个设备驱动适用于,矩阵键盘的每行,每列都是接到一个IO口,行线接的IO口有中断功能.需要在linux内核配置里选上相关的配置。在内核源码目录下:makemenuconfigARCH=armCROSS_COMPILE=arm-linux-gnueabihf-DeviceDrivers—>Inputdevicesupport—>

大家好,又见面了,我是你们的朋友全栈君。

这个设备驱动适用于,矩阵键盘的每行,每列都是接到一个IO口, 行线接的IO口有中断功能.
需要在linux内核配置里选上相关的配置。在内核源码目录下:

    make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

    Device Drivers  --->
        Input device support  --->
            [*]   Keyboards  --->
                 <*>   GPIO driven matrix keypad support 
选择上后,再编内核,再使用新的内核镜像启动系统

使用新内核启动后,可以查看出设备驱动是否已选择上:
/sys/bus/platform/drivers/目录下应有”matrix-keypad”目录

驱动源码在”drivers/input/keyboard/matrix_keypad.c”, 里面是一个平台驱动,我们只要写平台设备描述硬件的资源与此驱动匹配即可.

488 static struct platform_driver matrix_keypad_driver = {
489     .probe      = matrix_keypad_probe,
490     .remove     = __devexit_p(matrix_keypad_remove),
491     .driver     = {
492         .name   = "matrix-keypad",
493         .owner  = THIS_MODULE,
494 #ifdef CONFIG_PM
495         .pm = &matrix_keypad_pm_ops,
496 #endif
497     },
498 };
499 module_platform_driver(matrix_keypad_driver);


//通过阅读平台驱动的probe函数,可得知我们写的平台设备应提供具本哪些硬件信息.
378 static int __devinit matrix_keypad_probe(struct platform_device *pdev)
379 {
380     const struct matrix_keypad_platform_data *pdata; //平台设备提供的platform_data
381     const struct matrix_keymap_data *keymap_data;
382     struct matrix_keypad *keypad;
383     struct input_dev *input_dev;
384     unsigned short *keycodes;
385     unsigned int row_shift;
386     int err;

388     pdata = pdev->dev.platform_data;
    ... 
394     keymap_data = pdata->keymap_data;

    ...
433     matrix_keypad_build_keymap(keymap_data, row_shift,
434                    input_dev->keycode, input_dev->keybit); //从keymap_data里分解出行列键对应的键码
    ...
439     err = init_matrix_gpio(pdev, keypad); //注册行线的中断号
    ...
443     err = input_register_device(keypad->input_dev); //输入设备对象注册
    ...
456     return err;
457 }

/

//通过probe函数,可以确定我们写平台设备时只需通过platform_data成员提供平台驱动所需的信息,无需再提供resource.
//再确定结构体matrix_keypad_platform_data的每个成员的作用即可,如不清楚具体用途,可以在驱动代码里通过查看对成员值的访问推出用途.

"include/linux/input/matrix_keypad.h"

#define KEY(row, col, val) ((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\
                 (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\
                 ((val) & 0xffff))

#define KEY_ROW(k) (((k) >> 24) & 0xff)
#define KEY_COL(k) (((k) >> 16) & 0xff)
#define KEY_VAL(k) ((k) & 0xffff)

#define MATRIX_SCAN_CODE(row, col, row_shift) (((row) << (row_shift)) + (col))


struct matrix_keymap_data {
    const uint32_t *keymap; //装载按键对应的键码数组, 注意每个键码需要使用宏KEY来写。也就是一个32位数据里,行,列,键码各占用8, 8, 16位.
    unsigned int    keymap_size; //键码数组的元素个数
};

struct matrix_keypad_platform_data {
    const struct matrix_keymap_data *keymap_data; //键码数据对象的首地址

    const unsigned int *row_gpios; //行线用的IO口
    const unsigned int *col_gpios; //列线用的IO口

    unsigned int    num_row_gpios; //多少个行线
    unsigned int    num_col_gpios; //多少个列线

    unsigned int    col_scan_delay_us; //扫描列线时间隔时间

    unsigned int    debounce_ms; //防抖动的间隔时间

    unsigned int    clustered_irq; //行线是否共用一个中断, 设0则每个行线的中断是独立的
    unsigned int    clustered_irq_flags;

    bool        active_low; //键按下时,行线是否为低电平
    bool        wakeup;
    bool        no_autorepeat; //按键按下时是否重复提交按键, 设1就是不重复,设0重复
};

/
这里写图片描述

行线接: PA7, PA8, PA9, PA10
列线接: PA20, PA21, PC4, PC7

mypdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <mach/gpio.h>
#include <linux/input/matrix_keypad.h>

u32 keys[] = {
    KEY(0, 0, KEY_R),   //第0行,第0列的键的键码为KEY_R
    KEY(0, 1, KEY_E),   
    KEY(0, 2, KEY_B),   
    KEY(0, 3, KEY_O),   

    KEY(1, 0, KEY_T),   
    KEY(1, 1, KEY_ENTER),   
    KEY(1, 2, KEY_SPACE),   
    KEY(1, 3, KEY_L),   

    KEY(2, 0, KEY_S),   
    KEY(2, 1, KEY_A),   
    KEY(2, 2, KEY_B),   
    KEY(2, 3, KEY_C),

    KEY(3, 0, KEY_UP),   
    KEY(3, 1, KEY_DOWN),   
    KEY(3, 2, KEY_LEFT),   
    KEY(3, 3, KEY_RIGHT),
}; //键码数组

struct matrix_keymap_data mdata = {
    .keymap = keys,
    .keymap_size = ARRAY_SIZE(keys), 
};

u32 rows[] = {GPIOA(7), GPIOA(8), GPIOA(9), GPIOA(10)}; //行线的IO口
u32 cols[] = {GPIOA(20), GPIOA(21), GPIOC(4), GPIOC(7)}; //列线的IO口

struct matrix_keypad_platform_data pdata = {
    .keymap_data = &mdata,
    .row_gpios = rows,
    .col_gpios = cols,

    .num_row_gpios = ARRAY_SIZE(rows),
    .num_col_gpios = ARRAY_SIZE(cols),

    .col_scan_delay_us = 100,
    .debounce_ms = 10,  

    .active_low = 1,
    .no_autorepeat = 0,
};

struct platform_device mypdev = {
    .name = "matrix-keypad",
    .id = -1,
    .dev = {
        .platform_data = &pdata,
    },
};

module_driver(mypdev, platform_device_register, platform_device_unregister);
MODULE_LICENSE("GPL");

/
设备驱动的工作原理:
1) 设备驱动的probe函数

    static int __devinit matrix_keypad_probe(struct platform_device *pdev)
    {
        const struct matrix_keypad_platform_data *pdata;
        const struct matrix_keymap_data *keymap_data;
        struct matrix_keypad *keypad; //设备驱动对每个匹配上的设备准备的数据
        struct input_dev *input_dev;

        pdata = pdev->dev.platform_data;

        keymap_data = pdata->keymap_data;

        input_dev = input_allocate_device(); //发配输入设备对象的空间
            input_dev->open     = matrix_keypad_start; //当设备文件open时,触发调用matrix_keypad_start
            input_dev->close    = matrix_keypad_stop; //当设备文件close时,触发调用

        INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); //初始化工作任务,当工作任务keypad->work得到处理时,matrix_keypad_scan函数得到调用. 而且这个工作任务是指定多久后才会执行的.

        matrix_keypad_build_keymap(keymap_data, row_shift,
                   input_dev->keycode, input_dev->keybit); //从keymap_data里分解出行,列,键值数据

        err = init_matrix_gpio(pdev, keypad); //请求IO口,列线输出!active_low电平.申请行线的双边沿中断. 中断处理函数是matrix_keypad_interrupt. 而且关闭所有的行线的中断功能(disable_irq_nosync).

        err = input_register_device(keypad->input_dev); //输入设备对象注册
        ...
    }

2) 当设备文件open地, 触发调用matrix_keypad_start函数

static int matrix_keypad_start(struct input_dev *dev)
{
    struct matrix_keypad *keypad = input_get_drvdata(dev);

    keypad->stopped = false;
    ...
    schedule_delayed_work(&keypad->work, 0); //也就马上调用工作任务的处理函数matrix_keypad_scan
    return 0;
}   

3) 按键的扫描

static void matrix_keypad_scan(struct work_struct *work) //工作任务的处理函数
{
    struct matrix_keypad *keypad =
        container_of(work, struct matrix_keypad, work.work);
    struct input_dev *input_dev = keypad->input_dev;
    const struct matrix_keypad_platform_data *pdata = keypad->pdata;
    uint32_t new_state[MATRIX_MAX_COLS];
    int row, col, code;

    activate_all_cols(pdata, false); //把所有的列线改为输入功能

    memset(new_state, 0, sizeof(new_state));

     for (col = 0; col < pdata->num_col_gpios; col++) {

        activate_col(pdata, col, true);//把指定的列线改为输出

    //检查行线的电平, 按键的状态和数组new_state存放起来
        for (row = 0; row < pdata->num_row_gpios; row++)
            new_state[col] |=
                row_asserted(pdata, row) ? (1 << row) : 0;

        activate_col(pdata, col, false); //把所有的列线改为输入功能
    }

    //把按键上次状与现在的状态进行对比, 如果不一样就汇报键数据
    for (col = 0; col < pdata->num_col_gpios; col++) {
        uint32_t bits_changed;

        bits_changed = keypad->last_key_state[col] ^ new_state[col];
        if (bits_changed == 0)
            continue;

        for (row = 0; row < pdata->num_row_gpios; row++) {
            if ((bits_changed & (1 << row)) == 0)
                continue;

            code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
            input_event(input_dev, EV_MSC, MSC_SCAN, code);
            input_report_key(input_dev,
                     keypad->keycodes[code],
                     new_state[col] & (1 << row));
        }
    }
    input_sync(input_dev);
    memcpy(keypad->last_key_state, new_state, sizeof(new_state));

    activate_all_cols(pdata, true); //把所有列线改为输出

    /* Enable IRQs again */
    spin_lock_irq(&keypad->lock);
    keypad->scan_pending = false;
    enable_row_irqs(keypad); //恢复行线中断
    spin_unlock_irq(&keypad->lock);
}

4) 行线的中断处理函数

static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
{
    struct matrix_keypad *keypad = id;
    unsigned long flags;

    spin_lock_irqsave(&keypad->lock, flags);

    /* * See if another IRQ beaten us to it and scheduled the * scan already. In that case we should not try to * disable IRQs again. */
    if (unlikely(keypad->scan_pending || keypad->stopped))
        goto out;

    disable_row_irqs(keypad); //又关闭行线的中断功能,直到在matrix_keypad_scan函数触发时才会重新打开中断功能
    keypad->scan_pending = true;
    schedule_delayed_work(&keypad->work,
        msecs_to_jiffies(keypad->pdata->debounce_ms)); //安排工作任务在平台数据里设的debounce_ms后执行任务处理函数matrix_keypad_scan

out:
    spin_unlock_irqrestore(&keypad->lock, flags);
    return IRQ_HANDLED;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/138625.html原文链接:https://javaforall.net

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 费马小定理题

    费马小定理题费马小定理 假如 p 是质数 且 gcd a p 1 那么 nbsp A 题 HDU 4704 首先是挡板法 隔板法 然后用即可 高中数学范围不多叙述 然后得到答案是 nbsp 这题读入数据大 就算快速幂也肯定 TLE 所以用费马小定理 把数据规模降到 int 范围内 时间复杂度降低因为 1e9 7 是一个素数 基本可以假设 N 1 不是 1e9 7 的倍数 这一点我没想到 所以没写出来 所

    2025年11月16日
    5
  • 防止ASP Session丢失的方法[通俗易懂]

    防止ASP Session丢失的方法[通俗易懂]也许很多asp开发者都会遇到这样的情况:在会话期间(session)无故丢失session。换而言之,丢失了SessionID,同样session级的变量也会丢失。很多session丢失的原因是因为错误的程序或者是错误的虚拟目录结构。 SessionID的改变有下面几个原因。 原因一: Netscape的浏览器会认为”/App/user.asp”跟”/app/u

    2022年7月25日
    12
  • Unity3D :关于UGUI的网格重建、动静分离[通俗易懂]

    Unity3D :关于UGUI的网格重建、动静分离[通俗易懂]前言:无论是网上的攻略还是以前的经验来说,都说UGUI需要进行动静分离。也就是说同一个界面下的UI,可活动的元素放在一个Canvas下,不可活动的元素放在另一个Canvas下。虽然两个Canvas打断了合批,但是却减少了网格的重建时间,总体上是有优化的。究其原因,是因为在同一个Canvas下的某个元素发生变化时,同一Canvas下的所有元素都会进行网格重建(ReBatch)。而静态的元素在…

    2022年5月10日
    47
  • 广东公布重点项目名单:茂名天源石化碳三碳四资源利用项目等[通俗易懂]

    广东公布重点项目名单:茂名天源石化碳三碳四资源利用项目等[通俗易懂]广东省发展改革委官网3月31日公布《广东省2021年重点建设项目计划》。2021年广东共安排省重点项目1395个,总投资达7.28万亿元,年度计划投资8000亿元。石化重大项目方面:新开工项目有3个,总投资约267亿元,年度投资约60.5亿元,分别是恒力石化(惠州)PTA项目、东华能源(茂名)烷烃资源综合利用项目(一期)、茂名天源石化碳三碳四资源利用项目。续建项目有7个,总投资超过1500亿元,2021年度投资约160.9亿元。分别是埃克森美孚惠州乙烯项目、惠州石化产品结构优化及升级项目、惠州宇新轻

    2022年10月15日
    3
  • 自己动手制作纯净版的WinPE_pe软件能自己制作吗

    自己动手制作纯净版的WinPE_pe软件能自己制作吗WinPE作为系统维护工具,已经必不可少,WinPE有很多版本,官方介绍的WinPE版本如下:WinPE1.0基于WinXPWinPE1.1基于WinXPSP1WinPE1.2基于Win

    2022年8月1日
    7
  • linux MySQL启动命令

    linux MySQL启动命令linux7:1、servicemysqlstartstopstatus2、/etc/init.d/mysqlstartstop…

    2022年5月21日
    47

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号