Linux 驱动开发基础知识——总线设备驱动模型(八)

 个人名片:

🦁作者简介:学生
🐯个人主页:妄北y

🐧个人QQ:2061314755

🐻个人邮箱:2061314755@qq.com
🦉个人WeChat:Vir2021GKBS
🐼本文由妄北y原创,首发CSDN🎊🎊🎊
🐨座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。

专栏导航:

妄北y系列专栏导航:

C/C++的基础算法:C/C++是一种常用的编程语言,可以用于实现各种算法,这里我们对一些基础算法进行了详细的介绍与分享。🎇🎇🎇

QT基础入门学习:对QT的基础图形化页面设计进行了一个简单的学习与认识,利用QT的基础知识进行了翻金币小游戏的制作🤹🤹🤹

Linux基础编程:初步认识什么是Linux,为什么学Linux,安装环境,进行基础命令的学习,入门级的shell编程。🍻🍻🍻

Linux应用开发基础开发:分享Linux的基本概念、命令行操作、文件系统、用户和权限管理等,网络编程相关知识,TCP/IP 协议、套接字(Socket)编程等,可以实现网络通信功能。💐💐💐

Linux项目开发:Linux基础知识的实践,做项目是最锻炼能力的一个学习方法,这里我们会学习到一些简单基础的项目开发与应用,而且都是毕业设计级别的哦。🤸🤸🤸


非常期待和您一起在这个小小的互联网世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨ 

文章介绍:

🎉本篇文章对Linux驱动基础学习的相关知识进行分享!🥳🥳🥳

Linux驱动程序 = 驱动框架 + 硬件操作 = 驱动框架 + 单片机,我们需要掌握别人的驱动框架,了解框架的思想,才能更好的去修改和运用别人的框架

如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加油,一起奔跑,让我们顶峰相见!!!💪💪💪

🎁感谢大家点赞👍收藏⭐评论✍️

目录: 

目录

一、LED 模板驱动程序的改造

1.1 原来的框架

1.2 要实现的框架 

二、代码分析:

2.1 board_A_led.c

2.2 chip_demo_gpio.c 

2.3 leddrv.c

三、注意事项

3.1  release 函数

3.2 EXPORT_SYMBOL

四、上机测试

4.1 编译

4.2 挂载到开发板 


一、LED 模板驱动程序的改造

1.1 原来的框架

1.2 要实现的框架 

二、代码分析:

2.1 board_A_led.c

        平台设备文件


#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>

#include "led_resource.h"


static void led_dev_release(struct device *dev)
{
}

