当前位置: 首页 > news >正文

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用

  • 第十六章 GPIO 和 Pinctrl 子系统的使用
    • 16.1 Pinctrl 子系统重要概念
      • 16.1.1 引入
      • 16.1.2 重要概念
      • 16.1.3 示例
      • 16.1.4 代码中怎么引用pinctrl
    • 16.2 GPIO子系统重要概念
      • 16.2.1 引入
      • 16.2.2 在设备树中指定引脚
      • 16.2.3 在驱动代码中调用GPIO子系统
      • 16.2.4 sysfs中的访问方法
    • 16.3 基于GPIO子系统的LED驱动程序
      • 16.3.1 编写思路
      • 16.3.2 在设备树中添加Pinctrl信息
      • 16.3.3 在设备树中添加GPIO信息
      • 16.3.4 编程示例
    • 16.4 在xxxxxx_IMX6ULL上机实验
      • 16.4.1 确定引脚并生成设备树节点
      • 16.4.2 编译程序

第十六章 GPIO 和 Pinctrl 子系统的使用

在这里插入图片描述

参考文档:
a. 内核 Documentation\devicetree\bindings\Pinctrl\ 目录下:

Pinctrl-bindings.txt 

b. 内核 Documentation\gpio 目录下:

Pinctrl-bindings.txt 

c. 内核 Documentation\devicetree\bindings\gpio 目录下:

gpio.txt 

注意:本章的重点在于“使用”,深入讲解放在“驱动大全”的视频里。
前面的视频,我们使用直接操作寄存器的方法编写驱动。这只是为了让大家掌握驱动程序的本质,在实际开发过程中我们可不这样做,太低效了!如果驱动开发都是这样去查找寄存器,那我们就变成“寄存器工程师”了,即使是做单片机的都不执着于裸写寄存器了。
Linux 下针对引脚有 2 个重要的子系统:GPIO、Pinctrl。

16.1 Pinctrl 子系统重要概念

16.1.1 引入

无论是哪种芯片,都有类似下图的结构:
在这里插入图片描述

要想让 pinA、B 用于 GPIO,需要设置 IOMUX 让它们连接到 GPIO 模块;
要想让 pinA、B 用于 I2C,需要设置 IOMUX 让它们连接到 I2C 模块。
所以 GPIO、I2C 应该是并列的关系,它们能够使用之前,需要设置 IOMUX。有时候并不仅仅是设置 IOMUX,还要配置引脚,比如上拉、下拉、开漏等等。

现在的芯片动辄几百个引脚,在使用到 GPIO 功能时,让你一个引脚一个引脚去找对应的寄存器,这要疯掉。术业有专攻,这些累活就让芯片厂家做吧──他们是 BSP 工程师。我们在他们的基础上开发,我们是驱动工程师。开玩笑的,BSP 工程师是更懂他自家的芯片,但是如果驱动工程师看不懂他们的代码,那你的进步也有限啊。

所以,要把引脚的复用、配置抽出来,做成 Pinctrl子系统,给 GPIO、I2C等模块使用。 BSP工程师要做什么?看下图:
在这里插入图片描述
等 BSP工程师在 GPIO子系统、Pinctrl子系统中把自家芯片的支持加进去后,我们就可以非常方便地使用这些引脚了:点灯简直太简单了。
等等,GPIO模块在图中跟 I2C不是并列的吗?干嘛在讲 Pinctrl时还把 GPIO子系统拉进来?
大多数的芯片,没有单独的 IOMUX模块,引脚的复用、配置等等,就是在 GPIO模块内部实现的。 在硬件上 GPIO和 Pinctrl是如此密切相关,在软件上它们的关系也非常密切。
所以这 2个子系统我们一起讲解。

16.1.2 重要概念

从设备树开始学习 Pintrl会比较容易。
主要参考文档是:内核 Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
这会涉及 2个对象:pin controller、client device。
前者提供服务:可以用它来复用引脚、配置引脚。
后者使用服务:声明自己要使用哪些引脚的哪些功能,怎么配置它们。

a. pin controller:
在芯片手册里你找不到 pin controller,它是一个软件上的概念,你可以认为它对应 IOMUX──用来复用引脚,还可以配置引脚(比如上下拉电阻等)。
注意,pin controller和 GPIO Controller不是一回事,前者控制的引脚可用于 GPIO功能、I2C功能;后者只是把引脚配置为输出、输出等简单的功能。

