Pinctrl子系统中Pincontroller和client驱动程序的编写

往期内容

本专栏往期内容:

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

input子系统专栏:

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

I2C子系统专栏:

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

总线和设备树专栏:

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

img

目录

  • 往期内容
  • 1.回顾Pinctrl的三大作用
  • 2.需要做什么
  • 3.硬件功能
  • 4.编写设备树
  • 5.编写Pinctrl驱动程序
    • 5.1 核心:pinctrl_desc
    • 5.2 辅助函数
      • 5.2.1 for_each_child_of_node
      • 5.2.2 of_get_child_count
      • 5.2.3 of_find_property
      • 5.2.4 of_property_read_u32
      • 5.2.5 of_property_read_u32_index
      • 5.2.6 of_property_read_string_index
    • 5.3 代码
    • 5.4 编写测试的client驱动程序
  • 6.调试信息

1.回顾Pinctrl的三大作用

记住pinctrl的三大作用,有助于理解所涉及的数据结构:

  • 引脚枚举与命名(Enumerating and naming)

    • 单个引脚
    • 各组引脚
  • 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能

  • 引脚配置(Configuration):比如上拉、下拉、open drain、驱动强度等

Pinctrl子系统和其主要结构体引入

Pinctrl子系统pinctrl_desc结构体进一步介绍

Pinctrl子系统中client端设备树相关数据结构介绍和解析

2.需要做什么

img

  • pin controller:

    • 创建设备树节点
    • 编写驱动程序
  • 测试:

    • 创建client设备树节点
    • 编写驱动程序

3.硬件功能

假设这个虚拟的pin controller有4个引脚:

img

  • pin0,1,2,3都可以配置为GPIO功能 — function 1
  • pin0,1还可以配置为I2C功能 — function 2
  • pin2,3还可以配置为UART功能 — function 3

4.编写设备树

       Pincontroller:
virtual_pincontroller {compatible = "XXX,virtual_pinctrl";i2cgrp: i2cgrp {functions = "i2c", "i2c";groups = "pin0", "pin1";configs = <0x11223344  0x55667788>;};
};client:
virtual_i2c {compatible = "XXX,virtual_i2c";pinctrl-names = "default";pinctrl-0 = <&i2cgrp>;
};

5.编写Pinctrl驱动程序

5.1 核心:pinctrl_desc

  • 分配pinctrl_desc结构体
  • 设置pinctrl_desc结构体
  • 注册pinctrl_desc结构体

5.2 辅助函数

\Linux-4.9.88\include\linux\of.h📎of.h

include/linux/of.hfor_each_child_of_nodeof_get_child_countof_find_propertyof_property_read_u32of_property_read_u32_indexof_property_read_string_index

5.2.1 for_each_child_of_node

#define for_each_child_of_node(parent, child) \for (child = of_get_next_child(parent, NULL); child != NULL; \child = of_get_next_child(parent, child))

for_each_child_of_node 是一个宏,用于遍历给定父节点的所有直接子节点。它在遍历每个子节点时会将其赋值给变量 child,供后续操作使用。

参数

  • parent: 指向父节点的指针。
  • child: 用于存储当前遍历的子节点的指针。

说明

  • 该宏循环调用 of_get_next_child 函数,遍历所有直接子节点。
  • 在遍历的过程中,child 会依次指向 parent 的每一个子节点。
  • 使用 for_each_child_of_node 遍历时,用户需要确保释放所有的子节点,避免资源泄露。

5.2.2 of_get_child_count

static inline int of_get_child_count(const struct device_node *np)
{struct device_node *child;int num = 0;for_each_child_of_node(np, child)num++;return num;
}

功能

of_get_child_count 函数用于计算指定设备树节点的直接子节点数量。

参数

  • np: 指向设备树节点的指针。

说明

  • 该函数使用 for_each_child_of_node 宏遍历节点的所有直接子节点,每找到一个子节点,计数器 num 加 1。
  • of_get_child_count 通常用于确定设备树节点的子节点数量,以便在后续操作中做动态分配或判断。

