六、(正点原子)pinctrl子系统和gpio子系统

news2024/11/26 8:45:04

        前面我们使用设备树来驱动LED灯,其实就是将LED寄存器地址写入到设备树的属性reg中,通过OF函数 ,读取到LED灯的寄存器信息,从而操作寄存器来控制LED灯。在操作LED灯时候,我们使用到GPIO这个引脚,通过对这个GPIO的控制来实现LED灯的控制。

一、pinctrl子系统

        pinctrl子系统,用来设置pin(引脚)的复用功能和配置这个pin的电气属性。主要体现:

  • 获取设备树中pin信息。
  • 根据获取到的pin信息来设备pin的复用功能。
  • 根据获取到的pin信息来设备pin的电气属性,比如上/下拉、速度、驱动能力等。

        对于我们来说,只需要在设备树里面设置好某个pin的相关属性,其他的初始化工作pinctrl子系统来完成,pinctrl子系统源码目录为drivers/pinctrl

        pin配置信息详解

        要使用 pinctrl 子系统,我们需要在设备树里面设置 PIN 的配置信息,毕竟 pinctrl 子系统要
根据你提供的信息来配置 PIN 功能,一般会在设备树里面创建一个节点来描述 PIN 的配置信
息。在imx6ull.dtsi中有者三个节点表示imx6ullpin引脚

iomuxc: iomuxc@020e0000 {
	compatible = "fsl,imx6ul-iomuxc";
	reg = <0x020e0000 0x4000>;
};


gpr: iomuxc-gpr@020e4000 {
	compatible = "fsl,imx6ul-iomuxc-gpr",
	"fsl,imx6q-iomuxc-gpr", "syscon";
	reg = <0x020e4000 0x4000>;
};

iomuxc_snvs: iomuxc-snvs@02290000 {
	compatible = "fsl,imx6ull-iomuxc-snvs";
	reg = <0x02290000 0x10000>;
};

 imx6ull的参考手册

        在imx6ull-alientek-emmc.dts中,对pin进行追加信息比如:

 不同的外设使用的 PIN 不同、其配置也不同,因此一个萝卜一个坑,将某个外设所使用的所有 PIN 都组织在一个子节点里面。其中,在iomux节点下有个imx6ul-evk的子结点,这个节点就是用来描述evk这个开发板外设所使用到的pin。简单来说就是在这个节点来表示出来evk开发板的各个功能使用到哪些引脚和引脚是哪一个。比如:hoggrp-1这个evk节点下面的子结点,表示热插拔功能所需要的引脚。

        关于MX6UL_PAD_UART1_RTS_B__GPIO1_IO19       0x17059 含义:

这是一个宏定义,定义在文件arch/arm/boot/dts/imx6ul-pinfunc.h 中, imx6ull.dtsi 会引用 imx6ull-pinfunc.h 这个头文件,而imx6ull-pinfunc.h 又会引用 imx6ul-pinfunc.h 这个头文件,

        在imx6ull参考手册中我们可以看出来MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 这个宏就是表示UART1_RTS_B这个引脚复用为GPIO1_IO19功能 。

<mux_reg   conf_reg    input_reg      mux_mode    input_val>
 0x0090    0x031C      0x0000         0x5         0x0

         0x0090mux_reg寄存器偏移地址,复用功能寄存器地址。

        0x031C conf_reg 寄存器偏移地址,表示IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B寄存器的地址。

        0x0000 input_reg 寄存器偏移地址,有些外设有 input_reg 寄存器,有 input_reg 寄存器的
外设需要配置 input_reg 寄存器。没有的话就不需要设置, UART1_RTS_B 这个 PIN 用做
GPIO1_IO19 的时候是没有 input_reg 寄存器,因此这里 intput_reg 是无效的。

        0x5mux_reg 寄 存 器 值,相 当 于 设 置IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B 寄存器为 0x5,也即是设置 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19

        0x0input_reg 寄存器值,应为这个引脚没有input_reg寄存器,所以无法向里面写入值。

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19       0x17059中的0x17059其实就是conf_reg寄存器的值,这个值由我们子设备,相当于配置这个引脚的电气属性。

         设备树中添加pinctrl节点模板

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

