Linux第73步_学习Linux设备树和“OF函数”

news2024/11/17 20:18:48

掌握设备树是 Linux驱动开发人员必备的技能

1、了解设备树文件

在3.x版本以前的Linux内核源码中,存在大量的“arc/arm/mach-xxx”和“arc/arm/plat-xxx”文件夹,里面很多个“.c”和“.h”文件,它们用来描述设备信息。而现在的ARM架构是采用“设备树”来描述设备信息。“设备树”英文名字叫“Device Tree”。描述设备树的文件叫做“DTS”,是Device Tree Source的缩写。它主要用来描述板子上有哪些设备构成,如:I2C控制器、GPIO控制器、SPI控制器、UART控制器等。

设备树源文件扩展名为“.dts”,设备树头文件扩展名为“.dtsi”,它们经过DTC工具编译后生成的二进制文件,就是“.dtb”文件。

“.dts”设备树文件可以通过“#include”来引用“.h”、“.dtsi”和“.dts”文件。在编写设备树头文件时,我们最好选择“.dtsi”作为后缀。

一般“.dtsi”文件用于描述芯片的“内部外设信息”,比如CPU架构、主频、外设寄存器地址范围,如:UART、IIC、SPI、TIMER等等。

2、了解节点

设备树是描述板子上的“设备信息”的文件,均采用“树形结构”。每个设备树文件只有一个“根节点”,而“不同设备树文件的根节点”会合在一起形成一个根节点。每个设备都是一个节点,称之为“设备节点”,每个节点都采用“属性信息”来描述其“节点信息”。

认识“标准属性”、“节点”和“节点标签”,见下图:

标准属性

1)、compatiable属性,也叫兼容性属性。

如:compatible = "cirrus,cs42l51";

这个compatible只有一个属性值,就是“cirrus,cs42l51”,其中“cirrus”表示厂商,“cs42l51”表示驱动模块。

再如:compatible = "cirrus,cs42l51","cirrus,My_cs42l51";

这个compatible就有两个属性值,就是“cirrus,cs42l51”和"cirrus,My_cs42l51",其中“cirrus”表示厂商,“cs42l51”表示驱动模块,“My_cs42l51”表示驱动模块。

驱动文件的“OF匹配表”

const struct of_device_id cs42l51_of_match[] = { { .compatible = "cirrus,cs42l51", },{ }};

设备首先会使用第一个兼容值在Limux内核里面查找。看看能不能找到与之匹配的驱动文件,如果没有找到,就使用第二个兼容值查,以此类推,直到查找完 commpatible属性中的所有值。

compatible属性用于将设备和驱动绑定起来。驱动程序文件有一个“OF匹配表”,这个“OF匹配表”保存着一些compatible的值,如果设备节点的compatible属性值和“OF匹配表”中的任何一个值相等,那么这个设备就可以使用这个驱动。

2)、model属性

用于描述开发板的名字或设备模块的信息。

比如:model = "STMicroelectronics STM32MP157C-DK2 Discovery Board";

3)、status属性

status属性值

描述

okay

表明设备是可操作的

disabled

表明设备当前是不可操作的,但是在未来可以变为可操作的,比如:热插拔设备插入以后。至于disabled的具体含义还要看设备的绑定文档。

fail

表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。

fail-sss

含义和“fai1”相同,后面的“sss部分”是检测到的错误内容。

4)、reg属性

reg属性的值一般是(address,length)对。

reg 属性一般用于描述“设备地址空间资源信息”或者“设备地址信息”,比如某个外设的“寄存器地址范围信息”,或者I2C器件的设备地址等。

5)、ranges属性

ranges = <child-bus-address,parent-bus-address,length>;

child-bus-address表示“子总线地址空间的物理地址”;

parent-bus-address表示“父总线地址空间的物理地址”;

length表示“子总线地址空间的长度”;

若ranges属性值为空,则说明子地址空间和父地址空间完全相同;

6)、“#address-cells”和“#size-cells”属性

