MX6ULL学习笔记 (八) platform 设备驱动实验

前言:

什么是 Linux 下的 platform 设备驱动

Linux下的字符设备驱动一般都比较简单,只是对IO进行简单的读写操作。但是I2C、SPI、LCD、USB等外设的驱动就比较复杂了,需要考虑到驱动的可重用性,以避免内核中存在大量重复代码,为此人们提出了驱动的分离与分层的思路,演化并诞生出了platform设备驱动。

一、驱动的分层分离

1. 驱动的分离

以I2C接口的三轴加速度传感器为例,传统的设备驱动如下图示:每个平台都有一个ADXL345的驱动,因此设备驱动要重复编写三次。

各平台的主机驱动是不同的,但是ADXL345是一样的,因此上图可以精简为一个ADXL345驱动和统一的接口API。

 实际上,除了ADXL345还有AT24C02、MPU6050等I2C设备,因此实际的驱动框架图如下示

驱动的分离即将主机驱动和设备驱动分隔开来,实际开发中,主机驱动一般由半导体厂家提供,设备驱动也会由器件厂家写好,我们只需要提供设备信息即可。也就是将设备信息从设备驱动中剥离开来,设备驱动使用标准方法获取到设备信息,然后根据获取到的设备信息来初始化设备

因此驱动只负责驱动,设备只负责设备,想办法将两者进行匹配即可。这就是Linux中的总线-驱动-设备模型,也就是常说的驱动分离

如上图示,当向系统注册一个驱动时,总线会在右侧的设备中查找,看看有没有与之匹配的设备,有的话就将两者联系起来;当向系统中注册一个设备时,总线会在左侧的驱动中查找,看有没有与之匹配的驱动,有的话也联系起来。

2. 驱动的分层

Linux下的驱动也是分层的,分层的目的是为了在不同的层处理不同的内容。以input输入子系统为例,input子系统负责管理所有跟输入有关的驱动,包括键盘、鼠标、触摸等。

  • 驱动层:获取输入设备的原始值,获取到的输入事件上报给核心层
  • 核心层:处理各种IO模型,并且提供file_operations操作集合
  • 事件层:和用户空间进行交互

3. platform平台驱动模型

根据总线-驱动-设备驱动模型,IIC、SPI、USB这样实实在在的总线是完全匹配的,但是要有一些外设是没法归结为具体的总线:比如定时器、RTC、LCD等。为此linux内核创造了一个虚拟的总线:platform总线,以及platform驱动、platform设备模型。

platform总线Linux内核使用bus_type结构体表示总线。

其定义在文件include/linux/device.h中

platform驱动platform驱动由platform_driver结构体表示。

此结构体定义在文件include/linux/platform_device.h中。

        编写platform驱动时,先要定义一个platform_driver结构体变量,然后实现结构体中的各个成员变量,重点是实现匹配方法以及probe函数。当驱动和设备匹配成功以后probe函数就会执行,具体的驱动程序在probe函数里面编写。之后通过以下函数向内核注册platform驱动或卸载platform驱动

③platform设备:platform_device结构体用来表示platform设备。

        注意若内核支持设备树的话,就无需使用该结构体来描述设备,而改用设备树了。该结构体定义在文件include/linux/platform_device.h中,在不支持设备树的Linux版本中,用户需要编写platform_device变量来描述设备信息,然后使用以下函数将设备信息注册到Linux内核中或从内核中注销掉,这里我们使用的linux是新版本的了,也就直接使用设备树就好了。

 


二、platform框架分析

1.platform总线注册

  和字符型驱动一样,我们要使用platform总线之前,也需要告诉一下内核,也就是注册。使用platform_bus_init函数去进行注册,既然要注册,那么我们也得告诉内核我们的一些信息。

注册的内容就是:

struct bus_type platform_bus_type = {

       .name           = "platform",

       .dev_groups   = platform_dev_groups,

       .match           = platform_match,

       .uevent          = platform_uevent,

       .pm        = &platform_dev_pm_ops,

}

对于platform平台而言,platform_match函数就是月老,负责驱动和设备的匹配。

2.platform驱动

