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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • JS中判断数组中是否包含某个元素indexof兼容性兼容性

    JS中判断数组中是否包含某个元素indexof兼容性兼容性1.前几天写了一个JS游戏,其中,判断数组中是否包含某个元素,开始使用如下方法判断if(appearAnimals.indexOf(randIndex)==-1){}目前主流的浏览器都能正常显示,但是遇到IE9以下版本就不行,通过逐行排查,才发现是indexOf的兼容性问题。IE9以前的版本都不支持此方法,现在写一个兼容的方法如下:if(!Array.indexOf){

    2022年10月19日
    3
  • CNN垃圾分类_垃圾分类卡通画

    CNN垃圾分类_垃圾分类卡通画基于TensorFlow和Keras的垃圾分类模型本篇博客主要介绍基于TensorFlow和Keras实现垃圾分类模型,目前是一篇占坑的博客,由于该项目目前用于参加比赛,因此暂时不能提供代码,感兴趣的可以私信我一起交流,识别结果如下所示:

    2022年10月6日
    4
  • [日常] Go语言圣经-匿名函数习题

    [日常] Go语言圣经-匿名函数习题

    2021年5月26日
    97
  • 嵌入式软件架构设计

    嵌入式软件架构设计摘要在开发中一直觉得好的软件架构可以做到事半功倍 而且可以做到代码复用和移植 但是如果没有架构或者说架构很差 那么移植将是很痛苦的事 特别是对不熟悉改功能的人来讲还不如自己写呢 移植不对那将是很头疼的事 所以随着开发越来越多 渐渐的发现我们考虑问题应该从广度和深度来考虑 做新项目的时候 更应该考虑到以后出现的可能性 比如说需求变更 底层变更 所以这个时候软件的架构和程序模块化就很重要

    2025年8月10日
    0
  • 锂电池3V升12V(AH6901)低成本替代6291升压芯片

    锂电池3V升12V(AH6901)低成本替代6291升压芯片锂电池3V升12V(AH6901)低成本替代6291升压芯片应用于数码相机,移动电源,蓝牙音箱,插卡音箱、LED台灯便捷DVD,MID,移动电话等数码产品AH6901是一款小封装(SOT23-6)、CC(恒流)模式的PWM升压IC,适用于锂电池(3~4.2V)输出5V,1A的移动电源应用。AH6901输入电压范围可由最低2.6伏特到最高6伏特,输出电压3.3–20V可调整且内部MOS输出开关电流可高达2A,封装为SOT23-6,工作频率为1MHZ,可以搭配3.3uh小型贴片电感,减少成品体积,非常适

    2022年10月6日
    4
  • asp.net(c#)网页跳转几种方法小结「建议收藏」

    asp.net(c#)网页跳转几种方法小结「建议收藏」在asp.net下,经常需要页面的跳转,下面是具体的几种方法。跳转页面是大部编辑语言中都会有的,正面我们来分别介绍一下关于.net中response.redirectsever.executeserver.transfer三种页面跳转的方法①response.redirect这个跳转页面的方法跳转的速度不快,因为它要走2个来回(2次postback),但他可以跳转到任何页面,没有站点页

    2022年7月20日
    17

发表回复

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

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