mtk-keypad[通俗易懂]

mtk-keypad[通俗易懂]一.keypad基本原理col作为输出,row作为输入检测,低电平有效colA~D轮流输出低电平,通过rol1~4上的低电平可以检测是哪个按键按下了但是存在这样的问题,A1,A2,B1同时按下,会造成B2按下的假象,称为鬼影(这3个键导通,colB打开,row2处也会检测到低电平)可以通过增加二极管的方式防止鬼影问题二.keypadporti…

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

一.keypad基本原理

mtk-keypad[通俗易懂]

col作为输出,row作为输入检测,低电平有效
col A~D轮流输出低电平,通过rol 1~4上的低电平可以检测是哪个按键按下了
但是存在这样的问题,A1,A2,B1同时按下,会造成B2按下的假象,称为鬼影(这3个键导通,colB打开,row2处也会检测到低电平)

mtk-keypad[通俗易懂]

可以通过增加二极管的方式防止鬼影问题

二.keypad porting

  • 1.dws中GPIO设置,mtk将ROW作为输出,COL作为输入检测,preloader的keypad.c文件中对keypad进行了设置
    •   KCOL:input + pull enable + pull up
    •        KROW:output + pull disable + pulldown

mtk-keypad[通俗易懂]

mtk-keypad[通俗易懂]

  • 2.dws keypad设置,mtk6750最多支持2*2按键矩阵,通过下拉框选择相应的按键,按键name、对应键值[Key_code_linux]在Keypad_YuSu.cmp这个文件中有定义

mtk-keypad[通俗易懂]

  • 3.添加新的按键参考FAQ13931

三.keypad代码分析

生成的keypad设备树节点信息如下,kpd-hw-init-map将键盘矩阵以一维数组(键值)的格式表示,一共72个

keypad@10010000 {
            compatible = "mediatek,mt6755-keypad", "mediatek,kp";
            reg = <0x10010000 0x1000>;
            interrupts = <0x0 0xa4 0x2>;
            mediatek,kpd-key-debounce = <0x400>;
            mediatek,kpd-sw-pwrkey = <0x74>;
            mediatek,kpd-hw-pwrkey = <0x8>;
            mediatek,kpd-sw-rstkey = <0x73>;
            mediatek,kpd-hw-rstkey = <0x11>;
            mediatek,kpd-use-extend-type = <0x0>;
            mediatek,kpd-hw-map-num = <0x48>;
            mediatek,kpd-hw-init-map = <0x72 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>;
            mediatek,kpd-pwrkey-eint-gpio = <0x0>;
            mediatek,kpd-pwkey-gpio-din = <0x0>;
            mediatek,kpd-hw-dl-key0 = <0x11>;
            mediatek,kpd-hw-dl-key1 = <0x0>;
            mediatek,kpd-hw-dl-key2 = <0x8>;
            mediatek,kpd-hw-recovery-key = <0x11>;
            mediatek,kpd-hw-factory-key = <0x0>;
            status = "okay";
        };

