4keypad调试

一:基础知识
    硬件原理
    NORMAL_TYPE
        Row0--Row7
        KCOL0--KCOL7
    EXTEND_TYPE    
        Row0--Row3
        KCOL0 KCOL0_R
        KCOL1 KCOL1_R
        KCOL2-KCOL2_R 
    读建方式
        1.kernel 按键配置
            Set to Y to enable MTK keypad driver.
            • CONFIG_ONEKEY_REBOOT_NORMAL_MODE
            Set to Y to set long press reboot by power key on normal mode.
            • CONFIG_ONEKEY_REBOOT_OTHER_MODE
            Set to Y to set long press reboot by power key on other boot mode.
            • CONFIG_ KPD_PMIC_LPRST_TD
            Long press reset time
            0: 8 second, 1: 11 second, 2: 14 second, 3: 5 second
            CONFIG_KEYBOARD_MTK=y
            CONFIG_TWOKEY_REBOOT_NORMAL_MODE=y
            CONFIG_TWOKEY_REBOOT_OTHER_MODE=y
            CONFIG_KPD_PWRKEY_USE_PMIC=y
        2.按键采用中断 tasklet 轮询方式读键值   tasklet 在软件中断上下文中运行,所以 tasklet 代码必须是原子的
            static DECLARE_TASKLET(kpd_keymap_tasklet, kpd_keymap_handler, 0); //初始化 tasklet
            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;

                kpd_get_keymap_state(new_state);

                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++) {
                        mask = 1U << j;
                        if (!(change & mask))
                            continue;

                        hw_keycode = (i << 4) + j;
                        /* bit is 1: not pressed, 0: pressed */
                        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]; //可以打印log ,查看
                        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);
                    }
                }

                memcpy(kpd_keymap_state, new_state, sizeof(new_state));
                kpd_print("save new keymap state\n");
                enable_irq(kp_irqnr); //开中断
            }     
            static irqreturn_t kpd_irq_handler(int irq, void *dev_id)
            {
             disable_irq_nosync(kp_irqnr); //在中断处理函数中使用,不会阻塞;用于屏蔽相应中断;
             tasklet_schedule(&kpd_keymap_tasklet);        /能使系统在适当的时候进行调度 kpd_keymap_handler
             return IRQ_HANDLED;        //中断是由本设备引起的
            }
            static int kpd_pdrv_probe(struct platform_device *pdev)
            {
                kp_irqnr = irq_of_parse_and_map(pdev->dev.of_node, 0);
                kpd_set_debounce(kpd_dts_data.kpd_key_debounce);
                r = request_irq(kp_irqnr, kpd_irq_handler, IRQF_TRIGGER_NONE, KPD_NAME, NULL);

                mt_eint_register();  //开中断
            }
        3.按键从kernel到Android的过程
            ./kernel-3.18/drivers/input/keyboard/mediatek/kpd.c  驱动
                
            ./kernel-3.18/include/uapi/linux/input.h             kernel 键值
                #define KEY_VOLUMEUP        115
            ./device/teksun/hys6737m_35_m0/mtk-kpd.kl     linux和android key映射
                key 115   VOLUME_UP
            ./frameworks/base/data/keyboards/Generic.kl
            ./frameworks/base/data/keyboards/qwerty.kl
            进入system/usr/keylyout/可以看到所用的KL
            frameworks/native/include/android/keycodes.h
                AKEYCODE_VOLUME_UP       = 24,
            frameworks/base/core/java/android/view/KeyEvent.java  Java 键盘事件
              public static final int KEYCODE_VOLUME_UP       = 24;
            frameworks/base/core/res/res/values/attrs.xml
                  修改XML文件描述符
            ./frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
             public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
                    final boolean keyguardOn = keyguardOn();
                    final int keyCode = event.getKeyCode();
                    if (keyCode == KeyEvent.KEYCODE_VOLUME_UP){
                    
                    }
                }
二:按键配置
    key主要指:
    POWER   //此键固定。不需要配置
    VOLUMEUP        VOLUMEDOWN
    
    查看原理图:
            KROW0        
    KCOL0    VOLUMEUP
    KCOL1    VOLUMEDOWN
    
    在dws中配置:
            Column0        Column1        
    Row0    VOLUMEUP    VOLUMEDOWN

    
    在dws中配置gpio(L - IN ; R - OUT)
            EintMode|Def.Mode    M0|M1|M2|M3|M4|M5|M6|M7|InPull En|InPull SelHigh|Def.Dir|In|Out|OutHigh|VarName1
    GPIO81            0:KPROW0    1  1                                               OUT     0    1    0        GPIO_KPD_KROW0_PIN
    GPIO84            0:KPCOL0    1  1                      1          1                 IN          1    1    0        GPIO_KPD_KCOL0_PIN
    GPIO85            0:KPCOL1    1  1                      1          1                 IN          1    1    0        GPIO_KPD_KCOL1_PIN

    dws配置EINT : NC 即可
