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

平台信息:

内核:linux2.6/linux3.0

系统:android/android4.0

平台:S5PV310(samsung exynos4210)

作者:xubin341719(欢迎转载,请注明作者)

一、硬件部分:

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

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

(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值的次数。

2、S5PV310的矩阵按键

硬件原理图如下:

硬件接口说明: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的矩阵按键接口

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

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

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

软件部分:

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

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

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

static uint32_t smdkv310_keymap[] __initdata = {
/* KEY(row, col, keycode) */
KEY(, , KEY_1), KEY(, , KEY_2), KEY(, , KEY_3),
KEY(, , KEY_4), KEY(, , KEY_5),
KEY(, , KEY_A), KEY(, , KEY_C), KEY(, , KEY_E),
KEY(, , KEY_B), KEY(, , KEY_D)//(1)、键值初始化;
}; static struct matrix_keymap_data smdkv310_keymap_data __initdata = {
.keymap = smdkv310_keymap,
.keymap_size = ARRAY_SIZE(smdkv310_keymap),
};
static struct samsung_keypad_platdata smdkv310_keypad_data __initdata = {
.keymap_data = &smdkv310_keymap_data,
.rows = , //(2)、行、列设定,8行、2列,其实我们只用了5行、1列;
.cols = ,
};
static void __init smdkv310_machine_init(void)
{
samsung_keypad_set_platdata(&smdkv310_keypad_data); //(3)、平台设备初始化;
}

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

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

#define MATRIX_MAX_ROWS        32
#define MATRIX_MAX_COLS 32
#define KEY(row, col, val) ((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\
(((col) & (MATRIX_MAX_COLS - )) << ) |\
((val) & 0xffff))

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

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

(2)、行列设定;

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

samsung_keypad_set_platdata(&smdkv310_keypad_data)。

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

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

key      DPAD_UP               WAKE_DROPPED
key DPAD_CENTER WAKE_DROPPED
key DPAD_DOWN WAKE_DROPPED
key DPAD_RIGHT WAKE_DROPPED
key DPAD_LEFT WAKE_DROPPED
key VOLUME_DOWN WAKE
key HOME WAKE_DROPPED
key MENU WAKE_DROPPED
key VOLUME_UP WAKE
key BACK WAKE_DROPPED
key POWER WAKE

总体对应图:

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

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

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

1、probe函数分析:

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;
………………
keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[]); 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, );
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;
}
…………
//(1)、input参数初始化;
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);
//(2)、打开、关闭函数;
input_dev->open = samsung_keypad_open;
input_dev->close = samsung_keypad_close; input_dev->evbit[] = BIT_MASK(EV_KEY);
if (!pdata->no_autorepeat)
input_dev->evbit[] |= BIT_MASK(EV_REP); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_dev->keycode = keypad->keycodes;
input_dev->keycodesize = sizeof(keypad->keycodes[]);
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, );
if (keypad->irq < ) {
error = keypad->irq;
goto err_put_clk;
}
//(3)、中断函数注册;
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;
}
//(4)、input驱动注册。
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 ; ………………
}

(1)、input参数初始化;

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

input_dev->open = samsung_keypad_open;
static int samsung_keypad_open(struct input_dev *input_dev)
{
struct samsung_keypad *keypad = input_get_drvdata(input_dev);
samsung_keypad_start(keypad);
return ;
}
其实open函数调用samsung_keypad_start()函数,对按键的寄存器一些操作,如下面寄存器列表中的。
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. */
writel(, keypad->base + SAMSUNG_KEYIFCOL);
}

(3)、中断函数注册;

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

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

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);
/* Clear interrupt. */
//(1)、清除中断;
writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
//(2)、扫描行列值,写入寄存器;
samsung_keypad_scan(keypad, row_state);
//(3)、键值上报,这是函数的主要部分了;
key_down = samsung_keypad_report(keypad, row_state);
//(4)、延时去抖动;
if (key_down)
wait_event_timeout(keypad->wait, keypad->stopped,
msecs_to_jiffies());
} while (key_down && !keypad->stopped);
return IRQ_HANDLED;
}

(1)、清除中断;

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

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

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

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

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

static void samsung_keypad_scan(struct samsung_keypad *keypad,
unsigned int *row_state)
{
struct device *dev = keypad->input_dev->dev.parent;
unsigned int col;
unsigned int val;
for (col = ; col < keypad->cols; col++) {
if (samsung_keypad_is_s5pv210(dev)) {
val = S5PV210_KEYIFCOLEN_MASK;
val &= ~( << col) << ;
} else {
val = SAMSUNG_KEYIFCOL_MASK;
val &= ~( << col);
}
writel(val, keypad->base + SAMSUNG_KEYIFCOL);
mdelay();
val = readl(keypad->base + SAMSUNG_KEYIFROW);
row_state[col] = ~val & (( << keypad->rows) - );
}
/* KEYIFCOL reg clear */
writel(, keypad->base + SAMSUNG_KEYIFCOL);
}

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