在注册platform驱动之前要定义一个结构体,为platform_driver,结构体内容为:

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 state);

       int (*resume)(struct platform_device *);

       struct device_driver driver;

//-> const struct of_device_id    *of_match_table;

//-> const char             *name;

       const struct platform_device_id *id_table;

       bool prevent_deferred_probe;

};

 然后使用platform_driver_register函数向内核注册platform驱动。向内核注册platform驱动的时候,如果驱动和设备匹配成功,最终会执行platform_driver的probe函数。

3.platform设备

1、无设备树的时候,此时需要驱动开发人员编写设备注册文件,使用platform_device_register函数注册设备。使用platform_device_register函数注册设备也同样需要告诉内核一些注册信息。也就是需要定义个结构体:

结构体platform_device:

struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;
	struct resource	*resource;

	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

 

2,有设备树,修改设备树的设备节点即可。使用兼容性列表,当设备与platform的驱动匹配以后,就会执行platform_driver->probe函数。


 三、编写 platform 驱动流程

接下来我们就来学习一下如何在设备树下编写 platform 驱动流程:

1.在设备树中创建设备节点

由于我们使用的linux是支持设备树的,那么毫无疑问,我们肯定要先在设备树中创建设备节点来描述设备信息,重点是要设置好 compatible 属性的值,因为 platform 总线需要通过设备节点的 compatible 属性值来匹配驱动!这点要切记。

2、编写platfrom的驱动兼容表

1)建立of_device_id 表,也就是驱动的兼容表:

static const struct of_device_id leds_of_match[] = {

    { .compatible = "atkalpha-gpioled" },     /* 兼容属性 */

    { /* Sentinel */ }

};

3、建立platform_driver结构体

static struct platform_driver leds_platform_driver = {

    .driver = {

       .name = "imx6ul-led",

       .of_match_table = leds_of_match,

        },

     .probe = leds_probe,

     .remove = leds_remove,

};

1)设置 platform_driver 中的 of_match_table 匹配表为上面创建的 leds_of_match

2)向总线注册驱动的时候,会检查当前总线下的所有设备,有没有与此驱动匹配的设备,如果有的话就执行驱动里面的probe函数。

3)卸载驱动的时候,会执行驱动里面的remove函数。

4、编写probe函数:

函数原型:

static int led_probe(struct platform_device *dev);

5、编写remove函数:

函数原型

static int led_remove(struct platform_device *dev);


四、实验程序编写

1.在设备树中创建设备节点

2.引入字符设备框架

这里直接引入我们之前写过的字符设备框架,在这份驱动的基础上来进行修改,如果没有看过之前那篇也没关系,下面也会贴出完整代码。

3.编写platfrom的驱动兼容表

/* 匹配列表 */
static const struct of_device_id led_of_match[] = {
	{ .compatible = "led-gpio" },
	{ /* Sentinel */ }
};

4.建立platform_driver结构体

/*platform_driver结构体*/
static struct platform_driver led_driver = {
    .driver = {
        .name = "imx6ul-led",
        .of_match_table = led_of_match,
    },
    .probe  =  led_probe,
    .remove =  led_remove,
};

5.编写probe函数:

当设备和驱动兼容表匹配上的时候就会运行peobe函数:

