Linux驱动开发(速记版)--平台总线

第四十七章 平台总线模型介绍

47.1 什么是平台总线?

        平台总线是Linux内核中的一种虚拟机制,用于连接和匹配平台设备与对应的平台驱动。它简化了设备与驱动之间的绑定过程,提高了系统对硬件的适配性和扩展性。

        当设备或驱动被注册时,平台总线会自动寻找并绑定匹配的组件,让设备能被正确初始化和控制。这种机制增强了嵌入式系统的灵活性和可移植性。

设备、平台总线、驱动的关系

47.2 平台总线的优势

引入平台总线模型后,解决了多设备驱动管理中的重复和冗余问题。其优势包括:

        设备与驱动分离:清晰划分设备和驱动的代码,提高了代码的可读性和维护性。

        增强代码重用性:允许同类型设备共享同一驱动代码,通过配置不同的设备文件来适应不同硬件,减少重复编写。

        减少重复性代码:避免为每个设备编写相似驱动代码,简化了开发流程。

        提高可移植性:使驱动不依赖于特定标准总线,便于跨平台移植和重用。

第四十八章 注册 platform 设备

48.1 注册 platform 设备

48.1.1 platform_device_register() 函数

platform_device_register() 函数是 Linux 内核中用于注册平台设备的一个关键函数。

platform_device结构体包含设备名称设备资源设备 ID 等信息,描述和标识平台设备。

// 平台总线设备注册函数原型  
int platform_device_register(struct platform_device *pdev)  
{  
    // 初始化设备结构体      
    device_initialize(&pdev->dev);  //设置设备的引用计数、类型等基本信息。
  
    // 根据架构设置特定数据(可选)  
    arch_setup_pdev_archdata(pdev);  
  
    // 将平台设备添加到内核中  
    return platform_device_add(pdev); //包括将其添加到设备层级结构和 platform 总线 
}

// 在头文件中的声明  
#include <linux/platform_device.h>  
extern void platform_device_register(struct platform_device *);
/*平台设备结构体*/
struct platform_device {  
    const char *name;     // 设备名称  
    int id;               // 设备ID号  
    struct device dev;    // 内嵌的具体的device结构体,表示设备在设备模型中的抽象  
    u32 num_resources;    // 资源数量  
    struct resource *resource; // 指向设备的资源描述符数组
                               // 资源描述符包含了设备的物理地址、大小、类型等信息。  
    const struct platform_device_id *id_entry; // 用于匹配设备和驱动程序的ID结构体
                                               // 通过该结构体可以实现设备和驱动的自动匹配。  
    // ... 其他成员,如指向设备对应的平台驱动程序的指针等  
};

48.1.2 platform_device_unregister() 函数

        platform_device_unregister() 函数用于从内核中取消注册并移除已经注册的平台设备

        这个函数通过减少设备的引用计数和将其从设备层级结构移除来完成清理工作。

/*从平台总线移除已经注册的设备*/
void platform_device_unregister(struct platform_device *pdev)  
{  
    // 将设备从platform总线的设备列表中移除  
    platform_device_del(pdev);  
  
    // 减少设备的引用计数,必要时释放设备结构体和相关资源  
    platform_device_put(pdev);  
}

// 在头文件中的声明  
#include <linux/platform_device.h>  
extern void platform_device_unregister(struct platform_device *);

48.1.3 platform_device 结构体

        platform_device 结构体是 Linux 内核中用于描述和管理平台设备的数据结构。

        它包含了设备名称设备ID资源信息以及与其他设备模型和架构相关的数据。

