iomuxc、pinctrl子系统、gpio子系统(学习总结)

news/2024/9/19 4:44:22/ 标签: linux, 驱动开发

iomuxc、pinctrl子系统、gpio子系统三者的关系

  1. 相互依赖:IOMUXC、pinctrl子系统和gpio子系统在功能上相互依赖。IOMUXC提供了引脚复用和电气属性的配置能力,pinctrl子系统负责从设备树中获取这些配置信息并完成初始化,而gpio子系统则在引脚被配置为GPIO功能时提供具体的操作接口。

  2. 分层结构:从系统架构的角度来看,这三者构成了一个分层的结构。IOMUXC位于最底层,提供硬件级别的引脚复用和电气属性配置;pinctrl子系统位于中间层,负责软件层面的引脚配置和初始化;gpio子系统则位于最上层,为开发者提供易于使用的GPIO操作接口。

  3. 协同工作:在实际应用中,这三者协同工作以实现引脚的有效管理和控制。例如,在配置一个LED灯的GPIO引脚时,首先需要通过IOMUXC将该引脚复用为GPIO功能,然后通过pinctrl子系统从设备树中获取该引脚的配置信息并完成初始化,最后通过gpio子系统提供的API接口来设置引脚的方向和输出值以控制LED灯的亮灭。

IOMUXC

一、IOMUXC的基本概念

  • 概念引入:为了推出功能丰富的核心板,SOC厂商引入了引脚复用-IOMUXC架构。这种架构通过输入输出多路复用器(Input/Output Multiplexer),使得每个引脚最多可复用好几种功能,每个功能又可以出现在不同的引脚上。
  • 核心功能:IOMUXC的核心功能是IO口复用,即利用有限的IO口资源,通过动态切换,满足多个内部外设的IO需求。

二、IOMUXC的架构与组成

  • 基本单元:IOMUX由一些基本的iomux_cell单元组成,每个基本IOMUX单元只处理一个pad复用信号。
  • 子块组成:IOMUXC通常由两个子块组成:IOMUXC_REGISTERS(包含所有IOMUXC寄存器)和IOMUXC_LOGIC(包含所有IOMUXC组合逻辑,如IP接口控制、地址解码器、可观察复用控制)。
  • 寄存器分类:在IOMUXC控制器中,寄存器主要分为三大类:IOMUXC_SW_PAD_CTRL_X(管脚控制寄存器)、IOMUXC_SW_MUX_CTL_PAD_X(输出路由寄存器)、IOMUXC_X_SELECT_INPUT(输入路由寄存器)。

三、IOMUXC的配置与使用

  • 配置方式:IOMUXC的配置通常涉及对控制器中的寄存器进行参数设置。这些参数包括控制寄存器的偏移地址、MUX控制寄存器的偏移地址、MUX模式、输入路由寄存器的偏移地址以及Daisy Chain模式等。
  • Linux内核配置:在Linux内核中,SOC厂商会把各功能引脚的配置参数值写到相应的宏定义头文件中(如xxx-pinfunc.h),用户可以根据芯片使用手册中的说明,在设备树(dtb)中添加或修改引脚定义。
  • 功能切换:IOMUXC只起了一个功能切换的作用,引脚配置完成后,还需要配置具体功能的控制器里寄存器参数,这样才能共同完成引脚的功能确定。

四、IOMUXC的应用场景

  • GPIO配置:在GPIO配置中,IOMUXC用于选择引脚的功能模式,如将某个引脚配置为GPIO输入或输出模式。
  • 外设接口配置:对于其他外设接口(如UART、SPI、I2C等),IOMUXC同样用于选择引脚的功能模式,以满足外设的IO需求。

五、总结

        IOMUXC是SOC设计中实现引脚复用的关键组件,它通过动态切换引脚功能,提高了IO资源的利用率,满足了不同外设和功能的IO需求。在配置和使用IOMUXC时,需要参考芯片使用手册和设备树文档,确保引脚功能的正确设置。

Pinctrl       