#address-cells的属性值为无符号32位整型,用于描述子节点的“reg和ranges”中的address所占的位数,1表示32位,2表示64位;

#size-cells的属性值为无符号32位整型,用于描述子节点的“reg和ranges”中的length所占的位数,0表示没有位数,1表示32位,2表示64位;

举例说明“reg、 #address-cells和#size-cells”属性:

cpus {   #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

   #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位   cpu0: cpu@0 {      compatible = "arm,cortex-a7";      device_type = "cpu";      reg = <0>;        //表示addres=0, 没有length,和前面定义一致

      clocks = <&scmi0_clk CK_SCMI0_MPU>;      clock-names = "cpu";      operating-points-v2 = <&cpu0_opp_table>;      nvmem-cells = <&part_number_otp>;       nvmem-cell-names = "part_number";       #cooling-cells = <2>;    }; };

scmi_sram: sram@2ffff000 {   compatible = "mmio-sram";   reg = <0x2ffff000 0x1000>//表示address=0x2ffff000,length=0x1000

   #address-cells = <1>;//表示scmi_shmreg和rangesaddress占32个位

   #size-cells = <1>;   //表示scmi_shmreg和rangeslength占32个位

   ranges = <0 0x2ffff000 0x1000>;  //分别为32位,和前面定义一致

   scmi0_shm: scmi_shm@0 {      reg = <0 0x80>;    //reg的address和length分别为32位,和前面定义一致

   };

};

soc {

   compatible = "simple-bus";   #address-cells = <1>; //表示sramreg和rangesaddres长度为32个位   #size-cells = <1>;    //表示sramreg和rangeslength长度为32个位

   interrupt-parent = <&intc>;   ranges = <0 0x10000000 0x100000>;

   sram: sram@10000000 {      compatible = "mmio-sram";      reg = <0x0 0x60000>;//reg的address和length均为32位,和前面定义一致

      #address-cells = <1>;      #size-cells = <1>;      ranges = <0 0x10000000 0x60000>;  //均为32位,和前面定义一致

   };};

以STM32MP157为例,创建设备树文件myfrst.dts ,要求在设备树里面的内容如下:

1)、芯片是由两个Cortex-A7 架构的32位CPU和Cortex-M4组成。

2)、STM32MP157内部sram,起始地址为 0x10000000,大小为384KB(0x60000)。

先搭建 “根节点框架”,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157"; };

接着添加cpus节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

   };

 };

接着添加cpu0节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

      cpu0: cpu@0 {        compatible = "arm,cortex-a7";        device_type = "cpu";        reg = <0>;        //表示addres=0, 没有length,和前面定义一致

     };

   };

 };

接着添加cpu1节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpu的reg和ranges的address占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

      cpu0: cpu@0 {        compatible = "arm,cortex-a7";        device_type = "cpu";        reg = <0>;        //表示addres=0, 没有length,和前面定义一致

     };

      cpu1: cpu@1 {        compatible = "arm,cortex-m4";        device_type = "cpu";        reg = <1>;        //表示addres=1, 没有length,和前面定义一致

     };

   };

 };

接着添加soc节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

      cpu0: cpu@0 {        compatible = "arm,cortex-a7";        device_type = "cpu";        reg = <0>;        //表示addres=0, 没有length,和前面定义一致

     };

      cpu1: cpu@1 {        compatible = "arm,cortex-m4";        device_type = "cpu";        reg = <1>;        //表示addres=1, 没有length,和前面定义一致

     };

   };

   soc {

      compatible = "simple-bus";      #address-cells = <1>; //表示sramreg和rangesaddres长度为32个位      #size-cells = <1>;    //表示sramreg和rangeslength长度为32个位

      ranges; //说明子地址空间和父地址空间完全相同;

   };

 };