5.2.3 of_find_property

extern struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);

功能

of_find_property 函数用于在指定节点中查找属性。

参数

  • np: 指向设备树节点的指针。
  • name: 要查找的属性名称(字符串)。
  • lenp: 指向一个整数的指针,用于存储找到的属性的长度(以字节为单位)。

说明

  • 该函数适用于检查节点中是否存在某个属性。若 lenp 不为 NULL,将返回属性的长度。
  • of_find_property 常用于从设备树中查找如 compatiblestatus 等属性。

5.2.4 of_property_read_u32

static inline int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value)
{return of_property_read_u32_array(np, propname, out_value, 1);
}

功能

of_property_read_u32 函数用于读取设备树节点中指定属性的 u32 整数值。

参数

  • np: 指向设备树节点的指针。
  • propname: 要读取的属性名称。
  • out_value: 指向 u32 类型的变量的指针,用于存储读取到的属性值

说明

  • 该函数通过调用 of_property_read_u32_array 实现,从设备树中读取一个 u32 类型的属性值。
  • 如果属性为数组且包含多个值,则只会读取第一个元素。如果需要读取多个值,可以直接调用 of_property_read_u32_array

5.2.5 of_property_read_u32_index

extern int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value);

功能

of_property_read_u32_index 函数用于读取设备树节点中 u32 类型数组属性的指定索引值。

参数

  • np: 指向设备树节点的指针。
  • propname: 属性名称。
  • index: 数组中的索引。
  • out_value: 指向 u32 变量的指针,用于存储读取到的值。

说明

  • 该函数适用于读取包含多个 u32 元素的数组属性,例如 reginterrupts 等。
  • of_property_read_u32_index 可以用来精确访问设备树中数组属性的特定元素。

5.2.6 of_property_read_string_index

/*** of_property_read_string_index() - Find and read a string from a multiple* strings property.* @np:		device node from which the property value is to be read.* @propname:	name of the property to be searched.* @index:	index of the string in the list of strings* @out_string:	pointer to null terminated return string, modified only if*		return value is 0.** Search for a property in a device tree node and retrieve a null* terminated string value (pointer to data, not a copy) in the list of strings* contained in that property.* Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if* property does not have a value, and -EILSEQ if the string is not* null-terminated within the length of the property data.** The out_string pointer is modified only if a valid string can be decoded.*/
static inline int of_property_read_string_index(const struct device_node *np,const char *propname,int index, const char **output)
{int rc = of_property_read_string_helper(np, propname, output, 1, index);return rc < 0 ? rc : 0;
}

功能

of_property_read_string_index 函数用于读取设备树节点中字符串数组属性的指定索引值。

参数

  • np: 指向设备树节点的指针。
  • propname: 属性名称。
  • index: 要读取的字符串在数组中的索引。
  • output: 指向 const char * 指针的指针,用于存储读取到的字符串地址。

说明

  • 该函数常用于读取如 pinctrl-names 之类的字符串数组属性,能够获取数组属性中的第 index 个字符串。
  • 例如,如果 pinctrl-names 属性中定义了 default, idle 两个状态值,可以使用该函数获取具体的字符串内容。

5.3 代码

📎core.h

📎virtual_pinctrl_driver.c

