android 物理按键

关键词:android   按键  矩阵按键 AD按键

平台信息:

内核:linux2.6/linux3.0

系统:android/android4.0

平台:S5PV310(samsung exynos4210)

一、硬件部分:

1、矩阵按键、IO按键、AD按键

    这个知识相对来说比较简单,不过上次真有一个网友不太清楚这个。所以这个基础部分我们在这里也说一下。

(1)、矩阵按键

android 物理按键_第1张图片

记得上大学时学单片机时,这个矩阵按键还是个重点呢,上面的图还是AT89S52的片子,工作原理比较简单,通过行、列来确定是那个按键按下,比如说上图标号为1的键按下,IO(P1.7,P1.3)有电平变化,程序可以通过这里来判断是那一个键按下的,同理标号为2的按键按下IO(P1.4,P1.0)有电平变化。

    这样做程序上要从两个IO来判断是那个键按下,多了一个步骤,但是在硬件上有一个优势,就是如果按键比较多的时候比较节省IO口,比如说上面4x4 = 16,8个IO可以做16个按键,8x8=64,16个IO可以做64个按键。

优点:可以用少的IO来做多个按键,判断按键比较准确;

缺点:程序上相对IO按键来说多了一步。

(2)、IO按键

        这个就比较简单了,用一个IO口的高低电平来判断按键是否按下。

优点:程序、硬件电路都比较简单,判断按键比较准确;

缺点:IO有限、按键多时不太合适。比如矩阵按键16个IO可以表示64个按键,IO的话只有16个。

(3)、AD按键

        这个在之前在做电视的时候用的比较多一点。

        AD按键就是通过一个ADC接口,如下图所示,给一个VCC电压,比如说S1接地时AD接口得到的模拟电压值为ADC=0;当S2按下时,ADC= VCC/(R1+R2)*R2;这样就可以得到不同的ADC值,程序中在这里判断是那个按键按下。

优点:程序、硬件电路都比较简单,一个IO可以做多个按键;

缺点:AD按键有时候判断不准确,所以在程序中要多加检测AD值的次数。

android 物理按键_第2张图片

2、S5PV310的矩阵按键

硬件原理图如下:

android 物理按键_第3张图片

硬件接口说明:vol+,vol-,back,home,menu为1*5的矩阵键盘,芯片接口信息如下:

XGNSS_GPIO_3/KP_COL3

XGNSS_GPIO_4/KP_COL4

XGNSS_GPIO_5/KP_COL5

XGNSS_GPIO_6/KP_COL6

XGNSS_GPIO_7/KP_COL7

XEINT17/KP_ROW1

我们这里1*5= 5也没有节省多少IO呀?情况是这样的,我们的原理图是从三星开发板上参考过来的,开发板上按键本来多一点,可是我们用不了那么多,人家那样做比较合理。可是我们“偷懒”,硬件上不用改,软件上也不用改,从这一点也可以看出我们国内做技术这个行业的有点……不太深入呀,整天老板在催,可是我们在细节上做不太好呀。三星在IO矩阵也有专用接口,所以就“奢侈”一次,用1*5的矩阵来实现5个按键。

3、S5PV310的矩阵按键接口

看一下芯片上的专用接口,如下图,全用的话有点多。

android 物理按键_第4张图片

关于专用接口的寄存器,这些寄存器我们后面要用得到的,按键的行、列信息会在这里面暂存的。

android 物理按键_第5张图片

以S5PV310为例,驱动代码:samsung-keypad.c

软件部分:

总体流程图如下,这个是在触摸屏基础上改过来的,感觉流程都是这个样子的。中断触发,中断处理。

android 物理按键_第6张图片

一、矩阵键行、列设定,和上报键值设定

在android-kernel-samsung-dev/arch/arm/mach-exynos/mach-smdkv310.c中

