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
配置函数。
主要做的事情有:
- 保存好传入的配置到dw->app_config。
- 根据传入的i2c speed配置i2c的lcnt,hcnt。
- 清中断。
注意这边.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
, 数据buffer
,buffer length
, slave address
即可传输i2c数据。i2c_read/i2c_write
->i2c_transfer
->api->transfer
->i2c_dw_transfer
看下底层的i2c_dw_transfer
函数,调用了i2c_dw_setup
做的事情有:
- 先关闭i2c controller。
- 屏蔽+清中断。
- 将之前存入
dw->app_data
写入IC_CON
寄存器。 - 配置
IC_TAR
寄存器。 - 配置
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