I2C overview
本文提供有关I2C系统以及如何插入I2C STM32驱动程序的基本信息。
目录
Framework purpose
本文旨在解释如何更准确地使用I2C:
- 如何在 Linux® BSP 上激活I2C接口
- 如何从内核空间访问I2C
- 如何从用户空间访问I2C。
本文介绍了在master 和 slave模式下的 Linux® I2C[1]接口。
通过该外部资源,提出了I2C[2] 的简介。
有关 slave 接口的说明,请参见slave-interface[3].
System overview
I2C 是“ IC间总线”的缩写,“ Inter-IC”总线是一种简单的总线协议,广泛用于低数据速率通信就足够了。
I2C是微处理器 I2C 外围设备接口的缩写。
在微处理器设备周围,用户可以添加许多 I2C 外部设备来创建定制板。 可以通过I2C从用户空间或内核空间访问每个外部设备。
Component description
Board external I2C devices
- 从设备X是相对于STM32表现为从设备的物理设备(通过 I2C 总线连接到STM32)。
STM32仍是 I2C 总线上的主机。
- 主设备X是相对于STM32充当主设备的物理设备 (通过 I2C 总线连接到STM32) 。
在这种情况下,STM32充当 I2C 总线上的从设备。
STM32 I2C internal peripheral controller
它对应于STM32 I2C适配器,该适配器处理与同一总线上连接的任何外部设备的通信。
它管理从设备(如果有的话),并且如果连接了外部主设备,则可以充当从设备。
STM32微处理器设备通常嵌入 I2C internal peripheral 的多个实例,以管理多个I2C总线。 提供了一个驱动程序,用于控制硬件。
i2c-stm32
内部STM32 I2C控制器驱动程序向基于i2c-core-base的ST I2C内部外围控制器抽象层提供了支持。
它定义了I2C核心基础要使用的所有I2C传输方法算法,其中包括I2C和SMBus[4] 传输API ,注册/注销从属API和功能检查。
即使I2C Core可以在整个标准I2C消息中模拟SMBus协议,所有SMBus功能都在驱动程序中实现。
i2c-core
这是通信的大脑:它实例化和管理所有总线和外围设备。
- 如其名称所述,为i2c-core ,它是I2C核心引擎,但它也负责解析适配器和/或设备的设备树条目
- i2c-core-smbus处理所有与SMBus相关的API。
- i2c-core-slave 管理充当STM32中的从设备的I2C设备。
- i2c-smbus处理特定的协议SMBus警报。 (由I2C核心库处理的SMBus主机通知)
Board peripheral drivers
该层表示与物理外围设备关联的所有驱动程序。
外围设备驱动程序可以编译为内核模块,也可以直接编译为内核(也称为内置).
i2c-dev
i2c-dev是用户与外围设备之间的接口。 它是一个内核驱动程序,它使用此dev-interface API提供对用户空间应用程序的I2C总线访问。 请参见示例API Description.
i2c-tools
I2C Tools软件包提供了:
- shell命令通过i2c-dev通过SMBus协议访问I2C
- library 将SMBus函数用于用户空间应用程序,所有这些函数都在这个SMBus协议API中进行了描述。
Note : 某些外围设备无需SMBus协议即可工作。
API description
libi2c
I2C工具[5] 软件包提供了一组Shell命令,这些命令主要使用SMBus协议访问I2C和API, 开发一个应用程序(libi2c)。
所有工具和libi2c均依赖SMBus API,但 i2ctransfer不是,因为它依赖于标准I2C协议。
工具和libi2c通过devfs读/写/ ioctl调用访问SMBus和I2C API。
SMBus协议构成 I2C 规范中定义的数据传输格式的子集。
SMBus规范中定义的标准方法无法访问不符合这些协议的I2C外设。
有关 I2C[6] 和SMBus协议[7]的更多详细信息,请参见外部参考。
libi2c API模拟“SMBus协议”[7] ,但在用户空间级别。
此库中的API与 SMBus protocol[7]中的API相同。除了特定的SMBus协议API(例如SMBus Host Notify和SMBus Alert)外,所有SMBus API均在此处重复。
User space application
User space application 正在使用内核驱动程序(i2c-dev),该驱动程序通过devfs提供I2C访问。
支持的系统调用: open(), close(), read(), write(), ioctl(), llseek(), release().
Constant | Description |
---|---|
I2C_SLAVE/I2C_SLAVE_FORCE | Sets slave address for read/write operations |
I2C_FUNCS | Gets bus functionalities |
I2C_TENBIT | 10bits address support |
I2C_RDWR | Combined R/W transfer (one STOP only) |
I2C_SMBUS | Perform an SMBus transfer instead of standard I2C |
以上命令是主要命令(框架中定义了更多命令): 请参见dev-interface API[8] 获取完整列表。
Kernel space peripheral driver
Kernel space peripheral driver 同时访问 I2C 和SMBus设备,并使用以下 I2C核心API[9]
Configuration
Kernel configuration
使用Linux Menuconfig工具在内核配置中激活I2C: Menuconfig or how to configure kernel.
[x] Device Drivers [x] I2C support [x] I2C device interface [ ] I2C Hardware Bus support [x] STMicroelectronics STM32F7 I2C support
这可以在您的内核中手动完成:
CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_STM32F7=y
如果软件需要SMBus特定协议,例如SMBus Alert协议和SMBus Host Notify协议,则添加:
[x] Device Drivers [x] I2C support [x] I2C device interface [ ] Autoselect pertinent helper modules [x] SMBus-specific protocols [ ] I2C Hardware Bus support [x] STMicroelectronics STM32F7 I2C support
这可以在您的内核中手动完成:
CONFIG_I2C_SMBUS=y
Device tree configuration
How to use the framework
本节介绍如何使用框架访问I2C外设。
i2c-tools package
在用户空间中将 I2C Tools 与基于SMBus API协议[7] 的shell命令配合使用,可以轻松快速地访问I2C,而无需编写任何代码。
用例 许多外壳命令允许检测I2C总线并通过SMBus协议访问I2C外设。 该软件包包括一个库,以便在C程序中使用SMBus协议。
有关详细说明,请访问 link。
User space application
Allows to develop an application using the i2c-dev kernel driver in user space with this device interface[8].
Use case : by loading i2c-dev module, user can access I2C through the /dev interface. Access to I2C can be done very easily with functions open(), ioctl(), read(), write() and close(). If the peripheral is compatible, SMBus protocol access is also possible using the I2C Tools library.
Kernel space driver
Allows to develop a driver compiled into the kernel or inserted as a module using this I2C core API[9]
The Linux kernel provides example about how to write an I2C client driver.[10]
Use case : control I2C peripheral with a specific driver inside the kernel space. The driver initializes all parameters while system is booting and creates an access to the peripheral data through sysfs for example.
Board description
To instantiate a peripheral, several methods exist: see instantiating devices[11] for more details.
The below information focuses on device tree, sysfs and Application Code.
Device tree
The device tree is a description of the hardware that is used by the kernel to know which devices are connected. In order to add a slave device on an I2C bus, complete the device tree with the information related to the new device.
Example : with an EEPROM
1 &i2c4 {
2 status = "okay";
3 i2c-scl-rising-time-ns = <185>;
4 i2c-scl-falling-time-ns = <20>;
5
6 dmas = <&mdma1 36 0x0 0x40008 0x0 0x0 0>,
7 <&mdma1 37 0x0 0x40002 0x0 0x0 0>;
8 dma-names = "rx", "tx";
9
10 eeprom@50 {
11 compatible = "at,24c256";
12 pagesize = <64>;
13 reg = <0x50>;
14 };
15 };
The EEPROM is now instantiated on the bus i2c-X (X depends on how many adapters are probed at runtime) at address 0x50 and it is compatible with the driver registered with the same property.
Please note the driver specifies a SCL rising/falling time as input.
Please refer to I2C device tree configuration for proper configuration and explanation.
Be aware the I2C specification reserves a range of addresses for special purposes, see slave addressing[12].
The below figure shows the relation between the device tree and how it is used :
sysfs
Through sysfs, i2c-core offers the possibility to instantiate and remove a peripheral:
Add a peripheral "myPeripheralName" attached to the bus x at the address 0xAA
Note that the field "myPeripheralName" should have the same name as the compatible driver string so that they match one another.
echo myPeripheralName 0xAA > i2c-x/new_device
Remove a peripheral attached to the bus x at the address 0xAA
echo 0xAA > i2c-x/delete_device
Into each driver directory (/sys/bus/i2c/drivers/at24/ for the EEPROM peripheral example), it is possible to:
bind a peripheral with a driver
echo 3-0050 > bind
unbind a peripheral with a driver
echo 3-0050 > unbind
Application code
Here is a minimalist code to register a new slave device onto I2C adapter without Device Tree usage.
1 #include <linux/i2c.h>
2
3 /* Create a device with slave address <0x42> */
4 static struct i2c_board_info stm32_i2c_test_board_info = {
5 I2C_BOARD_INFO("i2c_test07", 0x42);
6 };
7
8 /*
9 Module define creation skipped
10 */
11
12 static int __init i2c_test_probe(void)
13 {
14 struct i2c_adapter *adap;
15 struct i2c_client *client;
16
17 /* Get I2C controller */
18 adap = i2c_get_adapter(i);
19 /* Build new devices */
20 client = i2c_new_device(adap,&stm32_i2c_test_board_info);
21 }
How to trace and debug the framework
In Linux® kernel, there are standard ways to debug and monitor I2C. The debug can take place at different levels: hardware and software.
How to trace
Dynamic trace
Detailed dynamic trace is available here How to use the kernel dynamic debug
Board $> echo "file i2c-* +p" > /sys/kernel/debug/dynamic_debug/control
This command enables all traces related to I2C core and drivers at runtime.
Nonetheless at Linux® Kernel menu configuration level, it provides the granularity for debugging: Core and/or Bus.
Device Drivers -> [*] I2C support -> [*] I2C Core debugging messages [*] I2C Bus debugging messages
- I2C Core debugging messages (CONFIG_I2C_DEBUG_CORE)
Compile I2C engine with DEBUG flag. - I2C Bus debugging messages (CONFIG_I2C_DEBUG_BUS)
Compile I2C drivers with DEBUG flag.
Having both I2C Core and I2C Bus debugging messages is equivalent to using the above dynamic debug command: the dmesg output will be the same.
Bus snooping
Bus snooping is really convenient for viewing I2C protocol and see what has been exchanged between the STM32 and the devices.
As this debug feature uses Ftrace, please refer to the Ftrace article for enabling it.
In order to access to events for I2C bus snooping, the following kernel configuration is necessary:
Kernel hacking -> [*] Tracers -> [*] Trace process context switches and events
Depending on the protocol being used, it is necessary to enable i2c and/or smbus tracers as follow:
echo 1 > /sys/kernel/debug/tracing/events/i2c/enable echo 1 > /sys/kernel/debug/tracing/events/smbus/enable
Then tracing is enabled using the following command:
echo 1 > /sys/kernel/debug/tracing/tracing_on
After a transaction, trace can be read by looking at the trace file:
cat /sys/kernel/debug/tracing/trace
Here is part of the output, and how it looks like when using i2cdetect command on the i2c-0 bus:
... smbus_write: i2c-0 a=003 f=0000 c=0 QUICK l=0 [] ... smbus_result: i2c-0 a=003 f=0000 c=0 QUICK wr res=-6 ... smbus_write: i2c-0 a=004 f=0000 c=0 QUICK l=0 [] ... smbus_result: i2c-0 a=004 f=0000 c=0 QUICK wr res=-6
Notice that i2cdetect, i2cget/i2cput, i2cdump are doing smbus protocol based transactions. |
On the contrary, below output shows the result of a transaction done in I2C protocol mode:
... i2c_write: i2c-1 #0 a=042 f=0000 l=1 [45] ... i2c_result: i2c-1 n=1 ret=1 ... i2c_write: i2c-2 #0 a=020 f=0000 l=1 [45] ... i2c_result: i2c-2 n=1 ret=1
The utilization of traces of I2C bus is well described here I2C bus snooping[13].
How to debug
Detect I2C configuration
sysfs
When a peripheral is instantiated, i2c-core and the kernel export different files through sysfs :
/sys/class/i2c-adapter/i2c-x shows all instantiated I2C buses with 'x' being the I2C bus number.
/sys/bus/i2c/devices lists all instantiated peripherals. For example, there is a directory named 3-0050 that corresponds to the EEPROM peripheral at address 0x50 on bus number 3.
/sys/bus/i2c/drivers lists all instantiated drivers. Directory named at24/ is the driver of EEPROM.
/sys/bus/i2c/devices/3-0050/ / / / /i2c-3/3-0050/ / /drivers/at24/3-0050/
/sys/class/i2c-adapter/i2c-0/ /i2c-1/ /i2c-2/ /i2c-3/3-0050/ /i2c-4/ /i2c-5/
devfs
If i2c-dev driver is compiled into the kernel, the directory dev contains all I2C bus names numbered i2c-0 to i2c-n.
/dev/i2c-0 /i2c-1 /i2c-2 /i2c-3 /i2c-4 /i2c-n
i2c-tools
Check all I2C instantiated adapters:
Board $>i2cdetect -l
See i2c-tools for full description.
Source code location
- I2C Framework driver is in drivers/i2c drivers/i2c| |}} drivers/i2c drivers/i2c
- I2C STM32 Driver is in drivers/i2c/busses/i2c-stm32f7.c| |}} drivers/i2c/busses/i2c-stm32f7.c
- User API for I2C bus is in include/uapi/linux/i2c.h| |}} include/uapi/linux/i2c.h and I2C dev is include/uapi/linux/i2c-dev.h| |}} include/uapi/linux/i2c-dev.h .
To go further
Bootlin has written a nice walkthrough article: Building a Linux system for the STM32MP1: connecting an I2C sensor[14]
References
- ↑ http://www.i2c-bus.org/
- ↑ https://bootlin.com/doc/training/linux-kernel/
- ↑ Documentation/i2c/slave-interface| |}} Documentation/i2c/slave-interface slave interface description
- ↑ https://www.i2c-bus.org/smbus/
- ↑ https://i2c.wiki.kernel.org/index.php/I2C_Tools
- ↑ Documentation/i2c/summary| |}} Documentation/i2c/summary I2C and SMBus summary
- ↑ 7.07.17.27.3 Documentation/i2c/smbus-protocol| |}} Documentation/i2c/smbus-protocol SMBus protocol summary
- ↑ 8.08.1 Documentation/i2c/dev-interface| |}} Documentation/i2c/dev-interface dev-interface API
- ↑ 9.09.1 https://www.kernel.org/doc/html/latest/driver-api/i2c.html
- ↑ https://www.kernel.org/doc/html/latest/i2c/writing-clients.html
- ↑ Documentation/i2c/instantiating-devices| |}} Documentation/i2c/instantiating-devices How to instantiate I2C devices
- ↑ http://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing Slave addressing
- ↑ https://linuxtv.org/wiki/index.php/Bus_snooping/sniffing#i2c I2C Bus Snooping
- ↑ https://bootlin.com/blog/building-a-linux-system-for-the-stm32mp1-connecting-an-i2c-sensor/