[cpp] view plain copy print ?
  1. static uint32_t smdkv310_keymap[] __initdata = { 
  2.     /* KEY(row, col, keycode) */ 
  3.     KEY(0, 3, KEY_1), KEY(0, 4, KEY_2), KEY(0, 5, KEY_3), 
  4.     KEY(0, 6, KEY_4), KEY(0, 7, KEY_5), 
  5.     KEY(1, 3, KEY_A), KEY(1, 4, KEY_C), KEY(1, 5, KEY_E), 
  6.     KEY(1, 6, KEY_B), KEY(1, 7, KEY_D)//(1)、键值初始化; 
  7. }; 
  8.  
  9. static struct matrix_keymap_data smdkv310_keymap_data __initdata = { 
  10.     .keymap     = smdkv310_keymap, 
  11.     .keymap_size    = ARRAY_SIZE(smdkv310_keymap), 
  12. }; 
  13. static struct samsung_keypad_platdata smdkv310_keypad_data __initdata = { 
  14.     .keymap_data    = &smdkv310_keymap_data, 
  15.     .rows       = 2,         //(2)、行、列设定,8行、2列,其实我们只用了5行、1列; 
  16.     .cols       = 8, 
  17. }; 
  18. static void __init smdkv310_machine_init(void
  19.     samsung_keypad_set_platdata(&smdkv310_keypad_data); //(3)、平台设备初始化; 

(1)、KEY(row, col,keycode)

KEY这个宏在android-kernel-samsung-dev/include/linux/input/Matrix_keypad.h中实现:

[cpp] view plain copy print ?
  1. #define MATRIX_MAX_ROWS     32 
  2. #define MATRIX_MAX_COLS         32 
  3. #define KEY(row, col, val)  ((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\ 
  4.                  (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\ 
  5.                  ((val) & 0xffff)) 

keycode的值在android-kernel-samsung-dev/include/linux/input.h中有定义,如下:

[cpp] view plain copy print ?
  1. #define KEY_RESERVED        0 
  2. #define KEY_ESC         1 
  3. #define KEY_1           2 
  4. #define KEY_2           3 
  5. #define KEY_3           4 
  6. #define KEY_4           5 
  7. #define KEY_5           6 
  8. #define KEY_6           7 
  9. #define KEY_7           8 
  10. #define KEY_8           9 
  11. #define KEY_9           10 
  12. #define KEY_0           11 
  13. #define KEY_MINUS       12 
  14. #define KEY_EQUAL       13 
  15. #define KEY_BACKSPACE       14 
  16. #define KEY_TAB         15 
  17. #define KEY_Q           16 
  18. #define KEY_W           17 
  19. #define KEY_E           18 
  20. #define KEY_R           19 
  21. #define KEY_T           20 
  22. #define KEY_Y           21 
  23. #define KEY_U           22 

(2)、行列设定;

[cpp] view plain copy print ?
  1. .rows       = 2,        
  2. .cols       = 8, 

(3)、平台设备初始化;

[cpp] view plain copy print ?
  1. samsung_keypad_set_platdata(&smdkv310_keypad_data)。 

二、上面设定的keycode键值和上层相对应

4.0.3_r1/device/samsung/smdkv310/samsung-keypad.kl中

[cpp] view plain copy print ?
  1. key 2     DPAD_UP               WAKE_DROPPED 
  2. key 3     DPAD_CENTER           WAKE_DROPPED 
  3. key 4     DPAD_DOWN             WAKE_DROPPED 
  4. key 5     DPAD_RIGHT            WAKE_DROPPED 
  5. key 6     DPAD_LEFT             WAKE_DROPPED 
  6. key 18    VOLUME_DOWN       WAKE 
  7. key 30    HOME                      WAKE_DROPPED 
  8. key 32    MENU                      WAKE_DROPPED 
  9. key 46    VOLUME_UP             WAKE 
  10. key 48    BACK                      WAKE_DROPPED 
  11. key 10    POWER                     WAKE 

总体对应图:

以KEY_A为例,KEY_A 30最终和上层的keypad.kl中的30 HOME相对应

android 物理按键_第7张图片


三、矩阵键盘驱动程序分析

android-kernel-samsung-dev/drivers/input/keyboard/samsung-keypad.c

1、probe函数分析:

[cpp] view plain copy print ?
  1. static int __devinit samsung_keypad_probe(struct platform_device *pdev) 
  2.     const struct samsung_keypad_platdata *pdata; 
  3.     const struct matrix_keymap_data *keymap_data; 
  4.     struct samsung_keypad *keypad; 
  5.     struct resource *res; 
  6.     struct input_dev *input_dev; 
  7.     unsigned int row_shift; 
  8.     unsigned int keymap_size; 
  9.     int error; 
  10.     ……………… 
  11.     keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]); 
  12.  
  13.     keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL); 
  14.     input_dev = input_allocate_device(); 
  15.     if (!keypad || !input_dev) { 
  16.         error = -ENOMEM; 
  17.         goto err_free_mem; 
  18.     } 
  19.  
  20.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
  21.     if (!res) { 
  22.         error = -ENODEV; 
  23.         goto err_free_mem; 
  24.     } 
  25.  
  26.     keypad->base = ioremap(res->start, resource_size(res)); 
  27.     if (!keypad->base) { 
  28.         error = -EBUSY; 
  29.         goto err_free_mem; 
  30.     } 
  31.     ………… 
  32. //(1)、input参数初始化; 
  33.     keypad->input_dev = input_dev; 
  34.     keypad->row_shift = row_shift; 
  35.     keypad->rows = pdata->rows; 
  36.     keypad->cols = pdata->cols; 
  37.     init_waitqueue_head(&keypad->wait); 
  38.  
  39.     input_dev->name = pdev->name; 
  40.     input_dev->id.bustype = BUS_HOST; 
  41.     input_dev->dev.parent = &pdev->dev; 
  42.     input_set_drvdata(input_dev, keypad); 
  43. //(2)、打开、关闭函数; 
  44.     input_dev->open = samsung_keypad_open; 
  45.     input_dev->close = samsung_keypad_close; 
  46.  
  47.     input_dev->evbit[0] = BIT_MASK(EV_KEY); 
  48.     if (!pdata->no_autorepeat) 
  49.         input_dev->evbit[0] |= BIT_MASK(EV_REP); 
  50.  
  51.     input_set_capability(input_dev, EV_MSC, MSC_SCAN); 
  52.  
  53.     input_dev->keycode = keypad->keycodes; 
  54.     input_dev->keycodesize = sizeof(keypad->keycodes[0]); 
  55.     input_dev->keycodemax = pdata->rows << row_shift; 
  56.  
  57.     matrix_keypad_build_keymap(keymap_data, row_shift, 
  58.             input_dev->keycode, input_dev->keybit); 
  59.  
  60.     keypad->irq = platform_get_irq(pdev, 0); 
  61.     if (keypad->irq < 0) { 
  62.         error = keypad->irq; 
  63.         goto err_put_clk; 
  64.     } 
  65. //(3)、中断函数注册;  
  66.     error = request_threaded_irq(keypad->irq, NULL, samsung_keypad_irq, 
  67.             IRQF_ONESHOT, dev_name(&pdev->dev), keypad); 
  68.     if (error) { 
  69.         dev_err(&pdev->dev, "failed to register keypad interrupt\n"); 
  70.         goto err_put_clk; 
  71.     } 
  72. //(4)、input驱动注册。 
  73.     error = input_register_device(keypad->input_dev); 
  74.     if (error) 
  75.         goto err_free_irq; 
  76.  
  77.     device_init_wakeup(&pdev->dev, pdata->wakeup); 
  78.     platform_set_drvdata(pdev, keypad); 
  79.     return 0; 
  80.  
  81. ……………… 

