Linux驱动开发——PCI设备驱动

目录

一、 PCI协议简介

二、PCI和PCI-e

三、Linux PCI驱动

四、 PCI设备驱动实例

五、 总线类设备驱动开发习题



一、 PCI协议简介


        PCI (Peripheral Component Interconnect,外设部件互联) 局部总线是由Intel 公司联合其他几家公司一起开发的一种总线标准,最初是为了替代 ISA 之类的总线,用于解决当时的图形化界面显示器的带宽问题。相比于 ISA 总线,它最大的特点是高带宽、突发传输和即插即用(热插拔)。在 PCI 3.0 的规范中,PCI 局部总线的时钟速率有 33MHZ、66MHz 和133MHz 三种标准速率,支持的数据位宽有 32 位和 64 位两种。所以最低的数据传输率为33MHz x 32bit = 132MB/s,即每秒 132M 字节,这完全满足当时的图形显卡的要求。突发传输是指其地址总线和数据总线复用,在传输开始时先发地址,然后再连续传输若干个字节的数据,这样做的好处是可以减少芯片的管脚,并且一个传输周期可以完成若干个字节的传输。即插即用和前面谈到的 USB 类似,总线上的设备存放有配置信息,在初始化的过程中,主机会主动获取这些信息,从而分配其所需要的资源,这会在后面做更详细的介绍。随着 PCI 局部总线的发展,其应用的领域也越来越广泛,现在 PC 中独立的网卡、声卡、数据采集卡等使用的都是 PCI 局部总线。后来又推出了串行的标准,PCI-Express,其传输速率相当高,在 PCI-Express 3.0 规范中,其传输率可以达到 8GT/s即每秒 8G 次传输。因为使用的广泛性,在某些嵌入式系统中也使用了 PCI或 PCI-Express局部总线。下面简单介绍一下 PCI3.0 规范中驱动开发者需要关心的内容,下图是 PCI系统的连接框图(引自 PCI3.0 规范)。


        处理器(Processor)通过 Host/PCI桥(Bridge)连接到了 0号 PCI局部总线(PCI LocalBus #0),在这条局部总线上,有声卡 (Audio)、动态视频(Motion Video)、图形显卡(Graphics)、网卡(LAN)和 SCSI控制器等。通过 PCI-to-PCI Bridge,又扩展出了1号PCI局部总线(PCILocal Bus #1),在这条总线上又接入了其他 PCI功能设备。另外,还有PCI-ISA 桥,可以将 PCI 总线转换为传统的ISA 总线。

        PCI局部总线也是主从结构,在 PCI的规范中主设备叫发起者(Initiator),从设备叫目标(Target),传输由主设备发起,从设备进行响应。一个 PCI 设备都要实现目标的功能,但也可以实现发起者的功能,也就是说,一个设备既可以在某一时刻做主设备,也可以在另一个时刻做从设备。并且一条总线上允许有多个主设备,由仲裁器来决定哪个主设备可以获得总线的控制权。下面我们仅讨论 PCI的从设备。
        PCI 定义了三个物理地址空间,包括内存地址空间、I/O 地址空间和配置地址空间。

        其中配置地址空间是必需的,这个地址空间用于对设备的硬件进行配置。为了更好地理解这三个地址空间的访问,我们先来看看 PCI 的典型写传输时序图,如下图所示(引自PCI3.0 规范)。


        当发起者要对目标进行写操作时,会先将 FRAME 拉低,在之后的第一个时钟周期AD 总线上是发送地址,C/BE 是总线的命令,用于确定一个更具体的写操作,DEVSEI是被选中的目标发出的确认信号。在之后的若干个周期,AD 总线上是要写入的数据,C/BE 上是字节使能,用于确定哪个字节是有效的。IRDY 和 TRDY 分别是发起者和目标的准备信号,当任一个无效时,都会自动插入等待周期。在最后一个数据周期,FRAME无效,但传输最终完成是在 FRAME 无效后 IRDY 也无效的时刻。PCI 的读传输操作和写基本类似,只是数据的方向相反。上面涉及的总线命令如下图所示(引自 PCI3.0 规范)。


        I/O 读、I/O 写、内存读、内存写、配置读和配置写即我们前面提到的三个物理地址空间的读写。我们首先来看配置空间是如何寻址的,地址结构如下图所示。


        PCI规范定义了两种类型的配置空间地址,Type 0 用于选择总线上的一个设备,Type1 用于将请求传递给另一条总线。地址中的各个字段的含义如下。
        Bus Number:8 位总线地址,在 256条PCI 局部总线中选择一条。
        Device Number:5 位设备地址,在一条总线上的 32 个物理设备中选择一个
        Function Number:3 位功能地址,在一个物理设备上的8个功能中选择一个功能,也就是说,PCI 设备和 USB 设备类似,一个物理设备可以有多个功能,从而实现多个逻辑设备。
        Register Number:用于选择配置空间中的一个 32 位寄存器。

        在 PCI规范中,对配置空间的各寄存器都有具体的定义,整个配置空间有 64 个字节我们并不需要关心配置空间中每个寄存器的含义,下面列出最主要的一些寄存器(其他寄存器的定义及地址请参见 PCI规范)。


        Vendor ID:16 位,硬件厂商ID。
        Device ID:16 位,设备 ID。
        Class Code: 24 位,外设所属的类别,如大容量存储设备控制器类、网络控制器类显示控制器类等。为 0表示不属于某一具体的类。
        Subsystem Vendor ID: 16 位,子系统厂商 ID。
        Subsystem ID:16 位,子系统ID。

        Base Address Registers: 32 位,在计算机启动的过程中,会检查所有的 PCI 设备,其中一个重要的操作就是要获取其使用的内存空间和 I/O 空间的大小,然后给每一个空间分配一个基址,这个基址就是存放在基址寄存器中的。配置空间中共有 6 个这样的基址寄存器,在 Linux 驱动中简称 bar。
        上面的 ID 和 Class 用于匹配驱动程序,基址则用于驱动进行资源获取和映射操作,后面会进行更详细的描述。有了基址寄存器后,对内存空间和 IO 空间的访问问题也就迎刃而解了,因为我们只需要发出相应的内存地址或I/O地址就可以访问对应的空间了。
 

 

二、PCI和PCI-e

        PCI和PCIe都是计算机中用于连接设备的接口标准,但它们之间存在一些重要区别。

        PCI(Peripheral Component Interconnect)是一种早期的计算机总线标准,它被设计用于连接各种高速外围设备,如显卡、声卡、网卡等。PCI总线是一种共享总线,这意味着所有设备共享相同的带宽。因此,当多个设备同时尝试使用总线时,可能会出现性能下降或冲突。

        相比之下,PCIe(Peripheral Component Interconnect Express)是一种更现代的计算机总线标准,也被广泛应用于连接各种高速设备。与PCI不同,PCIe是一种点对点互连协议,这意味着每个设备都有自己的专用连接,不会与其他设备共享带宽。这使得PCIe在性能上大大超越PCI,特别是在高带宽应用和多设备环境中。

        总的来说,PCIe在性能、灵活性和扩展性方面都优于PCI,这也是为什么现在的计算机和设备大多采用PCIe接口的原因。但需要注意的是,由于PCIe的复杂性和成本较高,在一些低带宽和低成本的应用中,PCI仍然可能被使用。

        PCI-e在软件层面是兼容PCI的,PCI是并行传输,PCI-e是串行点对点传输。

三、Linux PCI驱动


        下面我们还是只讨论 PCI 从设备。PCI 设备在内中用 struct pci_dev 结构来表示,该结构的成员非常多,在此就不一一列出了,可以参见内核源码中的 include/linux/pci.h 文件。在里面会发现我们前面提到的 ID、类等成员,还有设备所使用的IRQ 线。设备的 ID 还有一个 struct pci_device_id 结构,驱动中通常会定义这样一个数组,来表示可以支持的设备列表,和前面的 USB 设备列表类似。和 PCI 设备结构相关的主要函数和宏如下。

int pci_enable_device(struct pci_dev *dev);
void pci_disable_device(struct pci_dev *dev);
pci_resource_start(dev, bar)
pci_resource_end(dev, bar)
pci_resource_flags(dev, bar)
pci_resource_len(dev,bar)
int pci_request_regions (struct pci_dev *pdev, const char *res_name);
void pci_release_regions(struct pci_dev *pdev);


pci_enable_device: 使能 PCI设备,在操作 PCI设备之前必须先使能设备

pci_disable_device:禁止PCI设备。
pci_resource_start: 获取 dev 中第 bar 个基址寄存器中记录的资源起始地址.

pci_resource_end:获取 dev 中第 bar 个基址寄存器中记录的资源结束地址。
pci_resource_flags:取 dev 中第 bar 个基址寄存器中记录的资源标志,是内存资源还是 IO 资源。
pci_resource_len:获取 dev 中第 bar 个基址寄存器中记录的资源大小。
pci_request_regions: 申请 PCI 设备 pdev 内的内存资源和I/0 资源,取名为res_name.

pci_release_regions: 释放 PCI设备 pdev 内的内存资源和 IO 资源。

        内核中用 struct pci_driver 结构来表示 PCI 设备驱动,相关的主要函数宏如下


 

void pci_unregister_driver(struct pci_driver *dev);
pci_register_driver(driver)
void pci_set_drvdata(struct pci_dev *pdev, void *data);
void *pci_get_drvdata(struct pci_dev *pdev);


pci_register_driver: 注册 PCI 设备驱动 driver
pci_unregister_driver: 注销 PCI 设备驱动 dev。
pci_set_drvdata:保存 data 指针到PCI设备pdev 中。
pci_get_drvdata: 从 PCI 设备 pdev 中获取保存的指针

PCI设备的配置空间访问的主要函数如下。

int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val);
int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val);
int pei_read_config_dword(const struct pci_dev *dev, int where, u32 *val);
int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val);
int pci_write_config_word(const struct pci_dev *dev, int where, u16 val);
int pei_write_config_dword(const struct pci_dev *dev, int where, u32 val);