/*当谁列表的设备和驱动匹配上后执行的peobe函数*/
static int led_probe(struct platform_device *dev)
{
    /* 动态注册字符设备的流程一般如下:
	1.调用 alloc_chrdev_region() 函数申请设备编号。
	2.使用 cdev_init() 函数初始化设备描述结构体。
	3.使用 cdev_add() 函数将设备号与设备描述结构体关联起来,注册字符设备驱动。
	4.使用 class_create() 函数创建一个设备类.
	5.使用 device_create() 函数创建一个设备
	*/
    int ret = 0;
    /*进入这个函数就表明匹配成功了*/
	printk("led driver and device was matched!\r\n");
	/*1 创建设备号
	   根据是否定义了设备号,通过条件判断选择不同的创建方式。
	   如果定义了设备号,则使用MKDEV宏将主设备号和次设备号合成为设备号,并调用register_chrdev_region()函数注册字符设备号。
	   如果没有定义设备号,则使用alloc_chrdev_region()函数动态分配设备号,并通过MAJOR和MINOR宏获取分配得到的主设备号和次设备号。*/
	if(gpioled.major){
        gpioled.devid = MKDEV(gpioled.major,0);
        register_chrdev_region(gpioled.devid,DEVICE_CNT,DEVICE_NAME);
    }
    else{
        alloc_chrdev_region(&gpioled.devid,0,DEVICE_CNT,DEVICE_NAME);
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
    }

	/* 2 初始化cdev
	   设置cdev结构体的拥有者为当前模块(THIS_MODULE),然后使用 cdev_init() 函数初始化cdev结构体。
	   参数包括待初始化的cdev结构体和用于操作该设备的file_operations结构体(hello_drv) */
	gpioled.cdev.owner= THIS_MODULE;
    cdev_init(&gpioled.cdev,&gpioled_fops);
    
    /* 3、添加一个cdev */
    cdev_add(&gpioled.cdev,gpioled.devid,DEVICE_CNT);
    
	/*4 创建设备类
	   使用 class_create() 函数创建一个设备类,设备类用于在/sys/class目录下创建子目录,以组织同一类设备的相关信息。
	   该函数的参数包括所属的模块(THIS_MODULE)和设备类的名称(DEVICE_NAME)。
	   如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */
    gpioled.class = class_create(THIS_MODULE,DEVICE_NAME);
    if(IS_ERR(gpioled.class))
    {
        printk("newchr fail!\r\n");
        return PTR_ERR(gpioled.class);
    }

	/*5 创建设备
	   使用 device_create() 函数创建一个设备,并在/dev目录下创建相应的设备节点。
	   参数包括设备所属的类(newchr.class)、父设备(NULL,如果没有父设备)、设备号(newchr.devid)、设备私有数据(NULL,一般为设备驱动程序提供一个指针)和设备名称(DEVICE_NAME)。
	   如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */
	gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, DEVICE_NAME);
    if(IS_ERR(gpioled.device))
    {
        printk("newchr fail!\r\n");
        return PTR_ERR(gpioled.device);
    }

    ret = myled_init(&gpioled);   //初始化ledgpio
    return 0;
}

6.编写remove函数:

static int led_remove(struct platform_device *dev)
{
    gpio_set_value(gpioled.led_gpio,1);
    gpio_free(gpioled.led_gpio);

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    /*在模块卸载时,使用 cdev_del() 函数注销字符设备驱动,并使用 unregister_chrdev_region() 函数释放设备号资源。*/
	/* 注销字符设备驱动 */
	cdev_del(&gpioled.cdev);/*  删除cdev */
	unregister_chrdev_region(gpioled.devid, DEVICE_CNT); /* 注销设备号 */
	device_destroy(gpioled.class, gpioled.devid);// 销毁设备,删除相应的设备节点
	class_destroy(gpioled.class);// 销毁设备类,释放相关资源
	printk("gpioled exit!\r\n");
    return 0;
}

完整代码:

/**************头文件区域*********************************************************/
#include <linux/ide.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>

#include <linux/of.h> 
#include <linux/of_address.h> 
#include <linux/of_gpio.h> 
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/fcntl.h>

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <linux/io.h>
/**********************************************************************************/

/************************函数定义-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file);
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr);
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr);
static int gpioled_open(struct inode *inode , struct file *file);
static int led_probe(struct platform_device *dev);
static int led_remove(struct platform_device *dev);
/************************函数定义-end********************************************/


/************************宏定义-begin***********************************************/
#define DEVICE_NAME "dtsplatled"
#define DEVICE_CNT  1
#define LED_ON     1
#define LED_OFF    0
/************************宏定义-end********************************************/

/************************结构体定义-begin***********************************************/
/* dtsled设备信息结构体 */
struct dtsled_dev
{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	struct device_node *nd; /* 设备节点 */
    int led_gpio; /* led 所使用的 GPIO 编号 */
};
struct dtsled_dev gpioled; /* led设备 */

