默認配置
默認配置
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
};
?
?