打印
[ARM9、LPC]

友坚UT4412BV03矩阵键盘驱动分析

[复制链接]
622|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一.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

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

2

帖子

0

粉丝