static struct resource resources[] = {
        {
                .start = GROUP_PIN(3,1),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
        {
                .start = GROUP_PIN(5,8),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
};


static struct platform_device board_A_led_dev = {
        .name = "100ask_led",
        .num_resources = ARRAY_SIZE(resources),
        .resource = resources,
        .dev = {
                .release = led_dev_release,
         },
};

static int __init led_dev_init(void)
{
    int err;
    
    err = platform_device_register(&board_A_led_dev);   
    
    return 0;
}

static void __exit led_dev_exit(void)
{
    platform_device_unregister(&board_A_led_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");

第27~38行:

static struct resource resources[] = {

如果我们再想增加一盏灯的话,我们可以在这里的平台设备在这里的资源再增加一盏灯

    

  .start = GROUP_PIN(3,1),

指定第3组里面的第1个引脚

第41~48行:

static struct platform_device board_A_led_dev = {

注册一个board_A_led_dev

 将上面代码资源数组中的资源添加到platform_device

 .name = "100ask_led":平台设备的名字,与chip_demo_gpio_driver进行匹配

 .num_resources = ARRAY_SIZE(resources):资源的个数

 .resource = resources:指向这个数组

第50~62行:

board_A.c 作为一个可加载模块,里面也有入口函数、出口函数。

static int __init led_dev_init(void)
static void __exit led_dev_exit(void)

设置注册好board_A_led_dev的入口函数和出口函数

 第64~67行:

module_init(led_dev_init);
module_exit(led_dev_exit);

修饰入口函数和出口函数

MODULE_LICENSE("GPL");

确定GPL协议

第50~57行:

        在入口函数中注册 platform_device 结构体,在 platform_device 结构体中指定使用哪个 GPIO 引脚。

static int __init led_dev_init(void)
{
    int err;
    
    err = platform_device_register(&board_A_led_dev);   
    
    return 0;
}

第23~48行:board_A_led_dev 结构体定义如下:

static void led_dev_release(struct device *dev)
{
}

static struct resource resources[] = {
        {
                .start = GROUP_PIN(3,1),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
        {
                .start = GROUP_PIN(5,8),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
};


static struct platform_device board_A_led_dev = {
        .name = "100ask_led",
        .num_resources = ARRAY_SIZE(resources),
        .resource = resources,
        .dev = {
                .release = led_dev_release,
         },
};

        在 resouces 数组中指定了 2 个引脚(第 27~38 行); 我们还提供了一个空函数         led_dev_release(第 23~25 行),它被赋给 board_A_led_dev 结构体(第 46 行),这个函数在卸载 platform_device 时会 被调用,如果不提供的话内核会打印警告信息。

第27~38行:

static struct resource resources[] = {

定义资源数组

 

2.2 chip_demo_gpio.c 

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>

#include "led_opr.h"
#include "leddrv.h"
#include "led_resource.h"

static int g_ledpins[100];
static int g_ledcnt = 0;

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
{   
    //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    
    printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("init pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("init pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("init pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("init pin of group 3 ...\n");
            break;
        }
    }
    
    return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
    //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
    printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));

    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("set pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("set pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("set pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("set pin of group 3 ...\n");
            break;
        }
    }

    return 0;
}

static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init,
    .ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
    return &board_demo_led_opr;
}

static int chip_demo_gpio_probe(struct platform_device *pdev)
{
    struct resource *res;
    int i = 0;

    while (1)
    {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
        if (!res)
            break;
        
        g_ledpins[g_ledcnt] = res->start;
        led_class_create_device(g_ledcnt);
        g_ledcnt++;
    }
    return 0;
    
}

static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    struct resource *res;
    int i = 0;

    while (1)
    {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
        if (!res)
            break;
        
        led_class_destroy_device(i);
        i++;
        g_ledcnt--;
    }
    return 0;
}


static struct platform_driver chip_demo_gpio_driver = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "100ask_led",
    },
};

static int __init chip_demo_gpio_drv_init(void)
{
    int err;
    
    err = platform_driver_register(&chip_demo_gpio_driver); 
    register_led_operations(&board_demo_led_opr);
    
    return 0;
}

static void __exit lchip_demo_gpio_drv_exit(void)
{
    platform_driver_unregister(&chip_demo_gpio_driver);
}

module_init(chip_demo_gpio_drv_init);
module_exit(lchip_demo_gpio_drv_exit);

MODULE_LICENSE("GPL");

 第138~144行:

chip_demo_gpio.c 中注册 platform_driver 结构体 , 它使用Bus/Dev/Drv 模型,当有匹配的 platform_device 时,它的 probe 函数就会被调用。 

在 probe 函数中所做的事情跟之前的代码没有差别。

138 static struct platform_driver chip_demo_gpio_driver = {
139 .probe = chip_demo_gpio_probe,
140 .remove = chip_demo_gpio_remove,
141 .driver = {
142 .name = "100ask_led",
143 },
144 };
145
146 static int __init chip_demo_gpio_drv_init(void)
147 {
148 int err;
149
150 err = platform_driver_register(&chip_demo_gpio_driver);
151 register_led_operations(&board_demo_led_opr);
152
153 return 0;
154 }

注册一个chip_demo_gpio_driver

.driver  = {
        .name   = "100ask_led",
   },

设备名称,与board_A_led_dev中设备名称进行对应

.probe      = chip_demo_gpio_probe:记录引脚

.remove     = chip_demo_gpio_remove:销毁设备

         第 150 行:向内核注册一个 platform_driver 结构体,这个结构体的核心在于第 100 行的 chip_demo_gpio_probe 函数。 chip_demo_gpio_probe 函数代码如下:

100 static int chip_demo_gpio_probe(struct platform_device *pdev)
101 {
102 struct resource *res;
103 int i = 0;
104
105 while (1)
106 {
107 res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
108 if (!res)
109 break;
110
111 g_ledpins[g_ledcnt] = res->start;
112 led_class_create_device(g_ledcnt);
113 g_ledcnt++;
114 }
115 return 0;
116
117 }

        第 107 行:从匹配的 platform_device 中获取资源,确定 GPIO 引脚。

        第 111 行:把引脚记录下来,在操作硬件时要用。

        第 112 行:新发现了一个 GPIO 引脚,就调用上层驱动的代码创建设备节点

第146~159行:

static int __init chip_demo_gpio_drv_init(void)
static void __exit lchip_demo_gpio_drv_exit(void)

设置注册好chip_demo_gpio_driver的入口函数和出口函数

 第64~67行:

module_init(chip_demo_gpio_drv_init);
module_exit(lchip_demo_gpio_drv_exit);

修饰入口函数和出口函数

MODULE_LICENSE("GPL");

确定GPL协议

第100~117行:

static int chip_demo_gpio_probe(struct platform_device *pdev)

board_A_led_dev提供 .probe 

        第105~144行:从资源里确定引脚

                第107行:获得设备pdev中的第i个IORESOURCE_IRQ资源

                第111行:记录引脚

                第112行:创建device

第119~135行:

static int chip_demo_gpio_remove(struct platform_device *pdev)

board_A_led_dev提供 .remove

        第126行:销毁设备pdev中的第i个IORESOURCE_IRQ资源

操作硬件的代码如下,第 31、63 行的代码里用到了数组 g_ledpins,里面的值来自 platform_device,在 probe 函数中根据 platform_device 的资源确定了引脚:

static int g_ledpins[100];
static int g_ledcnt = 0;

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
{   
    //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    
    printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("init pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("init pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("init pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("init pin of group 3 ...\n");
            break;
        }
    }
    
    return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
    //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
    printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));

    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("set pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("set pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("set pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("set pin of group 3 ...\n");
            break;
        }
    }