上面的函数分别实现了对配置空间的字节、字 (16 位) 和双字(32 位) 的读写操作。


四、 PCI设备驱动实例


这里使用的 PCI 设备是南京沁恒公司的 CH368EVT 评估板,该评估板使用了一片该公司设计的 CH368 PCI-Express 接口芯片,虽然是 PCI-Express 协议,但是在驱动上两者可以兼容,只是 PCI-Express 速率更高,能够支持更多的功能。选用该评估板的原因是其价格低廉,完全国产,也能够全面验证三个空间的读写操作。
        使用L1~L4 这 4个 LED 显示 I/O 数据端口 D3~DO 位的状态。灯亮代表 1,灯灭代表0。
CH368 的配置空间定义如下图所示。


         厂商 ID 和设备 ID 是我们比较关心的内容,驱动的设备列表中的 ID 要和这里的致。第一个基址寄存器是 I/O 地址空间的基址,有 232 个字节,定义如下图所示。另外,CH368 的内存空间有 32KB。

该设备的 Linux 驱动代码如下,为了尽量突出 PCI驱动的核心,并没有加入并发控制相关的代码。

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/uaccess.h>#include "ch368.h"#define CH368_MAJOR	256
#define CH368_MINOR	11
#define CH368_DEV_NAME	"ch368"struct ch368_dev {void __iomem *io_addr;void __iomem *mem_addr;unsigned long io_len;unsigned long mem_len;struct pci_dev *pdev;struct cdev cdev;dev_t dev;
};static unsigned int minor = CH368_MINOR;static int ch368_open(struct inode *inode, struct file *filp)
{struct ch368_dev *ch368;ch368 = container_of(inode->i_cdev, struct ch368_dev, cdev);filp->private_data = ch368;return 0;
}static int ch368_release(struct inode *inode, struct file *filp)
{return 0;
}static ssize_t ch368_read(struct file *filp, char __user *buf, size_t count, loff_t *f_ops)
{int ret;struct ch368_dev *ch368 = filp->private_data;count = count > ch368->mem_len ? ch368->mem_len : count;ret = copy_to_user(buf, ch368->mem_addr, count);return count - ret;
}static ssize_t ch368_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops)
{int ret;struct ch368_dev *ch368 = filp->private_data;count = count > ch368->mem_len ? ch368->mem_len : count;ret = copy_from_user(ch368->mem_addr, buf, count);return count - ret;
}static long ch368_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{union addr_data ad;struct ch368_dev *ch368 = filp->private_data;if (_IOC_TYPE(cmd) != CH368_MAGIC)return -ENOTTY;if (copy_from_user(&ad, (union addr_data __user *)arg, sizeof(union addr_data)))return -EFAULT;switch (cmd) {case CH368_RD_CFG:if (ad.addr > 0x3F)return -ENOTTY;pci_read_config_byte(ch368->pdev, ad.addr, &ad.data);if (copy_to_user((union addr_data __user *)arg, &ad, sizeof(union addr_data)))return -EFAULT;break;case CH368_WR_CFG:if (ad.addr > 0x3F)return -ENOTTY;pci_write_config_byte(ch368->pdev, ad.addr, ad.data);break;case CH368_RD_IO:ad.data = ioread8(ch368->io_addr + ad.addr);if (copy_to_user((union addr_data __user *)arg, &ad, sizeof(union addr_data)))return -EFAULT;break;case CH368_WR_IO:iowrite8(ad.data, ch368->io_addr + ad.addr);break;default:return -ENOTTY;}return 0;
}static struct file_operations ch368_ops = {.owner = THIS_MODULE,.open = ch368_open,.release = ch368_release,.read = ch368_read,.write = ch368_write,.unlocked_ioctl = ch368_ioctl,
};static int ch368_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{int ret;unsigned long io_start;unsigned long io_end;unsigned long io_flags;unsigned long io_len;void __iomem *io_addr = NULL;unsigned long mem_start;unsigned long mem_end;unsigned long mem_flags;unsigned long mem_len;void __iomem *mem_addr = NULL;struct ch368_dev *ch368;ret = pci_enable_device(pdev);if(ret)goto enable_err;io_start  = pci_resource_start(pdev, 0);io_end    = pci_resource_end(pdev, 0);io_flags  = pci_resource_flags(pdev, 0);io_len    = pci_resource_len(pdev, 0);mem_start = pci_resource_start(pdev, 1);mem_end   = pci_resource_end(pdev, 1);mem_flags = pci_resource_flags(pdev, 1);mem_len   = pci_resource_len(pdev, 1);if (!(io_flags & IORESOURCE_IO) || !(mem_flags & IORESOURCE_MEM)) {ret = -ENODEV;goto res_err;}ret = pci_request_regions(pdev, "ch368");if (ret)goto res_err;io_addr = ioport_map(io_start, io_len);if (io_addr == NULL) {ret = -EIO;goto ioport_map_err;}mem_addr = ioremap(mem_start, mem_len);if (mem_addr == NULL) {ret = -EIO;goto ioremap_err;}ch368 = kzalloc(sizeof(struct ch368_dev), GFP_KERNEL);if (!ch368) {ret = -ENOMEM;goto mem_err;}pci_set_drvdata(pdev, ch368);ch368->io_addr = io_addr;ch368->mem_addr = mem_addr;ch368->io_len = io_len;ch368->mem_len = mem_len;ch368->pdev = pdev;ch368->dev = MKDEV(CH368_MAJOR, minor++);ret = register_chrdev_region (ch368->dev, 1, CH368_DEV_NAME);if (ret < 0)goto region_err;cdev_init(&ch368->cdev, &ch368_ops);ch368->cdev.owner = THIS_MODULE;ret = cdev_add(&ch368->cdev, ch368->dev, 1); if (ret)goto add_err;return 0;add_err:unregister_chrdev_region(ch368->dev, 1);
region_err:kfree(ch368);
mem_err:iounmap(mem_addr);
ioremap_err:ioport_unmap(io_addr);
ioport_map_err:pci_release_regions(pdev);
res_err:pci_disable_device(pdev);
enable_err:return ret;
}static void ch368_remove(struct pci_dev *pdev)
{struct ch368_dev *ch368 = pci_get_drvdata(pdev);cdev_del(&ch368->cdev);unregister_chrdev_region(ch368->dev, 1);iounmap(ch368->mem_addr);ioport_unmap(ch368->io_addr);kfree(ch368);pci_release_regions(pdev);pci_disable_device(pdev);
}static struct pci_device_id ch368_id_table[] =	
{{0x1C00, 0x5834, 0x1C00, 0x5834, 0, 0, 0},{0,}
};
MODULE_DEVICE_TABLE(pci, ch368_id_table);static struct pci_driver ch368_driver = {.name = "ch368",.id_table = ch368_id_table,.probe = ch368_probe,.remove = ch368_remove,
};module_pci_driver(ch368_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("name <e-mail>");
MODULE_DESCRIPTION("CH368 driver");
#ifndef _CH368_H
#define _CH368_Hunion addr_data {unsigned char addr;unsigned char data;
};#define CH368_MAGIC   'c'#define CH368_RD_CFG	_IOWR(CH368_MAGIC, 0, union addr_data)
#define CH368_WR_CFG	_IOWR(CH368_MAGIC, 1, union addr_data)
#define CH368_RD_IO	_IOWR(CH368_MAGIC, 2, union addr_data)
#define CH368_WR_IO	_IOWR(CH368_MAGIC, 3, union addr_data)#endif


        代码第 19 行至第 27 行是设备结构的定义,包含了保存映射之后的 IO 地址和内存地址的 io_addr 和 mem_addr 指针成员、保存 IO 地址空间大小和内存地址空间大小的io_len 和 mem_len 成员、保存 PCI 设备结构的 pdev 指针成员。该 PCI设备实现为一个字符设备,所以有 cdev 和 dev 成员。
        代码第 224 行至第 242 行是 PCI驱动结构的定义、注册和注销。ch368_id_table 是该驱动支持的设备列表,其中的 ID 号要和上图中的 ID 号一致。
        当有匹配的 PCI设备被检测到后,ch368_probe 函数自动被调用。代码第 134 行首先使能了 PCI 设备,代码第 138 行至第 146 行分别获取了 IO 和内存的物理地址、标志和长度信息。代码第 148 行至第 151 行判断了获取的标志内的资源类型信息,如果不和预期的相同,则设备探测失败。代码第 153 行至第 167 行申请了 PCI 设备所声明的资源,然后进行了映射,获得了对应的虚拟地址。代码第 169 行至第 180 行分配了 struct ch368_dev 结构的内存空间,并对各成员进行了相应的初始化,还使用 pci_set_drvdata函
 

        总线类设备驱动微将该结构地址保存在了 PCI 设备结构之中,方便之后从 PCI 设备结构中获得该地址该函数之后的代码是字符设备相关的注册操作。ch368 remove 做的工作和 h368 probe第亿函数相反。


        ch368_open 和 ch368_release 没有做太多的工作,ch368_read 和 ch368_write 则是针对内存空间的读和写,因为在这片内存空间没有对应外接的设备,所以没有实际意义。比较实际的操作是在 ch368_ioctl 中,CH368_RD_CFG 命今用来读取配置空间的数据,CH368_WR_CFG 命令用于向配置空间写入数据。CH368_RD_IO和CH368_WR_IO则分别是对 I/0 空间进行读和写。union addr_data 用于传送地址和返回数据,这和ADC 驱动的例子是类似的。
        应用层的测试代码如下
 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>#include "ch368.h"int main(int argc, char *argv[])
{int i;int fd;int ret;union addr_data ad;unsigned char id[4];fd = open("/dev/ch368", O_RDWR);if (fd == -1)goto fail;for (i = 0; i < sizeof(id); i++) {ad.addr = i;if (ioctl(fd, CH368_RD_CFG, &ad))goto fail;id[i] = ad.data;}printf("VID: 0x%02x%02x, DID: 0x%02x%02x\n", id[1], id[0], id[3], id[2]);i = 0;ad.addr = 0;while (1) {ad.data = i++;if (ioctl(fd, CH368_WR_IO, &ad))goto fail;i %= 15;sleep(1);}
fail:perror("pci test");exit(EXIT_FAILURE);
}


        上面的代码在打开设备后先读取了配置空间的前 4 个字节,根据 PCI 规范,这4字节刚好是厂商ID 和设备ID。接下来在 while 循环中对 I/0 空间的第一个字节依次写入了0~15,这样 PCI 设备上的 4个 LED 灯就会按照此规律被点亮。前面说过,4个LED反映了写入 I/ O空间的数据的低 4 位的状态,数据位为 1 对应的灯被点亮,数据位为0对应的灯熄灭。
        使用下面的命令进行编译和测试。需要说明的是,需要有一台安装了 Linux 系统的物理机,并且物理机上要有对应的 PCIE 插槽才能插入该设备并进行测试。