static bool samsung_keypad_report(struct samsung_keypad *keypad,
unsigned int *row_state)
{
struct input_dev *input_dev = keypad->input_dev;
unsigned int changed;
unsigned int pressed;
unsigned int key_down = ;
unsigned int val;
unsigned int col, row; for (col = ; col < keypad->cols; col++) {
changed = row_state[col] ^ keypad->row_state[col];
key_down |= row_state[col];
if (!changed)
continue;
for (row = ; row < keypad->rows; row++) {
if (!(changed & ( << row)))
continue;
pressed = row_state[col] & ( << row);
dev_dbg(&keypad->input_dev->dev,
"key %s, row: %d, col: %d\n",
pressed ? "pressed" : "released", row, col);
//(1)、得到按键在矩阵中的位置;
val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
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]);
input_event(input_dev, EV_MSC, MSC_SCAN, val);
//(2)、上报键值keypad->keycodes[val];
input_report_key(input_dev,
keypad->keycodes[val], pressed);
}
//(3)、input上报后同步;
input_sync(keypad->input_dev);
}
memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));
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]);


(2)、上报键值keypad->keycodes[val],这个值是对于我们这个驱动来说的最终值;

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

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

【转】android 物理按键的更多相关文章

  1. Android 物理按键

    import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view. ...

  2. android 物理按键 监听

    android连接了一个4x4的矩阵键盘,linux内核中注册了按键,在app中监听键盘事件. package com.example.tony.keydemo; import android.sup ...

  3. Android常用的物理按键及其触发事件

    Activity和View都能接收触摸和按键,如果响应事件只需要在继承类里复写事件函数即可:当一个视图(如一个按钮)被触摸时,该对象上的 onTouchEvent() 方法会被调用.不过,为了侦听这个 ...

  4. Android系统移植与调试之------->增加一个双击物理按键打开和关闭闪光灯并将闪光灯状态同步到下拉菜单中

    最近有一个客户有这样的需求: 1.在[设置]--->[无障碍]中添加一个开关按钮. 如果打开开关的话,双击某个物理按键的时候,打开闪光灯,再双击该物理按键的时候,关闭闪光灯. 如果关闭开关的话, ...

  5. android 4.4 添加物理按键

    kernel下添加 Linux-3.4/drivers/input/keyboard/Makefile linux-3.4/drivers/input/keyboard/sw-keyboard.c s ...

  6. Android学习笔记物理按键事件处理

    常见的物理按键: Android为每个物理按键都提供了如下几个回调方法: 代码示例: package com.example.demo3; import androidx.appcompat.app. ...

  7. 关于Android中物理按键不响应的可能的一个问题。

    今天在工作中犯了一个错误,写的视频播放器突然物理音量键就不起作用了. 一開始以为是自己定义的音量条把系统的物理音量条按键给屏蔽掉了. 删除自己定义的音量条还是不行,又怀疑是是加入了什么权限之类的.重复 ...

  8. Android4.2.2由于越来越多的物理按键(frameworks)

    当我们改变frameworks之后可能: make: *** [out/target/common/obj/PACKAGING/checkapi-current-timestamp] 错误 38 解决 ...

  9. React Native 适配Android物理返回键,实现连续两次点击退出

    一直使用iPhone作为测试机开发,提交给测试同事Android版本后发现很多适配问题,其中一个非常明显的是,弹出一个modal后,点击Android的返回键,modal不会消失,直接navigati ...

随机推荐

  1. docker初步

    [Note,]由于docker的局限性,docker只能运行在64位的系统中 docker软件应用程序可以重复地运行在任何地方,因为它的容器包含了所有的环境依赖关系! docker有三种方式运行 作为 ...

  2. hdu 5432 Pyramid Split(二分搜索)

    Problem Description Xiao Ming is a citizen who's good at playing,he has lot's of gold cones which ha ...

  3. linux下部署svn服务器

    系统Linux debian 2.6.32-5-686 先安装svn工具:apt-get install subversion,耐心等待安装完成.安装完成后svn客户端.服务器都有了. 接者建立svn ...

  4. Unity 功夫猫

    最近在家里闲着蛋疼,突然看到一个HTML游戏感觉挺可爱的,就把素材拿过来自己写了一遍. 游戏有很多细节还是没有模仿出来. 里面有一个2DUGUI帧动画播放插件,写了我3个通宵. 还是对Unity的扩展 ...

  5. OSX: 私人定制Dock默认程序图标

    不论什么一个新用户第一次登陆后,OSX都会自己主动地在用户的Dock中列出系统默认的应用程序图标,这些图标随着OSX版本号的不同而不同. 系统管理员有的时候须要改变这些系统默认图标,或者加入自己的或者 ...

  6. [LeetCode] 034. Search for a Range (Medium) (C++/Java)

    索引:[LeetCode] Leetcode 题解索引 (C++/Java/Python/Sql) Github: https://github.com/illuz/leetcode 035. Sea ...

  7. ERROR 1130: Host is not allowed to connect to this MySQL server

    解决远程连接mysql错误1130代码的方法 今天在用远程连接Mysql服务器的数据库,不管怎么弄都是连接不到,错误代码是1130,ERROR 1130: Host 192.168.2.159 is ...

  8. html_day3

    总结学习html的第一天 表格的结构说明 <table></table> <tr></tr> <td></td> <th& ...

  9. iOS——protoco和delegate (事件代理)

    一:被代理人personOne personOne.h #import <Foundation/Foundation.h> @protocol SomeThing<NSObject& ...

  10. 指针参数的传递(节选 C++/C 高质量编程 林锐)

    指针参数是如何传递内存的 如果函数的参数是一个指针,不要指望用该指针去申请动态内存.示例7-4-1中,Test函数的语句GetMemory(str, 200)并没有使str获得期望的内存,str依旧是 ...