接着添加sram节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

      cpu0: cpu@0 {        compatible = "arm,cortex-a7";        device_type = "cpu";        reg = <0>;        //表示addres=0, 没有length,和前面定义一致

     };

      cpu1: cpu@1 {        compatible = "arm,cortex-m4";        device_type = "cpu";        reg = <1>;        //表示addres=1, 没有length,和前面定义一致

     };

   };

   soc {

      compatible = "simple-bus";      #address-cells = <1>; //表示sramreg和rangesaddres长度为32个位      #size-cells = <1>;    //表示sramreg和rangeslength长度为32个位

      ranges; //说明子地址空间和父地址空间完全相同;

      

      sram: sram@10000000 {        compatible = "mmio-sram";        reg = <0x0 0x60000>;//address和length均为32位,和前面定义一致

        ranges = <0 0x10000000 0x60000>;  //均为32位,和前面定义一致

     };

   };

 };

3、了解特殊节点

1)、aliases子节点

aliases {

   serial0 = &uart4;  //给&uart4起个别名叫“serial0”

};

2)、chosen子节点

chosen不是一个真实的设备,chosen节点主要是为了uboot向 Linux内核传递数据。

chosen {

   stdout-path = "serial0:115200n8";

//设置“stdout-path”属性,表示标准输出使用“serial0

};

4、学习“OF函数”

“OF函数原型”都定义在“include/linux/of.h”文件中。

1)、了解相关结构体

Linux内核使用device_node结构体来描述一个节点。

struct device_node {

const char *name;  /*节点名字*/

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;

#if defined(CONFIG_OF_KOBJ)

struct kobject kobj;

#endif

unsigned long _flags;

void *data;

#if defined(CONFIG_SPARC)

unsigned int unique_id;

struct of_irq_controller *irq_trans;

#endif

};

Linux内核中使用结构体property表示属性

struct property {

char *name;           /*属性名字*/

int length;              /*属性长度*/

void *value;          /*属性值*/

struct property *next;  /*下一个属性*/

#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)

unsigned long _flags;

#endif

#if defined(CONFIG_OF_PROMTREE)

unsigned int unique_id;

#endif

#if defined(CONFIG_OF_KOBJ)

struct bin_attribute attr;

#endif

}

resource结构体定义在文件 include/linux/ioport.h中。

struct resource {

resource_size_t start;   /*开始地址,占32位*/

resource_size_t end;     /*结束地址,占32位*/

const char *name;        /*资源的名字*/

unsigned long flags;     /*资源标志或资源类型*/

unsigned long desc;

struct resource *parent, *sibling, *child;

};

2)、通过节点的名字查找指定的节点:

struct device_node *of_find_node_by_name(struct device_node *from, const char *name)

from:开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树。
name:要查找的节点名字。
返回值:返回找到的节点,如果为NULL,表示查找失败。

3)、通过device_type属性查找指定的节点

struct device_node *of_find_node_by_type(struct device_node *from, const char *type)

from:开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树;

type:要查找的节点对应的type字符串,也就是 device_type属性值。

返回值:返回找到的节点,如果为NULL,表示查找失败。

4)、根据device_type属性和compatible属性查找指定的节点

struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible)

from:开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树;

type:要查找的节点对应的type字符串,也就是device_type属性值。若为NULL,则可忽略掉device_type属性值;

compatible:要查找的节点所对应的compatible 属性列表;

返回值:返回找到的节点,如果为NULL,表示查找失败。

5)、通过of_device_id匹配表来查找指定的节点

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)

from:开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树。matches: of_device_id匹配表,也就是在此匹配表里面查找节点。

match:找到的匹配的of_device_id

返回值:返回找到的节点,如果为NULL,表示查找失败。

6)、通过路径来查找指定的节点

inline struct device_node *of_find_node_by_path(const char *path)

path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。

返回值:返回找到的节点,如果为NULL,表示查找失败。

7)、获取指定节点的父节点

struct device_node *of_get_parent(const struct device_node *node)

node:要查找的父节点的节点名称。返回值: 返回找到的父节点。

8)、采用迭代方式查找子节点,即查找“prev”的下一个节点。

struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)

node:父节点。

prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。若设置为NULL,则表示从第一个子节点开始。

返回值: 返回找到的下一个子节点。

9)、查找指定的属性