/* 设备操作函数结构体 */
static const struct file_operations gpioled_fops = {
	.owner		= THIS_MODULE,
	.open		= gpioled_open,
	.read		= gpioled_read,
    .write      = gpioled_write,
    .release    = gpioled_release
};


/* 匹配列表 */
static const struct of_device_id led_of_match[] = {
	{ .compatible = "led-gpio" },
	{ /* Sentinel */ }
};


static struct platform_driver led_driver = {
    .driver = {
        .name = "imx6ul-led",
        .of_match_table = led_of_match,
    },
    .probe  =  led_probe,
    .remove =  led_remove,
};
/************************结构体定义-end***********************************************/


/************************file_operations操作函数-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr)
{
    int ret;
    unsigned char databuf[1];
    unsigned char ledstate;
    struct dtsled_dev *dev = file->private_data;
    
    ret = __copy_from_user(databuf,buf,size);

    if(ret < 0)
    {
        return -EFAULT;
    }
    
    ledstate = databuf[0];

    if(ledstate == LED_OFF){   
        gpio_set_value(dev->led_gpio,1);
    }
    else if(ledstate == LED_ON){
        gpio_set_value(dev->led_gpio,0);
    }
    
	return 0;
}
static int gpioled_open(struct inode *inode , struct file *file)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    file->private_data = &gpioled; /* 设置私有数据 */ 
	return 0;
}
/************************file_operations操作函数-end***********************************************/

/*****************led初始化函数************************/
static int myled_init(struct dtsled_dev *dev)
{
    int ret = 0;
   /* 1、设置 LED 所使用的 GPIO */ 
    dev->nd = of_find_node_by_path("/gpioled");
    if(dev->nd == NULL){
        printk("gpioled node cant not found!\r\n");
        return -EINVAL;
    }
    else{
        printk("gpioled node hase been found!\r\n");
    }

    /* 2、 获取设备树中的 gpio 属性,得到 LED 所使用的 LED 编号 */ 
    dev->led_gpio =  of_get_named_gpio(dev->nd,"gpios",0);
    if(dev->led_gpio < 0)
    {
		printk("can't get led-gpio\r\n");
        return -EINVAL;
    }
    printk("led-gpio num = %d\r\n", dev->led_gpio); 
    /* 3、设置 GPIO1_IO03 为输出,并且输出高电平,默认关闭 LED 灯 */ 
    ret = gpio_request(dev->led_gpio,"led0");
    if(ret < 0){
        printk("led-gpio request fail\r\n"); 
        return -EINVAL;
    }
    gpio_direction_output(dev->led_gpio,1);

    return ret;
}