五、 总线类设备驱动开发习题


1.I2C 总线协议规定是由 ( ) 来进行应答的。
[A] 数据发送者
[B] 数据接收者


2.I2C 总线协议规定所有访问都是由 ( )来发起的
[B] 从设备
[A] 主设备


3.SPI是 ( )总线。
[B] 异步
[A] 同步


4.SPI总线是 ( )的

[B] 半双工
[A] 单工

[C] 全双功


5.SPI 总线是 ( )的。

[A] 单主
[B] 多主


6.USB 的传输类型分为 (
[B] 等时传输
[A] 控制传输
[D] 块传输
[C] 中断传输
 


7.USB 的接口是由多个 ( ) 组成的。
[A] 配置
[B] 管道

[C] 端点


8.PCI的配置空间包括 ( ) 信息。
[B] 设备ID
[A] 厂商ID
[D] 地址空间大小
[C] 基地址

 

答案:B         A         A         A         C        ABCD        C        ABCD

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

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

相关文章

【数据结构】树与二叉树(十二):二叉树的递归创建(算法CBT)

文章目录 5.2.1 二叉树二叉树性质引理5.1&#xff1a;二叉树中层数为i的结点至多有 2 i 2^i 2i个&#xff0c;其中 i ≥ 0 i \geq 0 i≥0。引理5.2&#xff1a;高度为k的二叉树中至多有 2 k 1 − 1 2^{k1}-1 2k1−1个结点&#xff0c;其中 k ≥ 0 k \geq 0 k≥0。引理5.3&…

mysql基础 --子查询

文章目录 子查询子查询案例 子查询 一个查询语句&#xff0c;嵌套在另一个查询语句内部&#xff1b;子查询先执行&#xff0c;其结果被外层主查询使用&#xff1b;子查询放入括号内&#xff1b;子查询放在比较条件的右侧&#xff1b;子查询返回一条&#xff0c;为单行子查询(对…

python工具HIKVISION视频编码设备接入网关任意文件下载

python工具 构造payload /serverLog/downFile.php?fileName../web/html/serverLog/downFile.php漏洞证明 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免责声明&#xff1a;由于传播或利用此文所提供的信息、技术或方法而造成的任何…

PyTorch技术和深度学习——三、深度学习快速入门

文章目录 1.线性回归1&#xff09;介绍2&#xff09;加载自由泳冠军数据集3&#xff09;从0开始实现线性回归模型4&#xff09;使用自动求导训练线性回归模型5&#xff09;使用优化器训练线性回归模型 2.使用torch.nn模块构建线性回归模型1&#xff09;使用torch.nn.Linear训练…

通过SD卡给某摄像头植入可控程序

0x01. 摄像头卡刷初体验 最近研究了手上一台摄像头的sd卡刷机功能&#xff0c;该摄像头只支持fat32格式的sd卡&#xff0c;所以需要先把sd卡格式化为fat32&#xff0c;另外微软把fat32限制了最大容量32G&#xff0c;所以也只能用不大于32G的sd卡来刷机。 这里使用32G的sd卡来…

基于樽海鞘群算法优化概率神经网络PNN的分类预测 - 附代码

基于樽海鞘群算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于樽海鞘群算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于樽海鞘群优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

vColorPicker与vue3-colorPicker——基于 Vue 的颜色选择器插件

文章目录 前言样例特点 一、使用步骤&#xff1f;1. 安装2.引入3.在项目中使用 vcolorpicker 二、选项三、事件四、问题反馈问题所在安装引入例子效果图 前言 vColorPicker——官网 vColorPicker——GitHub 样例 vColorPicker是基于 Vue 的一款颜色选择器插件&#xff0c;仿照…

【入门Flink】- 10基于时间的双流联合(join)

统计固定时间内两条流数据的匹配情况&#xff0c;需要自定义来实现——可以用窗口&#xff08;window&#xff09;来表示。为了更方便地实现基于时间的合流操作&#xff0c;Flink 的 DataStrema API 提供了内置的 join 算子。 窗口联结&#xff08;Window Join&#xff09; 一…

面向对象基础(以python语言为例)

1、定义一个类&#xff1b;实例化类的对象&#xff1b;调用类中的方法 #定义一个类 class Student:#类方法&#xff08;即函数&#xff09;def study(self,course_name):print(f学生正在学习{course_name})def play(self):print("xx学生正在玩游戏")#实例化&#xf…

从0到1实现一个前端监控系统(附源码)

目录 一、从0开始 二、上报数据方法 三、上报时机 四、性能数据收集上报 收集上报FP 收集上报FCP 收集上报LCP 收集上报DOMContentLoaded 收集上报onload数据 收集上报资源加载时间 收集上报接口请求时间 五、错误数据收集上报 收集上报资源加载错误 收集上报js错…

Linux下C++调用python脚本实现LDAP协议通过TNLM认证连接到AD服务器

1.前言 首先要实现这个功能&#xff0c;必须先搞懂如何通过C调用python脚本文件最为关键&#xff0c;因为两者的环境不同。本质上是在 c 中启动了一个 python 解释器&#xff0c;由解释器对 python 相关的代码进行执行&#xff0c;执行完毕后释放资源。 2 模块功能 2.1python…

Windows server 2008 R2 IIS搭建ASP网站教程

一、安装应用程序服务器 提示安装成功 二、添加角色服务asp 三、asp网站配置 放入源码 设置网站首页为index.asp: 设置应用程序池 四、设置网站目录属性 五、access数据库连接配置 Cd c:\Windows\System32\inetsrv appcmd list apppool /xml | appcmd set apppool /…

K9203 996920302 面向DNP3的网络安全解决方案

K9203 996920302 面向DNP3的网络安全解决方案 2014年ISA卓越技术创新奖获得者&#xff0c;超电子&#xff0c;3eTI的CyberFence工业防火墙解决方案提供强大加密和应用程序级深度数据包检测(DPI)功能。最近&#xff0c;3eTI为其CyberFence产品线增加了DNP3(分布式网络协议)支持…

SpringBoot Web开发

SpringBoot3-Web开发 SpringBoot的Web开发能力&#xff0c;由SpringMVC提供。 Web开发的三种方式 方式处理过程注意事项实现效果全自动直接编写控制逻辑全部使用自动给配置默认效果手自一体Configuration、 配置WebMvcConfigurer、 配置WebMvcRegistrations不要标注 EnableWeb…

【Linux】:静动态库

静动态库 一.静态库1.设计静态库2.生成静态库3.发布静态库4.使用静态库 二.动态库1.设计动态库2.生成和发布动态库3.使用 一.静态库 程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。 静态库链接格式&#xff1a;libxxx.a(前缀是lib,后缀是…

基于安卓android微信小程序的四六级助手系统

项目介绍 随着我国教育需求不断增加&#xff0c;高校教育资源有限&#xff0c;教育经费相对不足的情况下&#xff0c;利用现代信息技术发展高等教育&#xff0c;不仅充分利用了优秀的教育资源&#xff0c;而且为更多的人提供接受高等教育的机会&#xff0c;同时这也是极大促进…

【Unity每日一记】“调皮的协程”,协程和多线程的区别在哪里

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

网络原理-UDP/TCP详解

一. UDP协议 UDP协议端格式 由上图可以看出&#xff0c;一个UDP报文最大长度就是65535. • 16位长度&#xff0c;表示整个数据报&#xff08;UDP首部UDP数据&#xff09;的最大长度&#xff08;注意&#xff0c;这里的16位UDP长度只是一个标识这个数据报长度的字段&#xff0…

机器视觉目标检测 - opencv 深度学习 计算机竞赛

文章目录 0 前言2 目标检测概念3 目标分类、定位、检测示例4 传统目标检测5 两类目标检测算法5.1 相关研究5.1.1 选择性搜索5.1.2 OverFeat 5.2 基于区域提名的方法5.2.1 R-CNN5.2.2 SPP-net5.2.3 Fast R-CNN 5.3 端到端的方法YOLOSSD 6 人体检测结果7 最后 0 前言 &#x1f5…

单链表按位序与指定结点 删除

按位序删除(带头结点) #define NULL 0 #include<stdlib.h>typedef struct LNode {int data;struct LNode* next; }LNode, * LinkList;//按位序删除&#xff08;带头结点&#xff09; bool ListInsert(LinkList& L, int i, int& e) {if (i < 1)return false;L…