二、gpio子系统

         前面pinctrl子系统主要是设置引脚的复用功能和电气属性,如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么接下来就要用到 gpio 子系统了。 gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。 gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO, Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO

        设备树中的gpio信息

        比如我们在imx6ull-alientek-emmc.dts,有usdhc1这样一个节点:

         pinctrl-name:用来表示设备的状态,这里有2个,分别为默认状态(default)和休眠状态(sleep)还可以自定义状态,比如state_100mhz:表示这个gpio运行为100mhz状态。

        pinctrl-0:表示第0个状态对应于"default"状态,对应的引脚在pinctrl-0里面定义。

        pinctrl-1:表示第1个状态对应于"state_100mhz"状态,对应的引脚在pinctrl-1里面定义。

        cd-gpios:描述usdhc1使用到的IO引脚“&gpio1”表示 引脚所使用的 IO 属于 GPIO1 组,“19”表示 GPIO1 组的第 19 号 IO,“GPIO_ACTIVE_LOW”表示低电平有效,如果改为“GPIO_ACTIVE_HIGH”就表示高电平有效。
 

        gpio子系统API函数 

        1、gpio_request函数

        用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request进行申请,定义在linux/gpio.h中函数原型如下:

static inline int gpio_request(unsigned gpio, const char *label)

        gpio:要申请的gpio标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信
息,此函数会返回这个 GPIO 的标号。

        label:gpio 设置个名字。

        返回值:0,申请成功;其他值,申请失败。

        2、gpio_free函数

        如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。函数原型如下:

static inline void gpio_free(unsigned gpio)

        gpio: 要释放的 gpio 标号。

        3、gpio_direction_input、gpio_direction_output函数 

        设置某个 GPIO 为输入输出,函数原型如下所示:

static inline int gpio_direction_input(unsigned gpio)

static inline int gpio_direction_output(unsigned gpio, int value)

        gpio: 要设置的 GPIO 标号。

        value:设置为输出时,GPIO 默认输出值。

        返回值:0,设置成功;负值,设置失败。

        4、gpio_get_value、gpio_set_value函数

        用于获取和设置某个 GPIO 的值(0 或 1),定义所示:

static inline int gpio_get_value(unsigned gpio)

static inline void gpio_set_value(unsigned gpio, int value)

       gpio: 要设置的 GPIO 标号。

       value: 要设置的值。

       返回值:负值,获取失败,否者得到的GPIO值。

        与gpio相关的OF函数

        1、of_gpio_named_count函数

        获取设备树某个属性里面定义了几个 GPIO 信息,要注意的是空的 GPIO 信息也会被统计到,比如:

gpios = <0
         &gpio1 1 2
         0
         &gpio2 3 4>;

        上述代码的“gpios”节点一共定义了 4 个 GPIO,但是有 2 个是空的,没有实际的含义。
通过 of_gpio_named_count 函数统计出来的 GPIO 数量就是 4 个,定义在linux/of_gpio.h里面,此函数原型如下:

static inline int of_gpio_named_count(struct device_node *np, const char* propname)

        np:设备节点。

        propname:要统计的GPIO属性名字。

        返回值:正值,统计到的 GPIO 数量;负值,失败。

        2、of_gpio_count函数

        和 of_gpio_named_count 函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属
性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的 GPIO 信息,函数原型如下所示:

static inline int of_gpio_count(struct device_node *np)

        np:设备节点。

        返回值:正值,统计到的 GPIO 数量;负值,失败。 

        3、of_get_named_gpio函数

        获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,
此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO
号,此函数在驱动中使用很频繁!函数原型如下:

static inline int of_get_named_gpio(struct device_node *np,
                                    const char *propname, 
                                    int index)

         np:设备节点。

        propname:要统计的GPIO属性名字。

        index:GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO
的编号,如果只有一个 GPIO 信息的话此参数为 0。

        返回值:正值,获取到的 GPIO 编号;负值,失败。
 

三、使用pinctrl子系统和gpio子系统

        我们以正点原子阿尔法开发板为例,在设备树中添加LED灯的信息,通过pinctrl子系统和gpio子系统来配置LED的引脚,从而控制LED灯的开关。

        1、修改设备树文件

        ①、添加pinctrl子结点

        在原理图中,LED灯连接到了GPIO1_IO03这个引脚,所以我们的将这个引脚复用为GPIO1_IO03功能,并配置引脚的电气属性。

        打开imx6ull-alientekemmc.dts,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_led”的子节点:

        ②、添加 LED 设备节点

        在根节点“/”下创建 LED 灯节点,节点名为“gpioled”

        ③、检查引脚是否被使用 

        我们所使用的设备树基本都是在半导体厂商提供的设备树文件基础上修改而来的,而半导体厂商提供的设备树是根据自己官方开发板编写的,很多 PIN 的配置和我们所使用的开发板不一样。检查 PIN 有没有被其他外设使用包括两个方面:

  • 检查 pinctrl 设置。
  • 如果这个 PIN 配置为 GPIO 的话,检查这个 GPIO 有没有被别的外设使用。

        编译设备树,使用新编译的设备树文件,查看/proc/device-tree/是否存在我们添加的节点:

        2、LED灯驱动程序编写 

        参考:五、(正点原子)设备树下的LED驱动-CSDN博客

         当我们驱动框架搭建好以后,需要使用OF函数,获取设备树中LED节点的属性信息。

        在LED控制函数中,我们就可以直接使用gpio子系统的API函数来对GPIO进行读写操作:

 最终的gpioled.c驱动文件:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ide.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/err.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>

