RK3568驱动指南|第七篇-设备树-第64章 device_node转换成platform_device实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


【公众号】迅为电子

【粉丝群】824412014(加群获取驱动文档+例程)

【视频观看】嵌入式学习之Linux驱动(第七期_设备树_全新升级)_基于RK3568

【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


第64章 device_node转换成platform_device实验

在上一章中,我们学习了dtb二进制文件展开成device_node的具体流程,而device_node这时候还并不能跟内核中的platform_driver进行对接,而为了让操作系统能够识别和管理设备,需要将设备节点转换为平台设备。

64.1 转换规格

在之前学习的平台总线模型中,device部分是用platform_device结构体来描述硬件资源的,所以内核最终会将内核认识的device_node树转换platform_ device,但是并不是所有的device_node都会被转换成platform_ device,只有满足要求的才会转换成platform_ device,转换成platform_device的节点可以在/sys/bus/platform/devices下查看,那device_node节点要满足什么要求才会被转换成platform_device呢?

根据规则1,首先遍历根节点下包含 compatible 属性的子节点,对于每个子节点,创建一个对应的 platform_device。

根据规则2,遍历包含 compatible 属性为 "simple-bus"、"simple-mfd" 或 "isa" 的节点以及它们的子节点。如果子节点包含 compatible 属性值则会创建一个对应的platform_device。

根据规则3,检查节点的 compatible 属性是否包含 "arm" 或 "primecell"。如果是,则不将该节点转换为 platform_device,而是将其识别为 AMBA 设备。

接下来将通过几个设备树示例对上述规则进行实践。

举例1:

/dts-v1/;/ {model = "This is my devicetree!";#address-cells = <1>;#size-cells = <1>;chosen {bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0, 115200";};cpu1: cpu@1 {device_type = "cpu";compatible = "arm,cortex-a35", "arm,armv8";reg = <0x0 0x1>;};aliases {led1 = "/gpio@22020101";};node1 {#address-cells = <1>;#size-cells = <1>;gpio@22020102 {reg = <0x20220102 0x40>;};};node2 {node1-child {pinnum = <01234>;};};gpio@22020101 {compatible = "led";reg = <0x20220101 0x40>;status = "okay";};
};

在上面的设备树中,总共有chosen、cpu1: cpu@1、aliases、node1、node2、gpio@22020101

这六个节点,其中前五个节点都没有compatible属性,所以并不会被转换为platform_device,而最后一个gpio@22020101节点符合规则一,在根节点下,且有compatible属性,所以最后会转换为platform_device。

举例2:

/dts-v1/;/ {model = "This is my devicetree!";#address-cells = <1>;#size-cells = <1>;chosen {bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0, 115200";};cpu1: cpu@1 {device_type = "cpu";compatible = "arm,cortex-a35", "arm,armv8";reg = <0x0 0x1>;};aliases {led1 = "/gpio@22020101";};node1 {#address-cells = <1>;#size-cells = <1>;compatible = "simple-bus";gpio@22020102 {reg = <0x20220102 0x40>;};};node2 {node1-child {pinnum = <01234>;};};gpio@22020101 {compatible = "led";reg = <0x20220101 0x40>;status = "okay";};
};

相较于示例1的设备树,这里在node1节点中添加了 compatible 属性,但是这个compatible属性值为simple-bus,我们需要继续看他的子节点,子节点 gpio@22020102 并没有compatible属性值,所以这里的node1节点不会被转换。

举例3:

/dts-v1/;/ {model = "This is my devicetree!";#address-cells = <1>;#size-cells = <1>;chosen {bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0, 115200";};cpu1: cpu@1 {device_type = "cpu";compatible = "arm,cortex-a35", "arm,armv8";reg = <0x0 0x1>;};aliases {led1 = "/gpio@22020101";};node1 {#address-cells = <1>;#size-cells = <1>;compatible = "simple-bus";gpio@22020102 {compatible = "gpio";reg = <0x20220102 0x40>;};};node2 {node1-child {pinnum = <01234>;};};gpio@22020101 {compatible = "led";reg = <0x20220101 0x40>;status = "okay";};
};