#include <linux/module.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/slab.h>
#include <linux/regmap.h>#include "core.h"// 全局变量,保存 pinctrl 设备结构体指针
static struct pinctrl_dev *global_pinctrl_dev;// 定义一个 pins 数组,描述 GPIO 引脚
static const struct pinctrl_pin_desc pin_descs[] = {{0, "pin0", NULL},{1, "pin1", NULL},{2, "pin2", NULL},{3, "pin3", NULL},
};// 全局配置数组
static unsigned long pin_configs[4];// 虚拟功能描述结构体
struct virtual_function_desc {const char *function_name;const char **groups;int group_count;
};// 定义不同功能组的引脚
static const char *function0_groups[] = {"pin0", "pin1", "pin2", "pin3"};
static const char *function1_groups[] = {"pin0", "pin1"};
static const char *function2_groups[] = {"pin2", "pin3"};// 定义功能描述数组
static struct virtual_function_desc virtual_functions[] = {{"gpio", function0_groups, 4},{"i2c",  function1_groups, 2},{"uart", function2_groups, 2},
};// 设备树匹配表
static const struct of_device_id virtual_pinctrl_of_match[] = {{ .compatible = "XXX,virtual_pinctrl", },{ },
};// 获取引脚组的数量
static int virtual_get_groups_count(struct pinctrl_dev *pctldev) {return pctldev->desc->npins;
}// 获取指定引脚组的名称
static const char *virtual_get_group_name(struct pinctrl_dev *pctldev, unsigned selector) {return pctldev->desc->pins[selector].name;
}// 获取指定引脚组的引脚编号
static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,const unsigned **pins, unsigned *npins) {if (selector >= pctldev->desc->npins)return -EINVAL;*pins = &pctldev->desc->pins[selector].number; // 设置引脚编号*npins = 1; // 每个组只包含一个引脚return 0;
}// 显示引脚调试信息
static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset) {seq_printf(s, "%s", dev_name(pctldev->dev)); // 打印设备名称
}// 将设备树节点映射到 pinctrl_map
static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev,struct device_node *np,struct pinctrl_map **map, unsigned *num_maps) {int i;int num_pins = 0;const char *pin;const char *function;unsigned int config;struct pinctrl_map *new_map;unsigned long *configs;// 计算 groups 数组的数量while (of_property_read_string_index(np, "groups", num_pins, &pin) == 0)num_pins++;new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL);// 获取引脚、功能和配置,并填充到新映射中for (i = 0; i < num_pins; i++) {of_property_read_string_index(np, "groups", i, &pin);of_property_read_string_index(np, "functions", i, &function);of_property_read_u32_index(np, "configs", i, &config);configs = kmalloc(sizeof(*configs), GFP_KERNEL);// 填充映射结构体new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;new_map[i*2].data.mux.function = function;new_map[i*2].data.mux.group = pin;new_map[i*2 + 1].type = PIN_MAP_TYPE_CONFIGS_PIN;new_map[i*2 + 1].data.configs.group_or_pin = pin;new_map[i*2 + 1].data.configs.configs = configs;configs[0] = config; // 保存配置new_map[i*2 + 1].data.configs.num_configs = 1; // 配置数量为 1}*map = new_map; // 返回新的映射*num_maps = num_pins * 2; // 映射数量return 0;
}// 释放映射结构体
static void virtual_dt_free_map(struct pinctrl_dev *pctldev,struct pinctrl_map *map, unsigned num_maps) {while (num_maps--) {if (map->type == PIN_MAP_TYPE_CONFIGS_PIN)kfree(map->data.configs.configs); // 释放配置内存kfree(map); // 释放映射内存map++;}
}// pinctrl 操作结构体
static const struct pinctrl_ops virtual_pctrl_ops = {.get_groups_count = virtual_get_groups_count,.get_group_name = virtual_get_group_name,.get_group_pins = virtual_get_group_pins,.pin_dbg_show = virtual_pin_dbg_show,.dt_node_to_map = virtual_dt_node_to_map,.dt_free_map = virtual_dt_free_map,
};// 获取功能数量
static int virtual_pmx_get_funcs_count(struct pinctrl_dev *pctldev) {return ARRAY_SIZE(virtual_functions);
}// 获取功能名称
static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev,unsigned selector) {return virtual_functions[selector].function_name;
}// 获取功能组
static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,const char * const **groups,unsigned * const num_groups) {*groups = virtual_functions[selector].groups; // 返回功能组*num_groups = virtual_functions[selector].group_count; // 返回功能组数量return 0;
}// 设置引脚复用功能
static int virtual_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,unsigned group) {printk("set %s as %s\n", pctldev->desc->pins[group].name, virtual_functions[selector].function_name);return 0;
}// pinmux 操作结构体
static const struct pinmux_ops virtual_pmx_ops = {.get_functions_count = virtual_pmx_get_funcs_count,.get_function_name = virtual_pmx_get_func_name,.get_function_groups = virtual_pmx_get_groups,.set_mux = virtual_pmx_set,
};// 获取引脚配置
static int virtual_pinconf_get(struct pinctrl_dev *pctldev,unsigned pin_id, unsigned long *config) {*config = pin_configs[pin_id]; // 获取配置return 0;
}// 设置引脚配置
static int virtual_pinconf_set(struct pinctrl_dev *pctldev,unsigned pin_id, unsigned long *configs,unsigned num_configs) {if (num_configs != 1)return -EINVAL; // 错误处理pin_configs[pin_id] = *configs; // 设置配置printk("config %s as 0x%lx\n", pctldev->desc->pins[pin_id].name, *configs);return 0;
}// 显示引脚配置调试信息
static void virtual_pinconf_dbg_show(struct pinctrl_dev *pctldev,struct seq_file *s, unsigned pin_id) {seq_printf(s, "0x%lx", pin_configs[pin_id]);
}// 显示引脚组配置调试信息
static void virtual_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,struct seq_file *s, unsigned pin_id) {seq_printf(s, "0x%lx", pin_configs[pin_id]);
}// pinconf 操作结构体
static const struct pinconf_ops virtual_pinconf_ops = {.pin_config_get = virtual_pinconf_get,.pin_config_set = virtual_pinconf_set,.pin_config_dbg_show = virtual_pinconf_dbg_show,.pin_config_group_dbg_show = virtual_pinconf_group_dbg_show,
};// probe 函数,用于初始化 pinctrl
static int virtual_pinctrl_probe(struct platform_device *pdev) {struct pinctrl_desc *pinctrl_desc;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);// 分配 pinctrl_desc 结构体pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pinctrl_desc), GFP_KERNEL);// 初始化 pinctrl_desc 结构体pinctrl_desc->name = dev_name(&pdev->dev);pinctrl_desc->owner = THIS_MODULE;// 设置引脚描述和数量pinctrl_desc->pins = pin_descs;pinctrl_desc->npins = ARRAY_SIZE(pin_descs);pinctrl_desc->pctlops = &virtual_pctrl_ops; // 设置 pinctrl 操作pinctrl_desc->pmxops = &virtual_pmx_ops; // 设置 pinmux 操作pinctrl_desc->confops = &virtual_pinconf_ops; // 设置 pinconf 操作// 注册 pinctrl 设备global_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pinctrl_desc, NULL);return 0;
}// remove 函数,清理 pinctrl
static int virtual_pinctrl_remove(struct platform_device *pdev) {printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}// platform driver 结构体
static struct platform_driver virtual_pinctrl_driver = {.probe      = virtual_pinctrl_probe,.remove     = virtual_pinctrl_remove,.driver     = {.name   = "XXX,virtual_pinctrl",.of_match_table = of_match_ptr(virtual_pinctrl_of_match),}
};// 初始化函数
static int __init virtual_pinctrl_init(void) {printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);// 注册 platform driverreturn platform_driver_register(&virtual_pinctrl_driver);
}// 清理函数
static void __exit virtual_pinctrl_exit(void) {printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);// 注销 platform driverplatform_driver_unregister(&virtual_pinctrl_driver);
}// 模块入口和出口
module_init(virtual_pinctrl_init);
module_exit(virtual_pinctrl_exit);MODULE_LICENSE("GPL");

