GPIO子系统层次与数据结构详解

往期内容

本专栏往期内容:

  1. Pinctrl子系统和其主要结构体引入
  2. Pinctrl子系统pinctrl_desc结构体进一步介绍
  3. Pinctrl子系统中client端设备树相关数据结构介绍和解析
  4. inctrl子系统中Pincontroller构造过程驱动分析:imx_pinctrl_soc_info结构体
  5. Pinctrl子系统中client端使用pinctrl过程的驱动分析
  6. Pinctrl子系统中Pincontroller和client驱动程序的编写

input子系统专栏:

  1. 专栏地址:input子系统
  2. input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    – 末片,有往期内容观看顺序

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有往期内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有往期内容观看顺序

img

前言

Linux 4.x内核文档

  • Linux-4.9.88\Documentation\gpio📎drivers-on-gpio.txt📎gpio.txt📎gpio-legacy.txt📎sysfs.txt📎board.txt📎consumer.txt📎driver.txt
  • Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt📎gpio.txt
  • Linux-4.9.88\drivers\gpio\gpio-74x164.c📎gpio-74x164.c

本文主要讲解GPIO子系统的层次结构体、提供的相关API接口以及相关数据结构,同时通过对内核的示例驱动代码添加一些注释使能更好的理解GPIO子系统。

1.GPIO子系统的层次

1.1 层次

img

1.2 GPIOLIB向上提供的接口

descriptor-basedlegacy
获得GPIO
gpiod_getgpio_request
gpiod_get_index
gpiod_get_arraygpio_request_array
devm_gpiod_get
devm_gpiod_get_index
devm_gpiod_get_array
-------------------------------------------
设置方向
gpiod_direction_inputgpio_direction_input
gpiod_direction_outputgpio_direction_output
-------------------------------------------
读值、写值
gpiod_get_valuegpio_get_value
gpiod_set_valuegpio_set_value
-------------------------------------------
释放GPIO
gpio_freegpio_free
gpiod_putgpio_free_array
gpiod_put_array
devm_gpiod_put
devm_gpiod_put_array

GPIO子系统有两套接口:基于描述符的(descriptor-based)、老的(legacy)。前者的函数都有前缀“gpiod_”,它使用gpio_desc结构体来表示一个引脚;后者的函数都有前缀“gpio_”,它使用一个整数来表示一个引脚。

有前缀“devm_”的含义是“设备资源管理”(Managed Device Resource),这是一种自动释放资源的机制。它的思想是“资源是属于设备的,设备不存在时资源就可以自动释放”。

实际上会调用到gpio_chip中的相关函数:

img

对应这两种方法有啥区别,下面在介绍gpio_desc中会提到。

1.3 GPIOLIB向下提供的接口

img

如果chip无法被注册(例如chip->base无效或者已经和其它不同的chip关联),则返回负数的errno。否侧返回0。

当函数在开机早期被调用,以便GPIOs可以自由适用于时,chip->parent设备必须在gpio框架的arch_initcall()调用之前注册好。否则,GPIOs的sysfs初始化将失败。

函数只能在core_initcall()初始化之后(即在core_initcall之后)调用。

如果chip_base为负,则要求动态分配一系列有效的GPIO。

先自己构造好GPIO控制器对应的gpio_chip(里面有对GPIO控制器对应引脚控制和中断的相关操作函数),通过上面该函数来构造gpio_device来表示一个GPIO控制器

2.重要的3个核心数据机构

记住GPIO Controller的要素,这有助于理解它的驱动程序:

  • 一个GPIO Controller里有多少个引脚?有哪些引脚?
  • 需要提供函数,设置引脚方向、读取/设置数值
  • 需要提供函数,把引脚转换为中断

以Linux面向对象编程的思想,一个GPIO Controller必定会使用一个结构体来表示,这个结构体必定含有这些信息:

  • GPIO引脚信息
  • 控制引脚的函数
  • 中断相关的函数

2.1 gpio_device