一、Pinctrl子系统的作用

        Pinctrl子系统的主要作用是管理和配置处理器上的引脚资源。在嵌入式系统中,引脚是处理器与外部世界进行通信和交互的接口。不同的处理器和硬件平台可能具有不同的引脚数量和功能,因此需要一种统一的机制来管理和配置这些引脚资源,以确保系统的正常运行。

二、Pinctrl子系统的组成

Pinctrl子系统主要由以下几个部分组成:

  1. Pinctrl Core:Pinctrl Core是Pinctrl子系统的核心部分,负责管理和维护系统上的引脚和Pinctrl配置,提供通用的Pinctrl接口供设备驱动程序使用,并管理设备驱动程序之间的引脚冲突和资源共享。

  2. Pinctrl驱动程序:Pinctrl驱动程序是一个用于管理芯片引脚的Linux内核驱动程序。它通过实现Pinctrl Core提供的接口来向Pinctrl Core注册自己,并提供芯片引脚的配置和控制功能。

  3. Pinctrl映射器:Pinctrl映射器是一个将Pinctrl配置映射到具体芯片引脚的模块。它负责解析Pinctrl配置,映射到具体的引脚,并将映射结果传递给Pinctrl驱动程序。Pinctrl映射器可以根据不同的芯片架构和芯片引脚布局进行定制。

三、Pinctrl子系统的工作原理

Pinctrl子系统的工作原理可以分为以下几个阶段:

  1. 解析阶段:在系统启动时,Pinctrl子系统会解析设备树(Device Tree)中的引脚配置信息。

  2. 配置阶段:解析完成后,Pinctrl子系统会根据解析得到的信息配置引脚资源。它会根据配置模式设置引脚的功能,并根据电气特性配置引脚的输入/输出电路。如果需要,Pinctrl子系统还会配置引脚的中断信息,以便处理外部事件。

  3. 应用阶段:配置完成后,Pinctrl子系统会将配置应用于处理器上的引脚。这意味着处理器上的引脚将按照配置信息进行工作。此时,其他驱动程序或应用程序可以通过Pinctrl子系统提供的接口来访问和操作这些引脚。

四、Pinctrl子系统的功能

Pinctrl子系统提供了丰富的功能,以满足不同的应用场景需求:

  1. 引脚复用:允许多个引脚通过复用的方式连接到芯片上的不同功能模块,从而提高了引脚的利用率。

  2. 引脚配置:支持对引脚进行详细的配置,包括设置引脚的电气特性(如上拉、下拉、开漏等)、驱动能力、速度等。

  3. 引脚组配置:支持将多个引脚组合成一个逻辑上的引脚组,以便进行批量配置和操作,提高了配置效率。

  4. 中断管理:支持引脚中断的配置和管理,使得开发者可以方便地处理外部事件。

五、imx6ull举例

以参数值为例进行分析:

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19       0x17059 /* SD1 CD */

参数分为前后的两部分 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059

首先是 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19  此参数是一个宏定义如下对应了5个数据

#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19            0x0090 0x031C 0x0000 0x5 0x0
参数值的宏定义对应了5个数据值对应关系如下表 :

说明数据值
mux_reg复用寄存器的地址或偏移量0x0090
conf_reg配置寄存器的地址或偏移量0x031C
input_reg输入寄存器的地址或偏移量0x0000
mux_mode复用模式0x5
input_val输入值0x0

iomuxc的 reg 属 性 可 知 IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000 。

1.mux_reg :0x0090

UART1_RTS_Bpinmux_reg寄存器地址就是 :0x020e0000+0x0090= 0x020e0090。

2.conf_reg :0x031C

UART1_RTS_Bpinconf_reg寄存器地址就是 :0x020e0000+0x031c= 0x020e031c

3.input_reg :0x0000

input_reg 寄存器偏移地址,有些外设有 input_reg 寄存器,有 input_reg 寄存器的外设需要配置 input_reg 寄存器。

UART1_RTS_Bpininput_reg寄存器没有。

4.mux_mode:0x5