    return 0;
}

static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init,
    .ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
    return &board_demo_led_opr;
}

第30和61行: 打印出想操作的引脚

第32和63行:判断引脚

第151行:

register_led_operations(&board_demo_led_opr);

 底层向上层调用

2.3 leddrv.c

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"


/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)


void led_class_create_device(int minor)
{
	device_create(led_class, NULL, MKDEV(major, minor), NULL, "100ask_led%d", minor); /* /dev/100ask_led0,1,... */
}
void led_class_destroy_device(int minor)
{
	device_destroy(led_class, MKDEV(major, minor));
}
void register_led_operations(struct led_operations *opr)
{
	p_led_opr = opr;
}

EXPORT_SYMBOL(led_class_create_device);
EXPORT_SYMBOL(led_class_destroy_device);
EXPORT_SYMBOL(register_led_operations);



/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{
	int err;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */


	led_class = class_create(THIS_MODULE, "100ask_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "led");
		return -1;
	}
	
	return 0;
}

/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	class_destroy(led_class);
	unregister_chrdev(major, "100ask_led");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");


 第30~33行:创建设备

       

void led_class_create_device(int minor)
EXPORT_SYMBOL(led_class_create_device);

产生依赖只有使用led_class_create_device才能产生led_class_create_device

第34~37行:销毁设备

void led_class_destroy_device(int minor)
EXPORT_SYMBOL(led_class_destroy_device);