三:调试方法
    1.1 kernel查看驱动上报事件 - 驱动打印:
        cat /proc/kmsg | grep "key"
        应用层事件 - 
        logcat | grep KeyCode  
        logcat | grep KeyEvent    
        
    1.2 kernel查看event上报事件 - kernel打印:
        getevent -i     -- 查看所有输入子设备的信息
        getevent –t -l /dev/input/event0
    1.3 查看中断计数 (按下+1, 松开+1)
        cat /proc/interrupts | grep "mtk-kpd"

    1.4 查看dws配置out目录生成的文件是否对
        ./out/target/product/hys6737m_35_m0/obj/BOOTLOADER_OBJ/build-hys6737m_35_m0/inc/cust_kpd.h
        ./out/target/product/hys6737m_35_m0/obj/PRELOADER_OBJ/inc/cust_kpd.h
        修改dws中gpio口发现只 有lk和 pl 在out目录有生成
         #define KPD_INIT_KEYMAP()   \
         {   \
             [1] = KEY_VOLUMEUP,     \
             [2] = KEY_VOLUMEDOWN,       \
         }
四:GPIO 框架外的调试:
    中断方式
        1.1 dts 配置
            / {    mc_key: mc_key@0 {
                compatible = "mediatek,mc_key";  //定义设备
            };    
                };
                /*mc-key*/
                #define    __MC_KEY_EINT_PIN__                PINMUX_GPIO6__FUNC_GPIO6
                #define    __MC_KEY_EINT_EN_PIN_NUM__        6
                &mc_key{
                    interrupt-parent = <&eintc>;
                    interrupts = <__MC_KEY_EINT_EN_PIN_NUM__ IRQ_TYPE_EDGE_FALLING>;
                    debounce = <__MC_KEY_EINT_EN_PIN_NUM__ 100>;
                    status = "okay";
                };
                &pio {
                    mc_key_default:mc_key_default{
                    };
                    mc_key_as_int: mc_key_as_int@0{
                            pins_cmd_dat {
                                pins = <__MC_KEY_EINT_PIN__>;
                                slew-rate = <0>;
                                bias-pull-up = <00>;
                            };
                    };
                };
                &keypad{
                    pinctrl-names = "default", "mc_key_as_int";
                    pinctrl-0 = <&mc_key_default>;
                    pinctrl-1 = <&mc_key_as_int>;

                };
        1.2 获取dts配置值
            #ifdef CUST_EINT_WPS_PIN

            #define CUST_EINT_KPD_PWRKEY_MAP            KEY_F1

            struct device_node *node;
            int key_mc_irq;
            u32 intmc[2] = {0, 0};

            struct pinctrl *key_mc_pinctrl;
            struct pinctrl_state *key_mc_set_input;

            static u8 kpd_wpskey_state = !IRQ_TYPE_LEVEL_LOW;
            static void kpd_wpskey_handler(unsigned long data);
            static DECLARE_TASKLET(kpd_wpskey_tasklet, kpd_wpskey_handler, 0);

            int mc_key_get_gpio_info(struct platform_device *pdev){        //采用平台节点
                int ret;
                //printk("[%s] key_mc_pinctrl+++++++++++++++++\n",pdev->name);
                key_mc_pinctrl = devm_pinctrl_get(&pdev->dev);
                    

                key_mc_set_input = pinctrl_lookup_state(key_mc_pinctrl, "mc_key_as_int");
                
                if (IS_ERR(key_mc_set_input)) {
                    ret = PTR_ERR(key_mc_set_input);
                    dev_err(&pdev->dev, " Cannot find key mc  pinctrl key_mc_pinctrl!\n");
                    return ret;
                }
                if(!IS_ERR(key_mc_set_input)){
                    pinctrl_select_state(key_mc_pinctrl, key_mc_set_input);
                }    
                //printk("[%s] key_mc_pinctrl+++++++++++++++++\n",pdev->name);
                return 0;
            }
            int  eint_mc_init(void){
                //printk("%s, mc_key start\n",__func__);
                node = of_find_compatible_node(NULL, NULL, "mediatek,mc_key");
                if(node) {
                    of_property_read_u32_array(node, "debounce", intmc, ARRAY_SIZE(intmc));
                    gpio_set_debounce(intmc[0], intmc[1]);
                    key_mc_irq = irq_of_parse_and_map(node, 0);
                
                    if(request_irq(key_mc_irq, kpd_wpskey_eint_handler, IRQ_TYPE_EDGE_FALLING, "P_KEY", NULL)){
                    //printk(KERN_ERR "key_mc_irq EXAMPLE KEY_MC_IRQ LINE NOT AVAILABLE!!\n");
                        return -EINVAL;
                    }
                    enable_irq(key_mc_irq);
                    __set_bit(CUST_EINT_KPD_PWRKEY_MAP, kpd_input_dev->keybit);
                    //wps_set_init();
                }
                return 0;
            }
            1.3 软件代码
                void kpd_wpskey_handler_hal(unsigned long data){

                    bool pressed;

                    kpd_wpskey_state = !kpd_wpskey_state;
                    pressed = (kpd_wpskey_state == !!IRQ_TYPE_LEVEL_LOW);

                    printk(KPD_SAY "(%s) HW keycode = using wps EINT\n",
                           pressed ? "pressed" : "released");

                    input_report_key(kpd_input_dev, CUST_EINT_KPD_PWRKEY_MAP, pressed);
                    input_sync(kpd_input_dev);
                    //wps_report(pressed);
                    printk("report Linux keycode = %u\n", CUST_EINT_KPD_PWRKEY_MAP);

                    /* for detecting the return to old_state */
                    if(pressed){
                        irq_set_irq_type(key_mc_irq, IRQ_TYPE_LEVEL_HIGH);

                    }else{
                        irq_set_irq_type(key_mc_irq, IRQ_TYPE_LEVEL_LOW);
                    }
                    enable_irq(key_mc_irq);

                }

                static void kpd_wpskey_handler(unsigned long data)
                {
                    kpd_wpskey_handler_hal(data);
                }

                irqreturn_t kpd_wpskey_eint_handler(int irq, void *dev_id)
                {
                    disable_irq_nosync(key_mc_irq);
                    tasklet_schedule(&kpd_wpskey_tasklet);
                    return IRQ_HANDLED;
                }

            

    轮询方式 无中断的偏码器轮询
        1.1 dts 配置 
            #define __ZKT_ENCODER_A_PIN__  PINMUX_GPIO34__FUNC_GPIO34    //PINMUX_GPIO30__FUNC_GPIO30//PINMUX_GPIO34__FUNC_GPIO34
            #define __ZKT_ENCODER_B_PIN__  PINMUX_GPIO35__FUNC_GPIO35    //PINMUX_GPIO30__FUNC_GPIO30//PINMUX_GPIO35__FUNC_GPIO35
            #define __ZKT_ENCODER_C_PIN__  PINMUX_GPIO32__FUNC_GPIO32
            #define __ZKT_ENCODER_D_PIN__  PINMUX_GPIO33__FUNC_GPIO33
            &keypad {
                pinctrl-names = "pin_default", "zkt_encoder_a_input","zkt_encoder_b_input","zkt_encoder_c_input",
                                "zkt_encoder_d_input","zkt_speaker_output0";
                pinctrl-0 = <&zkt_pin_default>;
                pinctrl-1 = <&zkt_encoder_a_input>;
                pinctrl-2 = <&zkt_encoder_b_input>;
                pinctrl-3 = <&zkt_encoder_c_input>;
                pinctrl-4 = <&zkt_encoder_d_input>;
                pinctrl-5 = <&zkt_speaker_output0>;
            };
            &pio {
                zkt_pin_default: zkt_pin_default@0 {

                };
                zkt_encoder_a_input: zkt_pin@1 {
                    pins_cmd_dat {
                        pins = <__ZKT_ENCODER_A_PIN__>;
                        slew-rate = <0>;
                        bias-disable;
                    };
                };
                zkt_encoder_b_input: zkt_pin@2 {
                    pins_cmd_dat {
                        pins = <__ZKT_ENCODER_B_PIN__>;
                        slew-rate = <0>;
                        bias-disable;
                    };
                };
                zkt_encoder_c_input: zkt_pin@3 {
                    pins_cmd_dat {
                        pins = <__ZKT_ENCODER_C_PIN__>;
                        slew-rate = <0>;
                        bias-disable;
                    };
                };
                zkt_encoder_d_input: zkt_pin@4 {
                    pins_cmd_dat {
                        pins = <__ZKT_ENCODER_D_PIN__>;
                        slew-rate = <0>;
                        bias-disable;
                    };
                };
                
                zkt_speaker_output0: zkt_pin@5 {
                    pins_cmd_dat {
                        pins = <__ZKT_SPEARER_PIN__>;
                        slew-rate = <1>;
                        output-low;
                    };
                };
                zkt_speaker_output1: zkt_pin@56{
                    pins_cmd_dat {
                        pins = <__ZKT_SPEARER_PIN__>;
                        slew-rate = <1>;
                        output-high;
                    };
                };
            };
    1.2 dts解析
            struct pinctrl *zktpinctrl1;
            struct pinctrl_state         *zkt_encoder_input0, *zkt_encoder_input1,            //as input
                                        *zkt_encoder_input2, *zkt_encoder_input3;            //as input


            int zkt_key_get_gpio_info(struct platform_device *pdev)  //采用平台节点
            {
                    int ret;

                //printk("zktpinctrl+++++++++++++++++\n");
                zktpinctrl1 = devm_pinctrl_get(&pdev->dev);
                if (IS_ERR(zktpinctrl1)) {
                    ret = PTR_ERR(zktpinctrl1);
                    dev_err(&pdev->dev, "fwq Cannot find mc led  zktpinctrl1!\n");
                    return ret;
                }    
                zkt_encoder_input0 = pinctrl_lookup_state(zktpinctrl1, "zkt_encoder_a_input");
                if (IS_ERR(zkt_encoder_input0)) {
                    ret = PTR_ERR(zkt_encoder_input0);
                    dev_err(&pdev->dev, "zkt Cannot find led pinctrl zkt_encoder_input0!\n");
                    return ret;
                }
                zkt_encoder_input1 = pinctrl_lookup_state(zktpinctrl1, "zkt_encoder_b_input");
                if (IS_ERR(zkt_encoder_input1)) {
                    ret = PTR_ERR(zkt_encoder_input1);
                    dev_err(&pdev->dev, "zkt Cannot find led pinctrl zkt_encoder_input1!\n");
                    return ret;
                }
                zkt_encoder_input2 = pinctrl_lookup_state(zktpinctrl1, "zkt_encoder_c_input");
                if (IS_ERR(zkt_encoder_input2)) {
                    ret = PTR_ERR(zkt_encoder_input2);
                    dev_err(&pdev->dev, "zkt Cannot find led pinctrl zkt_encoder_input2!\n");
                    return ret;
                }
                zkt_encoder_input3 = pinctrl_lookup_state(zktpinctrl1, "zkt_encoder_d_input");
                if (IS_ERR(zkt_encoder_input3)) {
                    ret = PTR_ERR(zkt_encoder_input3);
                    dev_err(&pdev->dev, "zkt Cannot find led pinctrl zkt_encoder_input3!\n");
                    return ret;
                }
                return 0;
            }
    1.3 软件代码


            static struct timer_list buttons_timer;
            static unsigned int pinval=0x0f,pinvalold=0xff;
            static unsigned int key_val=KEY_1; 
            static unsigned int key_table[]={
                9,//5,
                1,//4,
                6,//10,
                12,//3,
                10,//9,
                2,//8,
                5,//6,
                13,//7,
                15,//15,
                11,//13,
                0,//0,
                4,//2,
                14,//11,
                8,//1,
                3,//12,
                7,//14
            };
            static unsigned int key_report_val_table[] = {
                KEY_1,//1
                KEY_2,//2
                KEY_3,//3
                KEY_4,//4
                KEY_5,//5
                KEY_6,//6
                KEY_7,//7
                KEY_8,//8
                KEY_9,//9
                KEY_A,//10
                KEY_B,//11
                KEY_C,//12
                KEY_D,//13
                KEY_E,//14
                KEY_F,//15
                KEY_G,//16
            };
            void input_report_encoder(unsigned int temp)
            {
                unsigned int i,key_report_val;
                for(i=0;i<(sizeof(key_table)/4);i++)        //按键转换需要的值
                {
                    if(temp==key_table[i])
                    {
                        break;
                    }
                }
                key_report_val=key_report_val_table[i];            //上报
                key_val=key_report_val;
                input_report_key(kpd_input_dev, key_report_val, 1);
                input_sync(kpd_input_dev);
                input_report_key(kpd_input_dev, key_report_val, 0);
                input_sync(kpd_input_dev);    
            }
            #define GPIO_ENCODER_A_PIN 34//GPIO30
            #define GPIO_ENCODER_B_PIN 35//GPIO31
            #define GPIO_ENCODER_C_PIN 32//GPIO32
            #define GPIO_ENCODER_D_PIN 33//GPIO33
            static void buttons_timer_function(unsigned long data)  
            {  
            unsigned int pinvala=0,pinvalb=0,pinvalc=0,pinvald=0; 
                //读键值
                pinvala = gpio_get_value(GPIO_ENCODER_A_PIN); 

                pinvalb = gpio_get_value(GPIO_ENCODER_B_PIN); 

                pinvalc = gpio_get_value(GPIO_ENCODER_C_PIN); 

                pinvald = gpio_get_value(GPIO_ENCODER_D_PIN); 


                //printk("[ENCODER] buttons_timer_function\n");

                //printk("[ENCODER] pinvala = %d ,pinvalb = %d ,pinvalc = %d ,pinvald = %d \n", pinvala, pinvalb,pinvalc,pinvald);
                pinval = (pinvala|(pinvalb<<1)|(pinvalc<<2)|(pinvald<<3));
                //printk("[ENCODER] pinval = %d \n",pinval);
                //printk("[ENCODER] key_report_val_table = %d ,key_table = %d  \n", (sizeof(key_report_val_table)/4), (sizeof(key_table)/4));
    
                    
                    if(pinval!=pinvalold){
                        input_report_encoder(pinval);//上报
                    }
                pinvalold=pinval;

                mod_timer(&buttons_timer, jiffies + (HZ /5));  
                //buttons_timer=jiffies + (HZ /10);
                //add_timer(&buttons_timer);
            }  


            void encoder_driver_init(void)
            {
                unsigned int i ;
                if((!IS_ERR(zkt_encoder_input0))&(!IS_ERR(zkt_encoder_input1))&(!IS_ERR(zkt_encoder_input2))&(!IS_ERR(zkt_encoder_input3))){
                    pinctrl_select_state(zktpinctrl1, zkt_encoder_input0);
                    pinctrl_select_state(zktpinctrl1, zkt_encoder_input1);
                    pinctrl_select_state(zktpinctrl1, zkt_encoder_input2);
                    pinctrl_select_state(zktpinctrl1, zkt_encoder_input3);
                }    
                for(i=0;i<(sizeof(key_report_val_table)/4);i++) //unsigned int 4个字节
                {
                    __set_bit(key_report_val_table[i], kpd_input_dev->keybit); //set_bit()告诉input输入子系统支持哪些事件,哪些按键。例如:
                }

                init_timer(&buttons_timer);  
                buttons_timer.function = buttons_timer_function;  
                add_timer(&buttons_timer);  

                /* 修改定时器定时时间,定时10ms,即10秒后启动定时器 
                 * HZ 表示100个jiffies,1 jiffies的单位为10ms,即HZ = 100*10ms = 1s 
                 * 这里HZ/10即定时100ms 
                 */ 
                mod_timer(&buttons_timer, jiffies + (HZ /1));  

            }