/*平台总线设备结构体*/
struct platform_device {  
    const char *name;       // 设备的名称,用于唯一标识  
    int id;                 // 设备的ID,区分同种设备的不同实例,可选,-1 表示不使用  
    bool id_auto;           // ID 是否自动生成,通常不是直接操作的对象  
    struct device dev;      // 基本的设备管理和操作结构体,必须有效且实现 release 方法  
    u32 num_resources;      // 设备资源的数量  
    struct resource *resource; // 指向资源数组的指针  
    const struct platform_device_id *id_entry; // 用于匹配设备和驱动的ID表项  
    char *driver_override;  // 强制指定驱动名称  
    struct mfd_cell *mfd_cell; // 指向多功能设备(MFD)单元的指针  
    struct pdev_archdata archdata; // 存储特定于架构的设备数据  
};
/*资源结构体*/
struct resource {  
    resource_size_t start;  // 资源的起始物理地址  
    resource_size_t end;    // 资源的结束物理地址  
    const char *name;       // 资源的名称  
    unsigned long flags;    // 资源标志,如 IORESOURCE_MEM(内存资源)  
    // ... 其他成员  
};

        在实际使用中,platform_device 的创建和注册通常由内核的初始化代码、设备树(Device Tree)或特定的初始化函数完成,而不是直接在代码中手动构造。

        platform_device_register() 函数用于注册设备

        platform_device_unregister() 用于注销设备

48.1.4 resource 结构体

        resource 结构体用于在 Linux 内核中描述和管理系统资源,如内存区域、I/O 端口、中断等。它定义了资源的起始和结束地址名称标志位,以及与其他资源的父子或兄弟关系

/*资源结构体*/
struct resource {  
    resource_size_t start;    // 资源的起始地址  
    resource_size_t end;      // 资源的结束地址  
    const char *name;         // 资源的名称  
    unsigned long flags;      // 资源的标志位,用于表示属性、特征等  
    // ... 其他成员和宏定义略  
};

/*
示例标志位
#define IORESOURCE_IO       0x00000100  // I/O 端口资源  
#define IORESOURCE_MEM      0x00000200  // 内存资源  
  
// 资源属性和特征相关  
#define IORESOURCE_READONLY 0x00000400  // 资源是只读的  
#define IORESOURCE_CACHEABLE 0x00000800 // 资源支持缓存  
  
// 其他状态和控制  
#define IORESOURCE_DISABLED 0x00001000  // 资源当前被禁用  
#define IORESOURCE_BUSY     0x00002000  // 驱动程序将此资源标记为繁忙
*/

48.2 注册平台总线设备实验

        本实验将注册一个名为 "my_platform_device" 的平台设备。

        当注册平台设备时,该驱动程序提供了两个资源:

        一个内存资源和一个中断资源。这些资源被定义在名为 my_resources 的结构体数组中。

        内存资源:

                起始地址:MEM_START_ADDR(0xFDD60000)

                结束地址:MEM_END_ADDR(0xFDD60004)

                标记:IORESOURCE_MEM

        中断资源:

                中断资源号:IRQ_NUMBER(101)

                标记:IORESOURCE_IRQ

/*注册平台总线设备代码,platform_device.c 代码*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#define MEM_START_ADDR 0xFDD60000  //设备资源起始地址
#define MEM_END_ADDR 0xFDD60004    //设备资源结束地址  总共4字节
#define IRQ_NUMBER 101             //中断资源号

/* resource结构体数组定义,resources[0]放内存资源,resources[1]放中断资源 */
static struct resource my_resources[] = {
    {
        .start = MEM_START_ADDR, // 内存资源起始地址
        .end = MEM_END_ADDR, // 内存资源结束地址
        .flags = IORESOURCE_MEM, // 标记为内存资源
    },
    {
        .start = IRQ_NUMBER, // 中断资源号
        .end = IRQ_NUMBER, // 中断资源号
        .flags = IORESOURCE_IRQ, // 标记为中断资源
    },
};

/*平台设备释放函数*/
static void my_platform_device_release(struct device *dev)
{
    // 释放各种资源,由plat_form结构体的release成员指向该函数,在
    // platform_device_unregister()被调用且设备被成功注销时执行
}

/*定义平台设备结构体*/
static struct platform_device my_platform_device = {
    .name = "my_platform_device", // 设备名称
    .id = -1, // 设备 ID
    .num_resources = ARRAY_SIZE(my_resources), // 资源数量
    .resource = my_resources, // 资源数组
    .dev.release = my_platform_device_release, // 释放资源的回调函数
};
//platform_device结构体中的.dev.release字段被设置为一个函数,
//那么当platform_device_unregister()被调用且设备被成功注销时,这个函数将被执行。