每个GPIO Controller用一个gpio_device来表示:

  • 里面每一个gpio引脚用一个gpio_desc来表示
  • gpio引脚的函数(引脚控制、中断相关),都放在gpio_chip里
/*** struct gpio_device - GPIO 设备的内部状态容器* * @id: GPIO 芯片的数字 ID。* @dev: GPIO 设备对应的 `device` 结构体,表示 Linux 设备模型中的该 GPIO 设备。* @chrdev: GPIO 设备的字符设备结构,用于设备节点访问 GPIO。* @mockdev: 类设备,用于过时的 sysfs 接口;如果该接口不使用,可能为 NULL。* @owner: 模块的引用计数,当 GPIO 正在使用时,防止模块被卸载。* @chip: 指向 `gpio_chip` 的指针,保存 GPIO 芯片的静态数据,包括 GPIO 操作函数。* @descs: 描述符数组,包含该 GPIO 设备的所有 GPIO 线的描述符,数组大小为 `ngpio`。* @ngpio: GPIO 设备中的 GPIO 线数量,与 `descs` 数组大小相同。* @base: GPIO 的基础编号(已弃用),全局编号空间中的起始值,在设备创建时分配。* @label: GPIO 设备的描述性名称,例如 SoC 内的 IP 组件的部件号或名称。* @data: 驱动程序分配的每个实例的数据,用于存储驱动特定的自定义数据。* @list: 将多个 `gpio_device` 结构连接在一起,用于遍历。* * 该状态容器保存了一个 GPIO 设备的大多数运行时变量数据,* 在 GPIO 芯片被移除后,它仍可以被保留并在用户空间使用。*/
struct gpio_device {int                 id;             // GPIO 芯片的数字 IDstruct device       dev;            // GPIO 设备的 device 结构struct cdev         chrdev;         // 字符设备结构,用于字符设备节点struct device       *mockdev;       // 用于过时的 sysfs 接口的类设备,可能为 NULLstruct module       *owner;         // 模块引用计数,防止模块卸载struct gpio_chip    *chip;          // 指向 gpio_chip 的指针,保存 GPIO 芯片的静态信息struct gpio_desc    *descs;         // GPIO 描述符数组int                 base;           // 在全局 GPIO 空间中的基础编号(已弃用)u16                 ngpio;          // GPIO 设备中的 GPIO 线数量char                *label;         // 描述性名称void                *data;          // 驱动程序分配的自定义数据struct list_head    list;           // 链表结构,用于将多个 `gpio_device` 串联#ifdef CONFIG_PINCTRL/** 如果 CONFIG_PINCTRL 被启用,GPIO 控制器可以描述其在 SoC 中实际服务的管脚范围。* 该信息将由 pinctrl 子系统使用,以配置 GPIO 管脚使用。*/struct list_head    pin_ranges;     // pinctrl 的 GPIO 管脚范围
#endif
};