property *of_find_property(const struct device_node *np, const char *name, int *lenp)

np:设备节点。

name:属性名字。

lenp:属性值的字节数

返回值:返回找到的属性。

10)、获取属性中元素的数量

int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)

np:设备节点。

proname:需要统计元素数量的属性名字。

elem_size:元素长度。

返回值: 返回得到的属性元素数量。

11)、从属性中获取指定标号的数据值

int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)

np:设备节点。

proname:要读取的属性名字。index:要读取的值标号;

out_value:读取到的值返回值:0读取成功,负值,读取失败,-EINVAL表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

12)、读取属性中u8类型的数组数据

int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)

np:设备节点。proname:要读取的属性名字。out_value:读取到的数组值,数据类型为u8。

sz:要读取的数组元素数量。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

13)、读取属性中u16类型的数组数据

int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz)

np:设备节点。proname:要读取的属性名字。out_value:读取到的数组值,数据类型为u16。sz:要读取的数组元素数量。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

14)、读取属性中u32类型的数组数据

int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)

np:设备节点。proname:要读取的属性名字。out_value:读取到的数组值,数据类型为u32。sz:要读取的数组元素数量。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

15)、读取属性中u64类型的数组数据

int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)

np:设备节点。proname:要读取的属性名字。out_value:读取到的数组值,数据类型为u64。sz:要读取的数组元素数量。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

16)、读取属性中只有一个u8类型的数据

int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value)

np:设备节点。proname:要读取的属性名字。out_value:读取到的u8类型的数据值。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

17)、读取属性中只有一个u16类型的数据

int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value)

np:设备节点。proname:要读取的属性名字。out_value:读取到的u16类型的数据值。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

18)、读取属性中只有一个u32类型的数据

int of_property_read_u16(const struct device_node *np, const char *propname, u32 *out_value)

np:设备节点。proname:要读取的属性名字。out_value:读取到的u32类型的数据值。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

19)、读取属性中只有一个u64类型的数据

int of_property_read_u16(const struct device_node *np, const char *propname, u64 *out_value)

np:设备节点。proname:要读取的属性名字。out_value:读取到的u64类型的数据值。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

20)、读取属性中的字符串值

int of_property_read_string(struct device_node *np, const char *propname, const char **out_string)

np:设备节点。proname:要读取的属性名字。out_string:读取到的字符串值。返回值:0,读取成功,负值,读取失败。

21)、获取“#address-cells”属性值

int of_n_addr_cells(struct device_node *np)

np:设备节点。返回值:返回“#address-cells”的属性值。

22)、获取“#size-cells”属性值

int of_n_size_cells(struct device_node *np)

np:设备节点。返回值:返回“#size-cells”的属性值。

23)、査看“节点的compatible属性”是否有包含“compat指定的字符串”,也就是检查设备节点的兼容性

int of_device_is_compatible(const struct device_node *device, const char *compat)

device:设备节点;

compat:要查看的字符串。返回值:0,表示“节点的compatible属性”中不包含“compat指定的字符串”;正数,表示“节点的compatible属性”中包含“compat指定的字符串”。

24)、读取“reg”或者“assigned-addresses”属性值

const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags)

dev:设备节点。

index:要读取的地址标号。

size:地址长度。flags:参数,比如“IORESOURCE_IO”、“IORESOURCE_MEM”等返回值:返回读取到的地址数据首地址,若返回值为NUIL,则表示读取失败。

25)、将从设备树读取到的地址addr转换为物理地址

u64 of_translate_address(struct device_node *dev, const __be32 *addr)

dev:设备节点。

addr:要转换的地址。

返回值:返回得到的物理地址,如果为 OF_BAD_ADDR,则表示转换失败。

26)、将reg属性值转换为resource结构体类型

int of_address_to_resource(struct device_node *dev, int index, struct resource *r)

dev:设备节点。

index:地址资源标号

r:得到的resource 类型的资源值。

返回值:0,成功:负值,失败。

27)、将“reg属性中地址信息”转换为“虚拟地址”,如果reg属性有多段的话,可以通过index参数指定要完成内存映射的是哪一段