产生依赖只有使用(led_class_destroy_device才能产生led_class_destroy_device

第38~41行:底层向上层注册函数

void register_led_operations(struct led_operations *opr)
EXPORT_SYMBOL(register_led_operations);

产生依赖只有底层调用才能产生register_led_operations

三、注意事项

3.1  release 函数

        如果 platform_device 中不提供 release 函数,如下图所示不提供红框部分的函数:

        则在调用 platform_device_unregister 时会出现警告,如下图所示:

        你可以提供一个 release 函数,如果实在无事可做,把这函数写为空。 

3.2 EXPORT_SYMBOL

        a.c 编译为 a.ko,里面定义了 func_a;如果它想让 b.ko 使用该函数,那么 a.c 里需要导出此函数(如果 a.c, b.c 都编进内核,则无需导出):

EXPORT_SYMBOL(led_device_create);

        并且,使用时要先加载 a.ko。如果先加载 b.ko,会有类似如下“Unknown symbol”的提示:

 

四、上机测试

4.1 编译

编译程序,把代码上传代服务器后执行 make 命令。

4.2 挂载到开发板 

在开发板上挂载 NFS 

[root@100ask:/mnt]# insmod board_A_led.ko
[root@100ask:/mnt]# insmod  leddrv.ko
[root@100ask:/mnt]# insmod chip_demo_gpio.ko
[root@100ask:/mnt]# ls /dev/100ask_led*
[root@100ask:/mnt]# ./ledtest /dev/100ask_led0 on
[root@100ask:/mnt]# ./ledtest /dev/100ask_led0 off

 

大佬觉得有用的话点个赞 👍🏻 呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥任务在无形中完成,价值在无形中升华,让我们一起加油吧!🌙🌙🌙

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

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

相关文章

SAP SD出库单部分开票后无法继续开票

SAP SD出库单部分开票后无法继续开票。 凭证 80051268 没有包含任何带有未清数量的项目 消息编号 VF171 诊断 凭证80051268不包含可以转换到开票类型中的项目。 系统响应 系统拒绝任何后续处理。 步骤 请检查选择的销售和分销凭证。 除了修改VBUP的相关字段&#xff0c;还有…

服务器未启动而端口进程仍在运行如何查看并杀死

首先登录服务器然后查看当前监听的端口&#xff1a; sudo netstat -tuln比如这里的8080&#xff0c;我们此时并未启动服务器&#xff0c;但是它却正在运行&#xff0c;这会导致服务器刚启动就秒挂。如果没有日志的话会让人有点疑惑&#xff0c;这种情况可能是之前运行了该进程…

外汇天眼:SIX推出了新的SIX参考利率加密货币和SIX实时加密货币指数

全球金融信息提供商SIX今天宣布推出新的SIX参考利率加密货币和SIX实时加密货币指数。新的SIX参考利率加密货币指数和SIX实时加密货币指数涵盖了主要的加密资产比特币&#xff08;BTC&#xff09;和以太坊&#xff08;ETH&#xff09;&#xff0c;为市场及其表现提供了全面的快照…

C/C++ - 函数模板

目录 函数模板基础 函数模板定义 函数模板实例 函数模板调用 函数模板本质 模板函数特化 模板参数限定 默认模板参数 多个模板参数 非类型模板参数 函数模板拓展 模板参数匹配规则 函数模板基础 函数模板定义 使用 template <typename T>​​​​​ 或 templ…

python如何实现异步并发

下面是一个示例代码&#xff0c;展示了如何设计一个异步线程池&#xff0c;并实现线程池满了就等待&#xff0c;空了就继续扔的功能&#xff1a; import concurrent.futures import time # 创建一个线程池 thread_pool concurrent.futures.ThreadPoolExecutor(max_workers8) …

【Java】实现图书管理系统

文章目录 1. 设计背景2. 需求分析3. 设计思路4. 实现4.1 book包4.1.1 Book类4.1.2 BookList类(书架) 4.2 user包4.2.1 User 类4.2.2 AdminUser类&#xff08;管理员用户&#xff09;4.2.3 NormalUser类&#xff08;普通用户&#xff09; 4.3 operation包4.3.1 IOPeration接口4.…

docker笔记整理

Docker 安装 添加yum源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 安装docker yum -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin 启动docker systemctl start docker 查看docker状态 s…

指针(五)

1. sizeof 和 strlen 的对比 1.1 sizeof sizeof 计算变量所占用内存空间大小的&#xff0c;单位是字节&#xff0c;如果操作数是类型的话&#xff0c;计算的是使用类型创建的变量所占用空间的大小。 sizeof 只关注占用内存空间的大小&#xff0c;不在乎内存中存放了什么数据…

【博士每天一篇论文-算法】Continual Learning Through Synaptic Intelligence,SI算法

阅读时间&#xff1a;2023-11-23 1 介绍 年份&#xff1a;2017 作者&#xff1a;Friedemann Zenke&#xff0c;巴塞尔大学弗里德里希米歇尔研究所(FMI) Ben Poole&#xff0c;谷歌 DeepMind 研究科学家 期刊&#xff1a; International conference on machine learning. PMLR…

天线阵列车载应用——前言

本书的总体结构 这本参考书向读者介绍了汽车工业中使用的尖端天线阵列技术。新型通信多阵元天线系统是一种非常有前途的车载设备。例如&#xff0c;智能车辆公路系统(IVHS)包括车对车通信、汽车到路边系统、防撞雷达天线阵列和用于自动巡航控制应用的智能天线阵列&#xff0c;为…

数据可视化工具之选,三选一?

在数据可视化的世界中&#xff0c;选择一款合适的工具对于提升工作效率和洞察力至关重要。本文将对三款主流数据可视化工具进行详细比较&#xff0c;包括山海鲸可视化、Echarts和D3.js&#xff0c;以帮助您做出明智的选择。 山海鲸可视化 山海鲸可视化是一款免费且功能强大的…

手机如何扫描身份证?分享两个方法!

在现代社会&#xff0c;身份证已经成为我们生活中不可或缺的重要证件之一。有时候&#xff0c;我们需要将身份证信息提交给相关部门或者机构&#xff0c;而手动输入身份证信息不仅繁琐&#xff0c;还容易出错。这时&#xff0c;使用手机扫描身份证就成了一个方便快捷的选择。本…

Web性能优化之如何评估网页性能——性能指标和度量工具介绍

前言 用户在访问 web 网页时&#xff0c;大部分都希望网页能够在一秒完成。事实上&#xff0c;加载时间每多 1 秒&#xff0c;就会流失 7%的用户。如果时间超过 8s 用户就会感到不耐烦、会放弃访问。这也就是著名的 “8秒原则”。 虽然当今设备及网络环境都大幅提升&#xff…

C语言assert断言详解指针(3)

各位少年&#xff0c;大家好&#xff0c;我是博主那一脸阳光&#xff0c;今天分享assert法官的断言&#xff0c;指针宝箱的使用。 前言&#xff1a;如果你在计算机的世界中触犯了语法法规&#xff0c;那么编译器就要上线了&#xff0c;就会出现报错。然而想想我们在现实中设计到…

【Linux】Linux基本指令

目录 1.ls指令 2.cd指令 3.touch指令 4.mkdir指令 5.rmdir指令和rm指令 5.1rmdir指令 5.2rm指令 6.man指令 7.cp指令 8.mv指令 9.cat指令 10.more指令 && less指令 10.1more指令 10.2less指令 11.head指令 && tail指令 11.1head指令 11.2tai…

MySQL优化必备知识-索引篇

索引用于快速查找具有特定列值的行。如果没有索引&#xff0c;MySQL必须从第一行开始&#xff0c;然后读取整个表以查找相关行。表越大&#xff0c;成本越高。如果表中有相关列的索引&#xff0c;MySQL可以快速确定在数据文件中间查找的位置&#xff0c;而不必查看所有数据。这…

vue常用指令(v-on传递参数和事件修饰符)

1、传 递 自 定 义 参 数 : 函 数 调 用 传 参 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title…

企业的多域名SSL证书

多域名SSL证书作为一种加密通信的方式&#xff0c;可以有效保护多个网站的用户数据在传输过程中的安全。不管个人或者企事业单位 都可以申请多域名SSL证书&#xff0c;提高网站的安全性&#xff0c;保护网站数据传输安全。今天就随SSL盾了解多域名SSL证书旗下的企业多域名SSL证…

详解SpringCloud微服务技术栈:深入ElasticSearch(4)——ES集群

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;详解SpringCloud微服务技术栈&#xff1a;深入ElasticSearch&#xff08;3&#xff09;——数据同步&#xff08;酒店管理项目&a…

盛水最多的容器(Python+Go)

给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。(不能倾斜容器) 输入&#xff1a;[1,8,6,2,5…