以1号GPIO Controller,如下图,那么其就有一个gpio_device结构体来表示,可以看出他有四个引脚,每个引脚就是用其成员struct gpio_desc *descs来表示,这是个数组来着,具体是什么下文会讲解。

  • id就是1,因为这个1号GPIO控制器。

  • base就是引脚编号基值,在这里就是0,在传统legacy中有用,需要注意的是它是全局的。

    • 比如第二个GPIO控制器,有32个引脚(编号范围是pin0pin31),那么加上1号控制器全部就有36个引脚是吧,此时我通过legacy的方式去使用**编号**为10的引脚(也就是第11根pin)。可以知道10号引脚不在1号GPIO控制器内(因为其只有4根pin,pin03),那就会去2号控制器找,那么注意了,在2号控制器的base就是4(全局,注意是编号值,编号是从0开始的!),10引脚对应其哪一个???10-4=6,也就是编号值为6的引脚(在全局中对应第11根pin
    • descriptor-based中,就弃用了好像,按照上面的情景,我还是去找编号为10的引脚,在1号控制器没找到,因为其大于ngpio-1,那么就会去第二个控制器找,有32根pin(pin0pin31),那么要找的编号为10的引脚就直接对应到pin0pin31中的pin10,从全局上来看就是第4+11 = 15根引脚,是不是就和legacy不一样了

img

还有疑惑的可以看一下下面的图:

img

📎leddrv.c

2.2 gpio_chip

并不需要自己创建gpio_device,编写驱动时要创建的是gpio_chip,里面提供了:

  • 控制引脚的函数
  • 中断相关的函数
  • 引脚信息:支持多少个引脚?各个引脚的名字?

img

/*** struct gpio_chip - 抽象 GPIO 控制器* * @label: GPIO 设备的功能性名称,如部件号或 SoC 中实现该 GPIO 的 IP 模块名称。* @gpiodev: 内部状态结构的指针,用于 `gpio_device` 结构。* @parent: 可选的父设备,提供 GPIO 的来源。* @owner: 引用模块,防止在 GPIO 使用时移除模块。** @request: 可选的钩子函数,用于激活 GPIO 控制器模块(如开启模块电源和时钟);可能会导致休眠。* @free: 可选的钩子函数,用于释放 GPIO 控制器模块(如关闭电源和时钟);也可能休眠。* @get_direction: 获取 GPIO 方向(0 表示输出,1 表示输入),返回负数表示错误。* @direction_input: 将 GPIO 配置为输入;返回错误代码(如果失败)。* @direction_output: 将 GPIO 配置为输出,并设置初始输出值;返回错误代码。* @get: 获取 GPIO 信号的当前值,0 表示低电平,1 表示高电平,负值表示错误。* @set: 设置 GPIO 的输出值(高或低)。* @set_multiple: 为多个 GPIO 信号设置输出值,使用位掩码 `mask` 定义多个信号。* @set_debounce: 设置特定 GPIO 的去抖时间(仅限支持中断触发的 GPIO 芯片)。* @set_single_ended: 设置 GPIO 线为开漏、开源或非单端(例如从开漏恢复到正常模式),适用于支持这些功能的硬件。* @to_irq: 支持非静态 `gpio_to_irq()` 映射的可选钩子,不能在实现中休眠。* @dbg_show: 可选的调试显示例程,显示 GPIO 芯片的状态。* * @base: 该芯片处理的第一个 GPIO 编号;如果设置为负数,则动态分配编号。*        建议使用动态分配方式(base = -1),以避免全局静态 GPIO 编号空间。* @ngpio: 该控制器管理的 GPIO 数量。* @names: 可选的 GPIO 名称数组,为 GPIO 芯片的 GPIO 提供别名,数组长度必须为 `ngpio`。* @can_sleep: 若 `get()` 和 `set()` 函数会休眠(如通过 I2C 或 SPI 访问 GPIO 扩展芯片),则该标志应为真。* @irq_not_threaded: 若 `can_sleep` 为真但 IRQ 不需要线程化,则应设置此标志。** 通用 GPIO 控制器寄存器的接口:* * @read_reg: 通用 GPIO 的读取寄存器。* @write_reg: 通用 GPIO 的写入寄存器。* @pin2mask: GPIO 引脚到位掩码的转换器回调。* @reg_dat: 输入数据寄存器。* @reg_set: 输出设定寄存器(设为高电平)。* @reg_clr: 输出清除寄存器(设为低电平)。* @reg_dir: GPIO 方向寄存器。* @bgpio_bits: 通用 GPIO 的寄存器位数。* @bgpio_lock: 用于锁定 `bgpio_data`,保证数据写入的原子性。* @bgpio_data: 通用 GPIO 的数据寄存器,用于安全地清除/设置位。* @bgpio_dir: 方向寄存器的镜像。* * 与 GPIO IRQ 相关的字段:* * @irqchip: GPIO IRQ 芯片实现,通常由 GPIO 驱动提供。* @irqdomain: 中断翻译域,用于在硬件 IRQ 与 Linux IRQ 之间映射。* @irq_base: GPIO IRQ 芯片的第一个 Linux IRQ 编号(已弃用)。* @irq_handler: GPIO 中断的处理程序(通常为预定义的中断核心函数)。* @irq_default_type: GPIO 驱动初始化期间应用的默认中断触发类型。* @irq_parent: GPIO IRQ 芯片的父中断编号。* @irq_need_valid_mask: 如果设置为真,核心会分配 `irq_valid_mask`,且所有位均为 1。* @irq_valid_mask: 允许包含在 IRQ 域的有效 GPIO 位掩码。* @lock_key: 每个 GPIO IRQ 芯片的 lockdep 类。* * 设备树相关的字段(CONFIG_OF_GPIO):* * @of_node: 设备树中的节点指针。* @of_gpio_n_cells: GPIO 节点的单元数量。* @of_xlate: 用于 OF(设备树)的 GPIO 芯片转换回调。** 该结构体使平台能够抽象各种来源的 GPIO,提供一致的接口调用,* 并支持不同硬件架构的 GPIO 访问和控制。*/
struct gpio_chip {const char		*label;struct gpio_device	*gpiodev;struct device		*parent;struct module		*owner;int			(*request)(struct gpio_chip *chip,unsigned offset);void			(*free)(struct gpio_chip *chip,unsigned offset);int			(*get_direction)(struct gpio_chip *chip,unsigned offset);int			(*direction_input)(struct gpio_chip *chip,unsigned offset);int			(*direction_output)(struct gpio_chip *chip,unsigned offset, int value);int			(*get)(struct gpio_chip *chip,unsigned offset);void			(*set)(struct gpio_chip *chip,unsigned offset, int value);void			(*set_multiple)(struct gpio_chip *chip,unsigned long *mask,unsigned long *bits);int			(*set_debounce)(struct gpio_chip *chip,unsigned offset,unsigned debounce);int			(*set_single_ended)(struct gpio_chip *chip,unsigned offset,enum single_ended_mode mode);int			(*to_irq)(struct gpio_chip *chip,unsigned offset);void			(*dbg_show)(struct seq_file *s,struct gpio_chip *chip);int			base;u16			ngpio;const char		*const *names;bool			can_sleep;bool			irq_not_threaded;#if IS_ENABLED(CONFIG_GPIO_GENERIC)unsigned long (*read_reg)(void __iomem *reg);void (*write_reg)(void __iomem *reg, unsigned long data);unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin);void __iomem *reg_dat;void __iomem *reg_set;void __iomem *reg_clr;void __iomem *reg_dir;int bgpio_bits;spinlock_t bgpio_lock;unsigned long bgpio_data;unsigned long bgpio_dir;
#endif#ifdef CONFIG_GPIOLIB_IRQCHIPstruct irq_chip		*irqchip;struct irq_domain	*irqdomain;unsigned int		irq_base;irq_flow_handler_t	irq_handler;unsigned int		irq_default_type;int			irq_parent;bool			irq_need_valid_mask;unsigned long		*irq_valid_mask;struct lock_class_key	*lock_key;
#endif#if defined(CONFIG_OF_GPIO)struct device_node *of_node;int of_gpio_n_cells;int (*of_xlate)(struct gpio_chip *gc,const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};

内核中给了详细的英文注释,这里按照个人理解将其转译出来。

2.3 gpio_desc

使用GPIO子系统时,首先是获得某个引脚对应的gpio_desc。

gpio_device表示一个GPIO Controller,里面支持多个GPIO。

在gpio_device中有一个gpio_desc数组,每一引脚有一项gpio_desc。
img

struct gpio_desc {struct gpio_device	*gdev;   /* 所属 GPIO 设备的指针 */unsigned long		flags;   /* GPIO 的状态标志位 *//* 状态标志的位定义 */
#define FLAG_REQUESTED	0       /* 标记 GPIO 已被请求 */
#define FLAG_IS_OUT	1       /* 标记 GPIO 为输出方向 */
#define FLAG_EXPORT	2       /* 通过 sysfs_lock 保护的 GPIO 导出标志 */
#define FLAG_SYSFS	3       /* GPIO 是否通过 /sys/class/gpio 导出 */
#define FLAG_ACTIVE_LOW	6       /* GPIO 为低电平有效 */
#define FLAG_OPEN_DRAIN	7       /* GPIO 为开漏类型 */
#define FLAG_OPEN_SOURCE 8      /* GPIO 为开源类型 */
#define FLAG_USED_AS_IRQ 9      /* GPIO 已用作中断 */
#define FLAG_IS_HOGGED	11      /* GPIO 被占用 *//* 连接标签,用于标识 GPIO 功能或用途 */const char		*label;/* GPIO 的名称 */const char		*name;
};

3.怎么编写GPIO Controller驱动程序

分配、设置、注册gpioc_chip结构体,示例:drivers\gpio\gpio-74x164.c📎gpio-74x164.c,内核提供的 对 74x164 移位寄存器的 SPI 控制功能,将寄存器的位作为 GPIO 使用。通过实现 GPIO 接口函数,它可以与 Linux 的 GPIO 子系统交互,使用户空间可以访问和控制寄存器的引脚状态。 下面是个人对代码添加的一些注释

/**  74Hx164 - 通用串入/并出 8位移位寄存器 GPIO 驱动* *  该驱动程序实现了对 74x164 类的串入/并出移位寄存器的控制,提供 GPIO 接口。*/#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/module.h>#define GEN_74X164_NUMBER_GPIOS 8  // 每个移位寄存器有 8 个 GPIO 引脚// 定义 gen_74x164_chip 结构体,表示一个 GPIO 扩展器设备实例
struct gen_74x164_chip {struct gpio_chip gpio_chip;  // GPIO 控制器结构struct mutex lock;           // 保护移位寄存器缓冲区的互斥锁u32 registers;               // 移位寄存器数量u8 buffer[0];                // 缓冲区,用于存储所有移位寄存器的状态,大小动态分配
};// 将缓冲区内容写入硬件寄存器
static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
{return spi_write(to_spi_device(chip->gpio_chip.parent), chip->buffer,chip->registers);
}// 获取指定 GPIO 引脚的值
static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
{struct gen_74x164_chip *chip = gpiochip_get_data(gc);u8 bank = chip->registers - 1 - offset / 8;  // 计算目标寄存器u8 pin = offset % 8;                         // 计算目标寄存器的引脚位置int ret;mutex_lock(&chip->lock);                     // 获取锁,防止并发访问ret = (chip->buffer[bank] >> pin) & 0x1;     // 获取指定引脚的值mutex_unlock(&chip->lock);                   // 释放锁return ret;
}// 设置指定 GPIO 引脚的值
static void gen_74x164_set_value(struct gpio_chip *gc,unsigned offset, int val)
{struct gen_74x164_chip *chip = gpiochip_get_data(gc);u8 bank = chip->registers - 1 - offset / 8;  // 计算目标寄存器u8 pin = offset % 8;                         // 计算目标寄存器的引脚位置mutex_lock(&chip->lock);                     // 获取锁if (val)chip->buffer[bank] |= (1 << pin);        // 设置引脚为高电平elsechip->buffer[bank] &= ~(1 << pin);       // 设置引脚为低电平__gen_74x164_write_config(chip);             // 将配置写入硬件寄存器mutex_unlock(&chip->lock);                   // 释放锁
}// 设置多个 GPIO 引脚的值,使用掩码确定设置的目标引脚
static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask,unsigned long *bits)
{struct gen_74x164_chip *chip = gpiochip_get_data(gc);unsigned int i, idx, shift;u8 bank, bankmask;mutex_lock(&chip->lock);                     // 获取锁for (i = 0, bank = chip->registers - 1; i < chip->registers; i++, bank--) {idx = i / sizeof(*mask);shift = i % sizeof(*mask) * BITS_PER_BYTE;bankmask = mask[idx] >> shift;if (!bankmask)continue;chip->buffer[bank] &= ~bankmask;         // 清除当前引脚chip->buffer[bank] |= bankmask & (bits[idx] >> shift); // 设置新值}__gen_74x164_write_config(chip);             // 写入寄存器mutex_unlock(&chip->lock);                   // 释放锁
}// 配置 GPIO 引脚为输出模式,并设置初始值
static int gen_74x164_direction_output(struct gpio_chip *gc,unsigned offset, int val)
{gen_74x164_set_value(gc, offset, val);       // 设置引脚值return 0;
}// SPI 驱动探测函数,初始化 74x164 设备
static int gen_74x164_probe(struct spi_device *spi)
{struct gen_74x164_chip *chip;u32 nregs;           // 寄存器数量int ret;spi->bits_per_word = 8;                      // 配置 SPI 传输位宽ret = spi_setup(spi);                        // 设置 SPI 设备if (ret < 0)return ret;// 从设备树读取 "registers-number" 属性if (of_property_read_u32(spi->dev.of_node, "registers-number", &nregs)) {dev_err(&spi->dev, "设备树中缺少 registers-number 属性.\n");return -EINVAL;}// 为 chip 结构体分配内存chip = devm_kzalloc(&spi->dev, sizeof(*chip) + nregs, GFP_KERNEL);if (!chip)return -ENOMEM;spi_set_drvdata(spi, chip);                  // 设置驱动程序数据// 初始化 GPIO 控制器参数chip->gpio_chip.label = spi->modalias;chip->gpio_chip.direction_output = gen_74x164_direction_output;chip->gpio_chip.get = gen_74x164_get_value;chip->gpio_chip.set = gen_74x164_set_value;chip->gpio_chip.set_multiple = gen_74x164_set_multiple;chip->gpio_chip.base = -1;chip->registers = nregs;                     // 设置寄存器数量chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;// 从设备树中读取默认寄存器值并初始化缓冲区of_property_read_u8_array(spi->dev.of_node, "registers-default", chip->buffer, chip->registers);chip->gpio_chip.can_sleep = true;chip->gpio_chip.parent = &spi->dev;chip->gpio_chip.owner = THIS_MODULE;mutex_init(&chip->lock);                     // 初始化互斥锁// 将初始配置写入硬件寄存器ret = __gen_74x164_write_config(chip);if (ret) {dev_err(&spi->dev, "写入失败: %d\n", ret);goto exit_destroy;}// 注册 GPIO 控制器ret = gpiochip_add_data(&chip->gpio_chip, chip);if (!ret)return 0;exit_destroy:mutex_destroy(&chip->lock);                  // 清理互斥锁return ret;
}// SPI 驱动移除函数
static int gen_74x164_remove(struct spi_device *spi)
{struct gen_74x164_chip *chip = spi_get_drvdata(spi);gpiochip_remove(&chip->gpio_chip);           // 删除 GPIO 控制器mutex_destroy(&chip->lock);                  // 销毁互斥锁return 0;
}// 设备树匹配表,列出支持的设备
static const struct of_device_id gen_74x164_dt_ids[] = {{ .compatible = "fairchild,74hc595" },{ .compatible = "nxp,74lvc594" },{},
};
MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);// SPI 驱动程序结构
static struct spi_driver gen_74x164_driver = {.driver = {.name           = "74x164",.of_match_table = gen_74x164_dt_ids,},.probe    = gen_74x164_probe,.remove   = gen_74x164_remove,
};
module_spi_driver(gen_74x164_driver);            // 注册 SPI 驱动程序MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
MODULE_DESCRIPTION("74X164 8位移位寄存器的 GPIO 扩展器驱动");
MODULE_LICENSE("GPL v2");

