默认配置
默认配置
boards/arm/nucleo_f401re/
├── nucleo_f401re.dts ← 板卡设备树主入口
├── nucleo_f401re_defconfig ← 默认 Kconfig 配置
├── board.cmake ← CMake 构建入口
overlay
1.新增加驱动需要修改对应板的设备树文件,通常选择以 .overlay 的形式增加或修改设备树节点Overlay的路径:
———boards/<架构>/<板级名称>/
———或者sample/xxx/boards
2.Overlay加载优先级(从高到低)
———显式指定文件最高优先级
通过CMake变量 DTC_OVERLAY_FILE 显式指定的文件(如 -DDTC_OVERLAY_FILE="file1.overlay;file2.overlay"),按声明顺序加载,后声明者覆盖先声明者
———自动扫描路径优先级(未显式指定时)
构建系统按顺序加载以下路径的文件,后加载者覆盖先加载者:
———第一级:socs/<SOC型号>.overlay(如 socs/nrf52840.overlay)
———第二级:boards/<板卡名>.overlay(如 boards/nrf52840dk.overlay)
———第三级:应用根目录的 app.overlay
———示例:boards/nrf52840dk.overlay 可覆盖 socs/nrf52840.overlay 的配置。
3.硬件修订版覆盖
若存在板卡修订版(如 nrf52840dk_v2),则 boards/<板卡名>_<修订版>.overlay 会覆盖基础板级文件(boards/<板卡名>.overlay
4.示例
&i2c1 {status = "okay";fake_eeprom: eeprom@77 {compatible = "fake-eeprom";reg = <0x77>;size = <1024>;pagesize = <16>;address-width = <8>;timeout = <5>;buffer_size = <4096>;/* read-only; */};
};
yaml
1.对于 .yaml 文件,其他内容主要是指定该设备树节点下的所有属性的约束条件,如指定类型,是否必须定义等
YAML绑定文件必须位于名为 dts/bindings/ 的目录中,但该目录的具体父路径可以是:
———Zephyr基础目录(zephyr/dts/bindings/)
———应用层目录(如 sample/xxx/dts/bindings/)
———自定义模块目录(${ZEPHYR_MODULE_DIR}/dts/bindings/)
2.覆盖情况:
构建系统会自动扫描所有匹配 dts/bindings/ 的路径;当应用层绑定文件与基础或模块路径文件同名时,仅使用应用层文件进行节点验证与宏生成
3.示例
# Copyright (c) 2021 BrainCo Inc.
# SPDX-License-Identifier: Apache-2.0description: nucleo-g474re, fake-eepromcompatible: "fake-eeprom"include: [base.yaml, i2c-device.yaml]properties:size:type: intrequired: truedescription: Total EEPROM size in bytespagesize:type: intrequired: truedescription: EEPROM page size in bytesaddress-width:type: intrequired: truedescription: EEPROM address width in bitstimeout:type: intrequired: truedescription: EEPROM write cycle timeout in millisecondsread-only:type: booleanrequired: falsedescription: Disable writes to the EEPROMbuffer_size:type: intrequired: truedescription: EEPROM BUFFER
dts设备匹配驱动
获取设备
static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(temp_sensor)); //设备树节点的label属性
const struct device *const dev = DEVICE_DT_GET(DT_ALIAS(eeprom0)); //设备树节点的别名,如果有的话可以用这个
驱动匹配设备树节点
#define DT_DRV_COMPAT vendor_temp_sensor //需与设备树compatible中的逗号等非字母的特殊字符,替换为下划线
driver通过dts创建设备对象
#define INST_DT_FAKE_EEPROM(inst) DT_INST(inst, fake_eeprom)
#define FAKE_EEPROM_DEVICE(inst) static const struct eeprom_config eeprom_config_##inst = { .i2c = I2C_DT_SPEC_INST_GET(inst), .addr_width = DT_PROP(INST_DT_FAKE_EEPROM(inst), address_width), .readonly = DT_PROP(INST_DT_FAKE_EEPROM(inst), read_only), .timeout = DT_PROP(INST_DT_FAKE_EEPROM(inst), timeout), .size = DT_PROP(INST_DT_FAKE_EEPROM(inst), size), }; static uint8_t eeprom_buffer_##inst[DT_PROP(INST_DT_FAKE_EEPROM(inst), buffer_size)]; static struct eeprom_data eeprom_data_##inst = { .buffer = eeprom_buffer_##inst, .state = false, }; DEVICE_DT_INST_DEFINE(inst, &eeprom_init, NULL, &eeprom_data_##inst, &eeprom_config_##inst, POST_KERNEL, 10, &eeprom_api);
DT_INST_FOREACH_STATUS_OKAY(FAKE_EEPROM_DEVICE)
非dts匹配
driver创建设备对象
DEVICE_DEFINE(my_driver_0, "my_driver", my_driver_init, NULL, NULL, NULL, POST_KERNEL, 0, &api);
app获取设备
const struct device *dev = device_get_binding("my_driver");
设备驱动读写函数流程
通过I2C_DT_SPEC_INST_GET从设备树解析出i2c_dt_spec,并且也填充了i2c_dt_spec的bus成员,也即i2c总线,调用总线的api就可以操作i2c控制器的寄存器们,来完成读写工作;spi_dt_spec,gpio_dt_spec同理
struct s11059_dev_config {struct i2c_dt_spec bus;uint8_t gain;int64_t integration_time; /* integration period (unit: us) */
};#define S11059_INST(inst) \static struct s11059_data s11059_data_##inst; \static const struct s11059_dev_config s11059_config_##inst = { \.bus = I2C_DT_SPEC_INST_GET(inst), \.gain = DT_INST_PROP(inst, high_gain), \.integration_time = DT_INST_PROP(inst, integration_time)}; \SENSOR_DEVICE_DT_INST_DEFINE(inst, s11059_init, NULL, &s11059_data_##inst, \&s11059_config_##inst, POST_KERNEL, \CONFIG_SENSOR_INIT_PRIORITY, &s11059_driver_api);DT_INST_FOREACH_STATUS_OKAY(S11059_INST)static int s11059_control_write(const struct device *dev, uint8_t control)
{const struct s11059_dev_config *cfg = dev->config;const uint8_t opcode[] = {S11059_REG_ADDR_CONTROL, control};return i2c_write_dt(&cfg->bus, opcode, sizeof(opcode));
}static inline int i2c_write_dt(const struct i2c_dt_spec *spec,const uint8_t *buf, uint32_t num_bytes)
{return i2c_write(spec->bus, buf, num_bytes, spec->addr);
}static inline int i2c_write(const struct device *dev, const uint8_t *buf,uint32_t num_bytes, uint16_t addr)
{struct i2c_msg msg;msg.buf = (uint8_t *)buf;msg.len = num_bytes;msg.flags = I2C_MSG_WRITE | I2C_MSG_STOP;return i2c_transfer(dev, &msg, 1, addr);
}__syscall int i2c_transfer(const struct device *dev,struct i2c_msg *msgs, uint8_t num_msgs,uint16_t addr);static inline int z_impl_i2c_transfer(const struct device *dev,struct i2c_msg *msgs, uint8_t num_msgs,uint16_t addr)
{const struct i2c_driver_api *api =(const struct i2c_driver_api *)dev->api;if (!num_msgs) {return 0;}if (!IS_ENABLED(CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS)) {msgs[num_msgs - 1].flags |= I2C_MSG_STOP;}int res = api->transfer(dev, msgs, num_msgs, addr);i2c_xfer_stats(dev, msgs, num_msgs);if (IS_ENABLED(CONFIG_I2C_DUMP_MESSAGES)) {i2c_dump_msgs_rw(dev, msgs, num_msgs, addr, true);}return res;
}
主机驱动读写api
static DEVICE_API(i2c, nxp_ii2c_driver_api) = {.configure = nxp_ii2c_configure,.transfer = nxp_ii2c_transfer,
#ifdef CONFIG_I2C_CALLBACK.transfer_cb = nxp_ii2c_transfer_cb,
#endif
#ifdef CONFIG_I2C_RTIO.iodev_submit = i2c_iodev_submit_fallback,
#endif
};