/*驱动入口*/
static int __init my_platform_device_init(void)
{
    int ret;
    ret = platform_device_register(&my_platform_device); // 注册平台设备
    if (ret) {
        printk(KERN_ERR "Failed to register platform device\n");
        return ret;
    }
    printk(KERN_INFO "Platform device registered\n");
    return 0;
}

/*驱动出口*/
static void __exit my_platform_device_exit(void)
{
    platform_device_unregister(&my_platform_device); // 注销平台设备
    printk(KERN_INFO "Platform device unregistered\n");
}
module_init(my_platform_device_init);
module_exit(my_platform_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");

        来到/sys/bus/platform/devices 目录下,可以看到我们创建的 my_platform_device 平台总线设备。

第四十九章 注册 platform 驱动实验

49.1 注册 platform 驱动

49.1.1 platform_driver_register() 函数

        platform_driver_register() 在 Linux 内核中用于注册平台驱动程序到内核。

#include <linux/platform_device.h>  

/*注册平台驱动程序*/
int platform_driver_register(struct platform_driver *driver);
#define platform_driver_register(drv) \  
    __platform_driver_register(drv, THIS_MODULE)

int __platform_driver_register(struct platform_driver *drv,   //平台驱动结构体
                               struct module *owner)          //所属模块
{  
    drv->driver.owner = owner;                // 将当前模块与平台驱动程序关联  
    drv->driver.bus = &platform_bus_type;     // 设置总线类型为平台总线  
    drv->driver.probe = platform_drv_probe;   // 设置探测函数,发现匹配设备时回调  
    drv->driver.remove = platform_drv_remove; // 设置移除函数,移除设备时回调  
    drv->driver.shutdown = platform_drv_shutdown; // 设置关机函数,系统关机时调用  
    return driver_register(&drv->driver);     // 注册驱动程序到内核  
}

49.1.2 platform_driver_unregister() 函数

  platform_device_unregister() 用于从 Linux 内核中注销平台设备,以进行清理和释放操作。

#include <linux/platform_device.h> 

/*从内核注销平台驱动*/ 
void platform_driver_unregister(struct platform_driver *pdrv);
/*从内核注销平台驱动*/
void platform_driver_unregister(struct platform_driver *drv)  
{  
    driver_unregister(&drv->driver);  
}
/*移除平台驱动底层代码*/
void driver_unregister(struct device_driver *drv)  
{  
    /*参数检查:验证传入的设备驱动程序指针是否有效。*/
    if (!drv || !drv->p) {  
        printk(KERN_WARNING "Unexpected driver unregister!\n");  
        return;  
    }  
  
    /*移除属性组:从内核中移除与设备驱动程序关联的属性组*/
    if (drv->groups)  
        sysfs_remove_groups(&drv->p->kobj, drv->groups);  
    
    /*从总线中移除驱动程序,可能调用remove相关回调*/
    bus_remove_driver(drv);  
  
    kfree(drv->p->klist_devices.k_list);  
    kfree(drv->p);  
  
    module_put(drv->owner);  
}  

49.1.3 platform_driver 结构体

        platform_driver 结构体是 Linux 内核中用于编写平台设备驱动程序的关键数据结构,它包含驱动的匹配设备ID表,和一系列驱动操作相关的回调函数

/*平台总线驱动结构体*/
struct platform_driver {  
    int (*probe)(struct platform_device *);  // 探测函数,初始化设备  
    int (*remove)(struct platform_device *); // 移除函数,清理资源  
    void (*shutdown)(struct platform_device *); // 关闭函数,系统关闭时调用  
    int (*suspend)(struct platform_device *, pm_message_t); // 挂起函数  
    int (*resume)(struct platform_device *); // 恢复函数  
  
    struct device_driver driver; // 通用设备驱动程序数据,如驱动程序的名称、总线类型等。
                                 // 其中,name 参数需要与 platform_device 的 .name 参数相同,
                                 // 以便匹配成功并调用 probe 函数。 
    const struct platform_device_id *id_table; // 设备ID表,用于匹配设备和驱动 
                                //确定哪个平台设备与该驱动程序匹配。其优先级高于 driver.name。 
    bool prevent_deferred_probe; // 是否禁用延迟探测  
};

探测函数probe:系统检测到与驱动程序匹配的平台设备时,调用此函数初始化和配置设备。

移除函数remove:当平台设备从系统中移除时,调用此函数来执行清理和释放资源的操作。

关闭函数shutdown:当系统关闭时,调用此函数来执行与平台设备相关的关闭操作。

挂起函数suspend:系统进入挂起状态时,执行与平台设备相关的挂起操作。

恢复函数resume:系统从挂起状态恢复时,执行与平台设备相关的恢复操作。

device_driver结构体

        与设备驱动程序相关的通用数据,如驱动程序的名称、总线类型等。其中,name 参数需要与 platform_device 的 .name 参数相同,以便匹配成功并调用 probe 函数。

设备与驱动关联表id_table

        通过该表,可以确定哪个平台设备与该驱动程序匹配。其优先级高于 driver.name。

探测函数禁用布尔值

        如果设置为 true,则延迟探测将被禁用。

/*平台设备id匹配表*/
static const struct platform_device_id my_id_table[] = {  
    { "my_device", 0 },  
    { },  
};

49.2 注册平台总线驱动实验

// 平台设备的探测函数
static int my_platform_probe(struct platform_device *pdev)
{
    printk(KERN_INFO "my_platform_probe: Probing platform device\n");
    // 添加设备特定的操作
    // ... return 0;
}

// 平台设备的移除函数
static int my_platform_remove(struct platform_device *pdev)
{
    printk(KERN_INFO "my_platform_remove: Removing platform device\n");
    // 清理设备特定的操作
    // ... return 0;
}

// 定义平台驱动结构体
static struct platform_driver my_platform_driver = {
    .probe = my_platform_probe, .remove = my_platform_remove, 
    .driver = {
        .name = "my_platform_device", 
        .owner = THIS_MODULE, 
    }
};

// 模块初始化函数
static int __init my_platform_driver_init(void)
{
    int ret;
    // 注册平台驱动
    ret = platform_driver_register(&my_platform_driver);
    if (ret) {
        printk(KERN_ERR "Failed to register platform driver\n");
        return ret;
    }
    printk(KERN_INFO "my_platform_driver: Platform driver initialized\n");
    return 0;
}

// 模块退出函数
static void __exit my_platform_driver_exit(void)
{
    // 注销平台驱动
    platform_driver_unregister(&my_platform_driver);
    printk(KERN_INFO "my_platform_driver: Platform driver exited\n");
}

        平台总线设备驱动用来处理平台总线设备的生命周期,包括探测、移除、系统关闭、挂起、恢复这几个阶段。

        首先加载平台设备.ko文件,     来到/sys/bus/platform/devices 目录下,可以看到我们创建的 my_platform_device 平台总线设备文件夹

        然后加载平台设备驱动.ko文件,来到/sys/bus/platform/drivers 目录下,可以看到我们创建的 my_platform_driver 平台驱动文件夹就成功生成了。

第五十章 probe 函数编写

        驱动是要控制硬件的,因此正确做法是在 platform_driver的 probe函数中获得设备硬件资源。

50.1 获取 device 资源

方法1:直接访问 platform_device结构体的资源数组

        在Linux内核中,platform_device结构体包含了一个 resource数组,该数组存储了设备的硬件资源信息。驱动程序可以直接访问这个数组来获取所需的资源。

/*直接访问platform_device结构体的resource数组获取资源*/
if (pdev->num_resources >= 2) {  
    struct resource *res_mem = &pdev->resource[0]; // 获取内存资源  
    struct resource *res_irq = &pdev->resource[1]; // 获取中断资源  
    printk("Memory Resource: start = 0x%llx, end = 0x%llx\n", res_mem->start, res_mem->end);  
    printk("IRQ Resource: number = %lld\n", res_irq->start);  
}

方法2:使用 platform_get_resource()获取硬件资源

        platform_get_resource()提供了一种更灵活的方式来获取 platform_device的资源。它允许驱动程序根据资源的类型和索引来获取特定的资源。

/*获取平台设备的资源*/
struct resource *platform_get_resource(struct platform_device *pdev, //平台设备结构体
                                       unsigned int type,            //资源类型宏
                                       unsigned int num);            //资源编号
/*
示例标志位
#define IORESOURCE_IO       0x00000100  // I/O 端口资源  
#define IORESOURCE_MEM      0x00000200  // 内存资源  
  
// 资源属性和特征相关  
#define IORESOURCE_READONLY 0x00000400  // 资源是只读的  
#define IORESOURCE_CACHEABLE 0x00000800 // 资源支持缓存  
  
// 其他状态和控制  
#define IORESOURCE_DISABLED 0x00001000  // 资源当前被禁用  
#define IORESOURCE_BUSY     0x00002000  // 驱动程序将此资源标记为繁忙
*/
struct resource *res_mem;  
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 获取第一个内存资源  
if (res_mem) {  
    printk("Memory Resource: start = 0x%llx, end = 0x%llx\n", res_mem->start, res_mem->end);  
} else {  
    // 处理获取内存资源失败的情况  
    printk("Failed to get memory resource\n");  
}

第五十三章 点亮 LED 灯(平台总线)

        编写完成的 platform_led.c 代码。注册平台总线设备模块。

        open设备文件以后,向设备文件写入1,通过寄存器点灯,写入0,通过寄存器关灯。        

        在 模块入口函数中,通过

        platform_driver_register(&my_platform_driver); 注册平台驱动

        在 平台驱动的 probe函数中,注册字符设备cdev。完成整套逻辑。

struct device_test{
    dev_t dev_num; //设备号
    int major ; //主设备号
    int minor ; //次设备号
    struct cdev cdev_test; // cdev
    struct class *class; //类
    struct device *device; //设备
    char kbuf[32];
    unsigned int *vir_gpio_dr;
};

struct device_test dev1;

/*file_operations结构体填充部分*/
/*打开设备函数*/
static int cdev_test_open(struct inode *inode, struct file *file)
{
    file->private_data=&dev1;//设置私有数据
    printk("This is cdev_test_open\r\n");
    return 0;
}

/*向设备写入数据函数*/
static ssize_t cdev_test_write(struct file *file,
                               const char __user *buf,
                               size_t size,
                               loff_t *off)
{
    struct device_test *test_dev=(struct device_test *)file->private_data;
    // copy_from_user:用户空间向内核空间传数据
    if (copy_from_user(test_dev->kbuf, buf, size) != 0) 
    {
        printk("copy_from_user error\r\n");
        return -1;
    }

    //如果应用层传入的数据是 1,则打开灯
    if(test_dev->kbuf[0]==1){ 
        *(test_dev->vir_gpio_dr) = 0x8000c040; //设置数据寄存器的地址
        printk("test_dev->kbuf [0] is %d\n",test_dev->kbuf[0]); //打印传入的数据
    }
    //如果应用层传入的数据是 0,则关闭灯
    else if(test_dev->kbuf[0]==0) 
    { *(test_dev->vir_gpio_dr) = 0x80004040; //设置数据寄存器的地址
        printk("test_dev->kbuf [0] is %d\n",test_dev->kbuf[0]); //打印传入的数据
    }
    return 0;
}

/**从设备读取数据*/
static ssize_t cdev_test_read(struct file *file, 
                              char __user *buf, 
                              size_t size, 
                              loff_t *off)
{
    struct device_test *test_dev=(struct device_test *)file->private_data;
    // copy_to_user:内核空间向用户空间传数据
    if (copy_to_user(buf, test_dev->kbuf, strlen( test_dev->kbuf)) != 0) 
    {
        printk("copy_to_user error\r\n");
        return -1;
    }
    printk("This is cdev_test_read\r\n");
    return 0;
}

/*用户调用close关闭设备文件时,release函数被调用*/
static int cdev_test_release(struct inode *inode, struct file *file)
{
    printk("This is cdev_test_release\r\n");
    return 0;
}

/*设备操作函数*/
struct file_operations cdev_test_fops = {
    .owner = THIS_MODULE, //将 owner 字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
    .open = cdev_test_open, //将 open 字段指向 chrdev_open(...)函数
    .read = cdev_test_read, //将 open 字段指向 chrdev_read(...)函数
    .write = cdev_test_write, //将 open 字段指向 chrdev_write(...)函数
    .release = cdev_test_release, //将 open 字段指向 chrdev_release(...)函数
};

/*平台设备驱动函数编写*/
static int my_platform_driver_probe(struct platform_device *pdev)
{
    struct resource *res_mem;
    int ret;
    res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res_mem) {
        dev_err(&pdev->dev, "Failed to get memory resource\n");
        return -ENODEV;
    }

    /*注册字符设备驱动*/
    /*1 创建设备号*/
    ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "alloc_name"); //动态分配设备号
    if (ret < 0)
    {
            goto err_chrdev;
    }

    printk("alloc_chrdev_region is ok\n");
    dev1.major = MAJOR(dev1.dev_num); //获取主设备号
    dev1.minor = MINOR(dev1.dev_num); //获取次设备号
    printk("major is %d \r\n", dev1.major); //打印主设备号
    printk("minor is %d \r\n", dev1.minor); //打印次设备号

    /*2 初始化 cdev*/
    dev1.cdev_test.owner = THIS_MODULE;
    cdev_init(&dev1.cdev_test, &cdev_test_fops);

    /*3 添加一个 cdev,完成字符设备注册到内核*/
    ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
    if(ret<0)
    {
        goto err_chr_add;
    }

    /*4 创建设备类(设备节点)*/
    dev1. class = class_create(THIS_MODULE, "test");
    if(IS_ERR(dev1.class))
    {
        ret=PTR_ERR(dev1.class);
        goto err_class_create;
    }

    /*5 创建设备*/
    dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test");
    if(IS_ERR(dev1.device))
    {
        ret=PTR_ERR(dev1.device);
        goto err_device_create;
    }

    dev1.vir_gpio_dr=ioremap(res_mem->start,4); //将物理地址转化为虚拟地址
    if(IS_ERR(dev1.vir_gpio_dr))
    {
        ret=PTR_ERR(dev1.vir_gpio_dr); //PTR_ERR()来返回错误代码
        goto err_ioremap;
    }
    return 0;

