Because of its powerful and stable kernel, easy to expand and reduce, and rich hardware support, Linux has been widely used in embedded systems. Many embedded Linux systems, especially those with strong interaction with users, often need to be equipped with a special keyboard. At this time, developers need to write drivers for their special keyboards according to the actual situation.

Introduction to Linux keyboard driver

Most drivers in Linux use a hierarchical architecture, and keyboard drivers are no exception. In Linux, the keyboard driver is divided into two layers to achieve. The upper layer is a general keyboard abstraction layer, which does not depend on some functions of the underlying specific hardware in the keyboard driver, and is responsible for providing services to the underlying layer; the lower layer is the hardware processing layer, which is closely related to specific hardware, and is mainly responsible for hardware. Direct operation. The upper public part of the keyboard driver is in the driver/keyboard. c. The most important thing in this file is the handle_sCANcode function that the kernel exports with the EXPORT_SYMBOL macro. The function completed by handle_scancode is: first convert the scan code into a key code, and then convert the key code into a target code according to the pressing condition of the shift key, alt, etc., in general, the ASCII code, and finally put the ASCII code into the terminal device. In the buffer, and dispatch a tasklet responsible for echoing it back on the display. It can be seen that this function performs some of the most core tasks in the keyboard driver, and the logic functions of these cores are independent of the underlying hardware, so they can be separated and exported to the underlying hardware processing function calls. Several other callback functions are also defined in this file, which are called by the upper common part of the keyboard driver and implemented by the underlying hardware handler. Such as kbd_init_hw, kbd_translate, kbd_unexpected_up and so on. The kbd_translate is called by the handle_scancode, which is responsible for converting the scan code into a key code; the underlying hardware processing part of the keyboard driver has different implementations according to different hardware. For example, the underlying hardware processing functions of the standard keyboard on the PC platform are concentrated in the driver/Pc_keyb. c. This file includes keyboard interrupt handler function keyboard_interrupt, scan code to key code conversion function pckbd_translate and other functions closely related to the underlying hardware.

In this architecture, it is extraordinarily clear to add a special keyboard to the system. Developers only need to write the underlying hardware handlers in the driver to drive the keyboard. In general, the most important job in the underlying hardware processing function is to get the scan code of the pressed key in the keyboard interrupt processing, and call handle_scancode with it as the parameter. The scan code can be defined by itself, but it must be uniquely identified. The position of the key pressed on the keyboard. In addition, the developer also needs to provide the corresponding conversion function kbd_translate from the custom scan code to the key code. The specific key code conversion, the target code is placed in the input buffer of the terminal, and the work of echoing is completed by the handle_scancode. Here we can also see that the kernel export function handle_scancode plays a key role in the upper keyboard driver and the underlying hardware processing layer in the entire keyboard driver.

How to write keyboard driver with embedded Linux system

Applications

Below we will use a specific application example to illustrate the specific process of writing a driver for a special keyboard in an embedded Linux system.

1 hardware module description

The construction of this system uses Samsung's S3C2410 development board as the hardware platform. The hardware module of the special keyboard is mainly composed of two SN74hc164 chips and a matrix scanning circuit of 4 rows and 16 columns. The SN74hc164 is an 8-bit serial input and output output shift register, which is internally connected by 8 D flip-flops. The working principle is simply as follows. The SN74hc164 chip outputs the serial input on the A and B pins to the output pins QA to QH in parallel after 8 clock pulses on the rising edge of the clock CLK pulse. Its truth table is shown in Figure 1.