img

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

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

相关文章

干货丨通信网络与大模型的融合与协同

本文首发《中兴通讯技术》&#xff0c;2024年4月&#xff0c;第30卷第2期&#xff0c;作者&#xff1a;浙江大学在读本科生任天骐&#xff0c;浙江大学信息与电子工程学院副教授李荣鹏&#xff0c;浙江大学兼任教授、博士生导师张宏纲。边缘计算社区经过授权发布&#xff0c;以…

[ vulnhub靶机通关篇 ] 渗透测试综合靶场 DarkHole:1 通关详解 (附靶机搭建教程)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

对于一个STM32外设的应用有哪一些?可以列举一个实际的设计案例吗?

STM32 具有丰富的外设&#xff0c;以下是一些常见的应用&#xff1a; 1. **GPIO&#xff08;通用输入输出&#xff09;**&#xff1a; - 控制 LED 灯的亮灭。 - 读取按键状态。 - 与外部数字设备进行通信&#xff0c;如驱动数码管。 2. **USART&#xff08;通用同步异步收发器…

iDP3——改进3D扩散策略以赋能人形机器人的训练:不再依赖相机校准和点云分割(含DP3、Diff-Control、ControlNet详解)

前言 今天10.23日&#xff0c;明天1024则将作为长沙程序员代表&#xff0c;在CSDN和长沙相关部门举办的1024程序员节开幕式上发言&#xff0c;欢迎广大开发者来长工作 生活 考察 创业&#xff0c;​包括我司七月也一直在招聘大模型与机器人开发人员 后天&#xff0c;则将和相关…