/************************platfrom操作函数-begin***********************************************/
/*当谁列表的设备和驱动匹配上后执行的peobe函数*/
static int led_probe(struct platform_device *dev)
{
    /* 动态注册字符设备的流程一般如下:
	1.调用 alloc_chrdev_region() 函数申请设备编号。
	2.使用 cdev_init() 函数初始化设备描述结构体。
	3.使用 cdev_add() 函数将设备号与设备描述结构体关联起来,注册字符设备驱动。
	4.使用 class_create() 函数创建一个设备类.
	5.使用 device_create() 函数创建一个设备
	*/
    int ret = 0;
    /*进入这个函数就表明匹配成功了*/
	printk("led driver and device was matched!\r\n");
	/*1 创建设备号
	   根据是否定义了设备号,通过条件判断选择不同的创建方式。
	   如果定义了设备号,则使用MKDEV宏将主设备号和次设备号合成为设备号,并调用register_chrdev_region()函数注册字符设备号。
	   如果没有定义设备号,则使用alloc_chrdev_region()函数动态分配设备号,并通过MAJOR和MINOR宏获取分配得到的主设备号和次设备号。*/
	if(gpioled.major){
        gpioled.devid = MKDEV(gpioled.major,0);
        register_chrdev_region(gpioled.devid,DEVICE_CNT,DEVICE_NAME);
    }
    else{
        alloc_chrdev_region(&gpioled.devid,0,DEVICE_CNT,DEVICE_NAME);
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
    }

	/* 2 初始化cdev
	   设置cdev结构体的拥有者为当前模块(THIS_MODULE),然后使用 cdev_init() 函数初始化cdev结构体。
	   参数包括待初始化的cdev结构体和用于操作该设备的file_operations结构体(hello_drv) */
	gpioled.cdev.owner= THIS_MODULE;
    cdev_init(&gpioled.cdev,&gpioled_fops);
    
    /* 3、添加一个cdev */
    cdev_add(&gpioled.cdev,gpioled.devid,DEVICE_CNT);
    
	/*4 创建设备类
	   使用 class_create() 函数创建一个设备类,设备类用于在/sys/class目录下创建子目录,以组织同一类设备的相关信息。
	   该函数的参数包括所属的模块(THIS_MODULE)和设备类的名称(DEVICE_NAME)。
	   如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */
    gpioled.class = class_create(THIS_MODULE,DEVICE_NAME);
    if(IS_ERR(gpioled.class))
    {
        printk("newchr fail!\r\n");
        return PTR_ERR(gpioled.class);
    }

	/*5 创建设备
	   使用 device_create() 函数创建一个设备,并在/dev目录下创建相应的设备节点。
	   参数包括设备所属的类(newchr.class)、父设备(NULL,如果没有父设备)、设备号(newchr.devid)、设备私有数据(NULL,一般为设备驱动程序提供一个指针)和设备名称(DEVICE_NAME)。
	   如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */
	gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, DEVICE_NAME);
    if(IS_ERR(gpioled.device))
    {
        printk("newchr fail!\r\n");
        return PTR_ERR(gpioled.device);
    }

    ret = myled_init(&gpioled);

#if 0
    /* 5、设置 LED 所使用的 GPIO */ 
    gpioled.nd = of_find_node_by_path("/gpioled");
    if(gpioled.nd == NULL){
        printk("gpioled node cant not found!\r\n");
        return -EINVAL;
    }
    else{
        printk("gpioled node hase been found!\r\n");
    }

    /* 2、 获取设备树中的 gpio 属性,得到 LED 所使用的 LED 编号 */ 
    gpioled.led_gpio =  of_get_named_gpio(gpioled.nd,"gpios",0);
    if(gpioled.led_gpio < 0)
    {
		printk("can't get led-gpio\r\n");
        return -EINVAL;
    }
    printk("led-gpio num = %d\r\n", gpioled.led_gpio); 
    /* 3、设置 GPIO1_IO03 为输出,并且输出高电平,默认关闭 LED 灯 */ 
    gpio_request(gpioled.led_gpio,"led0");
    gpio_direction_output(gpioled.led_gpio,1);
#endif
    return 0;
}
static int led_remove(struct platform_device *dev)
{
    gpio_set_value(gpioled.led_gpio,1);
    gpio_free(gpioled.led_gpio);

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    /*在模块卸载时,使用 cdev_del() 函数注销字符设备驱动,并使用 unregister_chrdev_region() 函数释放设备号资源。*/
	/* 注销字符设备驱动 */
	cdev_del(&gpioled.cdev);/*  删除cdev */
	unregister_chrdev_region(gpioled.devid, DEVICE_CNT); /* 注销设备号 */
	device_destroy(gpioled.class, gpioled.devid);// 销毁设备,删除相应的设备节点
	class_destroy(gpioled.class);// 销毁设备类,释放相关资源
	printk("gpioled exit!\r\n");
    return 0;
}
/************************platfrom操作函数-endn***********************************************/



static int __init gpioled_init(void)
{
    return platform_driver_register(&led_driver);
}

static void __exit gpioled_exit(void)
{
    platform_driver_unregister(&led_driver);
}


module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("oudafa");




五   、运行测试

1.编写 Makefile 文件

编写完使用make命令编译驱动程序。

KERN_DIR = /home/odf/linux-imx/linux-imx

all:
	clear
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o dtsplatledApp dtsplatledApp.c 

clean:
	clear
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f dtsplatledApp


obj-m += dtsplatled.o 

 

2.使用nfs挂载到开发板上。