b. client device
“客户设备”,谁的客户?Pinctrl系统的客户,那就是使用 Pinctrl系统的设备,使用引脚的设备。它在设备树里会被定义为一个节点,在节点里声明要用哪些引脚。
下面这个图就可以把几个重要概念理清楚:
在这里插入图片描述
上图中,左边是 pincontroller节点,右边是 client device节点:

a. pin state:
对于一个“client device”来说,比如对于一个 UART设备,它有多个“状态”:default、sleep等,那对应的引脚也有这些状态。
怎么理解?
比如默认状态下,UART设备是工作的,那么所用的引脚就要复用为 UART功能。
在休眠状态下,为了省电,可以把这些引脚复用为 GPIO功能;或者直接把它们配置输出高电平。
上图中,pinctrl-names里定义了 2种状态:default、sleep。
第 0种状态用到的引脚在 pinctrl-0中定义,它是 state_0_node_a,位于 pincontroller节点中。
第 1种状态用到的引脚在 pinctrl-1中定义,它是 state_1_node_a,位于 pincontroller节点中。
当这个设备处于 default状态时,pinctrl子系统会自动根据上述信息把所用引脚复用为 uart0功能。
当这这个设备处于 sleep状态时,pinctrl子系统会自动根据上述信息把所用引脚配置为高电平。

b. groups和 function
一个设备会用到一个或多个引脚,这些引脚就可以归为一组(group);
这些引脚可以复用为某个功能:function。
当然:一个设备可以用到多能引脚,比如 A1、A2两组引脚,A1组复用为 F1功能,A2组复用为 F2功能。

c. Generic pin multiplexing node和 Generic pin configuration node
在上图左边的 pin controller节点中,有子节点或孙节点,它们是给 client device使用的。 可以用来描述复用信息:哪组(group)引脚复用为哪个功能(function);
可以用来描述配置信息:哪组(group)引脚配置为哪个设置功能(setting),比如上拉、下拉等。

注意:pin controller节点的格式,没有统一的标准!!!!每家芯片都不一样。 甚至上面的 group、function关键字也不一定有,但是概念是有的。

16.1.3 示例

在这里插入图片描述

16.1.4 代码中怎么引用pinctrl

这是透明的,我们的驱动基本不用管。当设备切换状态时,对应的 pinctrl就会被调用。 比如在 platform_device和 platform_driver的枚举过程中,流程如下:
在这里插入图片描述

当系统休眠时,也会去设置该设备 sleep状态对应的引脚,不需要我们自己去调用代码。

非要自己调用,也有函数:

devm_pinctrl_get_select_default(struct device *dev);      // 使用"default"状态的引脚 
pinctrl_get_select(struct device *dev, const char *name); // 根据 name选择某种状态的引脚 
pinctrl_put(struct pinctrl *p);   // 不再使用, 退出时调用 

16.2 GPIO子系统重要概念

16.2.1 引入

要操作 GPIO引脚,先把所用引脚配置为 GPIO功能,这通过 Pinctrl子系统来实现。
然后就可以根据设置引脚方向(输入还是输出)、读值──获得电平状态,写值──输出高低电平。 以前我们通过寄存器来操作 GPIO引脚,即使 LED驱动程序,对于不同的板子它的代码也完全不同。 当 BSP工程师实现了 GPIO子系统后,我们就可以:
a. 在设备树里指定 GPIO引脚
b. 在驱动代码中:
使用 GPIO子系统的标准函数获得 GPIO、设置 GPIO方向、读取/设置 GPIO值。
这样的驱动代码,将是单板无关的。

16.2.2 在设备树中指定引脚

在几乎所有 ARM芯片中,GPIO都分为几组,每组中有若干个引脚。所以在使用 GPIO子系统之前,就要先确定:它是哪组的?组里的哪一个?
在设备树中,“GPIO组”就是一个 GPIO Controller,这通常都由芯片厂家设置好。我们要做的是找到它名字,比如“gpio1”,然后指定要用它里面的哪个引脚,比如<&gpio1 0>。
有代码更直观,下图是一些芯片的 GPIO控制器节点,它们一般都是厂家定义好,在 xxx.dtsi文件中:
在这里插入图片描述

我们暂时只需要关心里面的这 2个属性:

gpio-controller; 
#gpio-cells = <2>; 
“gpio-controller”表示这个节点是一个 GPIO Controller,它下面有很多引脚。 
“#gpio-cells = <2>”表示这个控制器下每一个引脚要用 232位的数(cell)来描述。 