err_ioremap:
    iounmap(dev1.vir_gpio_dr);
err_device_create:
    class_destroy(dev1.class); //删除类
err_class_create:
    cdev_del(&dev1.cdev_test); //删除 cdev
err_chr_add:
    unregister_chrdev_region(dev1.dev_num, 1); //注销设备号
err_chrdev:
    return ret;
}

/*设备驱动remove函数*/
static int my_platform_driver_remove(struct platform_device *pdev)
{
    // 设备移除操作
    return 0;
}

static struct platform_driver my_platform_driver = {
    .driver = {
        .name = "my_platform_device", // 与 platform_device.c 中的设备名称匹配
        .owner = THIS_MODULE, },
        .probe = my_platform_driver_probe,
        .remove = my_platform_driver_remove, };

/*模块入口函数*/
static int __init my_platform_driver_init(void)
{
    int ret;
    ret = platform_driver_register(&my_platform_driver); // 注册平台驱动
    if (ret) {
        printk("Failed to register platform driver\n");
        return ret;
    }
    printk("Platform driver registered\n");
    return 0;
}

/*模块出口函数*/
static void __exit my_platform_driver_exit(void)
{
    /*注销字符设备*/
    unregister_chrdev_region(dev1.dev_num, 1); //注销设备号
    cdev_del(&dev1.cdev_test); //删除 cdev
    device_destroy(dev1.class, dev1.dev_num); //删除设备
    class_destroy(dev1.class); //删除类
    platform_driver_unregister(&my_platform_driver); // 注销平台驱动
    printk("Platform driver unregistered\n");
}

