【Linux】平台设备驱动
在设备驱动模型中,引入总线的概念可以对驱动代码和设备信息进行分离。但是驱动中总线的概念是软件层面的一种抽象,与我们SOC中物理总线的概念并不严格相等。
- 物理总线:芯片与各个功能外设之间传送信息的公共通信干线,其中又包括数据总线、地址总线和控制总线,以此来传输各种通信时序。
- 驱动总线:负责管理设备和驱动。制定设备和驱动的匹配规则,一旦总线上注册了新的设备或者是新的驱动,总线将尝试为他们进行配对。
一般对于I2C
、SPI
、USB
这些常见类型的物理总线来说,Linux内核会自动创建与之相应的驱动总线,因此I2C
设备、SPI
设备、USB
设备自然是注册挂载在相应的总线上。但是,实际项目开发中还有很多结构简单的设备,对他们进行控制并不需要特殊的时序。他们也就没有相应的物理总线,比如led、蜂鸣器和按键等,Linux内核将不会为他们创建相应的驱动总线。为了使这部分设备的驱动开发也能够遵循设备驱动模型,Linux内核引入了一种虚拟的总线–平台总线(platform_bus)。
平台总线用于管理、挂载那些没有相应物理总线的设备,这些设备被称为平台设备,对应的设备驱动则被称为平台驱动。平台设备驱动的核心依然是Linux设备驱动模型,平台设备使用platform_device
结构体来进行表示,其继承了设备驱动模型中的device结构体。而平台驱动使用platform_driver
结构体来进行表示,其则是继承了设备驱动模中的device_driver结构体。
平台设备
platform_device结构体
platform_device结构体(内核源码/include/linux/platform_device.h) struct platform_device {const char *name;int id;struct device dev;u32 num_resources;struct resource *resource;const struct platform_device_id *id_entry;/* 省略部分成员 */};
- name: 设备名称,总线进行匹配时,会比较设备和驱动的名称是否一致;
- id: 指定设备的编号,Linux支持同名的设备,而同名设备之间则是通过该编号进行区分;
- dev: Linux设备模型中的device结构体,linux内核大量使用了面向对象思想,platform_device通过继承该结构体可复用它的相关代码,方便内核管理平台设备;
- num_resources: 记录资源的个数,当结构体成员resource存放的是数组时,需要记录resource数组的个数,内核提供了宏定义ARRAY_SIZE用于计算数组的个数;
- resource: 平台设备提供给驱动的资源,如irq,dma,内存等等。该结构体会在接下来的内容进行讲解;
- id_entry: 平台总线提供的另一种匹配方式,原理依然是通过比较字符串,这部分内容会在平台总线小节中讲,这里的id_entry用于保存匹配的结果;
何为设备信息
平台设备的工作是为驱动程序提供设备信息,设备信息包括硬件信息和软件信息两部分。
- 硬件信息:驱动程序需要使用到什么寄存器,占用哪些中断号、内存资源和IO口等。
- 软件信息:以太网卡设备中的MAC地址、I2C设备中的设备地址、SPI设备的片选信号线等等。
对于硬件信息,使用结构体struct resource
来保存设备所提供的资源,比如设备使用的中断编号,寄存器物理地址等,结构体原型如下:
resource结构体(内核源码/include/linux/ioport.h)
/*
* Resources are tree-like, allowing
* nesting etc..
*/struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;/* 省略部分成员 */
};
- name: 指定资源的名字,可以设置为NULL;
- start、end: 指定资源的起始地址以及结束地址
- flags: 用于指定该资源的类型,在Linux中,资源包括I/O、Memory、Register、IRQ、DMA、Bus等多种类型,最常见的有以下几种:
资源宏定义 | 描述 |
---|---|
IORESOURCE_IO | 用于IO地址空间,对应于IO端口映射方式 |
IORESOURCE_MEM | 用于外设的可直接寻址的地址空间 |
IORESOURCE_IRQ | 用于指定该设备使用某个中断 |
IORESOURCE_DMA | 用于指定使用的DMA通道 |
设备驱动程序的主要目的是操作设备的寄存器。不同架构的计算机提供不同的操作接口,主要有IO端口映射和IO內存映射两种方式。 对应于IO端口映射方式,只能通过专门的接口函数(如inb、outb)才能访问; 采用IO内存映射的方式,可以像访问内存一样,去读写寄存器。在嵌入式中,基本上没有IO地址空间,所以通常使用IORESOURCE_MEM。
在资源的起始地址和结束地址中,对于IORESOURCE_IO或者是IORESOURCE_MEM,他们表示要使用的内存的起始位置以及结束位置; 若是只用一个中断引脚或者是一个通道,则它们的start和end成员值必须是相等的。
而对于软件信息,这种特殊信息需要我们以私有数据的形式进行封装保存,我们注意到platform_device结构体中, 有个device结构体类型的成员dev。在前面章节,我们提到过Linux设备模型使用device结构体来抽象物理设备, 该结构体的成员platform_data可用于保存设备的私有数据。platform_data是void *类型的万能指针, 无论你想要提供的是什么内容,只需要把数据的地址赋值给platform_data即可, 还是以GPIO引脚号为例,示例代码如下:
unsigned int pin = 10;struct platform_device pdev = {.dev = {.platform_data = &pin;}
}
将保存了GPIO引脚号的变量pin地址赋值给platform_data指针,在驱动程序中通过调用平台设备总线中的核心函数,可以获取到我们需要的引脚号。
注册/注销平台设备
platform_device_register函数(内核源码/drivers/base/platform.c)int platform_device_register(struct platform_device *pdev)
函数参数和返回值如下:
参数: pdev: platform_device类型结构体指针
返回值:
- 成功: 0
- 失败: 负数
platform_device_unregister函数(内核源码/drivers/base/platform.c)void platform_device_unregister(struct platform_device *pdev)
函数参数和返回值如下:
参数: pdev: platform_device类型结构体指针
返回值: 无
平台驱动
platform_driver结构体
platform_driver结构体(内核源码/include/platform_device.h)struct platform_driver {int (*probe)(struct platform_device *);int (*remove)(struct platform_device *);struct device_driver driver;const struct platform_device_id *id_table;.......
};
- probe: 函数指针,驱动开发人员需要在驱动程序中初始化该函数指针,当总线为设备和驱动匹配上之后,会回调执行该函数。我们一般通过该函数,对设备进行一系列的初始化。
- remove: 函数指针,驱动开发人员需要在驱动程序中初始化该函数指针,当我们移除某个平台设备时,会回调执行该函数指针,该函数实现的操作,通常是probe函数实现操作的逆过程。
- driver: Linux设备模型中用于抽象驱动的device_driver结构体,platform_driver继承该结构体,也就获取了设备模型驱动对象的特性;
- id_table: 表示该驱动能够兼容的设备类型。
platform_device_id结构体原型如下所示:
id_table结构体(内核源码/include/linux/mod_devicetable.h)struct platform_device_id {char name[PLATFORM_NAME_SIZE];kernel_ulong_t driver_data;};
name
是数组用于指定驱动的名称,总线进行匹配时,会依据该结构体的name成员与platform_device中的变量name进行比较匹配driver_data
则是用于来保存设备的配置。我们知道在同系列的设备中,往往只是某些寄存器的配置不一样,为了减少代码的冗余, 尽量做到一个驱动可以匹配多个设备的目的
注册/注销平台驱动
platform_driver_register函数int platform_driver_register(struct platform_driver *drv);
函数参数和返回值如下:
参数: drv: platform_driver类型结构体指针
返回值:
- 成功: 0
- 失败: 负数
platform_driver_unregister函数(内核源码/drivers/base/platform.c)void platform_driver_unregister(struct platform_driver *drv);
参数: drv: platform_driver类型结构体指针
返回值: 无
平台驱动获取设备信息
platform_get_resource()函数通常会在驱动的probe函数中执行,用于获取平台设备提供的资源结构体,最终会返回一个struct resource类型的指针,该函数原型如下:
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);
参数:
- dev: 指定要获取哪个平台设备的资源;
- type: 指定获取资源的类型,如IORESOURCE_MEM、IORESOURCE_IO等;
- num: 指定要获取的资源编号。每个设备所需要资源的个数是不一定的,为此内核对这些资源进行了编号,对于不同的资源,编号之间是相互独立的。
返回值:
- 成功: struct resource结构体类型指针
- 失败: NULL
假若资源类型为IORESOURCE_IRQ,平台设备驱动还提供以下函数接口,来获取中断引脚,
int platform_get_irq(struct platform_device *pdev, unsigned int num)
参数:
- pdev: 指定要获取哪个平台设备的资源;
- num: 指定要获取的资源编号。
返回值:
- 成功: 可用的中断号
- 失败: 负数
对于存放在device结构体中成员platform_data的软件信息,我们可以使用dev_get_platdata函数来获取,函数原型如下所示:
static inline void *dev_get_platdata(const struct device *dev)
{return dev->platform_data;
}
参数:
- dev: struct device结构体类型指针
返回值: device结构体中成员platform_data指针
平台总线
平台总线注册和匹配方式
在Linux的设备驱动模型中,总线是最重要的一环。每当有新的设备或者新的驱动加入到总线时,总线便会调用platform_match
函数对新增的设备或驱动,进行配对。内核中使用bus_type
来抽象描述系统重的总线,平台总线结构体原型如下:
platform_bus_type结构体(内核源码/driver/base/platform.c)struct bus_type platform_bus_type = {.name = "platform",.dev_groups = platform_dev_groups,.match = platform_match,.uevent = platform_uevent,.pm = &platform_dev_pm_ops,};EXPORT_SYMBOL_GPL(platform_bus_type);
内核用platform_bus_type来描述平台总线,该总线在linux内核启动的时候自动进行注册。这里重点是platform总线的match函数指针,该函数指针指向的函数将负责实现平台总线和平台设备的匹配过程。对于每个驱动总线, 它都必须实例化该函数指针。
id_table匹配方式
在定义结构体platform_driver时,我们需要提供一个id_table的数组,该数组说明了当前的驱动能够支持的设备。当加载该驱动时,总线的match函数发现id_table非空, 则会比较id_table中的name成员和平台设备的name成员,若相同,则会返回匹配的条目。
示例
编译一个driver.ko
和两个device.ko
文件,两个device都是杂项设备,共用主设备号,动态分配不同的次设备号,共用一个驱动。
haptic_dev0.c/** @Date: 2024-10-17 16:10:22* @LastEditors: zdk* @LastEditTime: 2024-10-18 10:50:21* @FilePath: \kernel\drivers\haptics\haptic_dev0.c*/
/** Silicon Integrated Co., Ltd haptic sih688x haptic driver file** Copyright (c) 2021 heater <daokuan.zhu@si-in.com>** This program is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 as published by* the Free Software Foundation*/#include <linux/init.h> //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>//定义的资源
//寄存器地址 和 大小
static struct resource haptic_dev_res[]=
{[0]= DEFINE_RES_MEM(0x01,1),[1]= DEFINE_RES_MEM(0x02,1)
};//定义私有数据
//整形数据表示状态 0,1,2等(可自定义)
static int status = 0;static void haptic_dev_release(struct device* dev)
{printk("%s\n",__func__);
}static struct platform_device haptic_dev=
{.name = "haptic_dev0",.id = 0,.num_resources = ARRAY_SIZE(haptic_dev_res),.resource = haptic_dev_res,.dev={.platform_data = &status,.release = haptic_dev_release,},
};static int __init haptic_dev_init(void)
{int ret = 0;//内核层只能使用printk,不能使用printfprintk(KERN_EMERG "%s\n",__FUNCTION__); ret = platform_device_register(&haptic_dev);return ret;
}static void __exit haptic_dev_exit(void)
{platform_device_unregister(&haptic_dev);printk(KERN_EMERG "%s\n",__FUNCTION__);
}module_init(haptic_dev_init);//模块入口
module_exit(haptic_dev_exit);//模块出口MODULE_AUTHOR("<daokuan.zhug@si-in.com>");//声明作者信息
MODULE_DESCRIPTION("Haptics Device V1.0.0"); //对这个模块作一个简单的描述
MODULE_LICENSE("GPL v2");//声明开源许可证// "GPL" 是指明 这是GNU General Public License的任意版本// “GPL v2” 是指明 这仅声明为GPL的第二版本
haptic_dev1.c/** @Date: 2024-10-17 16:10:22* @LastEditors: zdk* @LastEditTime: 2024-10-18 10:50:28* @FilePath: \kernel\drivers\haptics\haptic_dev1.c*/
/** Silicon Integrated Co., Ltd haptic sih688x haptic driver file** Copyright (c) 2021 heater <daokuan.zhu@si-in.com>** This program is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 as published by* the Free Software Foundation*/#include <linux/init.h> //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>//定义的资源
//寄存器地址 和 大小
static struct resource haptic_dev_res[]=
{[0]= DEFINE_RES_MEM(0x03,1),[1]= DEFINE_RES_MEM(0x04,1)
};//定义私有数据
//整形数据表示状态 0,1,2等(可自定义)
static int status = 1;static void haptic_dev_release(struct device* dev)
{printk("%s\n",__func__);
}static struct platform_device haptic_dev=
{.name = "haptic_dev1",.id = 1,.num_resources = ARRAY_SIZE(haptic_dev_res),.resource = haptic_dev_res,.dev={.platform_data = &status,.release = haptic_dev_release,},
};static int __init haptic_dev_init(void)
{int ret = 0;//内核层只能使用printk,不能使用printfprintk(KERN_EMERG "%s\n",__FUNCTION__); ret = platform_device_register(&haptic_dev);return ret;
}static void __exit haptic_dev_exit(void)
{platform_device_unregister(&haptic_dev);printk(KERN_EMERG "%s\n",__FUNCTION__);
}module_init(haptic_dev_init);//模块入口
module_exit(haptic_dev_exit);//模块出口MODULE_AUTHOR("<daokuan.zhug@si-in.com>");//声明作者信息
MODULE_DESCRIPTION("Haptics Device V1.0.0"); //对这个模块作一个简单的描述
MODULE_LICENSE("GPL v2");//声明开源许可证// "GPL" 是指明 这是GNU General Public License的任意版本// “GPL v2” 是指明 这仅声明为GPL的第二版本
haptic_drv.c/** @Date: 2024-10-17 16:08:08* @LastEditors: zdk* @LastEditTime: 2024-10-18 17:18:05* @FilePath: \kernel\drivers\haptics\haptic_drv.c*/#include <linux/init.h> //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include "haptic_ioctl.h"//打开设备
static int haptics_open(struct inode* inode,struct file * filp)
{ //驱动最好不使用全局变量,所以在probe函数中获取的资源,该如何在这里拿到呢???//使用字符设备的时候 可以使用container_of结合inode->i_cdev拿到//但是杂项设备该怎么办呢?printk("%s minor=%d\n",__FUNCTION__, MINOR(inode->i_rdev));return 0;
}//关闭设备
static int haptics_release(struct inode* inode ,struct file* filp)
{printk("%s\n",__FUNCTION__);return 0;
}//ioctl
static long haptics_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{int ret = 0;ioctl_protocol_t msg;//反解cmd中的字段int type = _IOC_TYPE(cmd);int dir = _IOC_DIR(cmd);int nr = _IOC_NR(cmd);int size = _IOC_SIZE(cmd);printk("dir=%d size=%d\n",dir,size);//检验cmd是否正确//1.校验cmd_typeif(DEVICE_TYPE != type){printk(KERN_ERR "cmd type error\n");ret =-1;return ret;}if(HAPTICS_READ_REG == cmd)//读寄存器{//校验nrif(HAPTICS_READ_REG_NR == nr){ret = copy_from_user(&msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk("read_reg:%#02x\n",msg.reg_addr);msg.reg_value=0xff;//这里模拟读寄存器的值//将读取到的值传给用户空间ret = copy_to_user((ioctl_protocol_t __user *)arg, &msg, sizeof(ioctl_protocol_t));}}else if(HAPTCIS_WRITE_REG == cmd)//写寄存器{//校验nrif(HAPTICS_WRITE_REG_NR == nr){ret = copy_from_user(&msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk("write_reg:%#02x=%#02x\n",msg.reg_addr,msg.reg_value);//模拟写寄存器的值}}else//{printk(KERN_ERR "unknown cmd\n");}return ret;
}typedef struct
{struct miscdevice miscdev;//定义一个杂项设备结构体struct resource* res;int status;
}haptic_miscdev_t;static int haptic_drv_probe(struct platform_device *pdev)
{int ret = 0;int* status=NULL;struct resource* res0=NULL;struct resource* res1=NULL;haptic_miscdev_t *hap_miscdev=NULL;struct file_operations *haptics_fops=NULL;res0 = platform_get_resource(pdev,IORESOURCE_MEM,0);res1 = platform_get_resource(pdev,IORESOURCE_MEM,1);printk("res0 start=%d size=%d\n",(int)res0->start,(int)(res0->end-res0->start+1));printk("res1 start=%d size=%d\n",(int)res1->start,(int)(res1->end-res1->start+1));status = dev_get_platdata(&pdev->dev);printk("status=%d\n",*status);haptics_fops = devm_kzalloc(&pdev->dev,sizeof(struct file_operations), GFP_KERNEL);haptics_fops->open = haptics_open;haptics_fops->release = haptics_release;haptics_fops->unlocked_ioctl = haptics_ioctl;hap_miscdev = devm_kzalloc(&pdev->dev,sizeof(haptic_miscdev_t), GFP_KERNEL);hap_miscdev->res = res0;hap_miscdev->status = *status;hap_miscdev->miscdev.name = pdev->name;hap_miscdev->miscdev.fops = haptics_fops;hap_miscdev->miscdev.minor = MISC_DYNAMIC_MINOR,ret = misc_register(&hap_miscdev->miscdev);/* save as drvdata *///platform_set_drvdata函数,将设备数据信息存入在平台驱动结构体中pdev->dev->driver_data中platform_set_drvdata(pdev, hap_miscdev);return ret;
}static int haptic_drv_remove(struct platform_device *pdev)
{int ret = 0;haptic_miscdev_t *hap_miscdev = platform_get_drvdata(pdev);misc_deregister(&hap_miscdev->miscdev);return ret;
}static struct platform_device_id haptic_dev_id[] =
{{.name = "haptic_dev0"},{.name = "haptic_dev1"},{}
};static struct platform_driver haptic_drv=
{.driver.name = "haptics_drv",.probe = haptic_drv_probe,.remove = haptic_drv_remove,.id_table = haptic_dev_id,
};static int __init haptic_drv_init(void)
{int ret = 0;//内核层只能使用printk,不能使用printfprintk(KERN_EMERG "%s\n",__FUNCTION__); ret = platform_driver_register(&haptic_drv);return ret;
}static void __exit haptic_drv_exit(void)
{platform_driver_unregister(&haptic_drv);printk(KERN_EMERG "%s\n",__FUNCTION__);
}module_init(haptic_drv_init);//模块入口
module_exit(haptic_drv_exit);//模块出口MODULE_AUTHOR("<daokuan.zhug@si-in.com>");//声明作者信息
MODULE_DESCRIPTION("Haptics Device V1.0.0"); //对这个模块作一个简单的描述
MODULE_LICENSE("GPL v2");//声明开源许可证// "GPL" 是指明 这是GNU General Public License的任意版本// “GPL v2” 是指明 这仅声明为GPL的第二版本
haptic_ioctl.h#ifndef __HAPTCIS_IOCTL_H__
#define __HAPTCIS_IOCTL_H__/** @Date: 2024-10-12 15:53:37* @LastEditors: zdk* @LastEditTime: 2024-10-18 17:22:18* @FilePath: \kernel\drivers\haptics\haptic_ioctl.h*/#include <linux/ioctl.h>
// #include <sys/ioctl.h> // 用户空间/*这里使用ioctl定义两个协议,读寄存器和写寄存器
*用户空间和内核空间共用的头文件,包含ioctl命令及相关宏定义,可以理解为一份“协议”文件
*/
//cmd中的type
#define DEVICE_TYPE 'H'
#define HAPTICS_READ_REG_NR (0)
#define HAPTICS_WRITE_REG_NR (1)
#define HAPTICS_READ_REG _IO(DEVICE_TYPE,HAPTICS_READ_REG_NR)
#define HAPTCIS_WRITE_REG _IO(DEVICE_TYPE,HAPTICS_WRITE_REG_NR)typedef struct
{uint8_t reg_addr;uint8_t reg_value;
}ioctl_protocol_t;#endif
再来看看sys目录下
总结
-
驱动代码中最好不要使用全局变量,因为驱动一般是支持多设备的,如果有全局变量会有冲突。
-
字符设备驱动中在open函数中可以使用container_of拿到设备信息,但是杂项设备驱动中怎么拿到设备信息暂时未知。
相关文章:

