3000ok新开传奇网站,h5个人网站模板下载,东莞阳光网招标平台,做导航网站怎么赚钱linux kernel 引入 dts 的背景
http://www.wowotech.net/linux_kenrel/why-dt.html
什么是 device tree
device tree 是一种描述硬件资源的数据结构。device tree 可以描述的信息包括 cpu 的数量和类别、内存基地址和大小、clock 控制器和 clock 使用情况、外设基地址以及…linux kernel 引入 dts 的背景
http://www.wowotech.net/linux_kenrel/why-dt.html
什么是 device tree
device tree 是一种描述硬件资源的数据结构。device tree 可以描述的信息包括 cpu 的数量和类别、内存基地址和大小、clock 控制器和 clock 使用情况、外设基地址以及配置信息、中断控制器和中断使用情况。
设备树解决了什么问题
本质上device tree 改变了原来用 hardcode 方式将 HW 配置信息嵌入到内核代码驱动程序的方法改用 bootloader 传递一个 DB 的形式。对于基于 ARM CPU 的嵌入式系统我们习惯于针对每一个 platform 进行内核的编译但是随着 ARM 在消费电子上的广泛应用我们期望 ARM 能够像 x86 那样用一个 kernel image 来支持多个 platform。
其实就是将驱动程序中硬编码的定义改为由设备树来描述优化了驱动程序同时让 kernel image 相对独立同一份 image 可在不同的 soc 上运行。
总之设备树是为 kernel 服务的让内核与设备尽可能的保持独立。
Linux 与 Zephyr 的 dts
linux
以下是 at5050_codec.c
// 通过 platform 驱动框架将驱动注册到系统中去.
// 驱动程序解析 dts, 避免硬编码到该文件中
static struct platform_driver at5k_codec_driver {.driver {.name at5050-codec,.owner THIS_MODULE,.of_match_table of_match_ptr(of_at5k_codec_match),},.probe at5k_codec_probe,.remove at5k_codec_remove,
};module_platform_driver(at5k_codec_driver);linux 中的驱动程序会通过 一套设备树接口获取设备树节点通过这些节点的描述实现驱动程序注意这里驱动程序是强依赖设备树的驱动程序不可能做到通用不同的 soc 之间寄存器操作存在差异通用的是驱动框架如上述 platform 框架或者字符设备、块设备、mtd 设备框架等等。
驱动程序需要有一套 设备树解析接口 。注册到系统中之后应用程序就能够使用设备了。
zephyr
以下是 gpio_stm32.c :
// 操作 pin 设备的虚函数表,注册到系统中去这里是真正意义上的驱动代码通常是直接操作寄存器或者调用 hal 层提供的接口
static const struct gpio_driver_api gpio_stm32_driver {.pin_configure gpio_stm32_config,.port_get_raw gpio_stm32_port_get_raw,.port_set_masked_raw gpio_stm32_port_set_masked_raw,.port_set_bits_raw gpio_stm32_port_set_bits_raw,.port_clear_bits_raw gpio_stm32_port_clear_bits_raw,.port_toggle_bits gpio_stm32_port_toggle_bits,.pin_interrupt_configure gpio_stm32_pin_interrupt_configure,.manage_callback gpio_stm32_manage_callback,
};// 注册 pin 设备系统初始化期间会处理好
DEVICE_DT_DEFINE(__node, \gpio_stm32_init, \PM_DEVICE_DT_GET(__node), \gpio_stm32_data_## __suffix, \gpio_stm32_cfg_## __suffix, \PRE_KERNEL_1, \CONFIG_GPIO_INIT_PRIORITY, \gpio_stm32_driver)#define GPIO_DEVICE_INIT_STM32(__suffix, __SUFFIX) \GPIO_DEVICE_INIT(DT_NODELABEL(gpio##__suffix), \__suffix, \DT_REG_ADDR(DT_NODELABEL(gpio##__suffix)), \STM32_PORT##__SUFFIX, \DT_CLOCKS_CELL(DT_NODELABEL(gpio##__suffix), bits),\DT_CLOCKS_CELL(DT_NODELABEL(gpio##__suffix), bus))// 根据 dts 文件中对于 gpio 的描述注册该 pin 设备到系统中
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpioa), okay)
GPIO_DEVICE_INIT_STM32(a, A);
#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(gpioa), okay) */以下是应用代码 main.c:
// led0 这个名字在设备树文件中定义
#define LED_BLUE DT_ALIAS(led0)
#define LED_RED DT_ALIAS(led1)// 根据设备树中的描述,得到操作该 pin 设备的句柄
static const struct gpio_dt_spec led_blue GPIO_DT_SPEC_GET(LED_BLUE, gpios);
static const struct gpio_dt_spec led_red GPIO_DT_SPEC_GET(LED_RED, gpios);int main(void)
{printk(hello zephyr.\n);// 通过统一设备树接口,操作 pin 设备而不是直接操作 hal 层方法if (!gpio_is_ready_dt(led_blue) !gpio_is_ready_dt(led_red)) {return 0;}if (gpio_pin_configure_dt(led_blue, GPIO_OUTPUT_ACTIVE) 0 || gpio_pin_configure_dt(led_red, GPIO_OUTPUT_ACTIVE) 0) {return 0;}sdram_test();while (1) {if (gpio_pin_toggle_dt(led_blue) 0 || gpio_pin_toggle_dt(led_red)) {return 0;}k_msleep(SLEEP_TIME_MS);}return 0;
}
// 可以看出应用程序在使用 pin 设备时使用的是一套带有 dt 字符的抽象接口这里让应用程序感受到了设备树的存在zephyr 支持两种设备驱动的注册一种是使用设备树相关宏 DEVICE_DT_DEFINE另外一种是不使用设备树相关宏 DEVICE_DEFINE如果使用设备树应用程序中操作设备时需要 依赖设备树具体操作如下 ADC_DT_SPEC_GET 使用该宏获取 adc 设备需要知道该设备在设备树中的命名static inline int adc_read_dt(const struct adc_dt_spec *spec,const struct adc_sequence *sequence)读取 struct adc_dt_spec 设备static inline int adc_channel_setup_dt(const struct adc_dt_spec *spec) 配置 adc 设备这里略微不合理我们希望应用程序不要考虑那么多不要知道设备树的存在。 如果不使用设备树而是使用 DEVICE_DEFINE 注册设备那么应用程序对设备的操作如下 static inline const struct device * device_get_binding(const char * name)返回一个 device 对象如果该设备是个 adc 设备使用如下接口操作设备static inline int adc_read(const struct device * dev, const struct adc_sequence * sequence)读取 adc 设备会调用到驱动层实现的虚函数表static inline int adc_channel_setup(const struct device * dev, const struct adc_channel_cfg * channel_cfg)配置 adc 设备这里应用程序通过 binding 接口得到了设备句柄
抛开 linux 历史原因直接从内核源码来看目前 dts 目前为什么是 linux 的标配
linux kernel 已经依赖 dts如 kernel 内部稳定的驱动框架platform、spi、i2c 等等。从驱动框架来看dts 是必须的从使用驱动框架的人linux 驱动开发人员来看dts 也是必须的。Linux 驱动开发人员需要使用 dts 中描述的信息来写驱动。
为什么各大主流 rtos 不引入 dts ?
首先 rtos 面向的是小型嵌入式设备业务逻辑没那么复杂其次如果引入 dts那么 dtb 文件如何放入 mcu / mpu 里面需要额外引入其他依赖。设备开机重启后需要保证 dtb 文件依然存在需要预留持久化存储空间。这可能也是 zephyr 为什么改变 dts 常规使用方法编译 dts 成为 dtb再使用一些工具把 dtb 放到持久化存储的地方而是改为编译时解析不用再考虑 dtb 文件放哪里了系统启动流程不用那么复杂了甚至系统都没起来堆都没初始化都能先初始化设备了。
综上dts 对于 rtos 来说意义不大甚至会引入副作用。
dts 文件如何使用 dtc 工具编译成 dtb
先使用 c / cpp 预处理器展开头文件
cpp -nostdinc -I /home/null/zephyrproject/zephyr/dts/arm/ -I /home/null/zephyrproject/zephyr/dts/common -undef -x assembler-with-cpp stm32h750_art_pi.dts stm32h750_art_pi.dts.preprocessed然后使用 dtc 工具
dtc -I dts -O dtb -o example.dtb example.dts如果 dts 文件中使用了 include 关键字则需要通过 c/cpp 预处理器来展开头文件dtc 工具是不负责展开头文件的只是编译否则会报错。