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

news2025/1/18 11:56:08

前言:

什么是 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.coloradmin.cn/o/1293786.html

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

相关文章

MATLAB - 绘制立体图(平面+水深)

目录 代码结果 代码 % 在 X-Y 平面上绘图 % 正常绘制平面图 [X,Y,Z] peaks; contour(X,Y,Z,20); hold on% ****重点******************************************** % 改为三维视图&#xff0c;具体可以help % view(3); %此时的平面图对应z0 &#xff1b;默认az-37.5&#x…

Sql Server关于表的建立、修改、删除

表的创建&#xff1a; &#xff08;1&#xff09;在“对象资源管理器”面板中展开“数据库”节点&#xff0c;可以看到自己创建的数据库&#xff0c;比如Product。展开Product节点&#xff0c;右击“表”节点&#xff0c;在弹出的快捷菜单中选择“新建表”项&#xff0c;进入“…

[FPGA 学习记录] 快速开发的法宝——IP核

快速开发的法宝——IP核 文章目录 1 IP 核是什么2 为什么要使用 IP 核3 IP 核的存在形式4 IP 核的缺点5 Quartus II 软件下 IP 核的调用6 Altera IP 核的分类 在本小节当中&#xff0c;我们来学习一下 IP 核的相关知识。 IP 核在 FPGA 开发当中应用十分广泛&#xff0c;它被称为…

FastAPI查询参数和字符串校验

在FastAPI中&#xff0c;你可以为参数声明额外的信息和校验。这对于查询参数来说尤其有用&#xff0c;因为它们通常用于过滤或排序结果。本教程将引导你如何使用Query对象来添加这些额外的校验。 导入所需库 首先&#xff0c;你需要导入FastAPI以及Query对象&#xff1a; fr…

【已解决】ImportError: cannot import name ‘Merge‘ from ‘keras.layers‘

问题描述 ImportError: cannot import name ‘Merge‘ from ‘keras.layers‘ 解决办法 1、tensorflow和keras版本要对应&#xff1b; 2、使用"merge" pip uninstall keras pip install keras2.3.1 from keras.layers import merge完结撒花 我这血中带泪的成长&…

从传统到胜利:广汽集团汽车产业创新之旅

置身于汽车行业百年未有之大变局&#xff0c;作为传统车企中的排头兵&#xff0c;广汽创新可圈可点&#xff0c;广汽近年来取得了骄人业绩&#xff0c;不论是整体产销规模&#xff0c;还是新能源汽车产业化、新技术领域开拓等&#xff0c;都呈现节节攀升的局面。本文奖从产业变…

2024年值得关注的8个未来数据库

2024年值得关注的8个未来数据库 关系型数据库管理系统在数据库技术领域占据主导地位已经多年了。当SQL在1970年代首次出现时&#xff0c;关系型数据库管理系统的使用和受欢迎程度迅速提升。很快&#xff0c;MySQL成为了大多数公司和团队首选的数据库。 然而&#xff0c;2023年…

时间序列预测实战(二十六)PyTorch实现Seq2Seq(LSTM)(结果可视化+自动保存结果)

一、本文介绍 本文给大家带来的时间序列模型是Seq2Seq&#xff0c;这个概念相信大家都不陌生了&#xff0c;网上的讲解已经满天飞了&#xff0c;但是本文给大家带来的是我在Seq2Seq思想上开发的一个模型和新的架构&#xff0c;其是专门为新手开发的&#xff0c;而且为了方便大…

上海亚商投顾:沪指探底回升微跌 AI概念股全线反弹

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 大指数早间震荡调整&#xff0c;午后探底回升集体翻红&#xff0c;临近尾盘又小幅走弱。AI概念股全线反弹&…

pytorch优化之SAM优化器

1. SAM介绍 人机验证 2. 案例 ❀精度优化❀优化策略1&#xff1a;网络SAM优化器_夏天&#xff5c;여름이다的博客-CSDN博客文章浏览阅读3.3k次&#xff0c;点赞10次&#xff0c;收藏30次。精度优化策略&#xff1a;SAM:Sharpness AwarenessMinimization锐度感知最小化论文&…

【CCF CSP】202309-2 坐标变换(其二)

80分超时暴力代码&#xff1a;读入数据&#xff0c;对于每个待操作坐标都重新对于所给区间进行平移&#xff0c;旋转的坐标变换 #include <iostream> #include <cmath>using namespace std;struct mani {int type;double num; }; mani all[100001];int main() {in…

在VSCode中运行Python脚本文件时如何传参

以下实验所处的操作系统环境说明&#xff1a; OS版本MacOSMonterey 12.1VSCodeOctober 2023 (version 1.84.2) 一、背景 在 VSCode 中写好 Python 脚本后&#xff0c;如果要运行起来&#xff0c;可以怎么做呢&#xff1f; 一般有以下几种方式&#xff1a; 1、直接在 VSCode…

简单使用selenium抓取微博热搜话题存储进Excel表格中

#test.pyimport requests from selenium import webdriver import time from write import write#首先打开浏览器 drive webdriver.Chrome()#设置隐式等待&#xff1a;等待元素找到&#xff0c;如果找到元素则马上继续执行语句&#xff0c;如果找不到元素&#xff0c;会在设定…

【性能测试】性能测试之性能测试指标详解(详细)

前言 刚开始&#xff0c;以为做性能测试&#xff0c;就是做些脚本、参数化、关联&#xff0c;压起来之后&#xff0c;再扔出一个结果。 但实际上不止这些内容&#xff0c;还要加上性能分析&#xff0c;关注调优之后响应时间有多大的提升&#xff0c;TPS 有多大的提高&#xf…

探索 HTML 语义化:让你的网页更有意义(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【链表Linked List】力扣-117 填充每个节点的下一个右侧节点指针II

目录 问题描述 解题过程 官方题解 问题描述 给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 N…

ELK(三)—安装可视化工具

目录复制 目录 一、ElasticSearch-Head可视化工具介绍1.1特性&#xff1a;1.2用法&#xff1a; 二、安装2.1docker安装2.2Chrome插件安装 一、ElasticSearch-Head可视化工具介绍 ElasticSearch-Head 是一个基于浏览器的 Elasticsearch 可视化工具&#xff0c;它提供了一个直观…

Linux中的网络时间服务器

本章主要介绍网络时间的服务器 使用chrony配置时间服务器配置chrony客户端服务器同步时间 1.1 时间同步的重要性 一些服务对时间要求非常严格&#xff0c;例如如图所示的由三台服务器搭建的ceph集群 这三台服务器的时间必须保持一致&#xff0c;如果不一致&#xff0c;就会显…

随机Numpy数组的创建方法(第2讲)

随机Numpy数组的创建方法 &#xff08;第2讲&#xff09;         &#x1f379;博主 侯小啾 感谢您的支持与信赖。☀️ &#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&…

python socket编程6 - 使用PyQt6 开发UI界面实现TCP server和TCP client单机通讯的例子

使用PyQt6 开发UI界面实现TCP server和TCP client单机通讯的示例。 一、PyQt6 实现的界面 二、TCP server代码的修改示意 界面提供网络参数的配置&#xff0c;以及提供人机交互过程中的数据获取和显示。 1、把上面的server代码封装成两个部分 A、class Server 负责接受UI界面…