device tree 预研
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 工具是不负责展开头文件的,只是编译,否则会报错。
相关文章:
device tree 预研
linux kernel 引入 dts 的背景 http://www.wowotech.net/linux_kenrel/why-dt.html 什么是 device tree device tree 是一种描述硬件资源的数据结构。device tree 可以描述的信息包括 cpu 的数量和类别、内存基地址和大小、clock 控制器和 clock 使用情况、外设基地址以及…...

英伟达股价分析:英伟达股价能否上涨到150美元,接下来该如何操作?
来源:猛兽财经 作者:猛兽财经 猛兽财经核心观点: (1)华尔街投行Oppenheimer已将英伟达的目标价上调到了150美元。 (2)产品方面的最新进展和合作伙伴关系进一步提升了英伟达的市场地位。 &…...

Rust 快速入门(一)
Rust安装信息解释 cargo:Rust的编译管理器、包管理器、通用工具。可以用Cargo启动新的项目,构建和运行程序,并管理代码所依赖的所有外部库。 Rustc:Rust的编译器。通常Cargo会替我们调用此编译器。 Rustdoc:是Rust的…...
java 程序在服务器出现时区错误问题(使用Date,LocalDateTime,ZonedDateTime都不正确)
排查 查询系统时区信息 timedatectl status打印java的时区信息 import java.util.TimeZone;public class CheckTimeZone {public static void main(String[] args) {TimeZone defaultTimeZone TimeZone.getDefault();System.out.println("Default TimeZone ID: "…...
Kotlin 语言的协程是什么?
目录 1. 什么是协程 2. 协程的基本概念 3. 如何使用协程 3.1. 引入依赖 3.2. 启动协程 3.3. 使用挂起函数 4. 结构化并发 5. 处理异常 6. 总结 Kotlin 的协程是一种轻量级的线程,可以用于简化异步编程。它允许你以顺序的方式编写异步代码,从而提…...

uniapp 游戏 - 使用 uniapp 实现的扫雷游戏
0. 思路 1. 效果图 2. 游戏规则 扫雷的规则很简单。盘面上有许多方格,方格中随机分布着一些雷。你的目标是避开雷,打开其他所有格子。一个非雷格中的数字表示其相邻 8 格子中的雷数,你可以利用这个信息推导出安全格和雷的位置。你可以用右键在你认为是雷的地方插旗(称为标…...
LeetCode组合总和
题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一个 数字可以 无限制重复被…...

MATLAB - 机械臂手眼标定(眼在手内) - 估计安装在机器人上的移动相机的姿态
系列文章目录 前言 本示例展示了如何为装有手眼构型摄像头的机械臂或机械手执行和验证手眼校准。 一、概述 执行手眼校准有助于操作配备末端执行器(简称 “手”)的机械臂,该末端执行器依赖于摄像头提供的视觉数据。一旦完成了眼在手外的校准&…...

【Unity】TextMeshPro 3.0.9无法显示emoji表情问题
需要下载TextMeshPro 3.2.x-pre.xxx版本,重新生成Sprite Asset文件解决 注意:若Package Manager没有搜到pre版本,那么可以去github下载到本地,再解压后,将文件夹移动到工程Packages文件夹下,然后打开Packa…...

金九银十软件测试面试题(800道)
今年你的目标是拿下大厂offer?还是多少万年薪?其实这些都离不开日积月累的过程。 为此我特意整理出一份(超详细笔记/面试题)它几乎涵盖了所有的测试开发技术栈,非常珍贵,人手一份 肝完进大厂 妥妥的&#…...

中国剩余定理 C++
题目 解题思路 原链接:https://www.acwing.com/solution/content/3539/ 大致步骤: 将第2,3,4…n个方程不断与第一个方程合并,得到方程a1k1a2k2m2-m1;用扩展欧几里得算法解出a1k1a2k2gcd(a1, a2)的结果,再将结果扩大(m2-m1)/d倍即…...

动态规划lc
先找到规律,然后找边界情况;部分特殊情况分类讨论 *递归 70.爬楼梯 简单 提示 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 示例 1: 输入:…...

介绍xshell的使用技巧
使用技巧目录 1. 开启左键选中即复制,右键点击即粘贴2. 开启撰写功能3. 开启日志记录功能 1. 开启左键选中即复制,右键点击即粘贴 参考:https://blog.csdn.net/chirrupy_hamal/article/details/108619262 2. 开启撰写功能 使用场景&#x…...

揭秘语音识别巨头1:国内外顶尖技术服务商全解析01(万字长文)
一、学习导航 解密语音识别巨头:国内顶尖技术服务商全解析00:学习地图 解密语音识别巨头:国内顶尖技术服务商全解析01:微软语音,商业No.1 解密语音识别巨头:国内顶尖技术服务商全解析02:百度…...
JAVA使用SM2算法生成密钥对加密解密加签验签
简介 SM2是非对称加密算法,一提非对称加密算法,第一想到的是RSA,没错,这个就是替代RSA的。它是基于椭圆曲线密码的公钥密码算法标准,其秘钥长度256bit,包含数字签名、密钥交换和公钥加密,用于替…...
uniapp(vue)打包web项目页面刷新后报404解决方案
一、问题概述 uniapp是一款优秀的跨平台开发框架,它可以帮助开发者快速构建出适用于多端的应用程序。然而,在项目打包后,有可能发现页面在刷新时会出现404错误。这无疑给用户体验带来了极大的困扰,下面我们就来分析一下这个问题。…...
ansible学习之ansible-vault
相关文档参考:http://www.ansible.com.cn/docs/playbooks_vault.html#what-can-be-encrypted-with-vault ansible-vault 功能介绍 Ansible-Vault是一个用于加密和管理Ansible playbook中敏感数据的工具。通过创建、编辑、加密、解密、查看和重置密码,可以安全地存储…...

封装el-upload组件,用于上传图片和视频的组件
使用环境 vue3element plus 需要根据后端返回结构修改的函数:onPreview onRemove onSuccess 组件使用 基本使用 源代码: <script setup> import AutoUploadFile from /components/auto-upload-file/index.vue function change(urls){console.log…...
6.将扩散模型与其他生成模型的关联(2)
1.归一化流与扩散模型 自一化流(Normalizing Flow)是生成模型,通过将易于处理的分布进行变换以队对高维数据进行建模。归一化流可以将简单的概率分布转化为极其复杂的分布,并用于强化学习、变分推理等领域。 现有的归一化流是基于变量替换公式构…...

【C++】基于红黑树封装set和map
🚀个人主页:小羊 🚀所属专栏:C 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 前言一、更高维度的泛型二、模版参数三、比较逻辑的重写四、迭代器4.1 const迭代器4.2 重载4.3 - -重载 五、完整代…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

Copilot for Xcode (iOS的 AI辅助编程)
Copilot for Xcode 简介Copilot下载与安装 体验环境要求下载最新的安装包安装登录系统权限设置 AI辅助编程生成注释代码补全简单需求代码生成辅助编程行间代码生成注释联想 代码生成 总结 简介 尝试使用了Copilot,它能根据上下文补全代码,快速生成常用…...