相较于示例2的设备树,这里在node1节点的子节点 gpio@22020102 中添加了 compatible 属性,node1节点的compatible属性值为simple-bus,然后需要继续看他的子节点,子节点 gpio@22020102 的compatible属性值为gpio,所以这里的gpio@22020102节点会被转换成platform_device。

举例4:

/dts-v1/;/ {model = "This is my devicetree!";#address-cells = <1>;#size-cells = <1>;chosen {bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";};cpul: cpu@1 {device_type = "cpu";compatible = "arm,cortex-a35", "arm,armv8";reg = <0x0 0x1>;amba {compatible = "simple-bus";#address-cells = <2>;#size-cells = <2>;ranges;dmac_peri: dma-controller@ff250000 {compatible = "arm,p1330", "arm,primecell";reg = <0x0 0xff250000 0x0 0x4000>;interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;#dma-cells = <1>;arm,pl330-broken-no-flushp;arm,p1330-periph-burst;clocks = <&cru ACLK DMAC_PERI>;clock-names = "apb_pclk";};dmac_bus: dma-controller@ff600000 {compatible = "arm,p1330", "arm,primecell";reg = <0x0 0xff600000 0x0 0x4000>;interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;#dma-cells = <1>;arm,pl330-broken-no-flushp;arm,pl330-periph-burst;clocks = <&cru ACLK_DMAC_BUS>;clock-names = "apb_pclk";};};};
};

amba 节点的compatible值为simple-bus,不会被转换为 platform_device,而是作为父节点用于组织其他设备,所以需要来查看他的子节点。

dmac_peri: dma-controller@ff250000 节点: 该节点的 compatible 属性包含 "arm,p1330" 和 "arm,primecell",根据规则3,该节点不会被转换为 platform_device,而是被识别为 AMBA 设备。

dmac_bus: dma-controller@ff600000 节点: 该节点的 compatible 属性包含 "arm,p1330" 和 "arm,primecell",根据规则3,该节点不会被转换为 platform_device,而是被识别为 AMBA 设备。

64.2 转换流程源码分析

首先进入到内核源码目录下的“drivers/of/platform.c”文件中,找到第555行,具体内容如下所示:

arch_initcall_sync(of_platform_default_populate_init);

arch_initcall_sync 是 Linux 内核中的一个函数,用于在内核初始化过程中执行架构相关的初始化函数。它属于内核的初始化调用机制,用于确保在系统启动过程中适时地调用特定架构的初始化函数。

在Linux内核的初始化过程中,各个子系统和架构会注册自己的初始化函数。这些初始化函数负责完成特定子系统或架构相关的初始化工作,例如初始化硬件设备、注册中断处理程序、设置内存映射等。而 arch_initcall_sync 函数则用于调用与当前架构相关的初始化函数。

当内核启动时,调用 rest_init() 函数来启动初始化过程。在初始化过程中,arch_initcall_sync 函数会被调用,以确保所有与当前架构相关的初始化函数按照正确的顺序执行。这样可以保证在启动过程中,特定架构相关的初始化工作得到正确地完成。

而of_platform_default_populate_init函数的作用是在内核初始化过程中自动解析设备树,并根据设备树中的设备节点创建对应的 platform_device 结构。它会遍历设备树中的设备节点,并为每个设备节点创建一个对应的 platform_device 结构,然后将其注册到内核中,使得设备驱动程序能够识别和操作这些设备。该函数的具体内容如下所示:

static int __init of_platform_default_populate_init(void)
{struct device_node *node;// 暂停设备链接供应商同步状态device_links_supplier_sync_state_pause();// 如果设备树尚未填充,则返回错误码if (!of_have_populated_dt())return -ENODEV;/** 显式处理某些兼容性,因为我们不想为/reserved-memory中的每个具有“compatible”的节点创建platform_device。*/for_each_matching_node(node, reserved_mem_matches)of_platform_device_create(node, NULL, NULL);// 查找节点 "/firmware"node = of_find_node_by_path("/firmware");if (node) {// 使用该节点进行设备树平台设备的填充of_platform_populate(node, NULL, NULL, NULL);of_node_put(node);}// 填充其他设备fw_devlink_pause();of_platform_default_populate(NULL, NULL, NULL);fw_devlink_resume();return 0;
}

第6行:暂停设备链接供应商的同步状态,确保设备链接的状态不会在此过程中被改变。