After the two SN74hc164 chips are connected in series, connect their CLK pin and CLR pin to the GPB2 and GPB4 ports of the S3C2410 development board respectively, and connect the A and B pins of the first SN74hc164 chip to the GPB1 of the development board. On the port, these three GPIO ports are configured as output ports. In this way, we use two SN74hc164 registers to realize that only three GPIO ports are occupied, and input is provided to 16 columns of the matrix scanning circuit, thereby saving cost and avoiding waste of GPIO resources. But this also brings some trouble to the implementation of the keyboard driver, the driver must first drive the SN74hc164, and then can control the 16 columns of the matrix circuit. The four row pins of the matrix circuit are respectively connected to the GPG6, GPG7, GPG8, GPG9 ports of the S3C2410, and the four ports are configured as interrupt sources. When no key is pressed, it is directly read as high potential. When using, the SN74hc164 chip first sets the 16 columns of the keyboard to low potential. When any key is pressed, the corresponding line GPG port will have a high-to-low voltage transition. This triggers an interrupt.

2 software module description

Initialization section. This section includes initialization on the hardware and software layers. In this example, the GPIO ports used by the matrix circuit and the SN74hc164 chip need to be configured so that the CPU can control and access them. In order to configure a GPIO port as an input/output or an interrupt source, you need to set the correct value in the corresponding GPIO control register. The specific value can be obtained by referring to the S3C2410 development board manual. For example, in order to set GPB1 to the input of SN74hc164, you need to set 2, 3 and 2 bits of GPBCON as binary 01. In order to set GPG6 as a low-voltage interrupt source, you need to set GPGCON to 12, 13 The bit is set to 10 in binary. After the hardware initialization operation is completed, it is initialized on the software layer. First register the keyboard interrupt handler to the system, and then set up a timer structure to hook it to the kernel's timer queue when the interrupt occurs. This timer will trigger a scan operation on the keyboard. Finally, the 16 columns of the matrix circuit are zeroed by the SN74hc164.

Interrupt processing section. As mentioned earlier, the work that this part of the software should do is to scan the special keyboard, determine which key is pressed, and get a stable scan code, then call the kernel export function handle_scancode. In this application, the layout of the special keyboard is similar to the layout of the PC standard keyboard, so we directly use the system scan code of the corresponding key on the PC keyboard as the scan code of each key on our special keyboard, and we will use the PC keyboard driver. The conversion function pckbd_translate of the scan code to the key code is used as our kbd_translate function.

The algorithm for determining which key is pressed is as follows. When the interrupt arrives, we can already determine which row the pressed key is based on the interrupt number. We also need to determine which column the pressed key is in. To do this, we first send a CLR signal to the two SN74hc164 chips in series, clear them, and then send 16 ones, so that the columns of the special keyboard are all high. At this time, we read high on the line port of the keyboard. Potential. At 16 clock pulses, the SN74hc164 chip is fed with 1 0 and 15 1 so that 0 is uniquely present on each column, while scanning at the keyboard row port. When the column being pressed is set to 0, its row will read a low potential. Using this "walking 0 method", we can determine which key on the keyboard has been pressed. But this simple scanning algorithm is not enough, because in this type of matrix scanning keyboard, each time the key is pressed and raised, there will be 10~20ms (the length of this time is determined by the hardware characteristics). As shown in Figure 2, in order to obtain stable button information, it is necessary to find a way to remove this jitter, in order to avoid the user's one button mistake as a few buttons to deal with. A common way to deburr is to not scan the keyboard immediately when a keyboard interrupt arrives. Instead, wait for a while, then skip the glitch and then scan the keyboard. The pseudo code is as follows:

Wait for a while, skipping the jitter;

Scanning keyboard

If no key is pressed on the keyboard

End returning;

If there is a key pressed on the keyboard

Wait a while again and check if the same key is still pressed.

If the same key is still pressed

Return the read scan code;

Else

Return directly

This kind of solution is certainly feasible, but it uses a busy method to deburr, and during the busy period, the system can't do any useful work. This is a luxury waste for embedded Linux systems where computing resources are inherently limited. In this application, we have designed a deburring solution for embedded systems with good results.