前端 react 面试题(二)

文章目录 hooks的使用规则为什么hooks要确保在函数组件的最顶层,而不能放置在循环或者条件语句中。react的事件模型react的合成事件是如何实现的react事件传参,可以使用箭头函数或bind方法,这两种哪一种更好使用箭头函数:使用`bind`方法:react的事件模型和vue的区别React …

【P2-10】ESP8266 WIFI模块连接原子云服务器与原子云APP通信

前言:本节实现ESP8266 WIFI模块连接原子云服务器与原子云APP通信。 演示视频: 【物联网】ESP8266 WIFI模块连接原子云服务器与原子云APP通信 目录 1.WIFI模块连接原子云服务器互相通信 2.WIFI模块与原子云APP通信 1.WIFI模块连接原子云服务器互相通信 原子云服务器登陆入…

2024-11-4 学习人工智能的Day21 openCV(3)

图像滤波 所为图像滤波通过滤波器得到另一个图像 什么是滤波器 在深度学习中&#xff0c;滤波器又称为卷积核&#xff0c;滤波的过程成为卷积 卷积核概念 卷积核大小&#xff0c;一般为奇数&#xff0c;如 3*35*57*7 为什么卷积核大小是奇数&#xff1f; 原因是&…

CSS基础知识六(浮动的高度塌陷问题及解决方案)