五:调试案例
案例一:    采用外部扩展按键   按键无反应
    现象: 所有按键无反应
    平台: android6.0,MTK6737M
    步骤: 
        1.1 查看dws .配置正确
        1.2 查看input注册
            getevent -i /dev/input/event0
            有注册 
        1.3 查看input设备上报
            getevent -l /dev/input/event0
            无上报
        1.4 查看input中断有无触发 
            cat /proc/interrupts | grep "mtk-kpd"
            无触发
            
        1.5 查看GPIO 寄存器 
            cat /sys/devices/virtual/misc/mtgpio/pin
            正确
        1.6 按Column键触发 GPIO 寄存器是否有变化
            cat /sys/devices/virtual/misc/mtgpio/pin
            无变化 ,怀疑是硬件问题
        1.7 用万用表量     Column GPIO 口
            无变化 确定是硬件问题
            
            经硬件检测,是在Column增加了一个1K限流电阻.在不扩展的的按键是正常能电压变化
            但扩展后, 电压给限制了,不变化,换0欧电阻,正常。
案例二:    某个按键无反应
        现象: 按键无反应。
        修改 代码
            static int kpd_pdrv_probe(struct platform_device *pdev)
            {    
             ...
            +#if 0
            #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;                                                    //会关了支持一些按键 正常的都会是9*n都是power键
            #endif
                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;
                }
            +#endif
                for (i = 0; i < KPD_NUM_KEYS; i++) {
                    if (kpd_keymap[i] != 0)
                        __set_bit(kpd_keymap[i], kpd_input_dev->keybit);
                }
            ...
            }
            或者在Column7上不要配置按键

你可能感兴趣的:(MTK)