【Linux】平台设备驱动
在设备驱动模型中,引入总线的概念可以对驱动代码和设备信息进行分离。但是驱动中总线的概念是软件层面的一种抽象,与我们SOC中物理总线的概念并不严格相等。 物理总线:芯片与各个功能外设之间传送信息的公共通信干线,其中又包括数…...

【Linux】命令行参数环境变量
文章目录 命令行参数环境变量环境变量的概念常见环境变量查看环境变量测试PATH修改PATH HOME和环境变量相关的命令环境变量是如何被组织的通过系统调用putenv和getenv获取或设置环境变量环境变量通常是具有全局属性的普通变量与环境变量的区别 命令行参数 main函数有三种形式&…...

libaom 源码分析:twopass_encoder.c 文件
libaom libaom 是 AOMedia(开放媒体联盟)开发的一个开源视频编解码器库,它是 AV1 视频压缩格式的参考实现,并被广泛用于多种生产系统中。libaom 支持多种功能,包括可扩展视频编码(SVC)、实时通信…...

ruoyi同时支持mysql+sqlserver+oracle+postgresql
需求背景 最近需要一个小demo,项目中需要同时连接sqlserver和mysql数据库。 操作教程 1、pom.xml -- 修改common/pom.xml<!-- 动态数据源 --> <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-star…...