module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

完整网络模型训练(一)

文章目录 一、网络模型的搭建二、网络模型正确性检验三、创建网络函数 一、网络模型的搭建 以CIFAR10数据集作为训练例子 准备数据集&#xff1a; #因为CIFAR10是属于PRL的数据集&#xff0c;所以需要转化成tensor数据集 train_data torchvision.datasets.CIFAR10(root&quo…

YOLO11震撼发布!

非常高兴地向大家介绍 Ultralytics YOLO系列的新模型&#xff1a; YOLO11&#xff01; YOLO11 在以往 YOLO 模型基础上带来了一系列强大的功能和优化&#xff0c;使其速度更快、更准确、用途更广泛。主要改进包括 增强了特征提取功能&#xff0c;从而可以更精确地捕捉细节以更…

[云]Kubernetes 的基础知识

目标&#xff1a; 实践实验室涵盖 Kubernetes 的基础知识&#xff08;这个句子的意思是在实验室中通过实践学习 Kubernetes 的基本概念&#xff09; 在此过程中理解 Kubernetes 概念&#xff08;这个句子的意思是在学习的过程中理解 Kubernetes 的相关概念&#xff09; 议程&…

【无人机设计与技术】四旋翼无人机的建模

摘要 本项目的目标是通过 Simulink 建模和仿真&#xff0c;研究四旋翼无人机的建模、姿态控制、定点位置控制及航点规划功能。无人机建模包含了动力单元模型、控制效率模型和刚体模型&#xff0c;并运用这些模型实现了姿态控制和位置控制。姿态控制为无人机的平稳飞行提供基础…