(1)、input参数初始化;

(2)、打开、关闭函数;

[cpp] view plain copy print ?
  1. input_dev->open = samsung_keypad_open; 
  2. static int samsung_keypad_open(struct input_dev *input_dev) 
  3.     struct samsung_keypad *keypad = input_get_drvdata(input_dev); 
  4.     samsung_keypad_start(keypad); 
  5.     return 0; 
  6. 其实open函数调用samsung_keypad_start()函数,对按键的寄存器一些操作,如下面寄存器列表中的。 
  7. static void samsung_keypad_start(struct samsung_keypad *keypad) 
  8.     unsigned int val; 
  9.     /* Tell IRQ thread that it may poll the device. */ 
  10.     keypad->stopped = false
  11.     clk_enable(keypad->clk); 
  12.     /* Enable interrupt bits. */ 
  13.     val = readl(keypad->base + SAMSUNG_KEYIFCON); 
  14.     val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN; 
  15.     writel(val, keypad->base + SAMSUNG_KEYIFCON); 
  16.     /* KEYIFCOL reg clear. */ 
  17.     writel(0, keypad->base + SAMSUNG_KEYIFCOL); 

(3)、中断函数注册;

[cpp] view plain copy print ?
  1. error=request_threaded_irq(keypad->irq,NULL, samsung_keypad_irq,IRQF_ONESHOT, dev_name(&pdev->dev), keypad); 

request_threaded_irq这个函数也许我们比较陌生,可是看下下面一个函数也许就不难理解了:

[cpp] view plain copy print ?
  1. static inline int __must_check 
  2. request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, 
  3.         const char *name, void *dev) 
  4.     return request_threaded_irq(irq, handler, NULL, flags, name, dev); 
这个函数跟中断的作用是一样的,keypad->irq= platform_get_irq(pdev, 0);于中段号,当有按键按下时,会跳到中断函数,samsung_keypad_irq中;

(4)、input驱动注册,input驱动比较重要,触摸屏、按键、gsensor、battery等都是通过input子系统上报的。

2、中断函数: samsung_keypad_irq分析,当有按键按下时,调用这个函数

[cpp] view plain copy print ?
  1. static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) 
  2.     struct samsung_keypad *keypad = dev_id; 
  3.  
  4.     unsigned int row_state[SAMSUNG_MAX_COLS]; 
  5.     unsigned int val; 
  6.     bool key_down; 
  7.     do
  8.         val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); 
  9.         /* Clear interrupt. */ 
  10. //(1)、清除中断; 
  11.         writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);  
  12. //(2)、扫描行列值,写入寄存器; 
  13.     samsung_keypad_scan(keypad, row_state); 
  14. //(3)、键值上报,这是函数的主要部分了; 
  15.         key_down = samsung_keypad_report(keypad, row_state); 
  16. //(4)、延时去抖动;  
  17.         if (key_down) 
  18.             wait_event_timeout(keypad->wait, keypad->stopped, 
  19.                        msecs_to_jiffies(50)); 
  20.     } while (key_down && !keypad->stopped); 
  21.     return IRQ_HANDLED; 