微信小程序绘制轨迹
1、map | uni-app官网 根据官网描述:通过从数据库获取POI数据,并通过 uni-id-common 内的路线规划API,计算路线、距离、时间。 2、 <map style"width:100%;height:96%;" id"myMap" :scale"scale" :longi…...

UNION 联合查询
1.UNION ALL联合查询 同样为了演示方便,先向 teacher 表插入多条测试数据: INSERT INTO teacher (name,age,id_number,email) VALUES (姓名一,17,42011720200604077X,NULL), (姓名二,18,42011720200604099X,123qq.com), (姓名三,19,42011720200604020X…...

blender 理解 积木组合 动画制作 学习笔记
一、学习blender视频教程链接 案例2:积木组合_动画制作_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Bt4y1E7qn?vd_sourced0ea58f1127eed138a4ba5421c577eb1&p10&spm_id_from333.788.videopod.episodes 二、说明 之前已经学习了如何制作积木组…...

关于 FreeSWITCH mod_sofia 注册过期时间的测试
FreeSWITCH 版本:1.10.12,以下简称 Fs SIP 终端:Eyebeam 1.5.14.4 Eyebeam 设置注册的过期时间为 30 Fs 设置为 120,下面是详细配置: <param name"sip-force-expires-max" value"120"/>…...

【LeetCode:349. 两个数组的交集 + 哈希表】
🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...

根据语音生成视频33搜帧
33搜帧,是一个能根据语音生成视频的网站,33搜帧 - 视频帧画面搜索引擎 33搜帧是一个使用AI技术构建的视频帧画面搜索引擎,和一般素材平台通过视频标签来搜索视频不同,33搜帧能搜索到视频素材中的每一帧画面,这个功能可…...

目标检测数据集图片及标签同步锐化
在目标检测任务中,数据集的质量直接影响到模型的性能。数据增强作为提升数据集多样性和模型泛化能力的常用手段,在图像处理过程中扮演着重要角色。锐化(Sharpening)技术是常见的图像增强方法之一,能够突出图像中的细节…...

滚雪球学Redis[6.4讲]:Redis消息队列:构建高效的消息通信与任务调度系统
全文目录: 🎉前言🚦Redis消息队列的使用场景🐳1. 异步任务处理🐋2. 任务调度🐬3. 模块解耦 ⚙️实现发布/订阅模型🐟️1. 发布者发布消息🐠2. 订阅者订阅频道🐡3. 实际应…...

《计算机视觉》—— 换脸
效果如下: 完整代码: import cv2 import dlib import numpy as npJAW_POINTS list(range(0, 17)) RIGHT_BROW_POINTS list(range(17, 22)) LEFT_BROW_POINTS list(range(22, 27)) NOSE_POINTS list(range(27, 35)) RIGHT_EYE_POINTS list(range(36…...

【JavaEE初阶】深入透析文件-IO关于文件内容的操作(四种文件流)
前言 🌟🌟本期讲解关于CAS的补充和JUC中有用的类,这里涉及到高频面试题哦~~~ 🌈上期博客在这里:【JavaEE初阶】文件-IO之实现文件系统的操作如何进行实现-CSDN博客 🌈感兴趣的小伙伴看一看小编主页&…...

复习:react 中的 refs,怎么使用,有哪些使用场景
在 React 中,refs(引用)是一个重要的特性,它允许开发者直接访问 DOM 元素或者 React 组件的实例。以下是对 React 中 refs 的使用及其使用场景的详细解释: 一、refs 的使用方法 字符串引用 在早期的 React 版本中,可以通过字符串来设置 ref。然而,这种方法已经被废弃,…...

Python OpenCV精讲系列 - 目标检测与识别深入理解(二十)
💖💖⚡️⚡️专栏:Python OpenCV精讲⚡️⚡️💖💖 本专栏聚焦于Python结合OpenCV库进行计算机视觉开发的专业教程。通过系统化的课程设计,从基础概念入手,逐步深入到图像处理、特征检测、物体识…...

golang中的上下文
背景 在Go语言中,使用context包来管理跨API和进程间的请求生命周期是常见的做法。特别是在涉及到并发编程时,如启动协程(goroutine)来处理异步任务,正确地传递和监听context变得尤为重要。比如,在gin框架中…...

Navigation2 算法流程
转自 https://zhuanlan.zhihu.com/p/405670882 此文仅作学习笔记 启动流程 在仿真环境中启动导航包的示例程序,执行nav2_bringup/bringup/launch/tb3_simulation_launch.py文件。ROS2的launch文件支持采用python语言来编写以支持更加复杂的功能,本文件…...

OpenAI swarm+ Ollama快速构建本地多智能体服务 - 1. 服务构建教程
OpenAI开源了多智能体编排的工程swarm,今天介绍一下swarm与OLLAMA如何结合使用的教程,在本地构建自己的多智能体服务,并给大家实践演示几个案例。 安装步骤 安装ollama,在官网下载对应操作系统的版本即可,下载后用ol…...

HTB:Wifinetic[WriteUP]
目录 连接至HTB并启动靶机 1.What is the name of the OpenWRT backup file accessible over FTP? 使用nmap对靶机21、22端口进行脚本、服务信息扫描 2.Whats the WiFi password for SSID OpenWRT? 3.Which user reused the WiFi password on thier local account? 4.…...

专业学习|马尔可夫链(概念、变体以及例题)
一、马尔可夫链的概念及组成 (一)学习资料分享 来源:024-一张图,但讲懂马尔可夫决策过程_哔哩哔哩_bilibili 马尔可夫链提供了一种建模随机过程的方法,具有广泛的应用。在实际问题中,通过转移概率矩阵及初…...

RK3576 安卓SDK编译环境搭建
编译 Android14 对机器的配置要求较高: 建议预留500G存储 多分配CPU和内存 建议使用 Ubuntu 20.04 操作系统或更高版本 sudo apt-get updatesudo apt-get install make gcc sudo apt-get install g++ patchelf gawk texinfo chrpath diffstat binfmt-support sudo apt-get …...

Renesas R7FA8D1BH (Cortex®-M85) 上光电编码器测速功能
目录 概述 1 软硬件 1.1 软硬件环境信息 1.2 开发板信息 1.3 调试器信息 2 硬件架构 2.1 硬件框架结构 2.2 测速功能原理介绍 2.2.1 理论描述 2.2.2 实现原理 2.2.3 系统硬件结构 3 软件实现 3.1 FSP配置项目 3.2 代码实现 3.2.1 初始化函数 3.2.2 功能函数 3.…...

软件测试学习笔记丨Linux三剑客-sed
本文转自测试人社区,原文链接:https://ceshiren.com/t/topic/32521 一、简介 sed(Stream editor)是一个功能强大的文本流编辑器,主要用于对文本进行处理和转换。它适用于自动化处理大量的文本数据,能够支持…...

Vue脚手架学习 vue脚手架配置代理、插槽、Vuex使用、路由、ElementUi插件库的使用
目录 1.vue脚手架配置代理 1.1 方法一 1.2 方法二 2.插槽 2.1 默认插槽 2.2 具名插槽 2.3 作用域插槽 3.Vuex 3.1 概念 3.2 何时使用? 3.3 搭建vuex环境 3.4 基本使用 3.5 getters的使用 3.6 四个map方法的使用 3.6.1 mapState方法 3.6.2 mapGetter…...

使用yml文件安装环境时,如何添加conda和pip的镜像源
博客参考 添加conda镜像源 name: NAME channels:- conda-forge- pytorch- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2- defaults depende…...

c语言经典100例
1.字符串转为数字 #include <stdio.h>int strToInt(char *s) {int num0;int sign1;int step1;if (*s -){sign -1;s;}while (*s > 0&&*s < 9){num num*10(*s-0);step 10;s;}return num*sign; }int main() {char a[10] "-1234";char *s a ;pr…...

百易云资产管理运营系统 ufile.api.php SQL注入漏洞复现
0x01 产品描述: 百易云资产管理运营系统,是专门针对企业不动产资产管理和运营需求而设计的一套综合解决方案。该系统能够覆盖资产的全生命周期管理,包括资产的登记、盘点、评估、处置等多个环节,同时提供强大的运营分析功能&#…...

【分布式微服务云原生】《Redis RedLock 算法全解析:应对时钟漂移与网络分区挑战》
《Redis RedLock 算法全解析:应对时钟漂移与网络分区挑战》 摘要: 本文深入探讨 Redis 的 RedLock 算法,详细阐述其步骤及工作原理,同时重点分析该算法如何处理时钟漂移和网络分区这两个常见的分布式系统问题。读者将通过本文深入…...

OceanBase 的写盘与传统数据库有什么不同?
背景 在数据库开发过程中,“写盘”是一项核心操作,即将内存中暂存的数据安全地转储到磁盘上。在诸如MySQL这样的传统数据库管理系统中,写盘主要有以下几步:首先将数据写入缓存池;其次,为了确保数据的完整性…...