3. I2C Usage
I2C (Inter-Integrated Circuit) is a widely used serial communication protocol primarily used for data communication between a master device (e.g., microcontroller, processor) and peripheral devices (such as sensors, displays, memories, etc.). It is simple and efficient, making it well-suited for short-distance, low-speed data transmission.
3.1 I2C Communication Working Principle
I2C uses two wires for communication:
- SCL (Serial Clock Line): The clock signal generated by the master device to synchronize data transmission.
- SDA (Serial Data Line): Used to transfer data between devices.
I2C is a multi-master, multi-slave protocol, meaning that multiple master and slave devices can be connected to the bus, but only one master device can control communication at any given time.
3.2 I2C Usage Example
3.2.1 I2C Pins
The ArmSoM series boards, such as the CM5-IO, have I2C peripherals on the 40-pin header. Here's an example using CM5-IO, referring to the CM5-IO 40PIN Pinout:
I2C | Pin | Function |
---|---|---|
I2C3_SDA_M0 | Pin 3 | I2C3 Data Line |
I2C3_SCL_M0 | Pin 5 | I2C3 Clock Line |
I2C7_SDA_M1 | Pin 19 | I2C7 Data Line |
I2C7_SCL_M1 | Pin 23 | I2C7 Clock Line |
I2C4_SDA_M3 | Pin 27 | I2C4 Data Line |
I2C4_SCL_M3 | Pin 29 | I2C4 Clock Line |
I2C6_SDA_M2 | Pin 89 | I2C6 Data Line |
I2C6_SCL_M2 | Pin 88 | I2C6 Clock Line |
3.2.2 Enabling I2C Communication Interface
By default, the I2C interface is disabled and needs to be enabled for use.
In the Armbian operating system, the /boot/armbianEnv.txt
file is used to configure parameters and device tree overlays during the system boot process. You can edit this file to enable or disable I2C device tree overlays, ensuring the I2C bus is correctly loaded at boot.
If you want to check or enable I2C-related device tree overlays, follow these steps:
- Check Device Tree Overlay Configuration
Open the file: Open the /boot/armbianEnv.txt
file using a text editor like nano
or vim
, for example:
sudo nano /boot/armbianEnv.txt
Here, we’ll enable i2c3-m0
as an example, by adding rk3576-i2c3-m0
like this:
overlays=rk3576-i2c3-m0
The overlays
line specifies the device tree overlays (e.g., i2c0
and i2c1
are common I2C bus overlays). If you don't see these, you can manually add them.
After editing, save the file and exit the editor. Then reboot the system to apply the changes:
// First, execute sync
root@armsom-cm5-io:/home/armsom# sync
root@armsom-cm5-io:/home/armsom# sudo reboot
- Q: If the system is rebooted by directly pulling the power, is it possible that files are not updated or the overlay system fails to start?
- A: When you abruptly disconnect the power or force a shutdown, there is a risk that files may not be properly synchronized from memory (RAM) to the storage device (e.g., hard drive or SSD). This happens because the operating system typically caches data in memory and writes it to the disk periodically. To avoid this issue, it is recommended to run the "sync" command before shutting down, ensuring that all data is written to the disk before pulling the power or shutting down.
- Verify I2C is Enabled
After rebooting, you can check if I2C is functioning properly by using the following commands:
Check for I2C devices: Use the i2cdetect
tool to check if the I2C bus is available:
root@armsom-cm5-io:/home/armsom# sudo apt-get install i2c-tools # If not installed, install i2c-tools
root@armsom-cm5-io:/home/armsom# sudo i2cdetect -y 3
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
-y 3
checks the first I2C bus. If a list of device addresses is returned, it means I2C has been successfully enabled and is working.
- Ensure that the hardware itself supports and has the appropriate I2C bus enabled. For custom hardware, make sure the connections are correct and the hardware supports the I2C interface.
- Different Armbian versions and hardware platforms may have different names for the device tree overlays, so adjust the overlay names according to your specific platform.
3.3 I2C Driver Parameter Configuration
The main configuration of I2C parameters is the I2C frequency configuration. Besides being dependent on the chip, the I2C frequency is primarily determined by the I2C SCL rise time. This is because the I2C protocol standard specifies requirements for both the rise and fall times, especially for the rise time. If the rise time exceeds the maximum value defined by the protocol, the I2C communication may fail. Below is a range of the maximum and minimum values defined by the protocol. The diagram below shows the relationship between the two:
The rise time (Tr) and fall time (Tf) need to be measured using an oscilloscope, as shown in the diagram below:
3.3.1 i2c-rk3x.c Configuration
The configuration for the i2c-rk3x.c driver is specified in the Device Tree (DTS), as described in the file Documentation/devicetree/bindings/i2c/i2c-rk3x.txt
. Below is an explanation of the key configuration options:
- clock-frequency: The default frequency is 100k and does not need to be configured. For other I2C frequencies, configuration is required, and the maximum frequency that can be configured is determined by the i2c-scl-rising-time-ns.
- For example, to configure for 400k, set
clock-frequency = <400000>
.
- For example, to configure for 400k, set
- i2c-scl-rising-time-ns: The SCL rise time is determined by the hardware. This can be adjusted by changing the pull-up resistors and must be measured using an oscilloscope. For example, if the measured SCL rise time is 365ns, set
i2c-scl-rising-time-ns = <365>
. (This is optional, but it is crucial to ensure that the rise time does not exceed the maximum rise time for the configured I2C frequency, as defined by the I2C protocol). - i2c-scl-falling-time-ns: The SCL fall time is generally constant and can be equivalent to
i2c-sda-falling-time-ns
. (This is also optional and can be omitted).
Example configuration:
&i2c1 {
status = "okay";
i2c-scl-rising-time-ns = <265>;
i2c-scl-falling-time-ns = <11>;
clock-frequency = <400000>;
es8316: es8316@10 {
#sound-dai-cells = <0>;
compatible = "everest,es8316";
reg = <0x10>;
clocks = <&cru SCLK_I2S_8CH_OUT>;
clock-names = "mclk";
spk-con-gpio = <&gpio0 11 GPIO_ACTIVE_HIGH>;
hp-det-gpio = <&gpio4 28 GPIO_ACTIVE_LOW>;
};
};
3.4 GPIO-Based I2C Simulation
The kernel already supports GPIO-based I2C simulation. Please refer to the document Documentation/devicetree/bindings/i2c/i2c-gpio.txt
. Below is an example of how to configure the I2C node in the DTS file.
i2c@4 {
compatible = "i2c-gpio";
gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>, /* sda */
<&gpio5 8 GPIO_ACTIVE_HIGH>; /* scl */
i2c-gpio,delay-us = <2>; /* ~100 kHz */
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c4_gpio>;
status = "okay";
gt9xx: gt9xx@14 {
compatible = "goodix,gt9xx";
reg = <0x14>;
touch-gpio = <&gpio5 11 IRQ_TYPE_LEVEL_LOW>;
reset-gpio = <&gpio5 10 GPIO_ACTIVE_HIGH>;
max-x = <1200>;
max-y = <1900>;
tp-size = <911>;
tp-supply = <&vcc_tp>;
status = "okay";
};
};
Note: It is generally not recommended to use GPIO for I2C as it is inefficient.
3.5 Common I2C Issues
If the i2c-rk3x.c
driver returns -6 (-ENXIO)
when calling the I2C transfer interface, this indicates a NACK error, meaning the slave device did not respond. Common causes for this issue include:
- Incorrect I2C address.
- The I2C slave device is not functioning properly, such as being powered off, incorrect power-up sequencing, or other abnormal device conditions.
- The I2C timing does not match the slave's requirements. For example, the slave may require a stop signal, but a repeat start signal is sent instead.
- External interference on the I2C bus, which can be detected by observing the ACK waveform with an oscilloscope.
When encountering the I2C log: "timeout, ipd: 0x00, state: 1"
, this means the I2C controller is not functioning correctly and cannot generate an interrupt state or send a start condition. Possible causes include:
- Incorrect I2C SCL or SDA pin I/O multiplexing (iomux).
- Incorrect pull-up voltage for I2C, such as insufficient voltage or missing pull-up power.
- I2C pins being pulled low by the peripheral device.
- The I2C clock is not enabled, or the clock source is too weak.
- Both the
CON_START
andCON_STOP
bits are configured simultaneously.
When encountering the I2C log: "timeout, ipd: 0x10, state: 1"
, this indicates that the I2C controller is working normally, but the CPU is not responding to the I2C interrupt. This may be because CPU0 is blocked (check via cat /proc/interrupts
), or the I2C interrupt bit is disabled.
When encountering logs like "timeout, ipd: 0x80, state: 1"
, and observing that ipd
is 0x80, this suggests that the SCL line is being held low by a slave device. To identify which slave is causing the issue:
- Elimination method: This works well when there are only a few peripherals, and the issue has a high reproduction rate.
- Hardware modification: You can add a resistor to the SCL line to detect the voltage difference caused by the resistor. The device causing the issue can be identified by comparing the voltage differences on the two sides of the resistor. Alternatively, an oscilloscope can be used to observe the waveform and identify which device (slave or host) is pulling the bus low.
Commonly, when SDA is pulled low, the responsible slave can be identified. Similarly, the same method can be used when the SCL line is pulled low by referring to the previously described methods for analyzing the issue.