OpenCV normalize() 函数详解及用法示例

OpenCV的normalize函数用于对数组&#xff08;图像&#xff09;进行归一化处理&#xff0c;即将数组中的元素缩放到一个指定的范围或具有一个特定的标准&#xff08;如均值和标准差&#xff09;。它有两个原型函数, 如下: Normalize()规范化数组的范数或值范围。当normTypeNORM…

制造企业为何需要PLM系统?PLM系统解决方案对制造业重要性分析

制造企业为何需要PLM系统&#xff1f;PLM系统解决方案对制造业重要性分析 新华社9月23日消息&#xff0c;据全国组织机构统一社会信用代码数据服务中心统计&#xff0c;我国制造业企业总量突破600万家。数据显示&#xff0c;2024年1至8月&#xff0c;我国制造业企业数量呈现稳…

简单线性回归分析-基于R语言

本题中&#xff0c;在不含截距的简单线性回归中&#xff0c;用零假设对统计量进行假设检验。首先&#xff0c;我们使用下面方法生成预测变量x和响应变量y。 set.seed(1) x <- rnorm(100) y <- 2*xrnorm(100) &#xff08;a&#xff09;不含截距的线性回归模型构建。 &…

计算机视觉综述

大家好&#xff0c;今天&#xff0c;我们将一起探讨计算机视觉的基本概念、发展历程、关键技术以及未来趋势。计算机视觉是人工智能的一个重要分支&#xff0c;旨在使计算机能够“看”懂图像和视频&#xff0c;从而完成各种复杂的任务。无论你是对这个领域感兴趣的新手&#xf…

