android 物理按鍵

來源:互聯網
上載者:User

關鍵詞: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(0, 3, KEY_1), KEY(0, 4, KEY_2), KEY(0, 5, KEY_3),KEY(0, 6, KEY_4), KEY(0, 7, KEY_5),KEY(1, 3, KEY_A), KEY(1, 4, KEY_C), KEY(1, 5, KEY_E),KEY(1, 6, KEY_B), KEY(1, 7, 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,         //(2)、行、列設定,8行、2列,其實我們只用了5行、1列;.cols= 8,};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_ROWS32#define MATRIX_MAX_COLS32#define KEY(row, col, val)((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\ (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\ ((val) & 0xffff))

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

#define KEY_RESERVED0#define KEY_ESC1#define KEY_12#define KEY_23#define KEY_34#define KEY_45#define KEY_56#define KEY_67#define KEY_78#define KEY_89#define KEY_910#define KEY_011#define KEY_MINUS12#define KEY_EQUAL13#define KEY_BACKSPACE14#define KEY_TAB15#define KEY_Q16#define KEY_W17#define KEY_E18#define KEY_R19#define KEY_T20#define KEY_Y21#define KEY_U22

(2)、行列設定;

    .rows       = 2,           .cols       = 8,

(3)、平台裝置初始化;

samsung_keypad_set_platdata(&smdkv310_keypad_data)。

二、上面設定的keycode索引值和上層相對應

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

key 2     DPAD_UP           WAKE_DROPPEDkey 3     DPAD_CENTER       WAKE_DROPPEDkey 4     DPAD_DOWN         WAKE_DROPPEDkey 5     DPAD_RIGHT        WAKE_DROPPEDkey 6     DPAD_LEFT         WAKE_DROPPEDkey 18    VOLUME_DOWN       WAKEkey 30    HOME              WAKE_DROPPEDkey 32    MENU              WAKE_DROPPEDkey 46    VOLUME_UP         WAKEkey 48    BACK              WAKE_DROPPEDkey 10    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[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;}…………//(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[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;}//(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 0;………………}

(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 0;}其實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(0, keypad->base + SAMSUNG_KEYIFCOL);}

(3)、中斷函數註冊;

error=request_threaded_irq(keypad->irq,NULL, samsung_keypad_irq,IRQF_ONESHOT, dev_name(&pdev->dev), keypad);

request_threaded_irq這個函數也許我們比較陌生,可是看下下面一個函數也許就不難理解了:

static inline int __must_checkrequest_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,        const char *name, void *dev){    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分析,當有按鍵按下時,調用這個函數

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(50));} 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 = 0; col < keypad->cols; col++) {if (samsung_keypad_is_s5pv210(dev)) {val = S5PV210_KEYIFCOLEN_MASK;val &= ~(1 << col) << 8;} else {val = SAMSUNG_KEYIFCOL_MASK;val &= ~(1 << col);}writel(val, keypad->base + SAMSUNG_KEYIFCOL);mdelay(1);val = readl(keypad->base + SAMSUNG_KEYIFROW);row_state[col] = ~val & ((1 << keypad->rows) - 1);}/* KEYIFCOL reg clear */writel(0, 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 = 0;unsigned int val;unsigned int col, row;for (col = 0; col < keypad->cols; col++) {changed = row_state[col] ^ keypad->row_state[col];key_down |= row_state[col];if (!changed)continue;for (row = 0; row < keypad->rows; row++) {if (!(changed & (1 << row)))continue;pressed = row_state[col] & (1 << 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子系統相關。 

這樣就完成了驅動部分的上報。


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.