Since the Linux kernel provides timer queues, we can use this mechanism to avoid busyness and improve system performance. When a key is pressed on the keyboard, the keyboard interrupt handler first closes the interrupt source, enters the polling mode, and hangs a TImerlist object into the timer queue. The timer that is hooked into the kernel is triggered on time, and the function it triggers does the following: first scans all the keys on the entire keyboard, and saves the result of the scan to a static 2D array variable snap_shot_matrix[16 ][4]. This variable describes the press of all the keys on the keyboard at this moment of the keyboard scan. If a key is not pressed, that is, it is in the released state, then the corresponding value in snap_shot_matrix is ​​set to 0. If a key is pressed, the corresponding value in snap_shot_matrix is ​​incremented by one, if The value is greater than a pre-specified number after incrementing by 1, we can think of this as a stable value, and set the value of the corresponding coordinate of the current_matrix of another two-dimensional array variable of size 16*4, otherwise set to 0. . This variable describes the stable value of the current button on the keyboard. That is to say, we first process the sampled data obtained in this scan and save it to snap_shot_matrix, and then filter the current_matrix according to the value in the variable, and perform deburring through such a process. After obtaining the stable value current_matrix of this scan, we compare it with the last stable value previous_matrix to determine whether the button on the keyboard has changed at the moment compared with the last scan, and the keyboard at the moment. Is there a key press on it? If you find that no keys are pressed on the keyboard, open the keyboard interrupt and switch back to interrupt mode. If a key is pressed on the keyboard and it is different from the last pressed key, we immediately call the key handler process_key, which calls the upper function handle_scancode in the keyboard driver. If the key pressed on the keyboard is the one that was last pressed, we will increment a counter. When the counter reaches a certain value, we will start the so-called Autorepeat function, that is, the user keeps pressing a certain key to drive The program automatically repeats the keyboard input. This counter is set to 0 when the pressed key changes. But as long as there are still keys on the keyboard that are pressed, we will copy the currently read keyboard stable value current_matrix to previous_matrix, and then hang the previously described timer object in the kernel timer queue. Scan the entire keyboard again later, until no keys are pressed on the keyboard.

Conclusion

With the advancement of the information society and computer software and hardware technologies, the design and application of embedded information products have developed rapidly. The need to add special keyboard drivers for their embedded Linux systems is becoming more and more common. After introducing the overall framework of the keyboard driver in Linux, this paper takes a special keyboard on the S3C2410 development board as an example, and focuses on the work that needs to be done when writing a driver for a special keyboard in an embedded Linux environment. The development provides a way of thinking and reference.

3 In 1 Wireless Charger

For Iphone:
For Iphone8/X/XR/XS Max


Compabile Models:


For Samsung:
For Galaxy S6, For Galaxy S6 Edge, For Galaxy S6 Edge+,
For Galaxy S6 Active, For Galaxy S6 Duos, For Galaxy Note Edge,
For Galaxy S7, For Galaxy S7 Edge, For Galaxy Note 5
For Galaxy S8, For Galaxy S8 Plus, For Galaxy Note 8

For Galaxy S9,For Galaxy S9 Plus


For Sony:
For Xperia Z4V, For Xperia Z3V
For Google:
For Nexus 4, For Nexus 5, For Nexus 6, For Nexus 7


For MOTORALA:
For Moto Droid Turbo, For Moto Droid Turbo 2, For Moto Droid 5

For NOKIA:
For Lumia 920, For Lumia 928, For Nokia Lumia 93, For Lumia 950, For Lumia 950 XL, For Lumia 1020, For Nokia Lumia 1050, For Nokia Lumia 822, For Nokia Lumia 735

For HTC:
For HTC ONE MAX T6, For HTC Incredible 4G, For HTC ONE mini 2, For HTC Droid DNA

For LG:
For LG Nexus 4, For LG Nexus 5, For LG G Pro, For LG D1L, For LG LTE2

For Others:
For YotaPhone 2, For Elephone P9000

Guangzhou HangDeng Tech Co. Ltd , http://www.hangdengtech.com