img

5.4 编写测试的client驱动程序

#include <linux/module.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/slab.h>
#include <linux/regmap.h>// 设备树匹配表,匹配虚拟 I2C 设备
static const struct of_device_id virtual_i2c_of_match[] = {{ .compatible = "XXX,virtual_i2c", },{ },
};// probe 函数,在设备被检测到时调用
static int virtual_i2c_probe(struct platform_device *pdev) {printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); // 输出当前文件和函数信息return 0; // 返回 0 表示成功
}// remove 函数,在设备被移除时调用
static int virtual_i2c_remove(struct platform_device *pdev) {printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); // 输出当前文件和函数信息return 0; // 返回 0 表示成功
}// 定义平台驱动结构体
static struct platform_driver virtual_i2c_driver = {.probe      = virtual_i2c_probe, // 设备探测函数.remove     = virtual_i2c_remove, // 设备移除函数.driver     = {.name   = "100ask_virtual_client", // 驱动名称.of_match_table = of_match_ptr(virtual_i2c_of_match), // 设备树匹配表}
};/* 1. 模块初始化函数 */
static int __init virtual_i2c_init(void) {    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); // 输出当前文件和函数信息// 注册平台驱动return platform_driver_register(&virtual_i2c_driver);
}/* 2. 模块清理函数 */
static void __exit virtual_i2c_exit(void) {printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); // 输出当前文件和函数信息// 注销平台驱动platform_driver_unregister(&virtual_i2c_driver);
}// 模块入口和出口
module_init(virtual_i2c_init);
module_exit(virtual_i2c_exit);// 模块许可证
MODULE_LICENSE("GPL");