为什么要用 2个数?其实使用多个 cell来描述一个引脚,这是 GPIO Controller自己决定的。比如可以用其中一个 cell来表示那是哪一个引脚,用另一个 cell来表示它是高电平有效还是低电平有效,甚至还可以用更多的 cell来示其他特性。
普遍的用法是,用第 1个 cell来表示哪一个引脚,用第 2个 cell来表示有效电平:

GPIO_ACTIVE_HIGH : 高电平有效 
GPIO_ACTIVE_LOW  :  低电平有效 

定义 GPIO Controller是芯片厂家的事,我们怎么引用某个引脚呢?在自己的设备节点中使用属性"[-]gpios",示例如下:
在这里插入图片描述

上图中,可以使用 gpios属性,也可以使用 name-gpios属性。

16.2.3 在驱动代码中调用GPIO子系统

在设备树中指定了 GPIO引脚,在驱动代码中如何使用?
也就是 GPIO子系统的接口函数是什么?
GPIO子系统有两套接口:基于描述符的(descriptor-based)、老的(legacy)。前者的函数都有前缀
“gpiod_”,它使用 gpio_desc结构体来表示一个引脚;后者的函数都有前缀“gpio_”,它使用一个整数来表示一个引脚。
要操作一个引脚,首先要 get引脚,然后设置方向,读值、写值。
驱动程序中要包含头文件,

#include <linux/gpio/consumer.h>   // descriptor-based 或 
#include <linux/gpio.h>            // legacy 

下表列出常用的函数:
在这里插入图片描述

有前缀“devm_”的含义是“设备资源管理”(Managed Device Resource),这是一种自动释放资源的机制。它的思想是“资源是属于设备的,设备不存在时资源就可以自动释放”。
比如在 Linux开发过程中,先申请了 GPIO,再申请内存;如果内存申请失败,那么在返回之前就需要先释放 GPIO资源。如果使用 devm的相关函数,在内存申请失败时可以直接返回:设备的销毁函数会自动地释放已经申请了的 GPIO资源。
建议使用“devm_”版本的相关函数。
举例,假设备在设备树中有如下节点:

 foo_device { compatible = "acme,foo"; ... led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */ <&gpio 16 GPIO_ACTIVE_HIGH>, /* green */ <&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */ power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>; }; 

那么可以使用下面的函数获得引脚:

struct gpio_desc *red, *green, *blue, *power; 
red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH); 
green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH); blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH); power = gpiod_get(dev, "power", GPIOD_OUT_HIGH); 

要注意的是,gpiod_set_value设置的值是“逻辑值”,不一定等于物理值。 什么意思?
旧的“gpio_”函数没办法根据设备树信息获得引脚,它需要先知道引脚号。
引脚号怎么确定?
在 GPIO子系统中,每注册一个 GPIO Controller时会确定它的“base number”,那么这个控制器里的第 n号引脚的号码就是:base number + n。
但是如果硬件有变化、设备树有变化,这个 base number并不能保证是固定的,应该查看 sysfs来确定 base number。

16.2.4 sysfs中的访问方法

在 sysfs中访问 GPIO,实际上用的就是引脚号,老的方法。

a. 先确定某个 GPIO Controller的基准引脚号(base number),再计算出某个引脚的号码。 方法如下:
① 先在开发板的/sys/class/gpio目录下,找到各个 gpiochipXXX目录:
在这里插入图片描述

② 然后进入某个 gpiochip目录,查看文件 label的内容
③ 根据 label的内容对比设备树
label内容来自设备树,比如它的寄存器基地址。用来跟设备树(dtsi文件)比较,就可以知道这对应哪一个 GPIO Controller。
下图是在 xxxxxx_imx6ull上运行的结果,通过对比设备树可知 gpiochip96对应 gpio4:
在这里插入图片描述

所以 gpio4这组引脚的基准引脚号就是 96,这也可以“cat base”来再次确认。

b. 基于 sysfs操作引脚
以 xxxxxx_imx6ull为例,它有一个按键,原理图如下:
在这里插入图片描述

那么 GPIO4_14的号码是 96+14=110,可以如下操作读取按键值: echo 110 >

/sys/class/gpio/export 
echo in > /sys/class/gpio/gpio110/direction 
cat /sys/class/gpio/gpio110/value 
echo  110 > /sys/class/gpio/unexport 