Linux操作系统中MongoDB

1、什么是MongoDB 1、非关系型数据库 NoSQL&#xff0c;泛指非关系型的数据库。随着互联网web2.0网站的兴起&#xff0c;传统的关系数据库在处理web2.0网站&#xff0c;特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心&#xff0c;出现了很多难以克服的问…

SpringBoot整合JPA详解

SpringBoot版本是2.0以上(2.6.13) JDK是1.8 一、依赖 <dependencies><!-- jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId></dependency><!--…

C# C++ 笔记

第一阶段知识总结 lunix系统操作 1、基础命令 &#xff08;1&#xff09;cd cd /[目录名] 打开指定文件目录 cd .. 返回上一级目录 cd - 返回并显示上一次目录 cd ~ 切换到当前用户的家目录 &#xff08;2&#xff09;pwd pwd 查看当前所在目录路径 pwd -L 打印当前物理…

Unity实战案例全解析:RTS游戏的框选和阵型功能(5)阵型功能 优化

前篇&#xff1a;Unity实战案例全解析&#xff1a;RTS游戏的框选和阵型功能&#xff08;4&#xff09;阵型功能-CSDN博客 本案例来源于unity唐老狮&#xff0c;有兴趣的小伙伴可以去泰克在线观看该课程 我只是对重要功能进行分析和做出笔记分享&#xff0c;并未无师自通&#x…