6.调试信息

开发板的/sys/kernel/debug/pinctrl/目录下,每一个pin controller都有一个目录,比如virtual_pincontroller。
里面有很多文件,作用如下:

Pinctrl的虚拟文件作用
pins单个引脚信息
pingroups引脚的组信息
pinmux-pins单个引脚的复用信息
pinmux-functionsfunction下的group(支持该function的group)
pinconf-pins单个引脚的配置
pinconf-groups引脚组的配置
pinconf-config可以通过写它修改指定设备、指定状态下、指定(组)引脚的config值
  • 单个引脚信息
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pins
registered pins: 4
pin 0 (pin0) virtual_pincontroller
pin 1 (pin1) virtual_pincontroller
pin 2 (pin2) virtual_pincontroller
pin 3 (pin3) virtual_pincontroller
  • 引脚的组信息
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pingroups
registered pin groups:
group: pin0
pin 0 (pin0)group: pin1
pin 1 (pin1)group: pin2
pin 2 (pin2)group: pin3
pin 3 (pin3)
  • 单个引脚的复用信息
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (pin0): virtual_i2c (GPIO UNCLAIMED) function i2c group pin0
pin 1 (pin1): virtual_i2c (GPIO UNCLAIMED) function i2c group pin1
pin 2 (pin2): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 3 (pin3): (MUX UNCLAIMED) (GPIO UNCLAIMED)
  • function下的group(支持该function的group)
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinmux-functions
function: gpio, groups = [ pin0 pin1 pin2 pin3 ]
function: i2c, groups = [ pin0 pin1 ]
function: uart, groups = [ pin2 pin3 ]
  • 单个引脚的配置
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
pin 0 (pin0): 0x11223344
pin 1 (pin1): 0x55667788
pin 2 (pin2): 0x0
pin 3 (pin3): 0x0
  • 引脚组的配置
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-groups
Pin config settings per pin group
Format: group (name): configs
0 (pin0): 0x11223344
1 (pin1): 0x55667788
2 (pin2): 0x0
3 (pin3): 0x0
  • 修改配置值
    内核源码:
drivers\pinctrl\pinconf.cpinconf_dbg_config_write

如果pin controller驱动程序中的pinconf_ops提供了pin_config_dbg_parse_modify函数,
就可以通过pinconf-config文件修改某个pin或某个group的配置值。

// 格式: modify <config> <devicename> <state> <pin_name|group_name> <newvalue>
echo "modify config_pin virtual_i2c default pin0 0xaabb" > /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-configcat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-config

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

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

相关文章

【C++动态规划】2435. 矩阵中和能被 K 整除的路径|1951

本文涉及知识点 C动态规划 LeetCode2435. 矩阵中和能被 K 整除的路径 给你一个下标从 0 开始的 m x n 整数矩阵 grid 和一个整数 k 。你从起点 (0, 0) 出发&#xff0c;每一步只能往 下 或者往 右 &#xff0c;你想要到达终点 (m - 1, n - 1) 。 请你返回路径和能被 k 整除的…