void __iomem *of_iomap(struct device_node *np, int index)

np:设备节点。

index:reg属性中要完成内存映射的段,如果reg属性只有一段的话,则index=0。

返回值:经过内存映射后的虚拟内存首地址,如果为NULL的话,则表示内存映射失败。

注意:也可以使用ioremap()函数来完成物理地址到虚拟地址的内存映射。在采用设备树以后,大部分的驱动都使用of_iomap()函数来获取内存地址所对应的虚拟地址。

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

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

相关文章

使用Portainer让测试环境搭建飞起来

Docker的用处不多加赘述&#xff0c;Docker目前有以下应用场景&#xff1a; 测试&#xff1a;Docker很适合用于测试发布&#xff0c;将 Docker 封装后可以直接提供给测试人员进行运行&#xff0c;不再需要测试人员与运维、开发进行配合&#xff0c;进行环境搭建与部署。 测试…

【考研数学】李林《880》vs 李永乐《660》完美使用搭配

没有说谁一定好&#xff0c;只有适不适合自身情况&#xff0c;针对自身弱点选择性价比才最高。 两者侧重点不同&#xff0c;660适合强化前期&#xff0c;弥补基础的不足&#xff0c;880适合强化后期&#xff0c;题型全面&#xff0c;提高我们对综合运用知识的能力。 选择习题…

动手学深度学习PyTorch版

基本的数据操作 import torch # 创建一个行向量&#xff0c;默认为从0开始的12个整数 # n维数组也称为张量 x torch.arange(12) x # 张量的形状 x.shape# 张量的大小,张量所有元素的个数 x.numel()#修改张量的形状 x x.reshape(3,4)#生成形状为3*4的两个向量&#xff0c;向…

C#实现归并排序算法

C#实现归并排序算法 以下是 C# 中的归并排序算法实现示例&#xff1a; using System;class MergeSortAlgorithm {// 合并两个子数组static void Merge(int[] arr, int left, int mid, int right){// 计算左子数组和右子数组的长度int n1 mid - left 1;int n2 right - mid;/…

力扣--滑动窗口438.找到字符串中所有字母异位词

思路分析&#xff1a; 使用两个数组snum和pnum分别记录字符串s和p中各字符出现的次数。遍历字符串p&#xff0c;统计其中各字符的出现次数&#xff0c;存储在pnum数组中。初始化snum数组&#xff0c;统计s的前m-1个字符的出现次数。从第m个字符开始遍历s&#xff0c;通过滑动窗…

STM32(14)USART

USART:一种片上外设&#xff0c;用来实现串口通信&#xff0c;就是stm32内部的串口 USART简介 串并转换电路 串行通信和并行通信 串行&#xff1a;一根数据线&#xff0c;逐个比特位发送 为什么要串并转换 移位寄存器 USART的基本模型 通过查询SR&#xff08;状态寄存器&…

w022郑州大学招新赛选拔赛

A-SW的与众不同数组_2022学年第一学期郑州大学ACM招新赛&选拔赛 (nowcoder.com) #include <bits/stdc.h> #define int long long using namespace std;void solve(){int n;cin >> n;vector<int> v;for(int i 1; i < n; i){int x;cin >> x;v.p…

Java集合面试题(day 02)

&#x1f4d1;前言 本文主要是【JAVA】——Java集合面试题的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&am…

一文扫荡,12个可视化图表js库,收藏备用。

hello&#xff0c;我是贝格前端工场&#xff0c;可视化图表在web前端开发中经常碰到&#xff0c;是不是很疑惑这些炫酷的图表是怎么实现的&#xff0c;其实是通过js库开发的&#xff0c;本文带来12个javascript库的介绍&#xff0c;欢迎关注我&#xff0c;阅读精彩内容。 一、什…

【新版Hi3521DV200处理器性能】