表示UART1_RTS_B复用为gpio1_io19 

5.input_val :0x0

input_val:就是写入 input_reg, 寄存器的值为0

        然后数据值 0x17059 就是 conf_reg 寄存器值。此值由用户自行设置,通 过此值来设置一个 IO 的上 / 下拉、驱动能力和速度等。在这里就相当于设置寄存器IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 的值为 0x17059 。

向设备树中的节点添加pinCtrl节点例子如下

pinctrl_test: testgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO00__GPIO1_IO00   config //onfig 是具体设置值..........>;
};
        设备树是通过属性来保存信息的,因此需要添加一个属性,属性名字一定要为“fsl,pins ”,
因为对于 I.MX 系列 SOC 而言, pinctrl 驱动程序是通过读取“ fsl,pins ”属性值来获取 PIN 的配
置信息.

GPIO

        GPIO子系统是Linux内核中用于统一和便捷地访问GPIO(通用输入输出)引脚的一个子系统。它提供了一组API函数,使得驱动程序和用户空间程序能够方便地设置GPIO的方向、读取或写入GPIO的值,以及处理GPIO中断等。以下是对GPIO子系统的详细介绍,包括相关的API函数和OF(设备树)函数。

一、GPIO子系统的作用

        GPIO子系统的主要作用是方便驱动开发者使用GPIO引脚。通过GPIO子系统,开发者可以在设备树中添加GPIO相关的信息,然后在驱动程序中使用GPIO子系统提供的API函数来操作GPIO引脚,如设置GPIO的方向、读取或写入GPIO的值等。这样,开发者就无需直接操作硬件寄存器,从而简化了驱动程序的开发过程。

二、GPIO子系统的架构

Linux的GPIO子系统驱动框架主要由以下几个部分组成:

  1. GPIO控制器驱动程序:这是与硬件相关的代码,用于处理GPIO控制器与系统总线之间的通信。该部分代码通常由芯片厂商提供,以与特定的GPIO硬件交互。

  2. 平台驱动程序:平台驱动程序用于与硬件平台交互,识别GPIO硬件并将其与相关的GPIO控制器驱动程序关联起来。平台驱动程序的主要任务是向系统注册GPIO控制器设备,与GPIO控制器驱动程序进行绑定,并为其分配资源。

  3. GPIO字符设备驱动程序:该驱动程序提供了用户空间API,使应用程序能够使用GPIO。用户可以通过/sys/class/gpio目录下的文件系统接口,通过读取和写入GPIO寄存器来控制GPIO。

三、GPIO子系统的API函数

GPIO子系统提供了一系列API函数,用于操作GPIO引脚。以下是一些常用的API函数及其功能:

  1. gpio_request(unsigned gpio, const char *label):用于申请一个GPIO引脚。在使用一个GPIO引脚之前,必须先通过此函数进行申请。

  2. gpio_free(unsigned gpio):用于释放之前申请的GPIO引脚。当不再需要使用某个GPIO引脚时,应调用此函数进行释放。

  3. gpio_direction_input(unsigned gpio):用于设置GPIO引脚为输入模式。

  4. gpio_direction_output(unsigned gpio, int value):用于设置GPIO引脚为输出模式,并可选地设置初始输出值。

  5. gpio_get_value(unsigned gpio):用于获取GPIO引脚的值(0或1)。

  6. gpio_set_value(unsigned gpio, int value):用于设置GPIO引脚的值(0或1)。

四、GPIO子系统的OF函数

在Linux内核中,设备树(Device Tree)是一种用于描述硬件设备的数据结构。GPIO子系统提供了一系列OF(设备树)函数,允许驱动程序从设备树中获取GPIO引脚的信息。以下是一些常用的OF函数及其功能:

  1. of_gpio_named_count(struct device_node *np, const char *propname):用于统计设备树中某个属性里面定义了几个GPIO信息。

  2. of_gpio_count(struct device_node *np):与of_gpio_named_count类似,但专门用于统计“gpios”属性的GPIO数量。

  3. of_get_named_gpio(struct device_node *np, const char *propname, int index):用于从设备树中获取指定属性的GPIO编号。该函数是驱动开发中常用的,因为它允许驱动程序根据设备树中的配置信息动态地获取GPIO编号。

