Device Tree

Consumer(应用层和其他driver如何调用i2c接口)

方法1:

设备树API

// 获取i2c device node id
#define I2C_DEV	DT_COMPAT_GET_ANY_STATUS_OKAY(test_i2c) // i2c deivce的compatible,不是i2c bus

// 通过I2C_DT_SPEC_GET宏从设备树获得i2c_dt_spec结构体
struct i2c_dt_spec {
	const struct device *bus;
	uint16_t addr;
};

#define I2C_DT_SPEC_GET_ON_I2C(node_id)					\
	.bus = DEVICE_DT_GET(DT_BUS(node_id)),				\
	.addr = DT_REG_ADDR(node_id)

static struct i2c_dt_spec i2c = I2C_DT_SPEC_GET(I2C_DEV);

可以调用i2c_xxx_dt进行i2c传输了

static inline int i2c_write_dt(const struct i2c_dt_spec *spec,
			       const uint8_t *buf, uint32_t num_bytes)
static inline int i2c_read_dt(const struct i2c_dt_spec *spec,
			      uint8_t *buf, uint32_t num_bytes)
static inline int i2c_write_read_dt(const struct i2c_dt_spec *spec,
				    const void *write_buf, size_t num_write,
				    void *read_buf, size_t num_read)

方法2:

// drivers/i2c.h
__syscall int i2c_configure(const struct device *dev, uint32_t dev_config);
__syscall int i2c_get_config(const struct device *dev, uint32_t *dev_config);

static inline int i2c_write(const struct device *dev, const uint8_t *buf,
			 uint32_t num_bytes, uint16_t addr)
static inline int i2c_read(const struct device *dev, uint8_t *buf,
			   uint32_t num_bytes, uint16_t addr)
static inline int i2c_write_read(const struct device *dev, uint16_t addr,
				 const void *write_buf, size_t num_write,
				 void *read_buf, size_t num_read)
__syscall int i2c_transfer(const struct device *dev,
			   struct i2c_msg *msgs, uint8_t num_msgs,
			   uint16_t addr);

等等还有一些读写函数。

没有__syscall标识的函数,user space应该不能调用?

参考zephyr/tests/drivers/i2c/i2c_api/src/test_i2c.c中的流程:

i2c_configure(i2c_dev, i2c_cfg); // 首先调用i2c_configure, 配置i2c controller。
i2c_get_config(i2c_dev, &i2c_cfg_tmp); // 接着调用get_config, 判断配置是否下对。
i2c_write(i2c_dev, datas, 2, 0x1E); // 可以发送i2c数据了。
i2c_read(i2c_dev, datas, 6, 0x1E); // i2c read读回。

Provider(I2C driver)

i2c_configure会调用到driver的api->configure。以i2c_dw.c dwsignware i2c ip为例,会进入i2c_dw_runtime_configure配置函数。

主要做的事情有:

  1. 保存好传入的配置到dw->app_config。
  2. 根据传入的i2c speed配置i2c的lcnt,hcnt。
  3. 清中断。

注意这边.configure函数并没有真正把配置写到寄存器中, 而是在.transfer函数中的set_up函数才会写入

static int i2c_dw_runtime_configure(const struct device *dev, uint32_t config)
{
	//...
	dw->lcnt = value;
	dw->hcnt = value;
	//...
	read_clr_intr(reg_base);
	//...
}

i2c_get_config类似,调用api->get_config(),这里i2c_dw.c未实现该回调函数。

接下来看I2C的read,write函数。这两个函数传入设备dev, 数据bufferbuffer length, slave address即可传输i2c数据。
i2c_read/i2c_write->i2c_transfer->api->transfer->i2c_dw_transfer

看下底层的i2c_dw_transfer函数,调用了i2c_dw_setup做的事情有:

  1. 先关闭i2c controller。
  2. 屏蔽+清中断。
  3. 将之前存入dw->app_data写入IC_CON寄存器。
  4. 配置IC_TAR寄存器。
  5. 配置dw->lcnt, dw->hcnt 等等参数,都写入寄存器。

配置完成后enable controller。set_bit_enable_en(reg_base)
接着就是一系列的发送i2c message配置流程。打开中断后因为tx_empty会进入ISR,发送/接收数据。

寄存器flow

CONFIG_I2C_STATS开关

看看打开这个对debug有没有帮助

Shell I2C 命令

打开CONFIG_I2C_SHELL,编译i2c_shell.c.

i2c -h 帮助菜单.

uart:~$ i2c -h
i2c - I2C commands
Subcommands:
  scan        :Scan I2C devices
  recover     :Recover I2C bus
  read        :Read bytes from an I2C device
  read_byte   :Read a byte from an I2C device
  write       :Write bytes to an I2C device
  write_byte  :Write a byte to an I2C device
  speed       :Configure I2C bus speed

device list 展示可用的设备.

uart:~$ device list
devices:
- clock@5000 (READY)
- gpio@842500 (READY)
- CRYPTOCELL_SW (READY)
- uart@8000 (READY)
- nrf91_socket (READY)
- i2c@9000 (READY)
- flash-controller@39000 (READY)
- bme280@76 (READY)
  requires: i2c@9000
- lis2dh@18 (READY)
  requires: gpio@842500
  requires: i2c@9000

i2c scan i2c@9000 扫描i2c bus上的设备.

uart:~$ i2c scan i2c@9000
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:             -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- 18 -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- 39 -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- 51 -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 --
4 devices found on i2c@9000

i2c 读写命令

uart:~$ i2c read_byte i2c@9000 0x58 0x12
Output: 0xff
uart:~$ i2c write_byte i2c@9000 0x58 0x12 0x00
uart:~$ i2c read_byte i2c@9000 0x58 0x12
Output: 0x0