【嵌入式Linux】Linux设备树详解

设备树是是Linux中一种用于描述硬件配置的数据结构,它在系统启动时提供给内核,以便内核能够识别和配置硬件资源。设备树在嵌入式Linux系统中尤其重要,因为这些系统通常不具备标准的硬件配置,需要根据实际的硬件配置来动态配置内核。在Linux中,设备树源文件的扩展名为.dts,其二进制编码文件为.dtb,将.dts编译成.dtb需要使用DTC工具,位于Linux内核的scripts/dtc文件夹下

基于 ARM 架构的 SOC 有很多种,一种 SOC 又可以制作出很多款板子,每个板子都有一个对应的 DTS 文件,那么如何确定编译哪一个 DTS 文件呢?我们就以 I.MX6ULL 这款芯片对应的板子为例来看一下,打开 arch/arm/boot/dts/Makefile

// 从381行开始
dtb-$(CONFIG_SOC_IMX6UL) += \
imx6ul-14x14-ddr3-arm2.dtb \
imx6ul-14x14-ddr3-arm2-emmc.dtb \
....
dtb-$(CONFIG_SOC_IMX6ULL) += \
imx6ull-14x14-ddr3-arm2-emmc.dtb \
imx6ull-14x14-ddr3-arm2-flexcan2.dtb \
imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \
imx6ull-14x14-ddr3-arm2-wm8958.dtb \
imx6ull-14x14-evk.dtb \
imx6ull-14x14-evk-btwifi.dtb \
imx6ull-14x14-evk-emmc.dtb \
imx6ull-14x14-evk-gpmi-weim.dtb \

当选中 I.MX6ULL 这个 SOC 以后(CONFIG_SOC_IMX6ULL=y),所有使用到IMX6ULL 这个 SOC 的板子对应的.dts 文件都会被编译为.dtb。如果使用了这一款SOC搓了一块板子,那么我们只需要新建一个该板子对应的.dts,然后将对应的.dtb文件名添加到dtb-$(CONFIG_SOC_IMX6ULL)下,这样在使用make编译设备树的时候就会将对应的.dts编译为二进制的.dtb

一般dtb文件会和根文件目录以及uboot一同烧录进启动存储设备中。(待补充)

如何编写DTS

虽然我们基本上不会从头到尾重写一个.dts 文件,大多时候是直接在 SOC 厂商提供的.dts文件上进行修改。但是我们肯定需要修改.dts文件,因此DTS 文件语法我们还是要学习一遍

.dtsi头文件

和 C 语言一样,设备树也支持头文件,设备树的头文件扩展名为.dtsi。一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART、IIC 等等。设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键—值对。比如我们使用的正点原子imx6ull使用的设备树是imx6ull-alientek-emmc.dts,而imx6ull-alientek-emmc.dts里又include了imx6ull.dtsi,以下是从imx6ull.dtsi 文件中缩减出来的设备树文件内容:

/ {aliases {can0 = &flexcan1;};cpus {#address-cells = <1>;#size-cells = <0>;cpu0: cpu@0 {compatible = "arm,cortex-a7";device_type = "cpu";reg = <0>;};};intc: interrupt-controller@00a01000 {compatible = "arm,cortex-a7-gic";#interrupt-cells = <3>;interrupt-controller;reg = <0x00a01000 0x1000>,<0x00a02000 0x100>;};}

第 1 行,“/”是根节点,每个设备树文件只有一个根节点。如果有多个dts或dtsi,那么这几个设备树文件的根节点会合并为一个根节点。aliases、cpus 和 intc 是三个子节点。

在设备树中节点命名格式为:label:node-name@unit-address,其中“label”是节点标签,主要是方便访问,“node-name”是节点名字,为 ASCII 字符串,“unit-address”一般表示设备的地址或寄存器首地址,如果没有则可以省去,nodename和unit-addr共同构成了节点名字。intc: interrupt-controller@00a01000就是这种命名格式,而label的存在使得使用&cpu0就可以访问cpu@0这个节点

每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。设备树源码中有如下几种数据形式:
1.字符串

compatible = “arm,cortex-a7”;

2.32位无符号整数,可以是一组值也可以是单个值

reg = <0 0x123456 100>;

3.字符串列表,使用‘,’分隔字符串

compatible = “fsl,imx6ull-gpmi-nand”, “fsl, imx6ul-gpmi-nand”;

标准属性

节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。除了用户自定义属性,有很多属性是标准属性,Linux 下的很多外设驱动都会使用这些标准属性

1. compatible 属性
compatible 属性也叫做“兼容性”属性,compatible 属性的值是一个字符串列表,这个属性的作用是告诉内核,当前的设备节点应该由哪个驱动来处理。内核会根据compatible属性中列出的字符串,去查找与之匹配的驱动程序。如果找到了匹配的驱动,内核就会尝试加载并初始化该驱动,以便操作相应的硬件设备,其格式如下:

compatible = “manufacturer,model”

其中 manufacturer 表示厂商,一般是芯片制造厂商,model 一般是模块对应的驱动名字,比如 imx6ull-alientek-emmc.dts 中 sound 节点是 I.MX6U-ALPHA 开发板的音频设备节点,I.MX6U-ALPHA 开发板上的音频芯片采用的欧胜出品的 WM8960,sound 节点的 compatible 属性值如下:

compatible = “fsl,imx6ul-evk-wm8960”,“fsl,imx-audio-wm8960”;

其中fsl表示厂商是飞思卡尔,“imx6ul-evk-wm8960”是驱动模块的名字,这个属性会遍历字符串列表中符合的驱动程序名称,然后再Linux内核中查找该驱动程序,如果第一个没有则第二个,第二个没有则第三个,直到找到位置。

在内核中,驱动程序通常会提供一个匹配表,这个表中列出了该驱动支持的所有compatible字符串。当内核解析设备树时,它会检查每个设备节点的compatible属性,并与驱动程序的匹配表进行对比,以确定是否应该由该驱动程序来处理该设备节点,比如在文件 imx-wm8960.c 中有如下内容

// 从632行开始
static const struct of_device_id imx_wm8960_dt_ids[] = {{ .compatible = "fsl,imx-audio-wm8960", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);static struct platform_driver imx_wm8960_driver = {.driver = {.name = "imx-wm8960",.pm = &snd_soc_pm_ops,.of_match_table = imx_wm8960_dt_ids,},.probe = imx_wm8960_probe,.remove = imx_wm8960_remove,
};

imx_wm8960_dt_idss 就是 imx-wm8960.c 这个驱动文件的匹配表,此匹配表只有一个匹配值“fsl,imx-audio-wm8960”,可以和imx6ull-alientek的dts中sound节点中的compatible 属性的字符串相匹配,那么这个节点就会使用此驱动文件。

2. model属性
model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比
如:

model = “wm8960-audio”;

3.status 属性
status 属性看名字就知道是和设备状态有关的,status 属性值也是字符串,字符串是设备的状态信息,可选的状态如表:

描述
“okay”表明设备是可操作的。
“disabled”表明设备当前是不可操作的,但是在未来可以变为可操作的,比如热插拔设备插入以后。至于 disabled 的具体含义还要看设备的绑定文档。
“fail”表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。
“fail-sss”含义和“fail”相同,后面的 sss 部分是检测到的错误内容

4.#address-cells 和#size-cells 属性
这两个属性的值都是无符号 32 位整形,#address-cells#size-cells 这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。#address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位),#size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。#address-cells#size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性都是和地址有关的内容,格式如下:

reg = <address1 length1 address2 length2 address3 length3……>

每个“address length”组合表示一个子节点设备或其寄存器的地址范围,其中 address 是起始地址,length 是地址长度,addressx表示第x个设备的起始地址,lengthx表示第x个设备的长度,当#address-cells=<1>的时候表示reg中的每一个address的长度是1字长,同样,#size-cells=<1>表示每个length的长度是1字长。

这部分看不懂没关系,后面会有详解

5.reg 属性
reg 属性的值一般是(address,length)对。reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息

6.ranges属性
ranges 是一个地址映射/转换表,ranges 属性每个项目由子地址、父地址和地址空间长度
这三部分组成,目的是将:

  • child-bus-address:子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长。
  • parent-bus-address:父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长。
  • length:子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长

如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换。以ranges = <0x0 0xe0000000 0x00100000>;为例,此属性值指定了一个 1024KB(0x00100000)的地址范围子,地址空间的物理起始地址为 0x0,父地址空间的物理起始地址为 0xe0000000,range会将子空间映射到父空间中,这样,我们在写程序的时候就直接以0x0为起点进行编程就好,地址映射会自动映射到父空间的对应位置

特殊属性

chosen属性
chosen 并不是一个真实的设备,chosen 节点主要是为了 uboot 向 Linux 内核传递数据,重点是 bootargs 参数。在chosen中会存储放着uboot中的bootargs数据,由uboot引导内核启动时传递进来。

aliases属性
单词 aliases 的意思是“别名”,因此 aliases 节点的主要功能就是定义别名,定义别名的目的就是为了方便访问节点。别名也可以用过在声明节点时候在@之前声明

设备树的工作方式

在DTS文件中,每个节点都有 compatible 属性,但根节点 compatible 属性值得拿出来单独说一下,通过根节点的 compatible 属性可以知道我们所使用的设备,一般第一个值描述了所使用的硬件设备名字,第二个值描述了设备所使用的 SOC。我们一般会将设备树和uboot一起烧录到启动设备中,uboot在引导Linux内核启动时,会将设备树dtb文件的首地址传给Linux内核,Linux内核检查设备树的根节点compatible 就可以获知当前设备的信息

以IMX6ULL设备为例子,其设备树arch/arm/mach-imx/mach-imx6ul.c的根节点信息如下:

/ {model = "Freescale i.MX6 ULL 14x14 EVK Board";compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

Linux内核中存储着其支持的板子和芯片的信息,Linux内核都用MACHINE_STARTMACHINE_END来定义一个machine_desc 结构体来描述这个设备,Linux内核会获取设备树文件dtb的根节点compatible 属性,然后遍历它支持的machine_desc 结构体,用于确认自己是否支持当前设备。该结构体位于arch/arm/include/asm/mach/arch.h,源码如下

#define DT_MACHINE_START(_name, _namestr)		\
static const struct machine_desc __mach_desc_##_name	\__used							\__attribute__((__section__(".arch.info.init"))) = {	\.nr		= ~0,				\.name		= _namestr,#endif

可以看到,要实现该架构体需要两个参数:以IMX6ULL设备为例子,这个设备的machine_desc 结构体实现位于文件 arch/arm/mach-imx/mach-imx6ul.c

static const char *imx6ul_dt_compat[] __initconst = {"fsl,imx6ul","fsl,imx6ull",NULL,
};DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)").map_io		= imx6ul_map_io,.init_irq	= imx6ul_init_irq,.init_machine	= imx6ul_init_machine,.init_late	= imx6ul_init_late,.dt_compat	= imx6ul_dt_compat,
MACHINE_END

machine_desc 结构体中有个.dt_compat 成员变量,此成员变量保存着本设备兼容属性。只要某个设备(板子)根节点“/”的 compatible 属性值与imx6ul_dt_compat 表中的任何一个值相等,那么就表示 Linux 内核支持此设备。而mach-imx6ul.c的根节点的compatible属性中的"fsl,imx6ull"显然与之匹配,那么Linux就可以确认该设备树dtb是自己支持的,从而将设备树加载进来。

Linux内核设备树与machine_desc匹配详解

上一小节说了Linux Kernel通过将uboot提供的设备树的compatible属性和自己的machine_desc 一 一对比,从而确定内核是否支持当前设备树。那内核具体是怎么匹配的呢?

Linux 内核调用 start_kernel 函数来启动内核,start_kernel 函数会调用setup_arch 函数来匹配 machine_descsetup_arch 函数定义在文件 arch/arm/kernel/setup.c 中,源码如下:

// 913行开始
void __init setup_arch(char **cmdline_p)
{const struct machine_desc *mdesc;setup_processor();mdesc = setup_machine_fdt(__atags_pointer);if (!mdesc)mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);machine_desc = mdesc;machine_name = mdesc->name;dump_stack_set_arch_desc("%s", mdesc->name);

其中setup_machine_fdt函数就是用于获取匹配的 machine_desc的, 参数就是 atags 的首地址,也就是 uboot 传递给 Linux 内核的 dtb 文件首地址,setup_machine_fdt 函数的返回值就是找到的最匹配的 machine_desc

函数 setup_machine_fdt 定义在文件 arch/arm/kernel/devtree.c

// 204行开始
const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{const struct machine_desc *mdesc, *mdesc_best = NULL;...if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))return NULL;mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);

找到匹配的 machine_desc 的过程就是用设备树根节点的compatible 属性值和 Linux 内核中 machine_desc.dt_compat 的值比较,看看那个相等,如果相等的话就表示找到匹配的 machine_desc,反之则获取下一个machine_desc结构体。

从源码可知, 函数of_flat_dt_match_machine就是用来对比设备树和machine_desc结构体的函数,其包含两个参数:,参数 mdesc_best是默认的 machine_desc, arch_get_next_mach是一个函数,该函数的工作就是获取 Linux 内核中下一个 machine_desc 结构体。我们进入of_flat_dt_match_machine这个函数再看看,源码如下:

	dt_root = of_get_flat_dt_root();while ((data = get_next_compat(&compat))) {score = of_flat_dt_match(dt_root, compat);if (score > 0 && score < best_score) {best_data = data;best_score = score;}}

首先是用of_get_flat_dt_root()来获取设备树根节点,然后使用wile循环查找每一个machine_desc,使用of_flat_dt_match函数将根节点 compatible 属性的值和每个 machine_desc 结构体中.dt_compat 的值进行比较,直至找到匹配的那个machine_desc

如何向设备树中添加或修改内容

添加或者修改内容应该就是向.dtsi文件中新增节点等等,比如如第一版硬件上有一个 IIC 接口的六轴芯片 MPU6050,第二版硬件又要把这个 MPU6050 更换为 MPU9250 等。我们就要同步的修改设备树文件。或者假设现在有个六轴芯片
fxls8471,fxls8471 要接到 I.MX6U-ALPHA 开发板的 I2C1 接口上,那么相当于需要在 i2c1 这个节点上添加一个 fxls8471 子节点。最简单的处理方法就是在imx6ull.dtsi文件下的 i2c1 下直接添加一个名为 fxls8471 的子节点

i2c1: i2c@021a0000 {#address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";reg = <0x021a0000 0x4000>;interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_I2C1>;status = "disabled";//fxls8471 子节点fxls8471@1e {compatible = "fsl,fxls8471";reg = <0x1e>;};
};

但是这样会有个问题,其他所有使用到 I.MX6ULL这颗 SOC 的板子都会引用 imx6ull.dtsi 这个文件。直接在 i2c1 节点中添加 fxls8471 就相当于在其他的所有板子上都添加了 fxls8471 这个设备。所以我们应该在对应板子的dts文件中去改,而不是直接改这个设备树头文件。I.MX6U-ALPHA 开发板使用的设备树文件为imx6ull-alientek-emmc.dts,因此我们需要在
imx6ull-alientek-emmc.dts 文件中完成数据追加的内容,方式如下:

&i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";mag3110@0e {compatible = "fsl,mag3110";reg = <0x0e>;position = <2>;};fxls8471@1e {compatible = "fsl,fxls8471";reg = <0x1e>;position = <0>;interrupt-parent = <&gpio5>;interrupts = <0 8>;};
};