#define GPIOLED_CNT      1
#define GPIOLED_NAME     "gpioled"

#define LED_ON      1
#define LED_OFF     0



/* gpioled设备结构体 */
typedef struct gpioled_dev{
    dev_t devid;                    /* 设备号   */
    int major;                      /* 主设备号 */
    int minor;                      /* 次设备号 */
    struct cdev cdev;               /* 字符设备 */
    struct class *class;            /* 类      */
    struct device *device;          /* 类的设备 */
    struct device_node *nd;         /* 节点 */
    int led_gpio;                   /* LED的gpio编号 */
}gpioled_dev;
gpioled_dev gpioled;



/* LED等控制函数 */
static int Led_Switch(unsigned char Led_Status)
{

    if(Led_Status == LED_ON){
        gpio_set_value(gpioled.led_gpio,0);
    }else if(Led_Status == LED_OFF){
        gpio_set_value(gpioled.led_gpio,1);
    }else{
        return -1;
    }
    return 0;
}

/* 打开驱动文件 */
static int gpioled_open (struct inode *Inode, struct file *File)
{
    File->private_data = &gpioled;   /* 设置私有数据 */
    return 0;
}
/* 关闭驱动文件 */
static int gpioled_release (struct inode *Inode, struct file *File)
{

    return 0;
}
/* 对驱动文件进行写操作 */
static ssize_t gpioled_write (struct file *File, const char __user *buf,
                            size_t Count, loff_t *Loff)
{
    int ret = 0;
    unsigned char databuf[1];

    ret = copy_from_user(databuf,buf,Count);
    if(ret < 0){
        return ret;
    }

    Led_Switch(databuf[0]);
    return 0;
}

/* 字符设备的文件操作集合 */
static const struct file_operations gpioled_opts = {
    .owner = THIS_MODULE,
    .open = gpioled_open,
    .release = gpioled_release,
    .write = gpioled_write,
};

/* 模块入口 */
static int __init gpioled_init(void)
{
    int ret = 0;

    /* 获取设备节点:gpioled */
    gpioled.nd = of_find_node_by_path("/gpioled");
    if(gpioled.nd == NULL){
        ret = -EINVAL;
        goto fail_nd;
    }
    /* 获取设备树中的gpio属性,得到LED所使用的GPIO编号 */
    gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-gpio", 0);
    if(gpioled.led_gpio < 0){
        ret = -EINVAL;
        goto fail_nd;
    }
    printk("led-gpio num = %d\r\n",gpioled.led_gpio);

    /* 申请GPIO */
    ret = gpio_request(gpioled.led_gpio, "led-gpio");
    if(ret){
        goto fail_nd;
    }

    /* 将LED的GPIO设置为输出,默认关闭LED灯(低电平点亮) */
    ret = gpio_direction_output(gpioled.led_gpio, 0);
    if(ret < 0){
        goto fail_gpio;
    }

    /* 注册字符设备 */
    /* 1、注册设备号 */
    gpioled.major = 0;
    if(gpioled.major){               /* 指定了设备号 */
        gpioled.devid = MKDEV(gpioled.major,0);
        ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
    }else{                          /* 没有指定设备号 */
        ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
        
    }
    if(ret < 0){                    /* 注册设备号失败 */
        goto fail_devid;
    }
    printk("major=%d,minor=%d\r\n",gpioled.major,gpioled.minor);

    /* 2、注册字符设备 */
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_opts);               /* 初始化 */
    ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
    if(ret < 0){
        goto fail_cdev;
    }

    /* 自动添加节点 */
    /* 1、添加类 */
    gpioled.class = class_create(THIS_MODULE,GPIOLED_NAME);
    if(IS_ERR(gpioled.class)){
        ret = PTR_ERR(gpioled.class);
        goto fail_class;
    }
    /* 2、添加类的设备 */
    gpioled.device = device_create(gpioled.class, NULL,gpioled.devid, NULL, GPIOLED_NAME);
    if(IS_ERR(gpioled.device)){
        ret = PTR_ERR(gpioled.device);
        goto fail_device;
    }


    return 0;