将编译出来 dtsplatled.ko 和dtsplatledApp 拷贝到 rootfs/lib/modules/4.1.15 目录中,

sudo cp dtsplatled.ko dtsplatledApp /home/odf/nfs_rootfs/rootfs/lib/modules/4.1.15/ 

    驱动模块加载完成以后到/sys/bus/platform/drivers/目录下查看驱动是否存在,我们在

dtsplatled.c 中设置 led_driver (platform_driver 类型)的 name 字段为“imx6ul-led”,因此会在

/sys/bus/platform/drivers/目录下存在名为“imx6ul-led”这个文件

重启开发板,进 入到目录 lib/modules/4.1.15 中,输入如下命令加载 dtsplatled.ko 这个驱动模块。

insmod dtsplatled.ko

 驱动和模块都存在,当驱动和设备匹配成功以后就会输出如图一行语句:

 3.测试:

驱动和设备匹配成功以后就可以测试 LED 灯驱动了,输入如下命令打开 LED 灯:  

./ledApp /dev/dtsplatled 1 

 输入如下命令关闭 LED 灯:  

./ledApp /dev/dtsplatled 0

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

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

相关文章

玩转大数据13: 数据伦理与合规性探讨

1. 引言 随着科技的飞速发展&#xff0c;数据已经成为了现代社会的宝贵资产。然而&#xff0c;数据的收集、处理和利用也带来了一系列的伦理和合规性问题。数据伦理和合规性不仅关乎个人隐私和权益的保护&#xff0c;还涉及到企业的商业利益和社会责任。因此&#xff0c;数据…

Go语言深度优先搜索(DFS)

Go 语言代码段实现了深度优先搜索&#xff08;DFS&#xff09;算法&#xff0c;该算法用于遍历图数据结构。以下是代码的主要要点和执行流程的总结&#xff1a; 深度优先搜索函数 (DFS): 接收图的邻接表 (map[int][]int)、访问记录 (map[int]bool) 和当前节点作为参数。将当前…

SpringBoot项目静态资源默认访问目录

SpringBoot项目&#xff1a;静态资源默认访问目录 参考博客&#xff1a;https://blog.csdn.net/weixin_43808717/article/details/118281904

创建Vue2项目,引入chart.js,并生成柱形图、折线图、饼图

基础创建 1. 创建一个新的 Vue 2 项目 如果你还没有创建项目&#xff0c;可以使用 Vue CLI 来创建一个新项目。首先确保你已经安装了 Node.js 和 npm。然后安装 Vue CLI 并创建一个新项目。 npm install -g vue/cli vue create my-vue-chart-project在创建过程中选择 Vue 2 …

End-to-End Reconstruction-Classification Learning for Face Forgery Detection

一、研究背景 现有模型主要通过提取特定的伪造模式进行深度伪造检测&#xff0c;导致学习到的表征与训练集中已知的伪造类型高度相关&#xff0c;因此模型难以泛化到未知的伪造类型上使用。 二、研究动机 1.真实样本的特征分布相对更为紧凑&#xff0c;因此学习真实人脸之间的…

【GEE】时间序列多源遥感数据随机森林回归预测|反演|验证|散点图|完整代码

实验介绍 分类和回归之间的主要区别在于&#xff0c;在分类中&#xff0c;我们的预测目标是离散的类别&#xff0c;而在回归中&#xff0c;预测目标是连续的预测值。 本实验的研究区域位于佛蒙特州的埃塞克斯郡&#xff0c;使用训练数据来模拟土壤氧化还原深度&#xff0c;然…

Chart 7 内存优化

文章目录 前言7.1 Adreno GPU OpenCL内存7.1.1 内存声明周期7.1.2 Loacl Memory7.1.3 Constant memory(常量内存)7.1.4 Private Memory7.1.5 Global Memory7.1.5.1 Buffer Object7.1.5.2 Image Object7.1.5.3 Image object vs. buffer object7.1.5.4 Use of both Image and buf…

基于Python+Django+mysql图书管理系统