第9行:检查设备树是否已经被填充。如果设备树尚未填充,则返回错误码 -ENODEV。

第16行:遍历设备树中与 reserved_mem_matches 匹配的节点。这些节点是 /reserved-memory 中具有 "compatible" 属性的节点。

第17行:为 /reserved-memory 中匹配的节点创建 platform_device 结构。这些节点不会为每个节点都创建 platform_device,而是根据需要进行显式处理。

第20行:在设备树中查找路径为 "/firmware" 的节点。

第23行:使用找到的节点填充设备树中的平台设备。这些节点可能包含与固件相关的设备。

第28行:暂停固件设备链接,确保在填充其他设备时链接状态不会改变。

第29行:填充设备树中的其他设备。

第30行:恢复固件设备链接。

上诉内容中我们要着重关注的是第29行的of_platform_default_populate(NULL, NULL, NULL)函数,找到该函数的定义之后如下所示:

int of_platform_default_populate(struct device_node *root,const struct of_dev_auxdata *lookup,struct device *parent)
{return of_platform_populate(root, of_default_bus_match_table, lookup,parent);
}

该函数的作用是调用 of_platform_populate 函数来填充设备树中的平台设备,并使用默认的设备匹配表 of_default_bus_match_table,设备匹配表内容如下所示:

const struct of_device_id of_default_bus_match_table[] = {{ .compatible = "simple-bus", },{ .compatible = "simple-mfd", },{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */{} /* Empty terminated list */
};

上述的设备匹配表就是我们在第一小节中第2条规则,,函数将自动根据设备树节点的属性匹配相应的设备驱动程序,并填充内核的平台设备列表。接下来找到of_platform_populate函数的定义,该函数的具体内容如下所示:

int of_platform_populate(struct device_node *root,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent)
{struct device_node *child;int rc = 0;// 如果 root 不为空,则增加 root 节点的引用计数;否则,在设备树中根据路径查找 root 节点root = root ? of_node_get(root) : of_find_node_by_path("/");if (!root)return -EINVAL;pr_debug("%s()\n", __func__);pr_debug(" starting at: %pOF\n", root);// 暂停设备链接供应商同步状态device_links_supplier_sync_state_pause();// 遍历 root 节点的所有子节点for_each_child_of_node(root, child) {// 创建平台设备并添加到设备树总线rc = of_platform_bus_create(child, matches, lookup, parent, true);if (rc) {of_node_put(child);break;}}// 恢复设备链接供应商同步状态device_links_supplier_sync_state_resume();// 设置 root 节点的 OF_POPULATED_BUS 标志of_node_set_flag(root, OF_POPULATED_BUS);// 释放 root 节点的引用计数of_node_put(root);return rc;
}

该函数的具体执行步骤如下:

第10行:检查给定的设备树节点 node 是否为有效节点。如果节点为空,函数将立即返回。

第21行:遍历设备树节点的子节点,查找与平台设备相关的节点。这些节点通常具有 compatible 属性,用于匹配设备驱动程序。

第23行:对于每个找到的平台设备节点,创建一个 platform_device 结构,并根据设备树节点的属性设置该结构的各个字段。

第25行:将创建的 platform_device 添加到内核的平台设备列表中,以便设备驱动程序能够识别和操作这些设备。

接下来对该函数的第23行核心代码of_platform_bus_create(child, matches, lookup, parent, true)函数进行讲解,该函数的具体定义如下所示:

static int of_platform_bus_create(struct device_node *bus,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent, bool strict)
{const struct of_dev_auxdata *auxdata;struct device_node *child;struct platform_device *dev;const char *bus_id = NULL;void *platform_data = NULL;int rc = 0;/* 确保设备节点具有 compatible 属性 */if (strict && (!of_get_property(bus, "compatible", NULL))) {pr_debug("%s() - skipping %pOF, no compatible prop\n",__func__, bus);return 0;}/* 跳过不想创建设备的节点 */if (unlikely(of_match_node(of_skipped_node_table, bus))) {pr_debug("%s() - skipping %pOF node\n", __func__, bus);return 0;}if (of_node_check_flag(bus, OF_POPULATED_BUS)) {pr_debug("%s() - skipping %pOF, already populated\n",__func__, bus);return 0;}auxdata = of_dev_lookup(lookup, bus);if (auxdata) {bus_id = auxdata->name;platform_data = auxdata->platform_data;}if (of_device_is_compatible(bus, "arm,primecell")) {/** 在此处不返回错误以保持与旧设备树文件的兼容性。*/of_amba_device_create(bus, bus_id, platform_data, parent);return 0;}dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);if (!dev || !of_match_node(matches, bus))return 0;for_each_child_of_node(bus, child) {pr_debug("   create child: %pOF\n", child);rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);if (rc) {of_node_put(child);break;}}of_node_set_flag(bus, OF_POPULATED_BUS);return rc;
}

