Keywords: Android buttons matrix buttons ad buttons
Platform information:
Kernel: linux2.6/linux3.0
System: Android/android4.0
Platform: s5pv310 (Samsung exynos4210)
Author: xubin341719 (You are welcome to reprint it. Please note the author)
I. Hardware:
1. Matrix buttons, I/O buttons, and Ad buttons
This knowledge is relatively simple, but the last time a netizen was not quite clear about it. So let's talk about this basic part here.
(1) matrix buttons
I remember that when I was studying Single-Chip Microcomputer in college, this matrix button was still the focus. The figure above is still the AT89S52 film. The working principle is relatively simple. I used rows and columns to determine which button was pressed, for example, if I/O (p1.7, p1.3) changes when the key is pressed, the program can judge that the key is pressed here, similarly, press the I/O (p1.4, p1.0) button marked as 2 to change the level.
In this way, the program should judge from two Io that is the key press, with one more step. However, there is an advantage in hardware, that is, if there are many buttons, it will save I/O Ports, for example, the above 4x4 = 16, 8 Io can be made with 16 buttons, 8x8 = 64, 16 Io can be made with 64 buttons.
Advantages: Multiple buttons can be used with less I/O to make sure the buttons are accurate;
Disadvantages:The program is one step more than the I/O buttons.
(2) I/O buttons
This is relatively simple. It uses the high and low levels of an IO port to determine whether the key is pressed.
Advantages:The procedures and hardware circuits are relatively simple, and the buttons are accurate;
Disadvantages:I/O is limited and it is not suitable for many buttons. For example, if a matrix key has 16 I/O keys, it can represent 64 I/O keys, but only 16 I/O keys.
(3) AD buttons
This is a little more used when I was doing TV.
The ad button is to use an ADC interface, as shown in, to provide a VCC voltage. For example, when S1 is grounded, the analog voltage obtained by the ad interface is ADC = 0. When S2 is pressed, ADC = VCC/(R1 + R2) * R2; in this way, different ADC values can be obtained. In the program, the key is determined to be pressed.
Advantages:The program and hardware circuits are relatively simple. One Io can have multiple buttons;
Disadvantages:The ad button is sometimes inaccurate, so the number of times the ad value should be checked in the program.
2. s5pv310 matrix buttons
The hardware schematic is as follows:
Hardware interface description:Vol +, vol-, back, home, and menu are 1*5 matrix keyboards. The chip interface information is as follows:
Line |
Xgnss_gpio3/kp_col3 Xgnss_gpio_4/kp_col4 Xgnss_gpio_5/kp_col5 Xgnss_gpio_6/kp_col6 Xgnss_gpio_7/kp_col7 |
Column |
Xeint17/kp_row1 |
We have not saved much Io for 1*5 = 5 here? This is the case. Our schematic diagram is based on the Samsung Development Board. The buttons on the Development Board are a little more, but we can't use that much. It is more reasonable for people to do that. However, we are "lazy". We do not need to change the hardware or software. From this point, we can also see that we are a bit technical in China ...... Not very deep. The boss is urging me all day, but we are not very good at details. Samsung also has a dedicated interface in the I/O matrix, so it is "extravagant" to use a matrix of 1*5 to implement five buttons.
3. Matrix buttons of s5pv310
Let's take a look at the dedicated interfaces on the chip. For example, if you use all the interfaces, there are a lot of them.
For the registers of special interfaces, we will use these registers later. The row and column information of keys will be saved here.
Taking s5pv310 as an example, the driver code: samsung-keypad.c
Software:
The overall flowchart is as follows. This is changed based on the touch screen and the process looks like this. Interrupt trigger and interrupt handling.
I. Matrix key row and column settings, and report key value settings
In 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), key value initialization;}; 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), row, column setting, 8 rows, and 2 columns. In fact, we only use 5 rows and 1 column ;. cols = 8,}; static void _ init smdkv310_machine_init (void) {samsung_keypad_set_platdata (& smdkv310_keypad_data); // (3), platform device initialization ;}
(1), key (row, Col, keycode)
The key macro is implemented in 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))
The keycode value is defined in Android-kernel-Samsung-dev/include/Linux/input. H, as follows:
#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) Set columns and columns;
.rows = 2, .cols = 8,
(3) platform device initialization;
samsung_keypad_set_platdata(&smdkv310_keypad_data)。
2. the keycode value set above corresponds to the upper layer
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
Overall map:
Taking key_a as an example, key_a 30 eventually corresponds to 30 home in the upper-layer keypad. KL.
3. matrix keyboard driver analysis
Android-kernel-Samsung-dev/Drivers/input/keyboard/samsung-keypad.c
1. probe function analysis:
Static int _ devinit partition (struct platform_device * pdev) {const struct partition * pdata; const struct partition * 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 = kzarloc (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 parameter initialization; 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; annotate (input_dev, keypad); // (2). Open and Close the function; input_dev-> open = samsung_keypad_open; input_dev-> close = samsung_keypad_close; input_dev -> Evbit [0] = bit_mask (ev_key); If (! Pdata-> metadata) 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; values (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), interrupt function registration; 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 driver registration. Error = input_register_device (keypad-> input_dev); If (error) goto err_free_irq; then (& pdev-> Dev, pdata-> wakeup); platform_set_drvdata (pdev, keypad ); return 0 ;..................}
(1) input parameter initialization;
(2) Open and Close functions;
Input_dev-> open = inputs; static int samsung_keypad_open (struct input_dev * input_dev) {struct samsung_keypad * keypad = inputs (input_dev); samsung_keypad_start (keypad); Return 0 ;} in fact, the open function calls the samsung_keypad_start () function to perform some operations on the key register, as shown in the register list below. 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 | interval; writel (Val, keypad-> base + samsung_keyifcon);/* keyifcol Reg clear. */writel (0, keypad-> base + samsung_keyifcol );}
(3) interrupt function registration;
error=request_threaded_irq(keypad->irq,NULL, samsung_keypad_irq,IRQF_ONESHOT, dev_name(&pdev->dev), keypad);
The request_threaded_irq function may be unfamiliar to us, but it may be difficult to understand the following function:
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);}
This function serves the same purpose as the interrupt function. Keypad-> IRQ = platform_get_irq (pdev, 0); In the middle part number, when a key is pressed, it will jump to the interrupt function, samsung_keypad_irq;
(4) Input driver registration, input driver is more important, touch screen, buttons, gsensor, battery and so on are reported through the input subsystem.
2. Interrupt function: samsung_keypad_irq analysis. This function is called when a key is pressed.
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; Boolean key_down; do {val = readl (keypad-> base + samsung_keyifstsclr);/* clear interrupt. * /// (1). Clear the interrupt. writel (~ 0x0, keypad-> base + samsung_keyifstsclr); // (2), scan row and column values, write registers; samsung_keypad_scan (keypad, row_state); // (3) key value reporting, which is the main part of the function; key_down = samsung_keypad_report (keypad, row_state); // (4), delayed dejitter; If (key_down) wait_event_timeout (keypad-> wait, keypad-> stopped, msecs_to_jiffies (50);} while (key_down &&! Keypad-> stopped); Return irq_handled ;}
(1) Clear interruptions;
(2) scan the row and column values and write them into registers (analyzed later );
(3) key value reporting, which is the main part of the function (analyzed later );
(4) Delayed dejitter. If a key is pressed, there is a delay of a period of time to see if there is a real button. This is what we call dejitter;
3. When the button is pressed, the scan function samsung_keypad_scan of the row and column values is executed, and the corresponding row and column registers are written.
We know that for Matrix keyboards, the main control has a special interface and corresponding registers,
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. Write the corresponding register by scanning the key value, and then use
Static bool destroy (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). Obtain the position of the key in the Matrix. 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). Report the key value keypad-> keycodes [Val]; input_report_key (input_dev, keypad-> keycodes [Val], pressed);} // (3) synchronize data after input is reported; 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
For example, row = 1; Col = 6; row_shift = 3
Val = matrix_scan_code (row, Col, keypad-> row_shift) = (1) <(3) + (6) = 14;
It is equivalent to: () the value in this array: 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) Report the key value keypad-> keycodes [Val]. This value is the final value for our driver;
(3) synchronize input after reporting. This is related to the input subsystem.
This completes the reporting of the driver.