目录 1.浮动高度塌陷概念 2.下面是几种解决高度塌陷的几种方案&#xff1a; 解决方案一&#xff1a; 解决方案二&#xff1a; 解决方案三&#xff1a; 1.浮动高度塌陷概念 在CSS中&#xff0c;高度塌陷问题指的是父元素没有正确地根据其内部的浮动元素或绝对定位元素来计…

8、raid磁盘阵列

raid级别及概念 不同分区组成的逻辑硬盘&#xff0c;可以实现高可用&#xff0c;即阵列当中有一个分区的硬盘损坏&#xff0c;不影响整个阵列的使用&#xff0c;可以满足企业级的读写性能的要求。 raid磁盘阵列——raid级别&#xff1a; raid0&#xff0c;raid1&#xff0c;…

hivt实战

argoverse数据集和API forcasting包含tracking的结果&#xff0c;然后结合argo-api去获取hdmap数据 重要的api 数据结构 lane segment argoverse-api/argoverse/map_representation/lane_segment.py at master argoverse/argoverse-api GitHub 练习 get started with th…

CVE-2024-26229 Windows CSC服务权限提升漏洞复现

一、漏洞描述 csc.sys驱动程序中带有METHOD_NEITHER I/O控制代码的IOCTL地址验证不正确&#xff0c;导致任意地址写入漏洞。 METHOD_NEITHER是 Windows 驱动程序中的一种 I/O 控制代码数据传输方法。它允许驱动程序在处理 I/O 请求时&#xff0c;直接从用户模式缓冲区读取或向…