(1)、清除中断;

(2)、扫描行列值,写入寄存器(后面分析);

(3)、键值上报,这是函数的主要部分了(后面分析);

(4)、延时去抖动,如果有按键按下,有一个段时间的延时,看是否真正有按键,这就是所说的去抖动;

3、当按键按下时,行列值的扫描函数samsung_keypad_scan执行,写入相应行列寄存器

上图我们知道,对于矩阵键盘,主控有专门的接口,也有相应的寄存器,

[cpp] view plain copy print ?
  1. static void samsung_keypad_scan(struct samsung_keypad *keypad, 
  2.                 unsigned int *row_state) 
  3.     struct device *dev = keypad->input_dev->dev.parent; 
  4.     unsigned int col; 
  5.     unsigned int val; 
  6.     for (col = 0; col < keypad->cols; col++) { 
  7.         if (samsung_keypad_is_s5pv210(dev)) { 
  8.             val = S5PV210_KEYIFCOLEN_MASK; 
  9.             val &= ~(1 << col) << 8; 
  10.         } else
  11.             val = SAMSUNG_KEYIFCOL_MASK; 
  12.             val &= ~(1 << col); 
  13.         } 
  14.         writel(val, keypad->base + SAMSUNG_KEYIFCOL); 
  15.         mdelay(1); 
  16.         val = readl(keypad->base + SAMSUNG_KEYIFROW); 
  17.         row_state[col] = ~val & ((1 << keypad->rows) - 1); 
  18.     } 
  19.     /* KEYIFCOL reg clear */ 
  20.     writel(0, keypad->base + SAMSUNG_KEYIFCOL); 

4、通过扫描键值写入相应寄存器,然后通过

[cpp] view plain copy print ?
  1. static bool samsung_keypad_report(struct samsung_keypad *keypad, 
  2.                   unsigned int *row_state) 
  3.     struct input_dev *input_dev = keypad->input_dev; 
  4.     unsigned int changed; 
  5.     unsigned int pressed; 
  6.     unsigned int key_down = 0; 
  7.     unsigned int val; 
  8.     unsigned int col, row; 
  9.  
  10.     for (col = 0; col < keypad->cols; col++) { 
  11.         changed = row_state[col] ^ keypad->row_state[col]; 
  12.         key_down |= row_state[col]; 
  13.         if (!changed) 
  14.             continue
  15.         for (row = 0; row < keypad->rows; row++) { 
  16.             if (!(changed & (1 << row))) 
  17.                 continue
  18.             pressed = row_state[col] & (1 << row); 
  19.             dev_dbg(&keypad->input_dev->dev, 
  20.                 "key %s, row: %d, col: %d\n"
  21.                 pressed ? "pressed" : "released", row, col); 
  22. //(1)、得到按键在矩阵中的位置; 
  23.             val = MATRIX_SCAN_CODE(row, col, keypad->row_shift); 
  24. printk("key %s, row: %d, col: %d\n",pressed ? "pressed" : "released", row, col); 
  25. printk("test by xu_bin for val = %d,key = %d\n",val,keypad->keycodes[val]); 
  26.             input_event(input_dev, EV_MSC, MSC_SCAN, val); 
  27. //(2)、上报键值keypad->keycodes[val]; 
  28.             input_report_key(input_dev, 
  29.                     keypad->keycodes[val], pressed); 
  30.         } 
  31. //(3)、input上报后同步;  
  32.         input_sync(keypad->input_dev); 
  33.     } 
  34.     memcpy(keypad->row_state, row_state, sizeof(keypad->row_state)); 
  35.     return key_down; 

(1)、#defineMATRIX_SCAN_CODE(row, col, row_shift) (((row)<< (row_shift)) + (col))

row_shift = 3

如:row = 1; col = 6; row_shift = 3

val = MATRIX_SCAN_CODE(row, col,keypad->row_shift) = ((1)<<(3)+(6)) = 14;

就相当于:(1,6)这个数组里面的值:48

printk("key %s, row: %d, col:%d\n",pressed ? "pressed" : "released", row, col);

printk("test by xu_bin for val =%d,key = %d\n",val,keypad->keycodes[val]);

android 物理按键_第8张图片
(2)、上报键值keypad->keycodes[val],这个值是对于我们这个驱动来说的最终值;

(3)、input上报后同步,这个和input子系统相关。

这样就完成了驱动部分的上报。

你可能感兴趣的:(android 物理按键)