第14行:如果 strict 为真且设备节点 bus 没有兼容性属性,则输出调试信息并返回 0。这个条件判断确保设备节点具有 compatible 属性,因为 compatible 属性用于匹配设备驱动程序,对应我们在上一小节的第1条规则。

第21行:如果设备节点 bus 在被跳过的节点表中,则输出调试信息并返回 0。这个条件判断用于跳过不想创建设备的节点。

第27行:如果设备节点 bus 的 OF_POPULATED_BUS 标志已经设置,则输出调试信息并返回 0。这个条件判断用于避免重复创建已经填充的设备节点。

第34行:使用 lookup 辅助数据结构查找设备节点 bus 的特定配置信息,并将其赋值给变量 bus_id 和 platform_data。这个步骤用于获取设备节点的特定配置信息,以便在创建平台设备时使用,由于这里传入的参数为NULL,所以下面的条件判断并不会被执行。

第39行:如果设备节点 bus 兼容于 "arm,primecell",则调用 of_amba_device_create 函数创建 AMBA 设备,并返回 0,对应我们在上一小节学习的第3条规则。

第47行:调用 of_platform_device_create_pdata函数创建平台设备,并将其赋值给变量 dev。然后,检查设备节点 bus是否与给定的匹配表 `matches` 匹配。如果平台设备创建失败或者设备节点不匹配,那么返回 0。

第51行-第58行:遍历设备节点 bus 的每个子节点 child,并递归调用 of_platform_bus_create 函数来创建子节点的平台设备。

接下来对该函数的第47行 of_platform_device_create_pdata函数进行讲解,该函数的具体定义如下所示:

static struct platform_device *of_platform_device_create_pdata(struct device_node *np,const char *bus_id,void *platform_data,struct device *parent)
{struct platform_device *dev;/* 检查设备节点是否可用或已填充 */if (!of_device_is_available(np) ||of_node_test_and_set_flag(np, OF_POPULATED))return NULL;/* 分配平台设备结构体 */dev = of_device_alloc(np, bus_id, parent);if (!dev)goto err_clear_flag;/* 设置平台设备的一些属性 */dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);if (!dev->dev.dma_mask)dev->dev.dma_mask = &dev->dev.coherent_dma_mask;dev->dev.bus = &platform_bus_type;dev->dev.platform_data = platform_data;of_msi_configure(&dev->dev, dev->dev.of_node);of_reserved_mem_device_init_by_idx(&dev->dev, dev->dev.of_node, 0);/* 将平台设备添加到设备模型中 */if (of_device_add(dev) != 0) {platform_device_put(dev);goto err_clear_flag;}return dev;err_clear_flag:/* 清除设备节点的已填充标志 */of_node_clear_flag(np, OF_POPULATED);return NULL;

第10行:函数会检查设备节点的可用性,即检查设备树对应节点的status属性。如果设备节点不可用或已经被填充,则直接返回 NULL。

第15行:函数调用 of_device_alloc 分配一个平台设备结构体,并将设备节点指针、设备标识符和父设备指针传递给它。如果分配失败,则跳转到 err_clear_flag 标签处进行错误处理。

第19行,函数设置平台设备的一些属性。它将 coherent_dma_mask 属性设置为 32 位的 DMA 位掩码,并检查 dma_mask 属性是否为 NULL。如果 dma_mask 为 NULL,则将其指向 coherent_dma_mask。然后,函数设置平台设备的总线类型为 platform_bus_type,并将平台数据指针存储在 platform_data 属性中。接着,函数调用 of_msi_configure 和 of_reserved_mem_device_init_by_idx 来配置设备的 MSI 和保留内存信息。

