Due to the versatility of the I2C bus, Linux, as an excellent embedded operating system, must also have good support for it. In the Linux kernel source code, the driver of the I2C bus is based on the bus device driver model. The driver uses several special data structures, and defines the I2C bus protocol more abstractly and universally, greatly increasing the device driver. Portability. To write your own I2C device driver, you must have a deep understanding of the core I2C bus driver architecture. The I2C bus protocol has only two bus lines, one is the serial data line (SDA) and the other is the serial clock line (SCL). SDA is responsible for data transmission, and SCL is responsible for clock synchronization of data transmission. The I2C device is connected to the processor’s I2C bus controller through these two buses. Different devices are distinguished by a 7-bit address, and the data transmission is bidirectional. The direction is determined by a 1-bit binary number, and the address bit is added. The direction bit is the only sign for operating I2C devices. The connection between I2C devices and CPU is shown in Figure 1. There are 3 types of signals on the I2C bus, namely: start signal, end signal and response signal. These signals are represented by the level changes on SDA and SCL. Start signal (S): When SCL is high, SDA jumps from high to low, indicating the start of data transmission. End signal (P): When SCL is high, SDAY jumps from low to high, indicating the end of data transmission. Corresponding signal (ACK): After the slave receives 8-bit data, in the 9th clock cycle, pull down the SDA level, indicating that the data has been received. When the bus is idle, both SDA and SCL are at high level, and the host can send data to the slave when detecting that the bus is idle. The master first sends the start signal S, and then sends out 8-bit data (including the first 7 bits of the slave address and the direction bit of 1), and then waits for the slave to send back the confirmation signal ACK. When the 8th bit is 0, it means transmitting data to the slave. After receiving the confirmation signal, the master can continuously write 8-bit data to the slave; when the 8th bit is 1, it means reading data to the slave. At that time, the master can receive a series of data from the slave. Finally, when the total data transmission process is completed, the host sends an end signal P to indicate that the data transmission is completed this time. Because there are so many types of I2C devices, if you write a driver for each I2C device, it is obviously not realistic and impossible to do it. Therefore, Linux adopts hierarchical processing for I2C device drivers, which are divided into bus layer and device layer. Some common attributes of I2C device drivers are abstracted and summed up as the bus layer, and specific I2C device operations are regarded as the device layer. The relationship of the data structure [4, 7-8] used in the I2C device driver in Linux is shown in Figure 2. About this part of the code is located in /driver/i2c in the Linux kernel source tree. The key to understanding this hierarchical structure is to understand 4 data structures, i2c_driver and i2c_client belonging to the device layer, i2c_adapter and i2c_algorithm belonging to the bus layer. The four data structures are briefly explained below. struct i2c_driver: Each specific I2C device should correspond to a driver. This structure defines a series of function pointers and I2C device information used for I2C bus management in the Linux device model. The two most important members are the adapter detection function pointer at-tach_adapter, and the device ID table id_table. struct i2c_client: A specific device connected to the SDA and SCL buses is described by the i2c_client structure, and two member variables are defined to represent the adapter and driver corresponding to this specific device. struct i2c_adapter: This structure represents the specific I2C controller in the CPU, and essentially corresponds to a physical device. The most important member variable is the algo structure pointer that points to the adapter-driven program. struct i2c_algorithm: defines the function pointer of the specific adapter driver. Especially the master_xfer function pointer, this function implements the lowest operating method of the adapter, and is also an important function to be written in the bus layer in the I2C device driver. i2c_dev defines the read and write interface for reading and writing the application layer of the I2C device, but due to its lack of versatility, it is rarely used, so it is not introduced in detail. i2c_core plays a connecting role in the driver framework, which defines many important functions. For example: adapter registration/deregistration functions, adding/deleting device driver functions, adding/deleting I2C device functions, I2C transmission, sending and receiving functions. These functions are all interface functions that must be used in writing I2C device drivers. It is precisely because of these common interface functions that the code has strong portability and reusability. After understanding the basic framework of I2C device drivers in Linux, one of the first questions to be clear about writing your own device driver is that the kernel has already implemented that part, and that part that needs to be implemented. Because the I2C device driver is based on the bus device driver model, generally speaking, in the transplantation of Linux operating system, the Linux kernel has already implemented the bus part very well, so the driver of the bus part generally does not need to be concerned. What needs to be realized here is the i2c_driver and i2c_client structures of the device layer, and use the interface functions provided by the I2C subsystem to hook into the I2C bus. Every I2C device driver must first create an i2c_driver structure object, which contains some basic methods and information for I2C device detection and cancellation. Including the name of the device driver, the adapter's hook/cancel function pointer, etc. An example is shown below. The name field identifies the name of the driver (no more than 31 characters). The at-tach_adapter and detach_client fields are function pointers. These two functions are automatically called when the I2C device is registered. You need to implement these two by yourself. Functions. The i2c_driver object defined above is abstracted as an I2C driver model, which provides methods for detecting and deregistering I2C devices. The next step is to define the i2c_client structure, which represents a specific I2C device. The structure has a data pointer. It can point to any private device data, which may be used in more complex drivers. For each I2C device chip, the I2C device address of the device is set through the hardware connection. Therefore, the detection of I2C devices is generally done by the device address. Then, first declare the address list of the I2C device you want to detect and a macro in the driver code. An example is as follows: With the i2c_client structure representing the specific device and device ID, the attach_adapter and detach_client functions can be implemented. These two functions are automatically called by the system, and their implementation has a certain framework, which can be found in the driver example of the linux kernel source code. Because the code is too long, no specific analysis is done here. The implementation of different device functions will be slightly different. Generally, the work that attach_adapte needs to complete is to assign values ​​to the members of the i2c_client structure and call the interface function i2c_attach_cli-ent to attach the device to the adapter. The detach_client function does the opposite job. The last step is to write the initialization and exit functions of the module to add the driver to the I2C driver subsystem. Examples can be: At this point, the driver of the I2C device has been completed, but at this point, the driver has no practical use. It only provides a management framework for device drivers, so two additions must be made. The first aspect is to use the I2C bus to read and write the control/status registers of the external chip; the second aspect is to provide the application layer with the read and write interface of the I2C device, so that the application can read and write the device node to realize the I2C specific physical device Read and write. In order to realize the read and write operations of I2C device registers, the read and write interface functions provided by the Linux I2C subsystem must be used: Using these two functions to package according to the read and write timing of the chip, you can read and write the internal registers of the chip. Take writing the chip register as an example, you must write the address of the write register on the bus, and then write the write to the register The data, sample code is shown below. The sequence of reading the register is to write the address of the register to be read first, and then accept the data on the bus. The difference is not big, so it is not an example. To provide a read and write interface to the application layer, the I2C device driver must be encapsulated again for a simple character device driver. The I2C device is used as a simple character device, and the function pointer in the file operation function structure file_operation of the character device is implemented in turn. Corresponding interface functions, only the general framework is given here, and the specific implementation is very different for different chips. Define a character device structure cdev, and treat the I2C device as a normal character device. Define a file operation function structure, fill in the function pointer inside, and point out the specific function corresponding to the device operation. A general example is: The next step is to write the specific function corresponding to file_operaTIons. The last step is to add registration and deregistration operations for simple character devices in the initialization and exit functions of the module, including device number application and deregistration, and device registration and deregistration. At this point, after the compiled module is loaded into the kernel, various operations can be performed on the device file using the file system API in the user space. I2C bus is a very common interface technology in electronic system design, and Linux is a very popular embedded operating system. Writing driver programs for I2C bus devices under embedded Linux is a very important technology in embedded development and should not be ignored. This article first describes the overall framework of the Linux system I2C device driver, and then gives the general idea and framework for writing I2C device drivers, hoping to clarify the ideas for readers and deepen their understanding of writing I2C device drivers. All in all, the use of I2C bus communication can reach a very high rate, and multiple nodes can be connected to the bus, and each node is determined by a unique address. Ningbo Autrends International Trade Co.,Ltd. , https://www.supermosvape.com
The general idea and framework design of I2C device driver based on embedded Linux
0 Preface