static int kpd_pdrv_probe(struct platform_device *pdev)
{

    int i, r;
    int err = 0;
    struct clk *kpd_clk = NULL;
//获取clk,这里kpd-clk是通过ccf设置的
    kpd_clk = devm_clk_get(&pdev->dev, "kpd-clk");
    if (!IS_ERR(kpd_clk)) {
        int ret_prepare, ret_enable;

        ret_prepare = clk_prepare(kpd_clk);
        if (ret_prepare)
            kpd_print("clk_prepare returned %d\n", ret_prepare);
        ret_enable = clk_enable(kpd_clk);
        if (ret_enable)
            kpd_print("clk_enable returned %d\n", ret_prepare);
    } else {
        kpd_print("get kpd-clk fail, but not return, maybe kpd-clk is set by ccf.\n");
    }
//reg重映射
    kp_base = of_iomap(pdev->dev.of_node, 0);
    if (!kp_base) {
        kpd_info("KP iomap failed\n");
        return -ENODEV;
    };
//irq映射
    kp_irqnr = irq_of_parse_and_map(pdev->dev.of_node, 0);
    if (!kp_irqnr) {
        kpd_info("KP get irqnr failed\n");
        return -ENODEV;
    }
//申请input设备
    kpd_input_dev = input_allocate_device();
    if (!kpd_input_dev) {
        kpd_print("input allocate device fail.\n");
        return -ENOMEM;
    }
//input设备初始化
    kpd_input_dev->name = KPD_NAME;
    kpd_input_dev->id.bustype = BUS_HOST;
    kpd_input_dev->id.vendor = 0x2454;
    kpd_input_dev->id.product = 0x6500;
    kpd_input_dev->id.version = 0x0010;
    kpd_input_dev->open = kpd_open;
//解析dts中keypad节点的信息,赋值给kpd_dts_data结构体
    kpd_get_dts_info(pdev->dev.of_node);
//分配内存空间,用于存放键值和按键状态寄存器
    kpd_memory_setting();
//input设备支持EV_KEY事件
    __set_bit(EV_KEY, kpd_input_dev->evbit);
//powerkey连接PMIC,kpd_keymap[8]设置为空 #if defined(CONFIG_KPD_PWRKEY_USE_EINT) || defined(CONFIG_KPD_PWRKEY_USE_PMIC)
    __set_bit(kpd_dts_data.kpd_sw_pwrkey, kpd_input_dev->keybit);
    kpd_keymap[8] = 0;
#endif
//powerkey列除[8]外,其余都清空
if (!kpd_dts_data.kpd_use_extend_type) { for (i = 17; i < KPD_NUM_KEYS; i += 9) /* only [8] works for Power key */ kpd_keymap[i] = 0; }
//设置设备支持的键值
for (i = 0; i < KPD_NUM_KEYS; i++) { if (kpd_keymap[i] != 0) __set_bit(kpd_keymap[i], kpd_input_dev->keybit); } //reset按键 if (kpd_dts_data.kpd_sw_rstkey) __set_bit(kpd_dts_data.kpd_sw_rstkey, kpd_input_dev->keybit); kpd_input_dev->dev.parent = &pdev->dev;
//注册input设备 r
= input_register_device(kpd_input_dev); if (r) { kpd_info("register input device failed (%d)\n", r); input_free_device(kpd_input_dev); return r; } /* register device (/dev/mt6575-kpd) */ kpd_dev.parent = &pdev->dev; r = misc_register(&kpd_dev); if (r) { kpd_info("register device failed (%d)\n", r); input_unregister_device(kpd_input_dev); return r; } //初始化wake_lock wake_lock_init(&kpd_suspend_lock, WAKE_LOCK_SUSPEND, "kpd wakelock"); //设置按键消抖并申请中断处理 kpd_set_debounce(kpd_dts_data.kpd_key_debounce); r = request_irq(kp_irqnr, kpd_irq_handler, IRQF_TRIGGER_NONE, KPD_NAME, NULL); if (r) { kpd_info("register IRQ failed (%d)\n", r); misc_deregister(&kpd_dev); input_unregister_device(kpd_input_dev); return r; } #ifndef KPD_EARLY_PORTING /*add for avoid early porting build err the macro is defined in custom file */ long_press_reboot_function_setting(); /* /API 4 for kpd long press reboot function setting */ #endif hrtimer_init(&aee_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); aee_timer.function = aee_timer_func; //添加文件属性 err = kpd_create_attr(&kpd_pdrv.driver); if (err) { kpd_info("create attr file fail\n"); kpd_delete_attr(&kpd_pdrv.driver); return err; } kpd_info("%s Done\n", __func__); return 0; }

看下中断处理函数的内容:

static irqreturn_t kpd_irq_handler(int irq, void *dev_id)
{
//禁止中断,无需进行同步,防止死锁
    disable_irq_nosync(kp_irqnr);
//调度tasklet tasklet_schedule(
&kpd_keymap_tasklet); return IRQ_HANDLED; }

//定义tasklet,执行kpd_keymap_handler函数
static
DECLARE_TASKLET(kpd_keymap_tasklet, kpd_keymap_handler, 0);