第29行:函数调用 of_device_add 将平台设备添加到设备模型中。如果添加失败,则释放已分配的平台设备,并跳转到 err_clear_flag 标签处进行错误处理。

至此,关于device_node转换成platform_device的具体流程就分析完成了,函数调用流程图如下(图 64-1)所示:


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/138191.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SOLIDWORKS软件提供了哪些特征造型方法?硕迪科技

SOLIDWORKS作为一款三维设计软件&#xff0c;为用户提供了多种特征造型方法&#xff0c;以下是其中几种常用的&#xff1a; 实体建模特征&#xff1a;SOLIDWORKS使用实体建模技术来创建和编辑三维几何体。通过使用基本几何体&#xff08;如立方体、圆柱体、圆锥体等&#xff09…

Spring-Security前后端分离权限认证

前后端分离 一般来说&#xff0c;我们用SpringSecurity默认的话是前后端整在一起的&#xff0c;比如thymeleaf或者Freemarker&#xff0c;SpringSecurity还自带login登录页,还让你配置登出页,错误页。 但是现在前后端分离才是正道&#xff0c;前后端分离的话&#xff0c;那就…

@ConfigurationProperties使用

一直有个疑问,在使用ConfigurationProperties注解作用一个配置类时,如果该配置类继承了一个父类,那么父类的那些配置字段是否可以读取配置信息。 答案是可以的&#xff0c;前提是父类对应字段的set方法是public。 BaseProperties.java Getter Setter public class BasePropert…

React 递归手写流程图展示树形数据

需求 根据树的数据结构画出流程图展示&#xff0c;支持新增前一级、后一级、同级以及删除功能&#xff08;便于标记节点&#xff0c;把节点数据当作label展示出来了&#xff0c;实际业务中跟据情况处理&#xff09; 文件结构 初始数据 [{"ticketTemplateCode": &…

uniapp vue2 vuex 持久化

1.vuex的使用 一、uniapp中有自带vuex插件&#xff0c;直接引用即可 二、在项目中新建文件夹store,在main.js中导入 在根目录下新建文件夹store,在此目录下新建index.js文件 index.js import Vue from vueimport Vuex from vuexVue.use(Vuex)const store new Vuex.Store(…

代码随想录图论部分-695. 岛屿的最大面积|1020. 飞地的数量

695. 岛屿的最大面积 题目&#xff1a;给你一个大小为 m x n 的二进制矩阵 grid 。岛屿 是由一些相邻的 1 (代表土地) 构成的组合&#xff0c;这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0&#xff08;代表水&#xff0…

electron 内部api capturePage实现webview截图