五、举例

GPIO节点基本结构

在设备树中,一个典型的GPIO节点可能包含以下几个属性:

  1. gpio-controller:指定GPIO控制器的名称或引用。这通常是一个phandle,指向设备树中定义GPIO控制器的节点。

  2. #gpio-cells:表示每个GPIO引脚描述所占用的单元数。这通常与GPIO控制器的硬件设计相关。

  3. gpio-range:定义GPIO引脚的范围,包括起始引脚号和引脚数量。例如,<&gpio0 0 16>表示从GPIO控制器gpio0的第0个引脚开始,共有16个引脚。

  4. interrupt-parent:指定中断控制器的名称或引用,如果GPIO引脚用于中断的话。

  5. interrupts:描述GPIO引脚的中断配置,包括中断号、触发方式和中断处理函数等。

  6. status:表示GPIO节点的状态,通常是"okay"表示节点有效。

&gpio0 {  /* GPIO控制器引用,假设gpio0是在设备树的其他部分定义的GPIO控制器节点 */  gpio-controller; /* 声明这是一个GPIO控制器节点 */  #gpio-cells = <2>; /* 每个GPIO引脚描述占用2个单元 */  example_device_gpios: example_gpio@0 {  /* 定义一个名为example_gpio的GPIO子节点,起始地址为0 */  reg = <0>; /* 指定GPIO引脚的起始地址,这里为0 */  #gpio-cells = <1>; /* 在这个子节点中,每个GPIO描述占用1个单元(通常对于单个引脚来说是这样) */  gpio-range = <&gpio0 0 1>; /* 从gpio0控制器的第0个引脚开始,使用1个引脚 */  /* 如果需要,还可以添加中断相关属性 */  // interrupt-parent = <&intc>; /* 指定中断控制器 */  // interrupts = <0 1>; /* 中断号和触发方式(例如,0表示中断号,1表示边沿触发) */  status = "okay"; /* 节点状态为有效 */  };  
};  /* 注意:上述示例中的具体值(如#gpio-cells、gpio-range等)会根据实际的硬件和GPIO控制器设计而有所不同。 */  
/* 另外,设备树中的节点和属性命名也应遵循具体的硬件和驱动程序的要求。 */

解释

  • &gpio0:这是对设备树中其他地方定义的GPIO控制器节点的引用。
  • gpio-controller:声明这个节点是一个GPIO控制器节点。
  • #gpio-cells:指定每个GPIO描述所需的单元数。这取决于GPIO控制器的硬件设计。
  • example_device_gpios: example_gpio@0:定义了一个名为example_gpio的子节点,用于描述特定设备的GPIO配置。@0表示这个节点的起始地址(或索引)是0。
  • reg:指定了GPIO引脚的起始地址。在某些情况下,这个属性可能不是必需的,因为gpio-range已经提供了足够的信息。
  • #gpio-cells(在子节点中):在这个特定的子节点中,每个GPIO描述所需的单元数。对于单个引脚来说,这通常是1。
  • gpio-range:定义了从哪个GPIO控制器的哪个引脚开始使用,以及使用多少个引脚。
  • interrupt-parentinterrupts(可选):如果GPIO引脚用于中断,则需要指定中断控制器和中断配置。
  • status:表示节点的状态。通常是"okay"表示节点有效且应该被内核包含。

请注意,上述模板是一个简化的示例,实际设备树中的GPIO节点可能会根据具体的硬件和驱动程序要求包含更多的属性和子节点。在编写设备树时,应仔细参考硬件手册和驱动程序文档以确保正确的配置。

CODE-LED