注意:如果驱动程序已经使用了该引脚,那么将会 export失败,会提示下面的错误:
对于输出引脚,假设引脚号为 N,可以用下面的方法设置它的值为 1: echo N >

/sys/class/gpio/export 
echo out > /sys/class/gpio/gpioN/direction 
echo 1 > /sys/class/gpio/gpioN/value 
echo  N > /sys/class/gpio/unexport 

16.3 基于GPIO子系统的LED驱动程序

16.3.1 编写思路

GPIO的地位跟其他模块,比如 I2C、UART的地方是一样的,要使用某个引脚,需要先把引脚配置为 GPIO功能,这要使用 Pinctrl子系统,只需要在设备树里指定就可以。在驱动代码上不需要我们做任何事情。
GPIO本身需要确定引脚,这也需要在设备树里指定。
设备树节点会被内核转换为 platform_device。
对应的,驱动代码中要注册一个 platform_driver,在 probe函数中:获得引脚、注册 file_operations。
在 file_operations中:设置方向、读值/写值。
在这里插入图片描述

下图就是一个设备树的例子:
在这里插入图片描述

16.3.2 在设备树中添加Pinctrl信息

有些芯片提供了设备树生成工具,在 GUI界面中选择引脚功能和配置信息,就可以自动生成 Pinctrl子结点。把它复制到你的设备树文件中,再在 client device结点中引用就可以。
有些芯片只提供文档,那就去阅读文档,一般在内核源码目录Documentation\devicetree\bindings\pinctrl下面,保存有该厂家的文档。
如果连文档都没有,那只能参考内核源码中的设备树文件,在内核源码目录 arch/arm/boot/dts目录下。
最后一步,网络搜索。
Pinctrl子节点的样式如下:
在这里插入图片描述

16.3.3 在设备树中添加GPIO信息

先查看电路原理图确定所用引脚,再在设备树中指定:添加”[name]-gpios”属性,指定使用的是哪一个 GPIO Controller里的哪一个引脚,还有其他 Flag信息,比如 GPIO_ACTIVE_LOW等。具体需要多少个cell来描述一个引脚,需要查看设备树中这个 GPIO Controller节点里的“#gpio-cells”属性值,也可以查看内核文档。
示例如下:
在这里插入图片描述

16.3.4 编程示例

在实际操作过程中也许会碰到意外的问题,现场演示如何解决。
a. 定义、注册一个 platform_driver
b. 在它的 probe函数里:
b.1 根据 platform_device的设备树信息确定 GPIO:gpiod_get
b.2 定义、注册一个 file_operations结构体
b.3 在 file_operarions中使用 GPIO子系统的函数操作 GPIO: gpiod_direction_output、gpiod_set_value
好处:这些代码对所有的代码都是完全一样的!
使用 GIT命令载后,源码 leddrv.c位于这个目录下:

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
05_gpio_and_pinctrl\     01_led 

摘录重点内容:
a. 注册 platform_driver
注意下面第 122行的"xxxxxx,leddrv",它会跟设备树中节点的 compatible对应:

121 static const struct of_device_id xxxxxx_leds[] = { 
122     { .compatible = "xxxxxx,leddrv" }, 
123     { }, 
124 }; 
125 
126 /* 1. 定义 platform_driver */ 
127 static struct platform_driver chip_demo_gpio_driver = { 
128     .probe      = chip_demo_gpio_probe, 
129     .remove     = chip_demo_gpio_remove, 
130     .driver     = { 
131         .name   = "xxxxxx_led", 
132         .of_match_table = xxxxxx_leds, 
133     }, 
134 }; 
135 
136 /* 2. 在入口函数注册 platform_driver */ 
137 static int __init led_init(void) 
138 { 
139     int err; 
140 
141     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 
142 
143     err = platform_driver_register(&chip_demo_gpio_driver); 
144 
145     return err; 
146 } 

b. 在 probe函数中获得 GPIO
核心代码是第 87行,它从该设备(对应设备树中的设备节点)获取名为“led”的引脚。在设备树中,必定有一属性名为“led-gpios”或“led-gpio”。