相较于源文件,我们改了:

  • 添加新属性clock_frequency,用于指定i2c1时钟频率
  • status属性从disable改为okay
  • 添加新的i2c1 子节点 fxls8471,用于支持我们新加的设备

这个就是向节点追加或修改内容,重点就是通过&label 来访问节点,然后直接在里面编写要追加或者修改的内容。

Linux内核的设备树解析过程

Linux 内核在启动的时候会解析 DTB 文件,然后在/proc/device-tree 目录下生成相应的设备树节点文件。
在这里插入图片描述
Linux Kernel在 start_kernel 函数中完成了设备树节点解析的工作,最终实际工作的函数为 unflatten_dt_node。

设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。具体添加一个硬件的节点的时候需要什么属性会有对应的文档说明,具体路径为Linux 源码目录/Documentation/devicetree/bindings

设备树常用 OF 操作函数

设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的,我们在编写驱动的时候需要获取到这些信息。比如设备树使用 reg 属性描述了某个外设的寄存器地址为 0X02005482,长度为 0X400,我们在编写驱动的时候需要获取到 reg 属性的X02005482 和 0X400 这两个值,然后初始化外设。Linux 内核给我们提供了一系列的函数来获
取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_”,所以在很多资料里面也被叫做 OF 函数。这些 OF 函数原型都定义在 include/linux/of.h 文件中