/*************************************************************************
#    > File Name: pinctrl_gpio.c
#    > Author: HENG-W
#    > Mail: 
#    > Created Time: 2024年09月04日 星期三 18时36分27秒
#    > Describe: pinctrl_gpio子系统实验练习
#*************************************************************************/
/*头文件*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
/*
1.首先在 imx6ull-zealginat-emmc.dts 文件,
在根节点“/”下创建如下内容zealginat-pinctrl-gpio-led {compatible = "zealginat-pinctrl-gpio-led";status = "okay";pinctrl-0 = <&zealginat_zealgaint_pinctrl_led>;pinctrl-names = "pinctrl-gpio-led";pinctrl-gpio-led-pin = <&gpio1 3 GPIO_ACTIVE_LOW>;};2. iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“zealginat_zealgaint_pinctrl_led”的子节点zealginat_zealgaint_pinctrl_led: ledgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0x10B0>;};
3.检查 PIN 是否被其他外设使用!!!检查 PIN 是否被其他外设使用!!!检查 PIN 是否被其他外设使用!!!检查 PIN 是否被其他外设使用!!!查找如下两个参数是否有重复MX6UL_PAD_GPIO1_IO03__GPIO1_IO03<&gpio1 3 GPIO_ACTIVE_LOW>;
*/
/*定义设备数量、设备名、开灯关灯宏定义*/
#define GPIO_DEV_COUNT 1
#define GPIO_DEV_NAME "pinctrl_gpio"
#define LED_ON  1
#define LED_OFF 0/*定义pinctrl_gpio设备结构体*/
struct pinctrl_gpio_dev {dev_t devid;             /* 设备号*/int major;              /* 主设备号*/int minor;              /* 次设备号*/struct cdev cdev;       /* cdev结构体*/struct device *device;  /* 设备结构体指针*/struct class  *class;   /* 类结构体指针*/   struct device_node *node;   /* 设备树节点指针*/int led_gpio_num;       /* led gpio编号*/int led_status;         /* led状态*/
};
/*声明并初始化pinctrl_gpio_dev结构体*/  
static struct pinctrl_gpio_dev pinctrl_gpio_led;
/*打开设备*/
static int pinctrl_gpio_open(struct inode *inode, struct file *file)
{/*设置私有数据*/file->private_data = &pinctrl_gpio_led;printk("pinctrl_gpio_open\n");return 0;
}
/*向设备写数据*/
static ssize_t pinctrl_gpio_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{int retvale = 0;unsigned char databuf[1];unsigned char ledstatus;struct pinctrl_gpio_dev *dev = file->private_data; /* 获取私有数据*//*4、从用户空间拷贝数据*/retvale = copy_from_user(databuf, buf, count);if (retvale< 0) {printk("copy_from_user failed\n");return -EFAULT;}ledstatus = databuf[0]; /* 获取用户输入 状态值*/if (ledstatus == LED_ON) {gpio_set_value(dev->led_gpio_num, LED_ON);dev->led_status = LED_ON;} else if (ledstatus == LED_OFF) {gpio_set_value(dev->led_gpio_num, LED_OFF);dev->led_status = LED_OFF;} else {printk("input error\n");}return 0;
}
/*关闭设备*/
static int pinctrl_gpio_release(struct inode *inode, struct file *file)
{printk("pinctrl_gpio_release\n");return 0;
}/*设备操作函数结构体*/
static const struct file_operations pinctrl_gpio_fops = {.owner = THIS_MODULE,.open = pinctrl_gpio_open,.write = pinctrl_gpio_write,.release = pinctrl_gpio_release,
};/*驱动程序加载函数*/
static int __init pinctrl_gpio_init(void)
{int ret;/*1、获取设备树节点*/pinctrl_gpio_led.node = of_find_node_by_path("/zealginat-pinctrl-gpio-led");if (!pinctrl_gpio_led.node) {printk("can't find zealginat-pinctrl-gpio-led node\n");return -ENOENT;}/*2、获取led gpio编号*/pinctrl_gpio_led.led_gpio_num = of_get_named_gpio(pinctrl_gpio_led.node, "pinctrl-gpio-led-pin", 0);if (pinctrl_gpio_led.led_gpio_num < 0) {printk("can't get pinctrl-gpio-led-pin\n");return -ENOENT;} else {printk("pinctrl-gpio-led-pin num:%d\n", pinctrl_gpio_led.led_gpio_num);}/*3、设置GPIO1_IO03为输出模式,输出高电平,默认关闭*/ret = gpio_request(pinctrl_gpio_led.led_gpio_num, "pinctrl-gpio-led-pin");if (ret < 0) {printk("can't request gpio %d\n", pinctrl_gpio_led.led_gpio_num);gpio_free(pinctrl_gpio_led.led_gpio_num);return -1;}ret = gpio_direction_output(pinctrl_gpio_led.led_gpio_num, LED_ON);/*设置为输出模式,默认关闭,高电平关闭所以是LED_ON  1 */if (ret < 0) {printk("can't set direction of gpio %d\n", pinctrl_gpio_led.led_gpio_num);gpio_free(pinctrl_gpio_led.led_gpio_num);return -1;}  /*4、申请字符设备号*//*初始化主设备号*/pinctrl_gpio_led.major = 0;if (pinctrl_gpio_led.major){pinctrl_gpio_led.devid = MKDEV(pinctrl_gpio_led.major, 0);ret = register_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT, GPIO_DEV_NAME);    /* 申请设备号*/}else{ret = alloc_chrdev_region(&pinctrl_gpio_led.devid, 0, GPIO_DEV_COUNT, GPIO_DEV_NAME);   /* 申请设备号*/pinctrl_gpio_led.major = MAJOR(pinctrl_gpio_led.devid);                                 /* 获取主设备号*/pinctrl_gpio_led.minor = MINOR(pinctrl_gpio_led.devid);                                 /* 获取次设备号*/                          }if (ret<0){printk("can't register char device\n");goto err_unregister_chrdev;}else{printk("pinctrl_gpio_led major num:%d, minor num:%d\n", pinctrl_gpio_led.major, pinctrl_gpio_led.minor);}/*5、创建字符设备*/pinctrl_gpio_led.cdev.owner = THIS_MODULE;cdev_init(&pinctrl_gpio_led.cdev, &pinctrl_gpio_fops);ret = cdev_add(&pinctrl_gpio_led.cdev, pinctrl_gpio_led.devid, GPIO_DEV_COUNT);if (ret < 0) {printk("can't add cdev\n");goto err_destroy_cdev;}else {printk("cdev add success\n");}/*6、创建类*/pinctrl_gpio_led.class = class_create(THIS_MODULE, GPIO_DEV_NAME);if (IS_ERR(pinctrl_gpio_led.class)) {printk("can't create class\n");goto err_destroy_class;} else {printk("class create success\n");}/*7、创建设备*/pinctrl_gpio_led.device = device_create(pinctrl_gpio_led.class, NULL, pinctrl_gpio_led.devid, NULL, GPIO_DEV_NAME);if (IS_ERR(pinctrl_gpio_led.device)) {printk("can't create device\n");goto err_device_create;     } else {printk("device create success\n");}return 0;
err_device_create:class_destroy(pinctrl_gpio_led.class);cdev_del(&pinctrl_gpio_led.cdev);unregister_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT);return 0;
err_destroy_class:cdev_del(&pinctrl_gpio_led.cdev);unregister_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT);return 0;
err_destroy_cdev:unregister_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT);return 0;
err_unregister_chrdev:return 0;
}/*驱动程序卸载载函数*/
static void __exit pinctrl_gpio_exit(void)
{gpio_free(pinctrl_gpio_led.led_gpio_num);device_destroy(pinctrl_gpio_led.class, pinctrl_gpio_led.devid);class_destroy(pinctrl_gpio_led.class);cdev_del(&pinctrl_gpio_led.cdev);unregister_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT);printk("pinctrl_gpio_led exit\n");
}module_init(pinctrl_gpio_init);
module_exit(pinctrl_gpio_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("HENG-W");
MODULE_DESCRIPTION("pinctrl_gpio driver for i.MX6ull");
MODULE_VERSION("1.0");


http://www.ppmy.cn/news/1520823.html

相关文章

UE 【材质编辑】自定义材质节点

使用UE的材质编辑器&#xff0c;蓝图提供了大量的节点函数&#xff1a; 实际上&#xff0c;这是一段封装好的包含一串HLSL代码的容器。打开“Source/Runtime/Engine/Classes/Material”&#xff0c;可以看到很多不同节点的头文件&#xff1a; 照葫芦画瓢 以UMaterialExpressi…

notepad++将换行替换成空

将多行里的换行置为一行&#xff0c;例如将下面的6行置为3行 crrlH打开替换框&#xff0c; 替换目标为【,\r\n】&#xff0c;替换成空&#xff0c;勾选循环查找和 正则表达式&#xff0c;全部替换即可。 替换后的效果

应该怎么从0搭建一个图像识别系统,如果想考计算机的研究生应该如何准备

搭建一个图像识别系统的过程可以分为以下几个步骤&#xff1a; 数据收集和准备&#xff1a;收集包含标注的图像数据集&#xff0c;并将其准备为训练集和测试集。确保数据集的多样性和代表性。 特征提取和选择&#xff1a;选择适当的特征提取方法&#xff0c;如卷积神经网络&am…

如何配置iSAID_Devkit环境

这个库有点年头了&#xff0c;使用README.md里的conda env create -f environment.yml会说包之间有冲突, 没法安装. 解决方法: 自己建立一个conda env, conda create -n py_isaid pip python3.6.8 记得自己提前定好python版本use gpt to transform environment.yml to setup.p…

mac安装spark

参考&#xff1a;在Mac上安装Spark apache-spark-3.5.1_mac安装spark-CSDN博客 几个需要用到的路径&#xff1a; hadoop的bin目录&#xff1a;/opt/homebrew/Cellar/hadoop/3.4.0/bin spark的conf目录/opt/homebrew/Cellar/apache-spark/3.5.2/libexec/conf spark的bin目录&am…

Elasticsearch之原理详解

简介 ES是使用 Java 编写的一种开源搜索引擎&#xff0c;它在内部使用 Lucene 做索引与搜索&#xff0c;通过对 Lucene 的封装&#xff0c;隐藏了 Lucene 的复杂性&#xff0c;取而代之的提供一套简单一致的 RESTful API 然而&#xff0c;Elasticsearch 不仅仅是 Lucene&#…

SpringCloud Alibaba】(十三)学习 RocketMQ 消息队列

目录 1、MQ 使用场景与选型对比1.1、MQ 的使用场景1.2、引入 MQ 后的注意事项1.3、MQ 选型对比 2、下载、安装 RocketMQ 及 RocketMQ 控制台2.1、下载安装 RocketMQ2.2、测试 RocketMQ 环境2.3、RocketMQ 控制台【图形化管理控制台】2.3.1、下载、安装2.3.2、验证 RocketMQ 控制…

day-49 使数组中所有元素相等的最小操作数

思路 第一个数和最后一个数要变为一致&#xff0c;需要操作n-1次&#xff0c;然后第二个数和倒数第二个数要操作n-3次 解题过程 以此类推即可得出答案 Code class Solution {public int minOperations(int n) {int ans0;int t(n-1);while(t>0){anst;t-2;}return ans;} }作…

String核心设计模式——建造者模式

目录 建造者模式 优点 缺点 使用场景 结构 步骤 1 Item.java Packing.java 步骤 2 Wrapper.java Bottle.java 步骤 3 Burger.java ColdDrink.java 步骤 4 VegBurger.java ChickenBurger.java Coke.java Pepsi.java 步骤 5 Meal.java 步骤 6 MealBuilder…

Proteus 仿真设计:开启电子工程创新之门

摘要&#xff1a; 本文详细介绍了 Proteus 仿真软件在电子工程领域的广泛应用。从 Proteus 的功能特点、安装与使用方法入手&#xff0c;深入探讨了其在电路设计、单片机系统仿真、PCB 设计等方面的强大优势。通过具体的案例分析&#xff0c;展示了如何利用 Proteus 进行高效的…

CSP-CCF ★201512-2 消除类游戏★

一、问题描述 问题描述 消除类游戏是深受大众欢迎的一种游戏&#xff0c;游戏在一个包含有n行m列的游戏棋盘上进行&#xff0c;棋盘的每一行每一列的方格上放着一个有颜色的棋子&#xff0c;当一行或一列上有连续三个或更多的相同颜色的棋子时&#xff0c;这些棋子都被消除。当…

Spring的核心设计模式——工厂模式

目录 工厂模式 工厂模式的类型 优点 缺点 使用场景 结构 实现 步骤 1 步骤 2 步骤 3 步骤 4 步骤 5 工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一&#xff0c;它提供了一种创建对象的方式&#xff0c;使得创建对象的…

Scratch教师节:给老师的一封信

小虎鲸Scratch资源站-免费Scratch作品源码,素材,教程分享平台! 【Scratch教师节特别献礼】—— 给老师的一封信&#xff1a;编程之光&#xff0c;照亮梦想之路 在这个金秋送爽、硕果累累的季节里&#xff0c;我们迎来了一个特别而温馨的日子——教师节。在这个充满感激与敬意的…

无人机纪录片航拍认知

写在前面 博文内容为纪录片航拍简单认知&#xff1a;纪录片 航拍镜头&#xff0c;航拍流程&#xff0c;航拍环境条件注意事项介绍航拍学习书籍推荐《无人机商业航拍教程》读书笔记整理&#xff0c;适合小白认知理解不足小伙伴帮忙指正 &#x1f603;,生活加油 99%的焦虑都来自于…

使用模板:封装栈、队列

栈 #include <iostream>using namespace std;//封装栈 template<typename T> class myStack { private:T *data;int top-1; //记录栈顶元素下标int size; //栈的大小 public:myStack():size(10){data new T[size];top-1;}myStack(int size){data…

Axure RP实战:打造高效文字点选验证码

Axure RP实战&#xff1a;打造高效文字点选验证码 前言 在数字时代&#xff0c;网络安全和用户体验是设计在线表单时的两大关键考量。 验证码作为一种验证用户身份的技术&#xff0c;已经从简单的数字和字母组合&#xff0c;发展到了更为复杂和用户友好的形式。 今天&#…

实用的4大网站建设模板资源网站

WP模板牛 (wpniu.com ) WP模板牛是一个综合性的WordPress建站模板分享网站&#xff0c;提供中文WP模板、外贸WP模板、免费WP模板等100多个WordPress模板。使用这些模板可以快速搭建起属于自己的WordPress网站。 模板之家 (mymoban.com ) 模板之家是一个提供各种网站模板资源…

设计者模式之访问者模式

1.访问者模式概述 允许你在不改变对象结构中的元素类的前提下&#xff0c;向对象结构中的元素增加新的行为。这种模式将数据结构与作用于结构上的操作分离&#xff0c;从而使得我们可以独立地改变数据结构和操作。 2.访问者模式的组成部分 1&#xff09;Visitor&#xff08;访…

【C++】STL—vector的使用

目录 前言vector的常见构造迭代器的使用vector空间增长问题vector的增删查改尾插和尾删findinsert和erase 前言 vector是表示可变大小数组的序列容器。它就像数组一样&#xff0c;采用连续的存储空间来存储元素&#xff0c;且它的大小可以动态改变。并且vector在访问元素时也非…

【护网相关知识】

文章目录 一、什么是防火墙1.WAF2.IDS入侵检测系统3.IPS入侵防御系统4.安全工具5.OSI参考模型6.常见的网络攻击方式7.OWSPTOP10常见漏洞及基本原理8.常见漏洞及其基本原理9.流量分析10.域名系统11.渗透测试报告编写及安全事件处置 一、什么是防火墙 二代防火墙 三代防火墙部署…