77 /* 4. 从 platform_device获得 GPIO 
78  *    把 file_operations结构体告诉内核:注册驱动程序 
79  */ 
80 static int chip_demo_gpio_probe(struct platform_device *pdev) 
81 { 
82      //int err; 
83 
84      printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 
85 
86      /* 4.1 设备树中定义有: led-gpios=<...>; */ 
87     led_gpio = gpiod_get(&pdev->dev, "led", 0); 
88      if (IS_ERR(led_gpio)) { 
89              dev_err(&pdev->dev, "Failed to get GPIO for led\n"); 
90              return PTR_ERR(led_gpio); 
91      } 
92 

c. 注册 file_operations结构体:
这是老套路了:

93      /* 4.2 注册 file_operations      */ 
94      major = register_chrdev(0, "xxxxxx_led", &led_drv);  /* /dev/led */ 
95 
96      led_class = class_create(THIS_MODULE, "xxxxxx_led_class"); 
97      if (IS_ERR(led_class)) { 
98              printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 
99              unregister_chrdev(major, "led"); 
100             gpiod_put(led_gpio); 
101             return PTR_ERR(led_class); 
102     } 
103 
104     device_create(led_class, NULL, MKDEV(major, 0), NULL, "xxxxxx_led%d", 0); /* /dev/xxxxxx_led0 */ 
105 

d. 在 open函数中调用 GPIO函数设置引脚方向:

51 static int led_drv_open (struct inode *node, struct file *file) 
52 { 
53      //int minor = iminor(node); 
54 
55      printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 56      /* 根据次设备号初始化 LED */ 
57      gpiod_direction_output(led_gpio, 0); 
58 
59      return 0; 
60 } 

e. 在 write函数中调用 GPIO函数设置引脚值:

34 /* write(fd, &val, 1); */ 
35 static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) 
36 { 
37      int err; 
38      char status; 
39      //struct inode *inode = file_inode(file); 
40      //int minor = iminor(inode); 
41 
42      printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 
43      err = copy_from_user(&status, buf, 1); 
44 
45      /* 根据次设备号和 status控制 LED */ 
46      gpiod_set_value(led_gpio, status); 
47 
48      return 1; 
49 } 

f. 释放 GPIO:

gpiod_put(led_gpio); 

16.4 在xxxxxx_IMX6ULL上机实验

16.4.1 确定引脚并生成设备树节点

NXP公司对于 IMX6ULL芯片,有设备树生成工具。我们也把它上传到 GIT去了,使用 GIT命令载后,在这个目录下:

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
05_gpio_and_pinctrl\ 
tools\ 
imx\ 

安装“ Pins_Tool_for_i.MX_Processors_v6_x64.exe”后运行,打开 IMX6ULL的配置文件
“MCIMX6Y2xxx08.mex”,就可以在 GUI界面中选择引脚,配置它的功能,这就可以自动生成 Pinctrl的子节点信息。
xxxxxx_IMX6ULL使用的 LED原理图如下,可知引脚是 GPIO5_3:
在这里插入图片描述

在设备树工具中,如下图操作:
在这里插入图片描述

把自动生成的设备树信息,放到内核源码 arch/arm/boot/dts/xxxxxx_imx6ull-14x14.dts中,代码如下:
a. Pinctrl信息:

