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

imx6ull-驱动开发篇10——pinctrl 子系统

目录

前言

pinctrl 子系统

pinctrl简介

PIN 配置信息详解

引脚标识

 寄存器值

电气属性值 0x17059

PIN 驱动程序讲解

PIN 配置

of_device_id

驱动探测函数

注册驱动

获取PIN配置

注册PIN控制器

设备树中添加 pinctrl 节点模板

创建对应的节点

添加“fsl,pins”属性

添加 PIN 配置信息


前言

在上一讲内容里,设备树下的 LED 驱动实验,我们配置 LED 灯所使用的 GPIO 寄存器,驱动开发方式和裸机基本没啥区别。

Linux 内核提供了 pinctrl 和 gpio 子系统用于GPIO 驱动,借助 pinctrl 和 gpio 子系统来简化 GPIO 驱动开发。本讲实验,我们主要学习pinctrl子系统的配置信息、理解驱动代码、学会添加pinctrl节点模板。

以控制一个 LED为例:

pinctrl 子系统

Linux 驱动讲究驱动分离与分层, pinctrl 和 gpio 子系统就是驱动分离与分层思想下的产物。

  • ​分离​​:将硬件相关的代码(如寄存器操作)与业务逻辑(如设备功能)解耦。

  • ​分层​​:通过子系统抽象硬件共性,提供统一接口,避免重复造轮子。

pinctrl简介

pinctrl 子系统:引脚复用与电气属性管理​

​功能​

  • 控制引脚的 ​​复用功能​​(如 GPIO、UART、I2C 等)

  • 配置引脚的 ​​电气属性​​(上下拉、驱动强度、斜率等)

对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成。

 pinctrl 子系统源码目录为 drivers/pinctrl。

接下来我们学习以下I.MX6ULL 的 pinctrl 子系统驱动。

PIN 配置信息详解

要使用 pinctrl 子系统,一般会在设备树里面创建一个节点来描述 PIN 的配置信息。

打开 imx6ull.dtsi 文件,找到一个叫做 iomuxc 的节点,如下所示:

iomuxc: iomuxc@020e0000 {compatible = "fsl,imx6ul-iomuxc";  // 兼容性字符串,匹配i.MX6ULL的IOMUXC驱动reg = <0x020e0000 0x4000>;         // 寄存器基地址0x020e0000,地址空间长度16KB
};

iomuxc 节点就是 I.MX6ULL 的 IOMUXC 外设对应的节点。

再打开 imx6ull-alientek-emmc.dts,找到iomuxc 节点,如下所示内容:

&iomuxc {pinctrl-names = "default";            // 默认引脚状态名称pinctrl-0 = <&pinctrl_hog_1>;        // 默认使用的引脚配置组/* 开发板特定引脚配置 */imx6ul-evk {/* GPIO默认配置组 */pinctrl_hog_1: hoggrp-1 {fsl,pins = </* GPIO1_IO19配置:复用为GPIO,电气属性0x17059 */MX6UL_PAD_UART1_RTS_B__GPIO1_IO19     0x17059/* GPIO1_IO05配置:复用为USDHC1_VSELECT */MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT  0x17059/* GPIO1_IO09配置:复用为GPIO */MX6UL_PAD_GPIO1_IO09__GPIO1_IO09     0x17059/* GPIO1_IO00配置:复用为OTG ID检测,电气属性0x13058 */MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058>;};
....../* FlexCAN1控制器引脚配置 */pinctrl_flexcan1: flexcan1grp {fsl,pins = </* CAN_RX信号:复用为FLEXCAN1_RX,电气属性0x1b020 */MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX   0x1b020/* CAN_TX信号:复用为FLEXCAN1_TX,电气属性0x1b020 */MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX   0x1b020>;};
....../* 看门狗引脚配置 */pinctrl_wdog: wdoggrp {fsl,pins = </* 看门狗信号:复用为WDOG1_WDOG_ANY,电气属性0x30b0 */MX6UL_PAD_LCD_RESET__WDOG1_WDOG_ANY   0x30b0>;};};
};

这段代码向 iomuxc 节点追加数据,不同的外设使用的 PIN 不同、其配置也不同,将某个外设所使用的所有 PIN 都组织在一个子节点里面。