1.查找节点的 OF 函数

Linux 内核使用 device_node 结构体来描述一个节点,此结构体定义在文件 include/linux/of.h

struct device_node {const char *name;const char *type;phandle phandle;const char *full_name;struct fwnode_handle fwnode;struct	property *properties;struct	property *deadprops;	/* removed properties */struct	device_node *parent;struct	device_node *child;struct	device_node *sibling;struct	kobject kobj;unsigned long _flags;void	*data;
#if defined(CONFIG_SPARC)const char *path_component_name;unsigned int unique_id;struct of_irq_controller *irq_trans;
#endif
};

与查找节点有关的 OF 函数有 5 个:

// 通过函数名字查找节点,from用于表示从哪个设备节点开始查找
extern struct device_node *of_find_node_by_name(struct device_node *from,const char *name);// 通过设备类型查找结点
extern struct device_node *of_find_node_by_type(struct device_node *from,const char *type);// 根据设备类型以及兼容性comaptible查找节点
extern struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat);// 通过of_device_id匹配表查找指定节点
extern struct device_node *of_find_matching_node_and_match(struct device_node *from,const struct of_device_id *matches,const struct of_device_id **match);// 通过路径查找结点
static inline struct device_node *of_find_node_by_path(const char *path)
2.查找父/子节点的OF函数
// 用于获取指定节点的父节点
extern struct device_node *of_get_parent(const struct device_node *node);
extern struct device_node *of_get_next_parent(struct device_node *node);// 使用迭代的方法查找子节点,从某个节点出发找遍历子节点
extern struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);// 通过名字获取子节点
extern struct device_node *of_get_child_by_name(const struct device_node *node,const char *name);