【QT】Qt对话框

个人主页~ Qt窗口属性~ Qt窗口 五、对话框2、Qt内置对话框&#xff08;1&#xff09;Message Box&#xff08;2&#xff09;QColorDialog&#xff08;3&#xff09;QFileDialog&#xff08;4&#xff09;QFontDialog&#xff08;5&#xff09;QInputDialog 五、对话框 2、Qt内…

视频推荐的算法(字节青训)

题目&#xff1a; 西瓜视频 正在开发一个新功能&#xff0c;旨在将访问量达到80百分位数以上的视频展示在首页的推荐列表中。实现一个程序&#xff0c;计算给定数据中的80百分位数。 例如&#xff1a;假设有一个包含从1到100的整数数组&#xff0c;80百分位数的值为80&#…

线程基础知识、jmm(Java内存模型)

目录 线程基础知识 并发与并行 进程和线程 线程优先级 创建线程的方式主要有三种 休眠 作出让步 join() 方法 线程协作注意什么 理解线程状态 选择合适的协作工具 共享资源的访问控制 避免竞争条件 创建线程几种方式 线程状态&#xff0c;状态之间切换 新建&…

2.数组越界访问如何调试HardFault错误

数组越界 在项目开发过程中&#xff0c;配置串口外设是一个常见的任务&#xff0c;但在实际操作中&#xff0c;我们可能会遇到一些预料之外的问题。例如&#xff0c;在调试过程中&#xff0c;我们发现单片机只接受了一次数据后便不再接收&#xff0c;这无疑是一个棘手的问题。…

0-ARM Linux驱动开发-字符设备

一、字符设备概述 Linux 系统中&#xff0c;设备被分为字符设备、块设备和网络设备等。字符设备以字节流的方式进行数据传输&#xff0c;数据的访问是按顺序的&#xff0c;一个字节一个字节地进行读取和写入操作&#xff0c;没有缓冲区。例如&#xff0c;终端&#xff08;/dev…

openGauss数据库-头歌实验1-4 数据库及表的创建

一、创建数据库 &#xff08;一&#xff09;任务描述 本关任务&#xff1a;创建指定数据库。 &#xff08;二&#xff09;相关知识 数据库其实就是可以存放大量数据的仓库&#xff0c;学习数据库我们就从创建一个数据库开始吧。 为了完成本关任务&#xff0c;你需要掌握&a…

深入浅出 Spring Boot 与 Shiro:构建安全认证与权限管理框架

一、Shiro框架概念 &#xff08;一&#xff09;Shiro框架概念 1.概念&#xff1a; Shiro是apache旗下一个开源安全框架&#xff0c;它对软件系统中的安全认证相关功能进行了封装&#xff0c;实现了用户身份认证&#xff0c;权限授权、加密、会话管理等功能&#xff0c;组成一…

重学SpringBoot3-整合 Elasticsearch 8.x (一)客户端方式

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 这里写目录标题 1. 为什么选择 Elasticsearch&#xff1f;2. Spring Boot 3 和 Elasticsearch 8.x 的集成概述2.1 准备工作2.2 添加依赖 3. Elasticsearch 客户端配置方式…

MyBaitsPlus 基本用法简单整理

MyBaitsPlus 基本用法整理 查询单表查询查询单条数据写法一&#xff1a;&#xff08;this.getOne&#xff09;写法二&#xff1a;&#xff08;XxxMapper.selectById&#xff09;写法三&#xff1a;&#xff08;this.getById&#xff09; 查询 list 集合&#xff08;this.list&a…

基于MATLAB的战术手势识别

手势识别的研究起步于20世纪末&#xff0c;由于计算机技术的发展&#xff0c;特别是近年来虚拟现实技术的发展&#xff0c;手势识别的研究也到达一个新的高度。熵分析法是韩国的李金石、李振恩等人通过从背景复杂的视频数据中分割出人的手势形状&#xff0c;然后计算手型的质心…