以GPIO1_IO19为例:

/* GPIO1_IO19配置:复用为GPIO,电气属性0x17059 */MX6UL_PAD_UART1_RTS_B__GPIO1_IO19     0x17059
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0

引脚标识

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 

  • 前半部分:物理引脚名称,MX6UL_PAD_UART1_RTS_B​​物理引脚名称,对应芯片手册中的 ​​UART1_RTS_B​​ 引脚
  • 后半部分:配置该引脚,复用功能(GPIO1_IO19)

 寄存器值

宏定义后面跟着一串数字:

#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0

对应参数顺序:

<mux_reg,conf_reg,input_reg,mux_mode,input_val>

参数​

​值​

​说明​

mux_reg

0x0090

​复用功能寄存器偏移地址​​(相对于IOMUXC基地址0x020E0000

conf_reg

0x031C

​电气属性寄存器偏移地址​​(相对于IOMUXC基地址)

input_reg

0x0000

​输入寄存器偏移地址​​(此引脚无输入功能,故为0)

mux_mode

0x5

​复用模式值​​(ALT5模式对应GPIO1_IO19功能)

input_val

0x0

​输入功能选择值​​(未使用)

以mux_reg来看寄存器偏移,如图:

其它寄存器的具体细节,可以参考芯片手册来分析。

电气属性值 0x17059

/* GPIO1_IO19配置:复用为GPIO,电气属性0x17059 */MX6UL_PAD_UART1_RTS_B__GPIO1_IO19     0x17059

i.MX6UL 的引脚控制寄存器(IOMUXC_SW_PAD_CTL_PAD_*)按以下位域定义:

​位域​

​名称​

​值​

​功能说明​

16:13

HYS

0x1

输入滞后使能(抗抖动)

12

PUS

0x7

上拉/下拉选择(0=无, 1=下拉, 2=上拉)

11:10

PUE

0x0

上下拉使能(0=关闭, 3=保持器模式)

9:6

PKE

0x1

输入保持器使能

5:3

ODE

0x0

开漏输出禁用(推挽模式)

2:1

SPEED

0x2

驱动强度(00=低, 11=高)

0

SRE

0x1

压摆率(0=慢, 1=快)

0x17059的二进制展开​​:0001_0111_0000_0101_1001

对应配置:

  • ​​HYS=1​​:启用输入滞后
  • ​​PUS=7​​ (0111):47KΩ 上拉
  • ​​PUE=0​​:关闭上下拉(PUS 优先)
  • ​​PKE=1​​:启用输入保持器
  • ​​ODE=0​​:推挽输出
  • ​​SPEED=2​​:中速驱动
  • ​​SRE=1​​:快速压摆率

PIN 驱动程序讲解

PIN 配置

在文件drivers/pinctrl/freescale/pinctrl-imx6ul.c中,有如下内容:

/*** imx6ul_pinctrl_of_match - 设备树兼容性匹配表* * 用于匹配设备树中的iomuxc节点与对应的驱动数据*/
static struct of_device_id imx6ul_pinctrl_of_match[] = {{ .compatible = "fsl,imx6ul-iomuxc",          // 标准IOMUX控制器.data = &imx6ul_pinctrl_info,               // 关联i.MX6UL的引脚控制信息},{ .compatible = "fsl,imx6ull-iomuxc-snvs",     // SNVS域IOMUX控制器.data = &imx6ull_snvs_pinctrl_info,          // 关联i.MX6ULL SNVS引脚信息},{ /* sentinel */ }                               // 结束标记
};/*** imx6ul_pinctrl_probe - 驱动探测函数* @pdev: 平台设备结构体指针* * 1. 匹配设备树节点* 2. 获取SoC特定引脚信息* 3. 调用通用pinctrl初始化*/
static int imx6ul_pinctrl_probe(struct platform_device *pdev)
{const struct of_device_id *match;struct imx_pinctrl_soc_info *pinctrl_info;// 通过设备树匹配表查找对应设备match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev);if (!match)return -ENODEV;  // 未找到匹配项// 获取匹配项中存储的SoC特定数据pinctrl_info = (struct imx_pinctrl_soc_info *)match->data;// 调用i.MX系列通用pinctrl初始化函数return imx_pinctrl_probe(pdev, pinctrl_info);
}/*** imx6ul_pinctrl_driver - pinctrl平台驱动结构* * 注册驱动到内核平台驱动框架*/
static struct platform_driver imx6ul_pinctrl_driver = {.driver = {.name = "imx6ul-pinctrl",       // 驱动名称.owner = THIS_MODULE,           // 模块所有者.of_match_table = of_match_ptr(imx6ul_pinctrl_of_match), // 设备树匹配表},.probe = imx6ul_pinctrl_probe,      // 设备探测回调.remove = imx_pinctrl_remove,       // 设备移除回调
};

让我们分析这段代码:

of_device_id

of_device_id结构体数组,保存着这个驱动文件的兼容性值,设备树中的 compatible 属性值会和 of_device_id 中的所有兼容性字符串比较,查看是否可以使用此驱动。

static struct of_device_id imx6ul_pinctrl_of_match[] = {{ .compatible = "fsl,imx6ul-iomuxc",          // 标准IOMUX控制器.data = &imx6ul_pinctrl_info,               // 关联i.MX6UL的引脚控制信息},{ .compatible = "fsl,imx6ull-iomuxc-snvs",     // SNVS域IOMUX控制器.data = &imx6ull_snvs_pinctrl_info,          // 关联i.MX6ULL SNVS引脚信息},{ /* sentinel */ }                               // 结束标记
};

imx6ul_pinctrl_of_match 结构体数组一共有两个兼容性字符串, 分别为“fsl,imx6ul-iomuxc”和“fsl,imx6ull-iomuxc-snvs”,因此 iomuxc 节点与此驱动匹配,所以 pinctrl-imx6ul.c 会完成 I.MX6ULL 的 PIN 配置工作。

驱动探测函数

imx6ul_pinctrl_probe 函数就是 I.MX6ULL 这个 SOC 的 PIN 配置入口函数:

static int imx6ul_pinctrl_probe(struct platform_device *pdev)
{const struct of_device_id *match;struct imx_pinctrl_soc_info *pinctrl_info;// 通过设备树匹配表查找对应设备match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev);if (!match)return -ENODEV;  // 未找到匹配项// 获取匹配项中存储的SoC特定数据pinctrl_info = (struct imx_pinctrl_soc_info *)match->data;// 调用i.MX系列通用pinctrl初始化函数return imx_pinctrl_probe(pdev, pinctrl_info);
}

 imx6ul_pinctrl_probe 函数调用路径如下:

注册驱动

platform_driver结构体 是平台设备驱动,有个 probe 成员变量。当设备和驱动匹配成功以后, platform_driver 的 probe 成员变量所代表的函数就会执行。

static struct platform_driver imx6ul_pinctrl_driver = {.driver = {.name = "imx6ul-pinctrl",       // 驱动名称.owner = THIS_MODULE,           // 模块所有者.of_match_table = of_match_ptr(imx6ul_pinctrl_of_match), // 设备树匹配表},.probe = imx6ul_pinctrl_probe,      // 设备探测回调.remove = imx_pinctrl_remove,       // 设备移除回调
};

获取PIN配置

函数 imx_pinctrl_parse_groups 负责获取设备树中关于 PIN 的配置信息:

/** fsl,pins 属性中每个引脚由5个u32(PIN_FUNC_ID)和1个u32(CONFIG)组成,* 因此每个引脚总共占用24字节。*/
#define FSL_PIN_SIZE 24          // 标准引脚描述符的字节大小
#define SHARE_FSL_PIN_SIZE 20    // 共享引脚描述符的字节大小(无input_reg时)/*** imx_pinctrl_parse_groups - 解析设备树中的引脚组配置* @np: 设备树节点指针* @grp: 存储解析结果的引脚组结构* @info: SoC特定的引脚控制器信息* @index: 引脚组索引** 从设备树的fsl,pins属性中提取引脚复用和配置信息,* 填充到imx_pin_group结构中。*/
static int imx_pinctrl_parse_groups(struct device_node *np,struct imx_pin_group *grp,struct imx_pinctrl_soc_info *info,u32 index)
{int size, pin_size;const __be32 *list;  // 指向设备树属性数据的指针int i;u32 config;
....../* 遍历组内所有引脚 */for (i = 0; i < grp->npins; i++) {u32 mux_reg = be32_to_cpu(*list++);  // 复用寄存器地址u32 conf_reg;                        // 配置寄存器地址unsigned int pin_id;                 // 计算得到的引脚IDstruct imx_pin_reg *pin_reg;         // 引脚寄存器信息指针struct imx_pin *pin = &grp->pins[i]; // 当前引脚结构
....../* 计算引脚ID(根据复用或配置寄存器地址) */pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4;/* 存储寄存器映射信息 */pin_reg = &info->pin_regs[pin_id];pin->pin = pin_id;                   // 记录引脚IDgrp->pin_ids[i] = pin_id;            // 存储到组ID数组pin_reg->mux_reg = mux_reg;          // 复用寄存器地址pin_reg->conf_reg = conf_reg;        // 配置寄存器地址/* 提取引脚功能配置 */pin->input_reg = be32_to_cpu(*list++);  // 输入选择寄存器pin->mux_mode = be32_to_cpu(*list++);   // 复用模式(ALTx)pin->input_val = be32_to_cpu(*list++);  // 输入功能值
....../* 处理SION位(特殊功能位,位于复用寄存器中) */config = be32_to_cpu(*list++);          // 原始配置值if (config & IMX_PAD_SION)pin->mux_mode |= IOMUXC_CONFIG_SION; // 设置SION标志位pin->config = config & ~IMX_PAD_SION;   // 存储清理后的配置值}return 0;
}

设备树中的 mux_reg 和 conf_reg 值会保存在 info 参数中, input_reg、mux_mode、 input_val 和 config 值会保存在 grp 参数中。

static int imx_pinctrl_parse_groups(struct device_node *np,struct imx_pin_group *grp,struct imx_pinctrl_soc_info *info,u32 index)

获取 mux_reg、 conf_reg、 input_reg、 mux_mode 和 input_val 值。

        pin_reg->mux_reg = mux_reg;          // 复用寄存器地址pin_reg->conf_reg = conf_reg;        // 配置寄存器地址/* 提取引脚功能配置 */pin->input_reg = be32_to_cpu(*list++);  // 输入选择寄存器pin->mux_mode = be32_to_cpu(*list++);   // 复用模式(ALTx)pin->input_val = be32_to_cpu(*list++);  // 输入功能值

获取 config 值。

pin->config = config & ~IMX_PAD_SION;   // 存储清理后的配置值

注册PIN控制器

函数 pinctrl_register,用于向 Linux 内核注册一个 PIN 控制器,原型如下:

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,  // 描述引脚控制器的结构struct device *dev,               // 关联的硬件设备void *driver_data                 // 驱动私有数据
);

参数​

​类型​

​说明​

pctldesc

struct pinctrl_desc*

描述引脚控制器的结构体,包含操作函数集、引脚范围等关键信息

dev

struct device*

关联的硬件设备(通常为platform_device

driver_data

void*

驱动私有数据,可通过pinctrl_dev_get_drvdata()获取

参数 pctldesc ,就是要注册的 PIN 控制器, PIN 控制器用于配置 SOC的 PIN 复用功能和电气特性。参数 pctldesc 是 pinctrl_desc 结构体类型指针。

pinctrl_desc 结构体如下所示:

/** pinctrl_desc - 引脚控制器描述符* 该结构体描述一个引脚控制器及其功能,用于向pinctrl子系统注册引脚控制器*/
struct pinctrl_desc {const char *name;                                  /* 引脚控制器设备名称 */struct pinctrl_pin_desc const *pins;               /* 描述每个引脚的引脚描述符数组 */unsigned int npins;                                /* pins数组中的引脚数量 */const struct pinctrl_ops *pctlops;                 /* 引脚控制操作(引脚控制核心操作) */const struct pinmux_ops *pmxops;                   /* 引脚复用操作(多路复用功能配置) */const struct pinconf_ops *confops;                 /* 引脚配置操作(电气特性配置) */struct module *owner;                              /* 该引脚控制器的模块所有者 */#ifdef CONFIG_GENERIC_PINCONFunsigned int num_custom_params;                    /* 自定义配置参数的数量 */const struct pinconf_generic_params *custom_params; /* 自定义配置参数数组 */const struct pin_config_item *custom_conf_items;    /* 自定义参数的配置项 */
#endif
};

其中,有三个重要的结构体指针:

const struct pinctrl_ops *pctlops;                 /* 引脚控制操作(引脚控制核心操作) */
const struct pinmux_ops *pmxops;                   /* 引脚复用操作(多路复用功能配置) */
const struct pinconf_ops *confops;                 /* 引脚配置操作(电气特性配置) */

这三个结构体就是 PIN 控制器的“工具”,这三个结构体里面包含了很多操作函数,通过这些操作函数就可以完成对某一个PIN 的配置。

pinctrl_desc 结构体需要由用户提供,结构体里面的成员变量也是用户提供的。但是这个用户并不是我们这些使用芯片的程序员,而是半导体厂商,半导体厂商发布的 Linux 内核源码中已经把这些工作做完了。

比如在 imx_pinctrl_probe 函数中,有以下代码:

int imx_pinctrl_probe(struct platform_device *pdev,struct imx_pinctrl_soc_info *info)
{struct device_node *dev_np = pdev->dev.of_node;  // 获取设备树节点struct device_node *np;                          // 临时设备树节点指针struct imx_pinctrl *ipctl;                      // i.MX引脚控制器数据结构struct resource *res;                           // 资源指针struct pinctrl_desc *imx_pinctrl_desc;          // 引脚控制器描述符
......// 为引脚控制器描述符分配内存imx_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*imx_pinctrl_desc),GFP_KERNEL);if (!imx_pinctrl_desc)return -ENOMEM;  // 内存分配失败返回错误
......// 初始化引脚控制器描述符imx_pinctrl_desc->name = dev_name(&pdev->dev);  // 设置控制器名称imx_pinctrl_desc->pins = info->pins;           // 设置引脚描述数组imx_pinctrl_desc->npins = info->npins;         // 设置引脚数量imx_pinctrl_desc->pctlops = &imx_pctrl_ops;    // 设置控制操作函数集imx_pinctrl_desc->pmxops = &imx_pmx_ops;       // 设置复用操作函数集imx_pinctrl_desc->confops = &imx_pinconf_ops;  // 设置配置操作函数集imx_pinctrl_desc->owner = THIS_MODULE;         // 设置所属模块
......// 注册引脚控制器ipctl->pctl = pinctrl_register(imx_pinctrl_desc, &pdev->dev, ipctl);// ...其他代码...
}
  • 定义结构体指针变量 imx_pinctrl_desc。
  • 向指针变量 imx_pinctrl_desc 分配内存。
  • 初始化 imx_pinctrl_desc 结构体指针变量,重点是 pctlops、 pmxops 和 confops这三个成员变量,分别对应 imx_pctrl_ops、 imx_pmx_ops 和 imx_pinconf_ops 这三个结构体。
  • 调用函数 pinctrl_register 向 Linux 内核注册 imx_pinctrl_desc,注册以后 Linux 内核就有了对 I.MX6ULL 的 PIN 进行配置的工具。

imx_pctrl_ops、 imx_pmx_ops 和 imx_pinconf_ops 这三个结构体定义如下:

/* i.MX 引脚控制操作集合 */
static const struct pinctrl_ops imx_pctrl_ops = {.get_groups_count = imx_get_groups_count,      /* 获取引脚组数量 */.get_group_name = imx_get_group_name,          /* 获取引脚组名称 */ .get_group_pins = imx_get_group_pins,          /* 获取引脚组中的引脚列表 */.pin_dbg_show = imx_pin_dbg_show,              /* 调试接口:显示引脚信息 */.dt_node_to_map = imx_dt_node_to_map,          /* 从设备树节点生成引脚映射 */.dt_free_map = imx_dt_free_map,                /* 释放引脚映射资源 */
};
....../* i.MX 引脚复用操作集合 */  
static const struct pinmux_ops imx_pmx_ops = {.get_functions_count = imx_pmx_get_funcs_count, /* 获取复用功能数量 */.get_function_name = imx_pmx_get_func_name,    /* 获取复用功能名称 */.get_function_groups = imx_pmx_get_groups,     /* 获取支持某功能的引脚组 */.set_mux = imx_pmx_set,                        /* 设置引脚复用功能 */.gpio_request_enable = imx_pmx_gpio_request_enable, /* GPIO请求使能 */.gpio_set_direction = imx_pmx_gpio_set_direction,   /* 设置GPIO方向 */
};
....../* i.MX 引脚配置操作集合 */
static const struct pinconf_ops imx_pinconf_ops = {.pin_config_get = imx_pinconf_get,             /* 获取引脚配置参数 */.pin_config_set = imx_pinconf_set,             /* 设置引脚配置参数 */.pin_config_dbg_show = imx_pinconf_dbg_show,   /* 调试接口:显示引脚配置 */.pin_config_group_dbg_show = imx_pinconf_group_dbg_show, /* 调试接口:显示引脚组配置 */
};
......

这三个结构体下的所有函数就是 I.MX6ULL 的 PIN 配置函数。

设备树中添加 pinctrl 节点模板

我们学习一下如何在设备树中添加某个外设的PIN信息 。

关 于 I.MX 系 列 SOC 的 pinctrl 设 备 树 绑 定 信 息 可 以 参 考 文 档Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt。

这里我们虚拟一个名为“test”的设备, test 使用了GPIO1_IO00这个 PIN的GPIO 功能,

pinctrl 节点添加过程如下:

创建对应的节点

同一个外设的 PIN 都放到一个节点里面,打开 imx6ull-alientek-emmc.dts,在 iomuxc 节点中的“imx6ul-evk”子节点下添加“pinctrl_test”节点。

添加完成以后如下所示:

pinctrl_test: testgrp {/* 具体的 PIN 信息 */
};

添加“fsl,pins”属性

设备树是通过属性来保存信息的,因此我们需要添加一个属性,属性名字一定要为“fsl,pins”。

因为对于 I.MX 系列 SOC 而言, pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配置信息。

完成以后如下所示:

pinctrl_test: testgrp {fsl,pins = </* 设备所使用的 PIN 配置信息 */>;
};

添加 PIN 配置信息

最后在“fsl,pins”属性中添加具体的 PIN 配置信息,完成以后如下所示:

pinctrl_test: testgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config 是具体设置值*/>;
};

至此,我们已经在 imx6ull-alientek-emmc.dts 文件中,添加好了 test 设备所使用的 PIN 配置信息。

http://www.xdnf.cn/news/1249183.html

相关文章:

  • 福彩双色球第2025090期篮球号码分析
  • 生成式 AI 重塑自动驾驶仿真:4D 场景生成技术的突破与实践
  • Spring之【初识AOP】
  • Agent安全机制:权限控制与风险防范
  • 海康威视相机,MVS连接成功,但无图像怎么办?
  • 云计算一阶段Ⅱ——12. SELinux 加固 Linux 安全
  • 统计鱼儿分布情况 Java
  • libpq库使用
  • 二叉树算法
  • Mysql常用语句
  • Bilateral Reference for High-Resolution Dichotomous Image Segmentation
  • 智慧社区(八)——社区人脸识别出入管理系统设计与实现
  • 轻量应用服务器Centos系统上安装jdk8和Jdk17教程(详细)
  • (ZipList入门笔记二)为何ZipList可以实现内存压缩,可以详细介绍一下吗
  • 在AI时代,如何制定有效的职业规划?AI时代职业规划+AI产品经理角色
  • 探索设计模式的宝库:Java-Design-Patterns
  • FastDeploy2.0:报qwen2.embed_tokens.weight
  • 3. 为什么 0.1 + 0.2 != 0.3
  • 多传感器融合
  • Redis之Set和SortedSet类型常用命令
  • leetcode-python-删除链表的倒数第 N 个结点
  • VUE+SPRINGBOOT从0-1打造前后端-前后台系统-邮箱重置密码
  • 使用ProxySql实现MySQL的读写分离
  • ubuntu24安装vulkan-sdk
  • 一文搞定JavaServerPages基础,从0开始写一个登录与人数统计页面
  • Rust进阶-part4-智能指针2
  • 力扣106:从中序与后序遍历序列构造二叉树
  • VUE+SPRINGBOOT从0-1打造前后端-前后台系统-登录实现
  • Redis里面什么是sdshdr,可以详细介绍一下吗?
  • Linux lvm逻辑卷管理