基于PythonDjangomysql图书管理系统 一、系统介绍二、功能展示三、其它系统四、获取源码 一、系统介绍 程序开发软件&#xff1a;Pycharm 数据库&#xff1a;mysql 采用技术&#xff1a; Django(一个MVT框架&#xff0c;类似Java的SSM框架) 人生苦短&#xff0c;我用Python&a…

【Vue】日常错误总结(持续更新)

日常遇到的小问题汇总, 内容小篇幅少的就全放这里了, 内容多的会在Vue专栏单独分享~ 目录 【Q】 el-form-item值为 null 或 undefined显示““ 【Q】dialog内组件数据刷新总是延迟慢一拍 问题背景描述 解决方案 代码简单模拟 JS 【Q】el-input 不能输入的解决办法 方法…

LeetCode008之字符串转换整数 (相关话题:状态机)

题目描述 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数&#xff09;。 函数 myAtoi(string s) 的算法如下&#xff1a; 读入字符串并丢弃无用的前导空格检查下一个字符&#xff08;假设还…

TailwindCSS 支持文本文字超长溢出截断、文字文本省略号

前言 文本文字超长截断并自动补充省略号&#xff0c;这是前端日常开发工作中常用的样式设置能力&#xff0c;文字超长截断主要分为单行超长截断和多行超长截断。本文通过介绍基本CSS样式、tailwindcss 类设置两种基础方式来实现文字超长截断。 TailwindCSS 设置 单行文字超长…

编写Yaml文件当Poc,利用Nuclei扫描器去扫描漏洞

编写Yaml文件当Poc,利用Nuclei扫描器去扫描漏洞 YAML是一种数据序列化语言&#xff0c;它的基本语法规则注意如下&#xff1a; -大小写敏感 -使用缩进表示层级关系 -缩进时不允许使用Tab键&#xff0c;只允许使用空格。 -缩进的空格数目不重要&#xff0c;只要相同层级的元…

springboot_ssm_java学位论文盲审系统

本系统主要实现用户登录验证&#xff0c;用户使用邮箱&#xff0c;密码和选择身份进行登录&#xff0c;用户查看个人中心&#xff0c;提交论文&#xff0c;发表留言和问题反馈。用户在线注册。学生模块功能实现&#xff1a;学生注册&#xff0c;查看信息&#xff0c;修改资料&a…

C++新经典模板与泛型编程:将trait类模板用作模板参数

将trait类模板用作模板参数 template<typename T> struct SumFixedTraits;template<> struct SumFixedTraits<char> {using sumT int;static sumT initValue() {return 0;} };template<> struct SumFixedTraits<int> {using sumT __int64;sta…

2023年 - 我的程序员之旅和成长故事

2023年 - 我的程序员之旅和成长故事 &#x1f525; 1.前言 大家好&#xff0c;我是Leo哥&#x1fae3;&#x1fae3;&#x1fae3;&#xff0c;今天咱们不聊技术&#xff0c;聊聊我自己&#xff0c;聊聊我从2023年年初到现在的一些经历和故事&#xff0c;我也很愿意我的故事分…

力扣:199. 二叉树的右视图(Python3)

题目&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣&#xff08;LeetCode&#xff09…

Error: Cannot find module ‘E:\Workspace_zwf\mall\build\webpack.dev.conf.js‘

执行&#xff1a;npm run dev E:\Workspace_zwf\zengwenfeng-master>npm run dev> mall-app-web1.0.0 dev E:\Workspace_zwf\zengwenfeng-master > webpack-dev-server --inline --progress --config build/webpack.dev.conf.jsinternal/modules/cjs/loader.js:983thr…

PyQt6 QTimeEdit时间控件

​锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计39条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话…

微表情检测(三)----基于光流特征的微表情检测

Micro-expression spotting based on optical flow features 基于光流特征的微表情检测 Abstract 本文提出了一种高精度和可解释性的自动微表情检测方法。首先&#xff0c;我们设计了基于鼻尖位置的图像对齐方法&#xff0c;以消除由头部晃动引起的全局位移。其次&#xff0…

扁平按钮样式

上图 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>扁平按钮</title><style>body {margin: 0;padding: 0;height: 100vh;display: flex;justify-content: center;ali…
最新文章