keypad driver

    技术2022-05-18  20

    键盘是6x6矩阵式,在网上下了对应的PATCH,下载地址是

    https://patchwork.kernel.org/patch/71857/

     

    这个补丁会创建两个文件

    arch/arm/plat-mxc/include/mach/mxc_keypad.h //mxc_keypad_platform_data键盘平台设备的结构体

    /drivers/input/keyboard/mxc_keypad.c //驱动实现文件

    打好补丁后,会发现这个驱动是一个通用的驱动,在mx21,25,27,31等板上都可以用,所以要自己实现相应的keymap,自己实现注册键盘设备,还有注册键盘相应的时钟。

    1.添加键盘设备:

    2.6.32内核源码里没有对mx21键盘的支持,所以我们要自己添加键盘的结构体

    所以在arch/arm/mach-mx2/device.c里,加上

    static struct resource mxc_keypad_resources[] = {         {                .start = KPP_BASE_ADDR,                 .end = KPP_BASE_ADDR + SZ_4K - 1,                .flags = IORESOURCE_MEM,        },{                .start = MXC_INT_KPP,                .end = MXC_INT_KPP,                .flags = IORESOURCE_IRQ,        },};

    struct platform_device mxc_keypad_device = {         .name = "mxc-keypad", //这个名字必须和驱动里的名字相对应        .id = 0,        .num_resources = ARRAY_SIZE(mxc_keypad_resources),        .resource = mxc_keypad_resources,};接下来在arch/arm/mach-mx2/device.h里最后一行加上结构体的声明

    extern struct platform_device mxc_spi_device1;extern struct platform_device mxc_spi_device2;extern struct platform_device mxc_keypad_device;然后进入到arch/arm/mach-mx2/mx21ads.c里

    在注册的时候,添加键盘对应的注册函数,找到mxc_register_device函数,添加上

    static void __init mx21ads_board_init(void){        mxc_gpio_setup_multiple_pins(mx21ads_pins, ARRAY_SIZE(mx21ads_pins),                        "mx21ads");

            mxc_register_device(&mxc_uart_device0, &uart_pdata);        mxc_register_device(&mxc_uart_device2, &uart_norts_pdata);        mxc_register_device(&mxc_uart_device3, &uart_pdata);        mxc_register_device(&mxc_fb_device, &mx21ads_fb_data);        mxc_register_device(&mxc_sdhc_device0, &mx21ads_sdhc_pdata);        mxc_register_device(&mxc_nand_device, &mx21ads_nand_board_info);        mxc_register_device(&mxc_keypad_device, &keypad_data); //keypad_data是struct mxc_keypad_platform_data结构体,需要我们自己完成,在第二步里我会具体说明

            platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));}

    2.添加平台设备的数据(即定义keypad_data)

    首先,根据你的键盘的实际情况,设置好keymap数组,这里我建立一个文件"mxc_keymap.h",它是键盘的keymap数组,表明了键盘的实际按键排列情况。

    #ifndef __MXC_KEYMAP_H__

    #define __MXC_KEYMAP_H__

    static unsigned int keymap[48] = {     //这里定义大小为48不是说有48个按键,由于在补丁的驱动实现文件mxc_keypad.c里,在建立扫描码对应的键值时,每行默认为8个按键,所以这里每行都有两个是保留键。当然具体实现你也可以修改,在mxc_keypad.c里做相应变动即可        KEY_F1,                  //EXTRA 5        KEY_MACRO,               //#        KEY_0,                  //0        KEY_KPASTERISK,         //*        KEY_F8,                 //RECORD        KEY_POWER,              //POWER        KEY_RESERVED,        KEY_RESERVED,        KEY_F2,                 //EXTRA 4        KEY_9,                  //9        KEY_8,        KEY_7,        KEY_F9,                 //EXTRA 1        KEY_VOLUMEDOWN,        KEY_RESERVED,        KEY_RESERVED,        KEY_F3,                 //EXTRA 3        KEY_6,        KEY_5,        KEY_4,        KEY_F10,                //APP 4        KEY_VOLUMEUP,        KEY_RESERVED,        KEY_RESERVED,        KEY_F4,                 //EXTRA 2        KEY_3,        KEY_2,        KEY_1,        KEY_F11,                //APP 3        KEY_DOWN,               //DOWN        KEY_RESERVED,        KEY_RESERVED,        KEY_BACKSPACE,          //BACK        KEY_RIGHT,              //RIGHT        KEY_F6,                 //ACTION        KEY_LEFT,               //LEFT        KEY_HOME,               //HOME        KEY_F16,                //APP 2        KEY_RESERVED,        KEY_RESERVED,        KEY_END,                //END        KEY_F5,                 //KEY 2        KEY_UP,                 //UP        KEY_F7,                 //KEY 1        KEY_F12,                //SEND        KEY_F17,                //APP 1        KEY_RESERVED,        KEY_RESERVED,};

    #endif /*MXC_KEYMAP_H*/

    接下来,进入arch/arm/mach-mx2/mx21ads.c,首先添加包含文件mxc_keypad.h,然后添加静态结构体变量并初始化。

    在文件开头包含文件的地方添加上

    #include <mach/mxc_keypad.h>

    然后定义静态结构体keypad_data

    static struct mxc_keypad_platform_data keypad_data = {        .matrix_key_rows = 6, //实际用到的行,MX21键盘模块最大支持8x8,但我这里只用到了6x6        .matrix_key_cols = 6, //实际用到的列        .matrix_key_map = keymap, //按键映射数组        .matrix_key_map_size = 48, //映射的按键数        .debounce_ms = SAMPLE_PERIOD, //消除抖动的延时,这里我设置的是20ms,可以根据实际情况修改};

    3.时钟注册

    查看arch/arm/mach-mx2/clock-mx21.c

    可以发现其实键盘的时钟结构体已经注册过了,

            _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk)        _REGISTER_CLOCK("mxc-keypad", NULL, kpp_clk)       

    但是在mxc_keypad.c里获得时钟的时候,调用clk_get的参数与注册的时钟名不匹配,

    keypad->clk = clk_get(NULL, "kpp");        if (IS_ERR(keypad->clk)) {                dev_err(&pdev->dev, "failed to get keypad clock/n");                error = PTR_ERR(keypad->clk);                goto failed_free_io;        }这里有两种方法,第一修改_REGISTER_CLOCK("mxc-keypad",NULL,kpp_clk)

    改成 _REGISTER_CLOCK(NULL,"kpp",kpp_clk);

    第二种是修改mxc_keypad.c里的clk_get的参数,改成 clk_get("mxc-keypad",NULL);

    个人倾向于第一种,呵呵,改驱动的代码万一改错麻烦就大得多了。

    ----------------------------------------------

    这里我以为已经改好了,所以写了个测试的应用程序keytest来测试

    #include <stdio.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <signal.h>#include <linux/input.h>

    int fd;int loop_flag = 1;

    void exit_loop(){        loop_flag--;}

    void deal_int(int signum){        if(fd > 0)                close(fd);        exit_loop();}

    int main(){        struct input_event evt;

            fd = open("/dev/input/event0",O_RDWR);

            if(fd < 0)                printf("Can not open/n");

            while(loop_flag > 0)        {                read(fd,&evt,sizeof(struct input_event));                switch(evt.type)                {                        case EV_KEY:                                printf("Key -->/nCode: %d/nValue: %d/n",evt.code,evt.value);                        break;                        case EV_MSC:                              printf("MSC -->/nCode: %d/nValue: %d/n",evt.code,evt.value);                        break;                        case EV_SYN:                                printf("<-- /n");                        default:                                printf(".../n");                }        }

            return 0;}

    启动板子,发现键盘设备被识别,而且/dev/input/下有event0,

    cat sys/class/input/input0/name得到mxc-keypad

    说明驱动正常,设备也注册好了

    所以使用keytest来测试,结果发现没有发生EV_KEY,事件,这说明input_report_key函数没有成功

    查看内核源码,发现input_report_key()实际上调用的是input_event(),这个函数首先会检测合法位,也就是说会检测按键的code是否已经添加到了struct input_dev的keybit里(input_dev->keybit)

    所以回到驱动实现文件mxc_keypad.c里

    发现static void mxc_keypad_build_keycode(struct mxc_keypad *keypad)这个函数在添加扫描码的时候,使用循环来处理

    for (i = 0; i < pdata->matrix_key_map_size; i++) {                unsigned int key = pdata->matrix_key_map[i];                unsigned int row = KEY_ROW(key);                unsigned int col = KEY_COL(key);                unsigned int scancode = MATRIX_SCAN_CODE(row, col,                                                         MATRIX_ROW_SHIFT);

                    keycode = KEY_VAL(key);                keypad->keycodes[scancode] = keycode;                __set_bit(keycode, input_dev->keybit);        }发现错在KEY_ROW和KEY_COL这两个宏,查看include/linux/matrix_keypad.h

    KEY_ROW(k) ( ((k) >> 24) & 0xff )

    KEY_COL(k)   (((k) >> 16) & 0xff)

    也就是说这两个宏决定行列的规则是行是键值的高8位,列是键值的次高8位

    查看include/linux/input.h,发现相关的KEY_*(KEY_0,KEY_UP等)的值都没有超过255,这样确定行列时,就不能使用这两个宏了,所以我注释了原来的代码,自己修改成了

    /* MOD---         for (i = 0; i < pdata->matrix_key_map_size; i++) {                unsigned int key = pdata->matrix_key_map[i];                unsigned int row = KEY_ROW(key);                unsigned int col = KEY_COL(key);                unsigned int scancode = MATRIX_SCAN_CODE(row, col,                                                         MATRIX_ROW_SHIFT);

                    keycode = KEY_VAL(key);                keypad->keycodes[scancode] = keycode;                __set_bit(keycode, input_dev->keybit);        }*/        unsigned int row,col;        unsigned int key;        unsigned int scancode;

            row = col = 0;        for(i = 0;i < pdata->matrix_key_map_size;i++)        {                key = pdata->matrix_key_map[i];                scancode = MATRIX_SCAN_CODE(row,col,MATRIX_ROW_SHIFT);                col++;                if(col == MAX_MATRIX_KEY_COLS)                {                        col = 0;                        row++;                }   

                    key = KEY_VAL(key);                keypad->keycodes[scancode] = key;                __set_bit(key,input_dev->keybit);        }这里如果哪位有更好的方法欢迎留言,因为我觉得这个方法不是很好。

    重新编译,启动板子,运行keytest程序


    最新回复(0)