新版Hi3521DV200处理器性能 Hi3521DV200是针对多路高清/超高清&#xff08;1080p/4M/5M/4K&#xff09;DVR产品应用开发的新一代专业SoC芯片。Hi3521DV200集成了ARM Cortex-A7四核处理器和性能强大的神经网络推理引擎&#xff0c;支持多种智能算法应用。同时&#xff0c;Hi352…

Media Encoder 2024:未来媒体编码的新纪元 mac/win版

随着科技的飞速发展&#xff0c;媒体内容已成为我们日常生活中不可或缺的一部分。为了满足用户对高质量视频内容不断增长的需求&#xff0c;Media Encoder 2024应运而生&#xff0c;它凭借卓越的技术和创新的特性&#xff0c;重塑了媒体编码的未来。 Media Encoder 2024软件获…

绝赞春招拯救计划

huihut/interview: &#x1f4da; C/C 技术面试基础知识总结&#xff0c;包括语言、程序库、数据结构、算法、系统、网络、链接装载库等知识及面试经验、招聘、内推等信息。This repository is a summary of the basic knowledge of recruiting job seekers and beginners in t…

数据结构与算法-插值查找

引言 在计算机科学的广阔天地中&#xff0c;数据结构和算法扮演着至关重要的角色。它们优化了信息处理的方式&#xff0c;使得我们在面对海量数据时能够高效、准确地进行检索与分析。本文将聚焦于一种基于有序数组且利用元素分布规律的查找算法——插值查找&#xff08;Interpo…

活动预告|听云猿生数据创始人 CEO 曹伟分享云数据库行业十余年经验总结

3月16日&#xff0c;KubeBlocks 将携手 OceanBase 开源社区、AutoMQ 带来《LLMs 时代下的企业数据管理与降本增效之路》主题 meetup&#xff0c;扫描下方二维码&#xff0c;即刻报名&#x1f447;。 云猿生数据创始人 & CEO 曹伟将带来《KubeBlocks&#xff1a;把所有数据…

freeRTOS20240308

1.总结任务的调度算法&#xff0c;把实现代码再写一下 2.总结任务的状态以及是怎么样进行转换的

Flutter使用auto_updater实现windows/mac桌面应用版本升级功能

因为windows应用一般大家都是从网上下载的&#xff0c;后期版本肯定会更新&#xff0c;那用flutter开发windows应用&#xff0c;怎么实现应用内版本更新功能了&#xff1f;可以使用auto_updater库&#xff0c; 这个插件允许 Flutter 桌面 应用自动更新自己 (基于 sparkle 和 wi…

数字化转型导师坚鹏:人工智能在证券行业的应用与实践

人工智能在证券行业的应用与实践 课程背景&#xff1a; 证券公司数字化转型离不开人工智能&#xff0c;在证券公司数字化转型中&#xff0c;人工智能起到至关重要的作用&#xff0c;很多机构存在以下问题&#xff1a; 不清楚人工智能产业对我们有什么影响&#xff1f; 不知…

用python写一个自动进程守护,带UI

功能是指定程序关闭后自动重启&#xff0c;并点击1作为启动 原来的想法是群成员说的某软件打包后&#xff0c;软件进程被杀后&#xff0c;界面白屏。所以写了个计算器重启demo进行进程守护 import subprocess import time import pyautogui import psutil #用计算器做演示。 d…

【Linux基础(三)】信号

学习分享 1、信号的基本概念2、查看信号列表3、常见信号名称4、signal库函数5、发送信号kill6、kill - signal &#xff08;无参信号&#xff09;示例6.1、kill - signal (不可靠信号)示例6.2、kill - signal (可靠信号)示例 7、信号分类7.1、信号运行原理分类7.2、信号是否携带…

官网正在被哪些产品蚕食,定制网站又被哪些建站产品挤占。

2023-12-09 16:22贝格前端工场 官网建设是一个被大多数人看衰的市场&#xff0c;本文来理性分析下&#xff0c;谁在蚕食这个市场&#xff0c;谁又在挤占这个产品生存空间&#xff0c;欢迎大家评论&#xff0c;探讨。 网站正在被以下产品形式取代&#xff1a; 1. 移动应用&…