fail_device:
    class_destroy(gpioled.class);
fail_class:
    cdev_del(&gpioled.cdev);
fail_cdev:
    unregister_chrdev_region(gpioled.devid,GPIOLED_CNT);
fail_devid:
fail_gpio:
    gpio_free(gpioled.led_gpio);
fail_nd:
    return ret;
}

/* 模块出口 */
static void __exit gpioled_exit(void)
{
    /* 关灯 */
    Led_Switch(LED_OFF);
    
    /* gpio释放 */
    gpio_free(gpioled.led_gpio);

    /* 删除设备 */
    cdev_del(&gpioled.cdev);
    /* 注销设备号 */
    unregister_chrdev_region(gpioled.devid,GPIOLED_CNT);
    printk("gpioled_exit\r\n");

    /* 销毁类的设备 */
    device_destroy(gpioled.class,gpioled.devid);
    /* 销毁类 */
    class_destroy(gpioled.class);

}

/* 注册模块入口和出口函数 */
module_init(gpioled_init);
module_exit(gpioled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZhangXueGuo");

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

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

相关文章

Linux常用命令(17)—pastesortcomm命令(有相关截图)

写在前面&#xff1a; 最近在学习Linux命令&#xff0c;记录一下学习Linux常用命令的过程&#xff0c;方便以后复习。仅供参考&#xff0c;若有不当的地方&#xff0c;恳请指正。如果对你有帮助&#xff0c;欢迎点赞&#xff0c;关注&#xff0c;收藏&#xff0c;评论&#xf…

【Leetcode】2663. 字典序最小的美丽字符串

题目 题目链接&#x1f517;如果一个字符串满足以下条件&#xff0c;则称其为 美丽字符串 &#xff1a; 它由英语小写字母表的前 k 个字母组成。它不包含任何长度为 2 或更长的回文子字符串。 给你一个长度为 n 的美丽字符串 s 和一个正整数 k 。请你找出并返回一个长度为 n…

基于java+springboot+vue实现的智慧生活商城系统(文末源码+Lw)244

摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#xff0c;让整个世界都可以即时通…

自制HTML5游戏《开心消消乐》

1. 引言 游戏介绍 《开心消消乐》是一款基于HTML5技术开发的网页游戏&#xff0c;以其简单的操作方式、轻松的游戏体验和高度的互动性&#xff0c;迅速在社交平台上获得了广泛的关注和传播。玩家通过消除相同类型的元素来获得分数&#xff0c;游戏设计巧妙&#xff0c;易于上手…

UE4中性能优化和检测工具

UE4中性能优化和检测工具合集 简述CPUUnreal InsightUnreal ProfilerSimpleperfAndroid StudioPerfettoXCode TimeprofilerBest Practice GPUAdreno GPUMali GPUAndroid GPU Inspector (AGI) 内存堆内存分析Android StudioLoliProfilerUE5 Memory InsightsUnity Mono 内存Memre…

5.什么是C语言

什么是 C 语言? C语言是一种用于和计算机交流的高级语言, 它既具有高级语言的特点&#xff0c;又具有汇编语言的特点 非常接近自然语言程序的执行效率非常高 C语言是所有编程语言中的经典&#xff0c;很多高级语言都是从C语言中衍生出来的&#xff0c; 例如:C、C#、Object-C、…

驾考小技巧:老北京布鞋!距离高考出分还剩3天,我却看到有些孩子已经拿了“满分”——早读(逆天打工人爬取热门微信文章解读)

我20年驾校4000多块钱&#xff0c;你呢&#xff1f; 引言Python 代码第一篇 洞见 距离高考出分还剩3天&#xff0c;我却看到有些孩子已经拿了“满分”第二篇 视频新闻结尾 引言 昨天的文章顺利发出 看来“梅西” 这两个字在我们这边 不是敏感词 只是很多个罗粉搞得有点过头了 …

Spring框架的最新进展:2023年Spring Boot和Spring Cloud功能更新

引用 Spring框架的最新动态&#xff1a;2023年Spring Boot与Spring Cloud功能升级 Spring框架作为Java开发领域的重要技术之一&#xff0c;一直致力于简化开发流程和提高开发效率。2023年&#xff0c;Spring Boot和Spring Cloud两大核心组件再次迎来功能升级&#xff0c;为开…

以10位明星为你献上的祝福视频为标题的科普介绍文章-华媒舍

祝福视频的意义和影响 祝福视频是指明星通过录制的视频&#xff0c;向观众表达美好的祝愿和关怀。这种视频以真挚的情感和鼓舞人心的话语&#xff0c;给人们带来了不同寻常的温暖和感动。由于明星的影响力和号召力&#xff0c;他们的祝福视频可以更广泛地传播&#xff0c;让更…

RedTail 僵尸网络积极利用新漏洞发起攻击

自从 Palo Alto 的 PAN-OS 漏洞公开披露以来&#xff0c;研究人员发现已有攻击者将该漏洞纳入武器库中。 CVE-2024-3400 2024 年 4 月 11 日&#xff0c;Palo Alto 发布公告称基于 PAN-OS 的产品中存在的 0day 漏洞已经被攻击者利用&#xff0c;安全公司 Volexity 已经发现了…

【React】极客园--04.发布文章模块

实现基础文章发布 创建基础结构 import {Card,Breadcrumb,Form,Button,Radio,Input,Upload,Space,Select } from antd import { PlusOutlined } from ant-design/icons import { Link } from react-router-dom import ./index.scssconst { Option } Selectconst Publish () …

LeetCode 1-两数之和

LeetCode第1题 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现…

重磅!2024年最新影响因子(生态学/林学/土壤学/遥感/微生物/环境科学/植物科学) 收藏版!

2024年最新影响因子正式揭晓&#xff01;2024年6月20日&#xff0c;Clarivate Analytics&#xff08;科睿唯安&#xff09;发布了各大SCI期刊的2023年影响因子。从最新结果看&#xff0c;今年的影响因子继续“普跌”&#xff0c;其中顶刊Nature和Science均有下降&#xff0c;分…

萨科微slkor宋仕强论道华强北假货之六

萨科微slkor宋仕强论道华强北假货之六&#xff0c;华强北的假货这么多&#xff0c;搞得客户害怕、同行焦虑&#xff0c;话说“在华强北没有被坑过的&#xff0c;就不是华强北人”。我们金航标Kinghelm&#xff08;www.kinghelm.com.cn&#xff09;公司以前有一个贸易部&#xf…

R语言数据分析案例33-基于logistic回归下的信用卡违约情况分析

一、选题背景 随着互联网产业的蓬勃发展&#xff0c;传统金融行业开始向着金融互联网化和互联网金融快速转型。网络信贷、信用卡等凭借门槛低、快速便捷、高收益等特点&#xff0c;借助互联网平台存在的优势&#xff0c;迅速成长。然而高收益的背后也存在着高风险&#xff0c;…

Docker配置阿里云加速器(2续)

默认情况下镜像是从docker hub下载&#xff0c;由于docker hub服务器在国外&#xff0c;由于网络原因镜像下载速度较慢&#xff0c;一般会配置镜像加速进行下载 国内镜像加速器有阿里云、网易云、中科大等&#xff0c;本章配置阿里云镜像加速器&#xff0c;速度较快 镜像加速源…

JavaWeb——SQL简介

1. SQL的介绍 SQL是一门结构化查询语言&#xff0c;就是一门用来操作关系型数据库的数据库语言&#xff1b; 使用SQL语句&#xff0c;可以操作所有的关系数据库&#xff1b; 但是&#xff0c;不同的关系型数据库的SQL操作略有不同&#xff0c;称为“方言”&#xff1b; 2. S…

408数据结构-图的遍历 自学知识点整理

前置知识&#xff1a;图的存储与基本操作 图的遍历是指从图的某一顶点出发&#xff0c;按照某种搜索方法沿着图中的边对图中的所有顶点访问一次&#xff0c;且仅访问一次。因为树是一种特殊的图&#xff0c;所以树的遍历实际上也可以视为一种特殊的图的遍历。图的遍历算法是求解…

excel数据透视

Excel中&#xff0c;数据透视图&#xff08;PivotChart&#xff09;和数据透视表&#xff08;PivotTable&#xff09;是两个紧密相关的工具&#xff0c;用于分析数据。数据透视表是数据透视图的数据源&#xff0c;也就是说&#xff0c;数据透视图是基于数据透视表中的数据创建的…

matplotlib 创建多个子图

有些时候我们需要用for循环来创建多个子图&#xff0c;来对比特征。现在已画出8组随机数来作为示例。 from matplotlib import pyplot as plt import numpy as np #设置画布大小 figplt.figure(figsize(20,8)) #解决中文乱码问题 plt.rcParams[font.sans-serif] [SimHei] fo…