Chrome与傲游在隐私保护上的表现

在数字化时代&#xff0c;浏览器作为我们探索互联网世界的重要工具&#xff0c;其隐私保护功能的强弱直接关系到用户的信息安全。本文将深入对比Chrome与傲游两款主流浏览器在隐私保护方面的表现&#xff0c;并提供实用的教程&#xff0c;帮助用户更好地管理自己的隐私设置。&a…

zip文件加密成图片文件-到解密

加密 1&#xff0c;准备&#xff1a;图片 zip文件 2&#xff0c;新建一个.txt 根据自己的对应文件修改&#xff1a; copy 图片名.后缀/b压缩包名.后缀自定义图片名.后缀注意&#xff0c;图片后缀最后保持一至&#xff0c;测试了 jpg png 压缩包 zip 3&#xff0c;把上…

1-ARM Linux驱动开发-MIO控制

一、前言 MIO也属于是字符设备&#xff0c;将MIO复用为GPIO可以实现对LED等外设的控制&#xff0c;其本质是从寄存器层面对硬件的控制。这次主要记录一下GPIO控制LED的驱动。 官网ZYNQ寄存器手册https://docs.amd.com/r/en-US/ug1087-zynq-ultrascale-registers/Overview 二…

深入解析Sysmon日志:增强网络安全与威胁应对的关键一环