Mac 配置SourceTree集成云效

1、背景 工作使用的是自己的笔记本&#xff0c;一个是比较卡&#xff0c;在一个是敏感信息比较多还是使用公司的电脑&#xff0c;但是系统是Mac就很麻烦&#xff0c;在网上找了帖子记录一下 2、配置 打开终端 ssh-keygen -t rsa #一直回车就行 cd .ssh cat id_rsa.pub #查…

【快速上手】pyspark 集群环境下的搭建(Yarn模式)

目录 前言&#xff1a; 一、安装步骤 安装前准备 1.第一步&#xff1a;安装python 2.第二步&#xff1a;在bigdata01上安装spark 3.第三步&#xff1a;同步bigdata01中的spark到bigdata02和03上 二、启动 三、可打开yarn界面查看任务 前言&#xff1a; 上一篇介绍的是…

使用Python多线程抓取某图网数据并下载图片

前言 在互联网开发领域&#xff0c;数据抓取是一项非常实用的技术。通过数据抓取&#xff0c;我们可以从网页上获取所需的信息&#xff0c;并将其转化为结构化数据&#xff0c;以便进一步分析或使用。本文将介绍如何利用Python编写一个多线程程序来抓取网页上的图片数据&#…

《IMM交互式多模型滤波MATLAB实践》专栏目录,持续更新……

专栏链接&#xff1a;https://blog.csdn.net/callmeup/category_12816762.html 专栏介绍 关于IMM的例程 双模型EKF&#xff1a; 【逐行注释】基于CV/CT模型的IMM|MATLAB程序|源代码复制后即可运行&#xff0c;无需下载三模型EKF&#xff1a; 【matlab代码】3个模型的IMM例程&…

鸿蒙开发案例:指南针

【1】引言&#xff08;完整代码在最后面&#xff09; 在本文中&#xff0c;我们将介绍如何使用鸿蒙系统&#xff08;HarmonyOS&#xff09;开发一个简单的指南针应用。通过这个案例&#xff0c;你可以学习如何使用传感器服务、状态管理以及UI构建等基本技能。 【2】环境准备 …

人工智能的发展与未来:从Yann LeCun的观点谈起

引言 在当今的人工智能&#xff08;AI&#xff09;领域&#xff0c;AGI&#xff08;通用人工智能&#xff09;已成为热门话题。许多专家认为&#xff0c;随着技术的不断发展&#xff0c;AGI的实现只是时间问题。然而&#xff0c;Yann LeCun——图灵奖得主、Meta首席AI科学家&a…

【The Art of Unit Testing 3_自学笔记06】3.4 + 3.5 单元测试核心技能之:函数式注入与模块化注入的解决方案简介

文章目录 3.4 函数式依赖注入技术 Functional injection techniques3.5 模块化依赖注入技术 Modular injection techniques 写在前面 上一篇的最后部分对第三章后续内容做了一个概括性的梳理&#xff0c;并给出了断开依赖项的最简单的实现方案&#xff0c;函数参数值注入法。本…

电磁兼容(EMC):整改案例(六)Y电容过大导致雷击浪涌炸机

目录 1. 异常现象 2. 原因分析 3. 整改方案 4. 总结 1. 异常现象 某金属外壳带接地线的产品按GB/T 17626.5进行雷击浪涌测试&#xff0c;在L&#xff0c;N线对PE进行4kV浪涌电压测试时&#xff0c;出现炸机现象&#xff0c;AC-DC电源芯片损坏。而在L&#xff0c;N线间进行2…

代码之眼,陈欣的xml解密之路

第一章 在未来的世界里&#xff0c;科技已经发展到了令人难以想象的地步。人工智能、量子计算和生物技术交织在一起&#xff0c;创造了一个全新的社会形态。在这个世界中&#xff0c;有一个名为“代码守护者”的组织&#xff0c;专门负责维护全球信息系统的安全和稳定。 陈欣是…