&iomuxc_snvs { 
…… myled_for_gpio_subsys: myled_for_gpio_subsys{  fsl,pins = < MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03        0x000110A0 >; }; 

b. 设备节点信息(放在根节点下):

myled { compatible = "xxxxxx,leddrv"; pinctrl-names = "default"; pinctrl-0 = <&myled_for_gpio_subsys>; led-gpios = <&gpio5 3 GPIO_ACTIVE_LOW>;         

};

16.4.2 编译程序

编译设备树后,要更新设备树。

编译驱动程序时,“leddrv_未测试的原始版本.c”是有错误信息的,“leddrv.c”是修改过的。 测试方法,在板子上执行命令:

# insmod  leddrv.ko 
# ls /dev/xxxxxx_led0 
# ./ledtest /dev/xxxxxx_led0 on 
# ./ledtest /dev/xxxxxx_led0 off 

相关文章:

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用 第十六章 GPIO 和 Pinctrl 子系统的使用16.1 Pinctrl 子系统重要概念16.1.1 引入16.1.2 重要概念16.1.3 示例16.1.4 代码中怎么引用pinctrl 16.2 GPIO子系统重要概念16.2.1 引入16.2.2 在设备树中指定引脚16.2…...

Ubuntu系统下使用apt-get安装Mysql8

记录一下在Ubuntu20.04 64位系统下面使用apt-get方式安装mysql8关系型数据库 Centos下使用yum安装Mysql8&#xff08;Mysql5.7&#xff09;以及常见的配置和使用 首先肯定是检查下当前Ubuntu系统是否已经安装过mysql数据库 一般拿到新的云服务器是没有安装的 rootmyw:~# whe…...

jenkins联动显示或隐藏参数

1. 添加组件 Active Choices Plug-in 如jenkins无法联网&#xff0c;可在以下两个地址中下载插件&#xff0c;然后放到/home/jenkins/.jenkins/plugin下面重启jenkins即可 Active Choices Active Choices | Jenkins plugin 2. 效果如下&#xff1a; sharding为空时&#xf…...

Error: Activity class {xxx.java} does not exist

git切换到不同的branch之后&#xff0c;报下面的错误&#xff1a; Error: Activity class {xxx.java} does not exist 解决方案&#xff1a; 首先clean 然后会删除build目录 然后点击&#xff1a;Invalidate Caches Android Studio重启&#xff0c;然后重新build即可。...

保护模式阶段测试-模拟3环0环调用

保护模式阶段测试-模拟3环0环调用 最近又复习了一下保护模式相关的内容&#xff0c;然后打算搞个能够把段页的大部分知识能够串联起来的测试代码 最终想到的一个项目如下&#xff1a; 三环部分&#xff1a; 0.编写一个函数读取高2g的地址内容 1.通过设备通信到0环告诉0环我新…...

Dart笔记:stream_channel 包用法

标题1 标题2 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/133426961 【介绍】stream_channel是一个用…...

Java进阶必会JVM-深入浅出Java虚拟机

系列文章目录 送书第一期 《用户画像&#xff1a;平台构建与业务实践》 送书活动之抽奖工具的打造 《获取博客评论用户抽取幸运中奖者》 送书第二期 《Spring Cloud Alibaba核心技术与实战案例》 送书第三期 《深入浅出Java虚拟机》 文章目录 系列文章目录前言一、推荐书籍二…...

1200*B. Sorted Adjacent Differences(构造)

Problem - 1339B - Codeforces 解析&#xff1a; 题目要求每相邻两个值差的绝对值相等或递增。 先排序&#xff0c;可以想到我们先取两侧的数肯定相距最远&#xff0c;然后靠中心每次取两个数&#xff0c;这样符合题目要求。 直接遍历&#xff0c;先取的是答案靠后的数据&…...

恼人的TCP套接字部分发送成功场景

源起 以前就知道套接字有可能出现部分发送成功的可能&#xff0c;直到近段时间一个典型的使用场景触发了明确的此问题&#xff0c;才予以重视&#xff0c;比较深入地考虑解决这个问题的方案&#xff01; 分析 因为TCP的流式特征&#xff0c;如果出现部分发送成功&#xff0c…...

ROS2 中的轻量级、自动化、受控回放

一、说明 这篇文章描述了一种在 ROS2 中实现受控重播器的轻量级方法。用以测试中将现象重新播放一遍&#xff0c;以实现调参或故障定位的目的。所有源代码都可以在这里找到。该帖子也可在此处获得。 二、问题&#xff1a;不同步重播 任何曾经认真开发过 ROS2 的人都会知道这个问…...

Egg使用jwt拦截jtoken验证

安装 npm install egg-jwt注册插件 在config文件夹子下 plugin,js下 use strict;module.exports {//mysqlmysql: {enable: true,package: egg-mysql},//jwtjwt: {enable: true,package: egg-jwt} };使用中间件 在app文件下创建 middleware 文件夹 在middleware 文件下创建…...

装饰器模式详解和实现(设计模式 二)

装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许你动态地将对象添加到现有对象中&#xff0c;以提供额外的功能&#xff0c;同时又不影响其他对象。 实现示例 1.定义一个接口或抽象类&#xff0c;表示被装饰对象的公共接口 //抽…...

面试问到MySQL模块划分与架构体系怎么办

面试问到Mysql模块划分与架构体系怎么办 文章目录 1. 应用层连接管理器&#xff08;Connection Manager&#xff09;安全性和权限模块&#xff08;Security and Privilege Module&#xff09; 2. MySQL服务器层2.1. 服务支持和工具集2.2. SQL Interface2.3. 解析器举个解析器 …...

并查集及其优化

1.并查集 #define SIZE 100 int UFSets[SIZE];void Initial(int S[]) {for (int i 0; i < SIZE; i)S[i]-1; }int Find(int S[], int x) {//查while(S[x] > 0)x S[x];return x; }void Union(int S[], int Root1, int Root2) {//并if(Root1 Root2)return;S[Root2] Roo…...

LeetCode 周赛上分之旅 #48 一道简单的树上动态规划问题

⭐️ 本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问。 学习数据结构与算法的关键在于掌握问题背后的算法思维框架&#xff0c;你的思考越抽象&#xff0c;它能覆盖的问题域就越广&#xff0c;理解难度…...

mysql报错:Column Count Doesn‘t Match Value Count at Row 1

mysql中执行insert、update、delete报错&#xff1a;Column Count Doesnt Match Value Count at Row 1 的解决方案 通常情况&#xff1a;字段不匹配 如&#xff1a;student有id, name, age字段 -- 错误写法 INSERT INTO student VALUES(5,horse)-- 正确写法 INSERT INTO stu…...

安卓 kuaishou 设备did和egid 学习分析

did和egid注册 接口 https://gdfp.ksapisrv.com/rest/infra/gdfp/report/kuaishou/android did 是本地生成的16进制 或者 获取的 android_id public static final Random f16237a new Random(System.currentTimeMillis()); public static long m19668a() { return f1623…...

基于Vue+ELement实现增删改查案例与表单验证(附源码)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《ELement》。&#x1f3af;&#x1f3af; &#x1…...

webpack:使用externals配置来排除打包后的某个依赖插件IgnorePlugin的使用

背景 假设&#xff0c;我们写了一个库并使用 webpack 打包输出 bundle&#xff0c;但是这个库依赖一个第三方包&#xff0c;比如依赖 lodash&#xff0c;这时候我们不想把这个库打包进 bundle 里因为体积会变大&#xff0c;而且我们的主项目里已经安装了这个 lodash&#xff0…...

2023年中国工业脱水机行业供需分析:随着自动化和智能化技术的快速发展,销量同比增长4.9%[图]

工业脱水机行业是指专门从湿润的固体物料中去除水分的设备制造和相关服务。它广泛应用于食品加工、化工、制药、纺织、环保等行业&#xff0c;用于去除物料中的水分&#xff0c;提高产品质量和降低能耗。 工业脱水机行业分类 资料来源&#xff1a;共研产业咨询&#xff08;共研…...

[论文笔记]MacBERT

引言 今天带来MacBERT的阅读笔记。论文题目是 重新审视中文自然语言处理的预训练模型。 本篇主要是探讨中文预训练语言模型在非英文语言中的有效性,然后提出了一种简单而有效的模型,称为MacBERT,它在多个方面改进了RoBERTa,特别是采用纠错型掩码语言模型(MLM as correcti…...

AI发展目前最大挑战是什么?

影响AI成本的因素包括多个方面&#xff1a; 首先&#xff0c;AI技术的复杂性是其成本高昂的一个重要原因。AI技术需要进行大量数据处理、模型训练和优化&#xff0c;这需要耗费大量的计算资源和时间。同时&#xff0c;AI技术需要高水平的专业人才进行设计、开发和维护&#xf…...

自然语言处理NLP:LTP、SnowNLP、HanLP 常用NLP工具和库对比

文章目录 常见NLP任务常见NLP工具英文NLP工具中文NLP工具 常见NLP任务 Word Segmentation 分词 – Tokenization Stem extraction 词干提取 - Stemming Lexical reduction 词形还原 – Lemmatization Part of Speech Tagging 词性标注 – Parts of Speech Named entity rec…...

百度交易中台之内容分润结算系统架构浅析

作者 | 交易中台团队 导读 随着公司内容生态的蓬勃发展&#xff0c;内容产出方和流量提供方最关注的“收益结算”的工作&#xff0c;也就成为重中之重。本文基于内容分润结算业务为入口&#xff0c;介绍了实现过程中的重难点&#xff0c;比如千万级和百万级数据量下的技术选型和…...

【索引】常见的索引、B+树结构、什么时候需要使用索引、优化索引方法、索引主要的数据结构、聚簇索引、二级索引、创建合适的索引等重点知识汇总

目录 索引的分类 什么时候需要 / 不需要创建索引&#xff1f; 有什么优化索引的方法 MySQL索引主要使用的两种数据结构是什么 为什么 MySQL 采用 B 树作为索引 聚簇索引和二级索引 根据给定的表&#xff0c;如何创建索引比较好 索引的分类 普通索引&#xff1a;最基本的…...

Egg 封装接口返回信息

中间件封装 代码 const msgArr {"200":成功,"401":token失效 } module.exports (option, app) > {return async function(ctx, next) {try{//成功是返回的信息ctx.emit(code,data,msg)>{console.log(1111,code,data,msg)ctx.body {code,data:dat…...

Android AMS——创建APP进程(五)

接上一篇,在 ActivityTaskSupervisor 中会判断进程是否存在,如果进程不存在,则会创建进程,执行 startProcessAsync() 方法。如果进程存在,则执行 realStartActivityLocked() 方法。在APP 的启动时,进程是不存在的。所以我们先来分析一下进程不存在的情况。 一、创建进程…...

凉鞋的 Unity 笔记 102. 场景层次 与 GameObject 的增删改查

102. 场景层次 与 GameObject 的增删改查 在上一篇&#xff0c;我们完成了 Unity 引擎的 Hello world 输出&#xff0c;并且完成了第一个基本循环&#xff1a; 通过这次基本循环的完成&#xff0c;我们获得了一点点的 Unity 使用经验&#xff0c;这非常重要。 有实践经验后再…...

信息安全:网络安全审计技术原理与应用.

信息安全&#xff1a;网络安全审计技术原理与应用. 网络安全审计是指对网络信息系统的安全相关活动信息进行获取、记录、存储、分析和利用的工作。网络安全审计的作用在于建立“事后“安全保障措施&#xff0c;保存网络安全事件及行为信息&#xff0c;为网络安全事件分析提供线…...

嵌入式Linux应用开发-第十三章APP怎么读取按键值

嵌入式Linux应用开发-第十三章读取按键及按键驱动程序 第十三章 APP怎么读取按键值13.1 妈妈怎么知道孩子醒了13.2 APP读取按键的4种方法13.2.1 查询方式13.2.2 休眠-唤醒方式13.2.3 poll方式13.2.4 异步通知方式13.2.4.1 异步通知的原理&#xff1a;发信号13.2.4.2 应用程序之…...

怎么让人理解网站建设/广州疫情最新动态

使用session_set_save_handler()函数&#xff0c;将Session的内容写入数据库/*[email protected] Fahy*数据库为mysql&#xff0c;*数据库名为session&#xff0c;表名为session&#xff0c;*表中字段包括PHPSESSID,update_time,client_ip,data*/class Session{private static …...

python一句做网站/外贸推广方式都有哪些

集群流控 我们已经知道如何为应用接入限流了&#xff0c;但是到目前为止&#xff0c;这些还只是在单机应用中生效。也就是说&#xff0c;假如你的应用有多个实例&#xff0c;那么你设置了限流的规则之后&#xff0c;每一台应用的实例都会生效相同的流控规则&#xff0c;如下图…...

dream8网站建设教程视频/企业网站的域名是该企业的

2019独角兽企业重金招聘Python工程师标准>>> Javascript: 网页可见区域宽&#xff1a; document.body.clientWidth 网页可见区域高&#xff1a; document.body.clientHeight 网页可见区域宽&#xff1a; document.body.offsetWidth (包括边线的宽) 网页可见区域高&…...

做拆分盘网站/免费推广的方式

原文链接 http-server 是一个简单的零配置命令行HTTP服务器, 基于 nodeJs. 如果你不想重复的写 nodeJs 的 web-server.js, 则可以使用这个. 安装 (全局安装加 -g) : npm install http-server Windows 下使用: 在站点目录下开启命令行输入 http-server 访问: http://localhost…...

张家港做网站的公司/如何制作自己的网页

oracle数据泵EXPDP和IMPDP使用说明 首先标题不知道是否正确&#xff0c;oracle数据泵好像包含了好多概念&#xff0c;目前只是零星接触了一下EXPDP和IMPDP工具。据百度而来的资料&#xff0c;oracle10g之后推出的这个工具在性能上要比此前的EXP和IMP要高&#xff0c;而且支持从…...

网站开发推广渠道/提高工作效率的软件

原文地址&#xff1a;http://blog.csdn.net/honglei915/article/details/37564521 介绍 Kafka是一个分布式的、可分区的、可复制的消息系统。它提供了普通消息系统的功能。但具有自己独特的设计。这个独特的设计是什么样的呢&#xff1f;首先让我们看几个主要的消息系统术语&am…...