of_get_next_child函数是Linux内核中用于遍历设备树(Device Tree)的一个API。它允许你迭代地访问一个特定设备节点下的所有子节点。这个函数在驱动开发中非常有用,特别是当你需要处理一个设备节点下的所有子设备时。例如,在一个总线驱动中,你可能会使用这个函数来枚举所有连接到该总线的设备。通过不断地调用of_get_next_child并传入前一个返回的子节点,你可以遍历一个设备节点下的所有子节点。

3.提取属性值的OF函数

节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,Linux 内核中使用结构体 property 表示属性,此结构体同样定义在文件 include/linux/of.h 中,源码如下:

struct property {char *name;  // 属性名称int	length;  // 属性长度void *value;	// 属性值struct property *next;  // 下一个属性,内核中属性以链表的方式联系unsigned long _flags;unsigned int unique_id;struct bin_attribute attr;
};

同样,获取属性值的of函数有:

// 查找指定属性
extern struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);// 获取属性中元素的数量,比如像reg等属性可能是一个数组
extern int of_property_count_elems_of_size(const struct device_node *np,const char *propname, int elem_size);// 从属性中获取指定标号的u32类型数据,比如某个属性中还有多个u32类型值,那么可以通过该函数获取指定下标的u32属性值
extern int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value);
4.其他常用OF函数

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

逻辑卷建立