ARM Process state -- SPSR

Holds the saved process state for the current mode. 保存当前模式的已保存进程状态。 N, bit [31] Set to the value of PSTATE.N on taking an exception to the current mode, and copied to PSTATE.N on executing an exception return operation in the current mod…

袋鼠云数据资产平台:数据模型标准化建表重构升级

数据模型是什么&#xff1f;简单来说&#xff0c;数据模型是用来组织和管理数据的一种方式。它为构建高效且可靠的信息系统提供了基础&#xff0c;不仅决定了如何存储和管理数据&#xff0c;还直接影响系统的性能和可扩展性。 想要建立一个良好的数据模型&#xff0c;设计时需…

链表的基础知识

文章目录 概要整体架构流程 小结 概要 链表是一种常见的数据结构&#xff0c;它通过节点之间的连接关系实现数据的存储和访问。链表由一系列节点&#xff08;Node&#xff09;组成&#xff0c;每个节点包含数据和指向下一个节点的指针。链表的特点是物理存储单元上非连续、非顺…

Qt的互斥量用法

目的 互斥量的概念 互斥量是一个可以处于两态之一的变量:解锁和加锁。这样&#xff0c;只需要一个二进制位表示它&#xff0c;不过实际上&#xff0c;常常使用一个整型量&#xff0c;0表示解锁&#xff0c;而其他所有的值则表示加锁。互斥量使用两个过程。当一个线程(或进程)…

网络编程,端口号,网络字节序,udp

前面一篇我们讲了网络的基础&#xff0c;网络协议栈是什么样的&#xff0c;数据如何流动传输的&#xff1b;接下来这篇&#xff0c;我们将进行实践操作&#xff0c;真正的让数据跨网络进行传输&#xff1b; 1.网络编程储备知识 1.1 初步认识网络编程 首先我们需要知道我们的…

Java基础 3. 面向对象

Java基础 3. 面向对象 文章目录 Java基础 3. 面向对象3.1. 面向对象3.2. 对象的创建和使用3.3. 封装3.4. 构造方法3.5. this关键字3.6. static关键字JVM体系结构 [^现阶段不用掌握]3.7. 单例模式 [^初级]3.8. 继承3.9. 方法覆盖3.10. 多态3.11. super关键字3.12. final关键字3.…

你的虚拟猫娘女友,快来领取!--文心智能体平台

文章目录 一、引言二、赛事介绍2.1 简介2.2 比赛时间2.3 大赛具体链接2.4 第一期赛题 三、智能体创建流程3.1 进入文心智能体平台3.1 创建智能体3.1 虚拟猫娘女友特性3.1 智能体调优 四、引言智能体测试五、结语 一、引言 我是热爱生活的通信汪&#xff0c;今天这篇博文记录一…

[CSP-J 2022] 解密

题目来源&#xff1a;洛谷题库 [CSP-J 2022] 解密 题目描述 给定一个正整数 k k k&#xff0c;有 k k k 次询问&#xff0c;每次给定三个正整数 n i , e i , d i n_i, e_i, d_i ni​,ei​,di​&#xff0c;求两个正整数 p i , q i p_i, q_i pi​,qi​&#xff0c;使 n …