不断演进的网络安全领域中&#xff0c;保持对威胁的及时了解至关重要。Sysmon日志在这方面发挥了至关重要的作用&#xff0c;通过提供有价值的见解&#xff0c;使组织能够加强其安全姿态。Windows在企业环境中是主导的操作系统&#xff0c;因此深入了解Windows事件日志、它们的…

HTMLCSS:呈现的3D树之美

效果演示 这段代码通过HTML和CSS创建了一个具有3D效果的树的图形&#xff0c;包括分支、树干和阴影&#xff0c;通过自定义属性和复杂的变换实现了较为逼真的立体效果。 HTML <div class"container"><div class"tree"><div class"…

XingHan-Team团队官网系统源码 全开源

XingHan-Team 官网程序是一个现代化的企业官网管理系统&#xff0c;由星涵网络工作室开发。 本系统提供了完整的网站内容管理功能&#xff0c;包括用户管理、内容发布、成员查询、成员申请等功能。 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/8995…

JAVA:常见 JSON 库的技术详解

1、简述 在现代应用开发中&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;已成为数据交换的标准格式。Java 提供了多种方式将对象转换为 JSON 或从 JSON 转换为对象&#xff0c;常见的库包括 Jackson、Gson 和 org.json。本文将介绍几种常用的 JSON 处理…

【贪心】【可行范围内最大边界】SCNU习题 P25.跳跃游戏

算法思想&#xff1a; 每次迭代更新可行至的最大范围r(r必保证>原位置&#xff09;&#xff0c;至到迭代结束&#xff0c;若r>length of array则说明可以跳跃至此 #include <iostream> #include <vector> #include <string> #include <sstre…

Redis 组网方式入门

文章目录 一、组网方式1. 单实例模式描述优点缺点适用场景 2. 主从复制模式&#xff08;Master-Slave Replication&#xff09;描述优点缺点适用场景基于docker的redis主从复制1. 配置主节点2. 配置从节点3. 查看节点状态4. 验证主从数据同步5. 查看同步进度 3. 哨兵模式&#…