一.UT4412BV03开发板外接了一个3*3的矩阵键盘,通过对矩阵键盘的驱动的编写来实现按键的功能linux内核中已经包含了一个矩阵键盘的驱动,用户只需要简单修改就能实现按键的功能。 UT4412BV03矩阵键盘原理图 如图:在默认倩况下,矩阵键盘的行线被电阻上拉到高电平。如果某个按键被按下则其对应的行线和列线都为低电平 软件流程图 二.矩阵键盘驱动分析 1. 矩阵键盘对应的IO管脚的初始化 linux/arch/arm/mach-exynos/setup-keypad.c void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols) { if (rows > 8) { /* Set all the necessary GPX2 pins: KP_ROW[0~7] */ s3c_gpio_cfgall_range(EXYNOS4_GPX2(0), 8, S3C_GPIO_SFN(3), S3C_GPIO_PULL_UP); /* Set all the necessary GPX3 pins: KP_ROW[8~] */ s3c_gpio_cfgall_range(EXYNOS4_GPX3(0), (rows - 8), S3C_GPIO_SFN(3), S3C_GPIO_PULL_UP); } else { /* Set all the necessary GPX2 pins: KP_ROW[x] */ s3c_gpio_cfgall_range(EXYNOS4_GPX2(0), rows, S3C_GPIO_SFN(3), S3C_GPIO_PULL_UP); } /* Set all the necessary GPX1 pins to special-function 3: KP_COL[x] */ s3c_gpio_cfgrange_nopull(EXYNOS4_GPX1(0), cols, S3C_GPIO_SFN(3)); } 2.矩阵键盘平台设备和平台资源的初始化 linux/arch/arm/plat-samsung/dev-keypad.c static struct resource samsung_keypad_resources[] = { [0] = { .start = SAMSUNG_PA_KEYPAD, .end = SAMSUNG_PA_KEYPAD + 0x20 - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_KEYPAD, .end = IRQ_KEYPAD, .flags = IORESOURCE_IRQ, }, }; struct platform_device samsung_device_keypad = { .name = "samsung-keypad", .id = -1, .num_resources = ARRAY_SIZE(samsung_keypad_resources), .resource = samsung_keypad_resources, }; 3. SamSung-keypd.c矩阵键盘驱动分析 //填充一个平台驱动结构体 static struct platform_driver samsung_keypad_driver = { .probe = samsung_keypad_probe, //按键探测函数,驱动注册后最先执行的函数 .remove = __devexit_p(samsung_keypad_remove), .driver = { .name = "samsung-keypad", //平台驱动的名字 .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &samsung_keypad_pm_ops, #endif }, .id_table = samsung_keypad_driver_ids, }; //注册一个平台驱动 static int __init samsung_keypad_init(void) { return platform_driver_register(&samsung_keypad_driver); } //驱动注册后最先执行的探测函数分析 static int __devinit samsung_keypad_probe(struct platform_device *pdev) { const struct samsung_keypad_platdata *pdata; const struct matrix_keymap_data *keymap_data; struct samsung_keypad *keypad; struct resource *res; struct input_dev *input_dev; unsigned int row_shift; unsigned int keymap_size; int error; #ifdef CONFIG_WAKELOCK_KEEP wake_lock_init(&s_ut_wake_lock2, WAKE_LOCK_SUSPEND, "hold wake buttom"); keep_wakeup_timeout(2); #endif pdata = pdev->dev.platform_data; //获取平台私有数据 if (!pdata) { dev_err(&pdev->dev, "no platform data defined\n"); return -EINVAL; } keymap_data = pdata->keymap_data; if (!keymap_data) { dev_err(&pdev->dev, "no keymap data defined\n"); return -EINVAL; } if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS) return -EINVAL; if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS) return -EINVAL; /* initialize the gpio */ if (pdata->cfg_gpio) //调用GPIO初始化函数,设置对应引脚为按键模式 pdata->cfg_gpio(pdata->rows, pdata->cols); row_shift = get_count_order(pdata->cols); keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]); keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL); input_dev = input_allocate_device(); if (!keypad || !input_dev) { error = -ENOMEM; goto err_free_mem; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { error = -ENODEV; goto err_free_mem; } keypad->base = ioremap(res->start, resource_size(res)); if (!keypad->base) { error = -EBUSY; goto err_free_mem; } keypad->clk = clk_get(&pdev->dev, "keypad"); if (IS_ERR(keypad->clk)) { dev_err(&pdev->dev, "failed to get keypad clk\n"); error = PTR_ERR(keypad->clk); goto err_unmap_base; } //初始化keypad结构体 keypad->input_dev = input_dev; keypad->row_shift = row_shift; keypad->rows = pdata->rows; keypad->cols = pdata->cols; init_waitqueue_head(&keypad->wait); input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; input_set_drvdata(input_dev, keypad); input_dev->open = samsung_keypad_open; input_dev->close = samsung_keypad_close; input_dev->evbit[0] = BIT_MASK(EV_KEY); //表示输入设备为按键 if (!pdata->no_autorepeat) input_dev->evbit[0] |= BIT_MASK(EV_REP); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_dev->keycode = keypad->keycodes; input_dev->keycodesize = sizeof(keypad->keycodes[0]); input_dev->keycodemax = pdata->rows << row_shift; matrix_keypad_build_keymap(keymap_data, row_shift, input_dev->keycode, input_dev->keybit); keypad->irq = platform_get_irq(pdev, 0); if (keypad->irq < 0) { error = keypad->irq; goto err_put_clk; } //注册一个按键中断,当有按键按下时,将触发samsung_keypad_irq函数的执行 error = request_threaded_irq(keypad->irq, NULL, samsung_keypad_irq, IRQF_ONESHOT, dev_name(&pdev->dev), keypad); if (error) { dev_err(&pdev->dev, "failed to register keypad interrupt\n"); goto err_put_clk; } error = input_register_device(keypad->input_dev); if (error) goto err_free_irq; device_init_wakeup(&pdev->dev, pdata->wakeup); platform_set_drvdata(pdev, keypad); return 0; err_free_irq: free_irq(keypad->irq, keypad); err_put_clk: clk_put(keypad->clk); err_unmap_base: iounmap(keypad->base); err_free_mem: input_free_device(input_dev); kfree(keypad); return error; } //矩阵键盘按键使能函数 static void samsung_keypad_start(struct samsung_keypad *keypad) { unsigned int val; /* Tell IRQ thread that it may poll the device. */ keypad->stopped = false; clk_enable(keypad->clk); /* Enable interrupt bits. */ val = readl(keypad->base + SAMSUNG_KEYIFCON); //使能按键在上升沿和下降沿出发按键中断 val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN; writel(val, keypad->base + SAMSUNG_KEYIFCON); /* KEYIFCOL reg clear. */ //KEYIFCOL寄存器写零,使每个行输入引脚为正常输入模式 writel(0, keypad->base + SAMSUNG_KEYIFCOL); } //按键中断函数 static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) { struct samsung_keypad *keypad = dev_id; unsigned int row_state[SAMSUNG_MAX_COLS]; unsigned int val; bool key_down; do { val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); //清除中断标志位 writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); //调用按键扫描函数 samsung_keypad_scan(keypad, row_state); //按键值上报到android用户空间 key_down = samsung_keypad_report(keypad, row_state); if (key_down) //当按键按下时调用下面函数来达到消斗动作用 wait_event_timeout(keypad->wait, keypad->stopped, msecs_to_jiffies(50)); } while (key_down && !keypad->stopped); //等待按键释放 return IRQ_HANDLED;
4.按键值的定义 // linux/arch/arm/mach-exynos/mach-smdk4x12.c 按键值定义在input.h中,并且最终按键值要和android中定义的键值匹配才行 static uint32_t smdk4x12_keymap0[] __initdata = { 158 172 108 KEY(0, 0, KEY_BACK), KEY(0, 1, KEY_HOMEPAGE), KEY(0, 2, KEY_DOWN), 114 139 115 KEY(1, 0, KEY_VOLUMEDOWN), KEY(1, 1, KEY_MENU), KEY(1, 2, KEY_VOLUMEUP), 103 105 106 KEY(2, 0, KEY_UP), KEY(2, 1, KEY_LEFT), KEY(2, 2, KEY_RIGHT), }; static struct matrix_keymap_data smdk4x12_keymap_data0 __initdata = { .keymap = smdk4x12_keymap0, .keymap_size = ARRAY_SIZE(smdk4x12_keymap0), }; static struct samsung_keypad_platdata smdk4x12_keypad_data0 __initdata = { .keymap_data = &smdk4x12_keymap_data0, .rows = 3, //3行 .cols = 3, //3列 }; android代码中定义的按键值如下 kernel中定义的键值要和android中定义的键值一一对应 /device/samsung/smdk4x12$ key 103 DPAD_UP WAKE_DROPPED key 3 DPAD_CENTER WAKE_DROPPED key 108 DPAD_DOWN WAKE_DROPPED key 106 DPAD_RIGHT WAKE_DROPPED key 105 DPAD_LEFT WAKE_DROPPED key 114 VOLUME_DOWN WAKE key 172 HOME WAKE_DROPPED key 139 MENU WAKE_DROPPED key 115 VOLUME_UP WAKE key 158 BACK WAKE_DROPPED
|