官方文档 .capturePage([rect]) rect Rectangle (可选) - 要捕获的页面区域。 返回 Promise - 完成后返回一个NativeImage 在 rect内捕获页面的快照。 省略 rect 将捕获整个可见页面。 async function cap(){ let image await webviewRef.value.capturePage() console.log(im…

Postman的环境变量和全局变量

近期在复习Postman的基础知识&#xff0c;在小破站上跟着百里老师系统复习了一遍&#xff0c;也做了一些笔记&#xff0c;希望可以给大家一点点启发。 多种环境&#xff1a;开发环境、测试环境、预发布环境、生产环境&#xff0c;可以用环境变量来解决。 今天的分享就到这里&a…

【论文阅读】Progressive Spatio-Temporal Prototype Matching for Text-Video Retrieval

资料链接 论文链接&#xff1a;https://openaccess.thecvf.com/content/ICCV2023/papers/Li_Progressive_Spatio-Temporal_Prototype_Matching_for_Text-Video_Retrieval_ICCV_2023_paper.pdf 代码链接&#xff1a;https://github.com/imccretrieval/prost 背景与动机 文章发…

LabVIEW在OPC中使用基金会现场总线

LabVIEW在OPC中使用基金会现场总线 本文讨论了如何使用开放的OPC&#xff08;用于过程控制的OLE&#xff09;接口访问基金会现场总线网络和设备。 NI-FBUS通信管理器随附了一个OPC数据访问服务器。 &#xff08;NI-FBUS Configurator自动包含NI-FBUS通信管理器。&#xff09…

Visual Studio2010保姆式安装教程(VS2010 旗舰版),以及如何运行第一个C语言程序,超详细

安装前请关闭杀毒软件&#xff0c;系统防火墙&#xff0c;断开网络连接 参考链接&#xff1a;请点击 下载链接&#xff1a; 通过百度网盘分享的文件&#xff1a;VS2010.zip 链接:https://pan.baidu.com/s/1yQUUCxMJP7FMaistFX94SQ 提取码:96ga 复制这段内容打开「百度网盘APP …

Linux下的调试工具——GDB

GDB 1.什么是GDB GDB 是由 GNU 软件系统社区提供的调试工具&#xff0c;同 GCC 配套组成了一套完整的开发环境&#xff0c;GDB 是 Linux 和许多 类Unix系统的标准开发环境。 一般来说&#xff0c;GDB 主要能够提供以下四个方面的帮助&#xff1a; 启动程序&#xff0c;可以按…

GF0-57CQD-002 测量参数:加速度、速度、位移–现场可配置

GF0-57CQD-002 测量参数:加速度、速度、位移–现场可配置 GF0-57CQD-002 是一款创新的双通道变送器&#xff0c;专为精确的振动测量而设计。它激励并读取来自加速度计的信号&#xff0c;并将整体振动值作为电流/电压信号传输。它测量加速度、速度和位移等不同参数的振动。配置…

模电学习路径--google镜像chatgpt

交流通路实质 列出电路方程1&#xff0c;方程1对时刻t做微分 所得方程1‘ 即为 交流通路 方程1对时刻t做微分&#xff1a;两个不同时刻的方程1相减&#xff0c;并 令两时刻差为 无穷小 微分 改成 差 模电学习路径&#xff1a; 理论 《电路原理》清华大学 于歆杰 朱桂萍 陆文…

【数据结构】二叉树的遍历递归算法详解

二叉树的遍历 &#x1f4ab;二叉树的结点结构定义&#x1f4ab;创建一个二叉树结点&#x1f4ab;在主函数中手动创建一颗二叉树&#x1f4ab;二叉树的前序遍历&#x1f4ab;调用栈递归——实现前序遍历&#x1f4ab;递归实现中序和后序遍历 &#x1f4ab;二叉树的结点结构定义 …

稳定扩散AI 纹理生成器

推荐基于稳定扩散(stable diffusion) AI 模型开发的自动纹理工具&#xff1a; DreamTexture.js自动纹理化开发包 - NSDT 什么是稳定扩散&#xff1f; 从技术上讲&#xff0c;Stable Diffusion 是一种用于机器学习的潜在扩散模型 &#xff08;LDM&#xff09;。这种类型的专用深…

【dbeaver】添加mysql高低版本选择驱动

添加mysql高低版本选择驱动 连接到数据库->全部->查询mysql MySQL 版本驱动 8.0 MySQL 5 版本驱动 5.7.x 其他需要就&#xff1a;https://downloads.mysql.com/archives/c-j/ 密码查看 项目设置密码&#xff1a; File -> Project security ->设置密码 It i…

Ubuntu 22.04 安装水星无线 USB 网卡

我的 USB 网卡是水星 Mercury 的&#xff0c; 在 Ubuntu 22.04 下面没有自动识别。 没有无线网卡的时候只能用有线接到路由器上&#xff0c;非常不方便。 寻思着把无线网卡驱动装好。折腾了几个小时装好了驱动。 1.检查网卡类型 & 安装驱动 使用 lsusb 看到的不一定是准确…

法治智能起航 | 拓世法宝AI智慧政务一体机重塑法治格局,开启智能司法新篇章

在科技的巨轮推动下&#xff0c;我们的社会正快速迈向一个以数据和智能为核心的新时代。在这个波澜壮阔的变革中&#xff0c;人工智能&#xff08;AI&#xff09;显得尤为突出&#xff0c;它不仅是科技进步的象征&#xff0c;更是未来发展的助力者。 2023年&#xff0c;最高人…

医学影像系统源码(MRI、CT三维重建)

一、MRI概述 核磁共振成像&#xff08;英语&#xff1a;Nuclear Magnetic Resonance Imaging&#xff0c;简称NMRI&#xff09;&#xff0c;又称自旋成像&#xff08;英语&#xff1a;spin imaging&#xff09;&#xff0c;也称磁共振成像&#xff08;Magnetic Resonance Imag…