逻辑卷 lvm逻辑卷即为&#xff1a;logical volume manager逻辑管理卷&#xff0c;是linux系统下管理硬盘分区的一种机制&#xff0c;lvm适合于管理大型存储文件&#xff0c;用户可以动态的对磁盘进行扩容 作用 lvm&#xff1a;linux系统的一个重要的存储技术 不同的硬盘的不…

【持续更新】【NLP项目】【自然语言处理】智能聊天机器人——“有问必答”【Chatbot】第2章、《模式一:问候模式》

智能聊天机器人——“有问必答” 【注】该项目已开源&#xff0c;开源地址为&#xff1a;链接&#xff0c;代码更新可能不及时。 第2章、《模式一&#xff1a;问候模式》 主窗体的布局如下图所示&#xff1a; 共九种功能模式&#xff0c;最下方为关闭窗口按钮。 点击问候模…

时序预测 | Matlab基于TSA-LSTM-Attention被囊群优化算法优化长短期记忆网络融合注意力机制多变量多步时间序列预测

时序预测 | Matlab基于TSA-LSTM-Attention多变量多步预测 目录 时序预测 | Matlab基于TSA-LSTM-Attention多变量多步预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 时序预测 | Matlab基于TSA-LSTM-Attention被囊群优化算法优化长短期记忆网络融合注意力机制多变量多…

attributeerror: ‘FreeTypeFont‘ object has no attribute ‘getsize‘问题

我在训练yolov9的时候报错&#xff1a;attributeerror: ‘FreeTypeFont‘ object has no attribute ‘getsize‘。看过很多博客分析&#xff0c;都是说FreeTypeFont 字体的原因&#xff0c;其实真实问题出现yolo版本安装的Pillow库更新后&#xff0c;getsize&#xff08;&#…

三维天地:数字技术推动汽车产业实验室管理变革创新

近日,2024汽车技术与装备发展论坛在苏州成功召开。论坛以“共筑汽车产业新质生产力”为主题,聚焦新技术、新装备、新生态展开深入研讨,探索装备制造与汽车产业的融合发展路径。北京三维天地科技股份有限公司受邀参会。 在同期举办的检测认证高质量发展论坛上,三维天地咨询总监宫…

产品人必读书籍丨这本书告诉了我在AI时代如何成为一名顶级PM!!

《人工智能产品经理》——AI时代的PM修炼手册&#xff0c;这是一本不太工具的工具书。 很多人不了解产品经理需要具备的能力和素质。那么产品经理人工智能呢&#xff1f;这就更懵了&#xff0c;全是新词汇组成的新职业&#xff0c;到底怎样才算合格的人工智能产品经理&#x…

免费文件夹加密工具

1、去掉了miniExcel引用包&#xff0c;删除掉了excel配置文件 2、增加了密码登录功能&#xff0c;可以修改密码 3、使用sqlite3数据库来保存文件夹列表和用户密码&#xff0c;用户密码采用md5加盐 4、使用了 antdui来美化下界面 5、未解锁文件平不能被移除 其他还是保持老样…

第四届计算机图形学、人工智能与数据处理国际学术会议

在线投稿&#xff1a;学术会议-学术交流征稿-学术会议在线-艾思科蓝 第四届计算机图形学、人工智能与数据处理国际学术会议&#xff08;ICCAID 2024&#xff09;将于 2024年12月13日-15日在中国南昌举行。本次会议主要围绕“计算机图形学、人工智能与数据处理”的最新研究…

辐射发射测试新境界:深入解析TS-RadiMation套件多种操作方法(一)

TS-RadiMation套件作为辐射发射测试的得力助手&#xff0c;支持多种测试方法。 多频段手动模式电波暗室固定高度测试GTEM小室测试手动模式&#xff08;单频段&#xff09; 本文将详细介绍如何操作手动模式及手动模式&#xff08;单频段&#xff09;这两种模式&#xff0c;助您…

基于matlab的基于Tent混沌映射改进的麻雀搜索算法SSA优化BP神经网络预测