static void kpd_keymap_handler(unsigned long data)
{
    int i, j;
    bool pressed;
    u16 new_state[KPD_NUM_MEMS], change, mask;
    u16 hw_keycode, linux_keycode;
//mtk通过5组寄存器来保存按键的状态,这里回读寄存器并保存为new_state
    kpd_get_keymap_state(new_state);
//激活锁唤醒系统,500ms后就释放掉
    wake_lock_timeout(&kpd_suspend_lock, HZ / 2);

    for (i = 0; i < KPD_NUM_MEMS; i++) {
    
    
//每组中按键状态未改变则对比下一组,按位处理 change
= new_state[i] ^ kpd_keymap_state[i]; if (!change) continue; for (j = 0; j < 16; j++) {
//每组(16位)中对比按位查看是否状态发生改变 mask
= 1U << j; if (!(change & mask)) continue; hw_keycode = (i << 4) + j; /* bit is 1: not pressed, 0: pressed */ //按键是否按下,寄存器中0表示按键处于按下状态
pressed
= !(new_state[i] & mask); if (kpd_show_hw_keycode) kpd_print("(%s) HW keycode = %u\n", pressed ? "pressed" : "released", hw_keycode); BUG_ON(hw_keycode >= KPD_NUM_KEYS); linux_keycode = kpd_keymap[hw_keycode]; if (unlikely(linux_keycode == 0)) { kpd_print("Linux keycode = 0\n"); continue; } kpd_aee_handler(linux_keycode, pressed); //上报键值 input_report_key(kpd_input_dev, linux_keycode, pressed); input_sync(kpd_input_dev); kpd_print("report Linux keycode = %u\n", linux_keycode); } } //kpd_keymap_state保存new_state,用于下轮对比 memcpy(kpd_keymap_state, new_state, sizeof(new_state)); kpd_print("save new keymap state\n");
//按键处理完毕,打开中断 enable_irq(kp_irqnr); }

 

转载于:https://www.cnblogs.com/ant-man/p/9204977.html

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

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

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


相关推荐

  • 程序心形曲线绘制_java输出心形图案

    程序心形曲线绘制_java输出心形图案代码如下:importjava.awt.Color;importjava.awt.Graphics;importjava.awt.Image;importjava.awt.Toolkit;importjavax.swing.JFrame;@SuppressWarnings(“serial”)publicclassDemoextendsJFrameimplementsRunna…

    2022年10月16日
    2
  • Vue上传文件操作(没有CV,认真看)

    Vue上传文件操作(没有CV,认真看)项目场景: 通过vue上传文件基本操作问题描述:使用html上传文件时,很容易理解,那么vue文件上传呢?我们学了vue不可能还往里面写原生html的内容吧!先放代码再解释:<template><div><el-form:model=”form”><el-uploadaction=”url”:auto-upload=”false”:on-change=”onchanger”:fil

    2022年10月10日
    2
  • Python 函数 | map 函数详解

    Python 函数 | map 函数详解博友们 你们好 很高兴能在这里和大家分享技术文章 我是 AmoXiang 是一名在职企业员工 Amo 来源于上家公司的英文名 本意是爱 作为刚入行几年互联网行业的小白 博主写博客一方面是为了记录自己的学习过程以及所犯的错误 更多的是希望能够帮助到很多处于起步阶段的萌新 让他们少走弯路 在编写文章时 参考了大量书籍及一些互联网大佬的资源 并结合了自己的工作经验 博主本着科学 严谨的态度 力求精益求精 但疏漏之处在所难免 敬请广大博友批评指正 博客主页 https blog csdn net xw16

    2025年12月3日
    4
  • c++程序设计基础(第五版)(上) 习题与解答

    c++程序设计基础(第五版)(上) 习题与解答习题与解答C++程序设计基础(第5版)(上)习题与解答第1章练习题同步练习1.1一、选择题1.一个最简单的C++程序,可以只有一个()。(A)库函数 (B)自定义函数 (C)main函数 (D)空函数2.函数名是(),用于识别和调用函数。(A)关键字 (B)标识符 (C)常数 (D)语句3.用C++语言编写的源程序要成为目标程序必须要经过()…

    2022年4月28日
    97
  • winrar下载_标致408后尾晃完美解决方案

    winrar下载_标致408后尾晃完美解决方案本文参考撸Linux的文章https://www.lulinux.com/archives/13192018年wineQQ最完美解决方案(多Linux发行版通过测试并稳定运行)腾讯官方早就在10年代初就停止运营Linux版QQ,然而民间折腾Linux版QQ的脚步从来没停过。时至今日,它总算有了完美无暇的方案——deepinwine系列。要感谢deepin公司的最大努力。也…

    2025年9月16日
    4
  • redis如何设置密码及验证密码_无线密码忘记了怎么在手机上查看

    redis如何设置密码及验证密码_无线密码忘记了怎么在手机上查看一、前言对于redis而言,其并没有实现访问控制这个功能,但是可以提供一个轻量级的auth认证方式。可以通过编辑对应的redis配置文件。redis.conf来启动二、设置密码1、找到redis的配置文件redis.conf配置文件中的参数:requirepass,就是配置redis访问密码的参数;#默认情况下,是注释的requirepassxxxx;设置requirepass密码如下。2、然后需要重启下redis服务,才能生效#1、kill掉redis进程#2、启动re

    2025年9月20日
    5

发表回复

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

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