基于Tent混沌映射改进的麻雀搜索算法SSA优化BP神经网络预测 1 普通BP网络 代码如有需要&#xff0c;联系 596520206 %构建网络netnewff(inputn,outputn,hiddennum);% 网络参数net.trainParam.epochs100; % 训练次数net.trainParam.lr0.01; % 学习速…

[前端][基础]JavaScript

1&#xff0c;JavaScript简介 JavaScript 是一门跨平台、面向对象的脚本语言&#xff0c;而Java语言也是跨平台的、面向对象的语言&#xff0c;只不过Java是编译语言&#xff0c;是需要编译成字节码文件才能运行的&#xff1b;JavaScript是脚本语言&#xff0c;不需要编译&…

10:00面试,10:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

Java三大特性之一——多态(详细版)

文章目录 一、什么是多态二、重写2.1、重写的规则 三、多态的实现条件四、向上转型五、向下转型六、动态绑定七、使用多态的优缺点7.1、优点7.2、缺点 八、避免在构造方法中调用重写的方法 一、什么是多态 Java多态是面向对象编程的一个重要特性&#xff0c;它允许不同的对象对…

连锁餐饮企业-凡塔斯,用千里聆RPA搭建用户评价管理系统,提升门店服务满意度

凡塔斯是大型连锁餐饮企业昊澜餐饮集团旗下餐饮品牌&#xff0c;是牛排自助餐头部品牌&#xff0c;旗下拥有凡塔斯、百分好、食物链KING自助烤肉及餐饮人才商学院等多个行业知名品牌。 创立至今&#xff0c;集团管理门店已发展到福建、广东、江西、浙江等十多个省市&#xff0c…

设备状态监控一定要直观,可视化大屏最适合这个工作

一、引言 在现代工业生产和各类设施运行中&#xff0c;设备的稳定运行至关重要。为了确保设备能够高效、可靠地工作&#xff0c;及时了解设备的状态是关键。而设备状态监控一定要直观&#xff0c;只有这样才能让操作人员和管理人员迅速掌握设备的运行情况&#xff0c;及时发现…

xxe靶机实战

靶机地址&#xff1a;https://www.vulnhub.com/entry/xxe-lab-1,254/ 下载好后解压 直接拖拽.ovf格式的文件到虚拟机里 打开kali扫描主机,靶机开着或者后台运行就行 arp-scan -I eth0 -l 扫描出来目标靶机ip地址192.168.142.145 nmap扫描端口 nmap -A -sS -T4 -P- --min-rat…

钉钉内集成第三方免密登录(Vue+.Net)

需要实现的效果就是在钉钉内点击应用能跳转到第三方网站并且免密登录 1.登录钉钉PC端管理后台 2.通过管理后台进去开发者后台 3.应用开发 创建H5微应用 4.应用创建成功后直接点权限管理全部授权 5.设置H5登录地址 6. 应用管理发布 至此需要配置的步骤全部已完成&#xff0c;…

画动态爱心(Python-matplotlib)

介绍 氵而已 由于用的是 AI&#xff0c;注释得非常清楚&#xff0c;自己改改也可以用 代码 # -*- coding: utf-8 -*- # Environment PyCharm # File_name 尝试1 |User Pfolg # 2024/11/05 22:45 import numpy as np import matplotlib.pyplot as plt import matplo…

理解 WordPress | 第五篇:页面构建器选择指南

WordPress 专题致力于从 0 到 1 搞懂、用熟这种可视化建站工具。 第一阶段主要是理解。 第二阶段开始实践个人博客、企业官网、独立站的建设。 如果感兴趣&#xff0c;点个关注吧&#xff0c;防止迷路。 什么是 WordPress 构建器 WordPress 构建器&#xff08;Page Builder&am…

硬件基础07 功率放大器

一、功放理论 在多级放大电路中&#xff0c;输出信号往往要送去驱动—定的装置。例如&#xff0c;这类装置包括收音机中扬声器的音圈、电动机的控制绕组等。多级放大电路除了应有电压放大级外&#xff0c;还要求有一个能输出一定信号功率的输出级。这类主要用于向负载提供功率的…