Linux GPIO 开发指南

news2024/10/7 4:27:44

文章目录

  • Linux GPIO 开发指南
    • 1 概述
      • 1.1 编写目的
      • 1.2 适用范围
      • 1.3 相关人员
    • 2 模块介绍
      • 2.1 模块功能介绍
      • 2.2 相关术语介绍
      • 2.3 总体框架
      • 2.4 state/pinmux/pinconfig
      • 2.5 源码结构介绍
    • 3 模块配置
      • 3.1 kernel menuconfig 配置
      • 3.2 device tree 源码结构和路径
        • 3.2.1 device tree 对 gpio 控制器的通用配置
        • 3.2.2 board.dts 板级配置
    • 4 模块接口说明
      • 4.1 pinctrl 接口说明
        • 4.1.1 pin4ctrl_get
        • 4.1.2 pinctrl_put
        • 4.1.3 devm_pinctrl_get
        • 4.1.4 devm_pinctrl_put
        • 4.1.5 pinctrl_lookup_state
        • 4.1.6 pinctrl_select_state
        • 4.1.7 devm_pinctrl_get_select
        • 4.1.8 devm_pinctrl_get_select_default
        • 4.1.9 pin_config_get
        • 4.1.10 pin_config_set
      • 4.2 gpio 接口说明
        • 4.2.1 gpio_request
        • 4.2.2 gpio_free
        • 4.2.3 gpio_direction_input
        • 4.2.5 __gpio_get_value
        • 4.2.6 __gpio_set_value
        • 4.2.7 of_get_named_gpio
        • 4.2.8 of_get_named_gpio_flags
    • 5 使用示例
      • 5.1 使用 pin 的驱动 dts 配置示例
        • 5.1.1 配置通用 GPIO 功能/中断功能
        • 5.1.2 用法二
      • 5.2 接口使用示例
        • 5.2.1 配置设备引脚
        • 5.2.2 获取 GPIO 号
        • 5.2.3 GPIO 属性配置
      • 5.3 设备驱动使用 GPIO 中断功能
      • 5.4 设备驱动设置中断 debounce 功能
    • 6 FAQ
      • 6.1 常用 debug 方法
        • 6.1.1 利用 sunxi_dump 读写相应寄存器
        • 6.1.2 利用 sunxi_pinctrl 的 debug 节点
        • 6.1.3 利用 pinctrl core 的 debug 节点
        • 6.1.4 GPIO 中断问题排查步骤
          • 6.1.4.1 GPIO 中断一直响应
          • 6.1.4.2 GPIO 检测不到中断

Linux GPIO 开发指南

1 概述

1.1 编写目的

本文档对内核的 GPIO 接口使用进行详细的阐述,让用户明确掌握 GPIO 配置、申请等操作的编程方法。

1.2 适用范围

​ 表 1-1: 适用产品列表

内核版本驱动文件
Linux-4.9 及以上pinctrl-sunxi.c

1.3 相关人员

本文档适用于所有需要在 Linux 内核 sunxi 平台上开发设备驱动的相关人员。

2 模块介绍

Pinctrl 框架是 linux 系统为统一各 SoC 厂商 pin 管理,避免各 SoC 厂商各自实现相同 pin 管理子系统而提出的。目的是为了减少 SoC 厂商系统移植工作量。

2.1 模块功能介绍

许多 SoC 内部都包含 pin 控制器,通过 pin 控制器,我们可以配置一个或一组引脚的功能和特性。在软件上,Linux 内核 pinctrl 驱动可以操作 pin 控制器为我们完成如下工作:

枚举并且命名 pin 控制器可控制的所有引脚;

提供引脚的复用能力

提供配置引脚的能力,如驱动能力、上拉下拉、数据属性等。

与 gpio 子系统的交互

实现 pin 中断

2.2 相关术语介绍

​ 表 2-1: Pinctrl 模块相关术语介绍

术语解释说明
SUNXIAllwinner 一系列 SOC 硬件平台
Pin controller是对硬件模块的软件抽象,通常用来表示硬件控制器。能够处理引脚复用、属性配置等功能
Pin根据芯片不同的封装方式,可以表现为球形、针型等。软件上采用常用一组无符号的整数 [0-maxpin] 来表示
Pin groups外围设备通常都不只一个引脚,比如 SPI,假设接在 SoC 的 {0,8,16,24} 管脚,而另一个设备 I2C 接在 SoC 的 {24,25} 管脚。我们可以说这里有两个pin groups。很多控制器都需要处理 pin groups。因此管脚控制器子系统需要一个机制用来枚举管脚组且检索一个特定组中实际枚举的管脚
Pinconfig管脚可以被软件配置成多种方式,多数与它们作为输入/输出时的电气特性相关。例如,可以设置一个输出管脚处于高阻状态,或是 “三态”(意味着它被有效地断开连接)。或者可以通过设置将一个输入管脚与 VDD 或 GND 相连 (上拉/下拉),以便在没有信号驱动管脚时使管脚拥有确认值
Pinmux引脚复用功能,使用一个特定的物理管脚(ball/pad/finger/等等)进行多种扩展复用,以支持不同功能的电气封装习惯
Device tree犹如它的名字,是一棵包括 cpu 的数量和类别、内存基地址、总线与桥、外设连接,中断控制器和 gpio 以及 clock 等系统资源的树,Pinctrl 驱动支持从device tree 中定义的设备节点获取 pin 的配置信息

2.3 总体框架

Sunxi Pinctrl 驱动模块的框架如下图所示,整个驱动模块可以分成 4 个部分:pinctrl api、pinctrl common frame、sunxi pinctrl driver,以及 board configuration。(图中最上面一层 device driver 表示 Pinctrl 驱动的使用者)

​ 图 2-1: pinctrl 驱动整体框架图

Pinctrl api: pinctrl 提供给上层用户调用的接口。

Pinctrl framework:Linux 提供的 pinctrl 驱动框架。

Pinctrl sunxi driver:sunxi 平台需要实现的驱动。

Board configuration:设备 pin 配置信息,一般采用设备树进行配置。

2.4 state/pinmux/pinconfig

Pinctrl framework 主要处理 pinstate、pinmux 和 pinconfig 三个功能,pinstate 和 pinmux、pinconfig 映射关系如下图所示。

​ 图 2-2: pinctrl 驱动 framework 图

系统运行在不同的状态,pin 配置有可能不一样,比如系统正常运行时,设备的 pin 需要一组配置,但系统进入休眠时,为了节省功耗,设备 pin 需要另一组配置。Pinctrl framwork 能够有效管理设备在不同状态下的引脚配置。

2.5 源码结构介绍

linux
|
|-- drivers
| |-- pinctrl
| | |-- Kconfig
| | |-- Makefile
| | |-- core.c
| | |-- core.h
| | |-- devicetree.c
| | |-- devicetree.h
| | |-- pinconf.c
| | |-- pinconf.h
| | |-- pinmux.c
| | `-- pinmux.h
| `-- sunxi
| |-- pinctrl-sunxi-test.c
| |-- pinctrl-sun*.c
| `-- pinctrl-sun*-r.c
`-- include
`-- linux
`-- pinctrl
|-- consumer.h
|-- devinfo.h
|-- machine.h
|-- pinconf-generic.h
|-- pinconf.h
|-- pinctrl-state.h
|-- pinctrl.h
`-- pinmux.h

3 模块配置

3.1 kernel menuconfig 配置

进入 longan 根目录,执行./build.sh menuconfig

进入配置主界面,并按以下步骤操作:

首先,选择 Device Drivers 选项进入下一级配置,如下图所示:

​ 图 3-1: 内核 menuconfig 根菜单

选择 Pin controllers, 进入下级配置,如下图所示:

​ 图 3-2: 内核 menuconfig device drivers 菜单

选择 Allwinner SoC PINCTRL DRIVER, 进入下级配置,如下图所示:

​ 图 3-3: 内核 menuconfig pinctrl drivers 菜单

Sunxi pinctrl driver 默认编译进内核,如下图(以 sun50iw9p1 平台为例,其他平台类似)所示:

​ 图 3-4: 内核 menuconfig allwinner pinctrl drivers 菜单

3.2 device tree 源码结构和路径

对于 Linux4.9:

设备树文件的配置是该 SoC 所有方案的通用配置,对于 ARM64 CPU 而言,设备树的路径为:kernel/{KERNEL}/arch/arm64/boot/dts/sunxi/sun*-pinctrl.dtsi。

设备树文件的配置是该 SoC 所有方案的通用配置,对于 ARM32 CPU 而言,设备树的路径为:kernel/{KERNEL}/arch/arm32/boot/dts/sun*-pinctrl.dtsi。

板级设备树 (board.dts) 路径:/device/config/chips/{IC}/configs/{BOARD}/board.dts

device tree 的源码结构关系如下:

board.dts
|--------sun*.dtsi
		     |------sun*-pinctrl.dtsi
		     |------sun*-clk.dtsi

对于 Linux5.4:

设备树文件的配置是该 SoC 所有方案的通用配置,对于 ARM64 CPU 而言,5.4 内核中不再维护单独的 pinctrl 的 dtsi,直接将 pin 的信息放在了:kernel/{KERNEL}/arch/arm32/boot/dts/sun*.dtsi

设备树文件的配置是该 SoC 所有方案的通用配置,对于 ARM32 CPU 而言,5.4 内核中不再维护单独的 pinctrl 的 dtsi,直接将 pin 的信息放在了:kernel/{KERNEL}/arch/arm32/boot/dts/sun*.dtsi

板级设备树 (board.dts) 路径:/device/config/chips/{IC}/configs/{BOARD}/board.dts

device tree 的源码包含关系如下:

board.dts
    |--------sun*.dtsi

3.2.1 device tree 对 gpio 控制器的通用配置

在 kernel/{KERNEL}/arch/arm64/boot/dts/sunxi/sun*-pinctrl.dtsi* 文件中 (Linux5.4 直接放在 sun.dtsi 中),配置了该 SoC 的 pinctrl 控制器的通用配置信息,一般不建议修改,有 pinctrl 驱动维护者维护。目前,在 sunxi 平台,我们根据电源域,注册两个 pinctrl 设备:r_pio 设 备 (PL0 后的所有 pin) 和 pio 设备 (PL0 前的所有 pin),两个设备的通用配置信息如下:

r_pio: pinctrl@07022000 {
	compatible = "allwinner,sun50iw9p1-r-pinctrl"; //兼容属性,用于驱动和设备绑定
	reg = <0x0 0x07022000 0x0 0x400>; //寄存器基地址0x07022000和范围0x400
    clocks = <&clk_cpurpio>;          //r_pio设置使用的时钟 
    device_type = "r_pio";            //设备类型属性 
    gpio-controller;                  //表示是一个gpio控制器 
    interrupt-controller;             //表示一个中断控制器,不支持中断可以删除 
    #interrupt-cells = <3>;           //pin中断属性需要配置的参数个数,不支持中断可以删除 
    #size-cells = <0>;                //没有使用,配置0
    #gpio-cells = <6>;                //gpio属性配置需要的参数个数,对于linux-5.4为3

    /*
     * 以下配置为模块使用的pin的配置,模块通过引用相应的节点对pin进行操作
     * 由于不同板级的pin经常改变,建议通过板级dts修改(参考下一小节)
     */
    s_rsb0_pins_a: s_rsb0@0 {
        allwinner,pins = "PL0", "PL1";
        allwinner,function = "s_rsb0";
        allwinner,muxsel = <2>;
        allwinner,drive = <2>;
        allwinner,pull = <1>;
    };

    /*
     * 以下配置为linux-5.4模块使用pin的配置,模块通过引用相应的节点对pin进行操作
     * 由于不同板级的pin经常改变,建议将模块pin的引用放到board dts中
     *(类似pinctrl-0 = <&scr1_ph_pins>;),并使用scr1_ph_pins这种更有标识性的名字)。
     */
    scr1_ph_pins: scr1-ph-pins {
        pins = "PH0", "PH1";
        function = "sim1";
        drive-strength = <10>;
        bias-pull-up;
    };
};

pio: pinctrl@0300b000 {
    compatible = "allwinner,sun50iw9p1-pinctrl"; //兼容属性,用于驱动和设备绑定
    reg = <0x0 0x0300b000 0x0 0x400>; //寄存器基地址0x0300b000和范围0x400
    interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>, /* AW1823_GIC_Spec: GPIOA: 83-32=51 */
            <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>,
            <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>,
            <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
            <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
            <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>,
            <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>; //该设备每个bank支持的中断配置和gic中断号,每个中断号对应一个支持中断的bank
    device_type = "pio"; //设备类型属性
    clocks = <&clk_pio>, <&clk_losc>, <&clk_hosc>; //该设备使用的时钟
    gpio-controller;          //表示是一个gpio控制器
    interrupt-controller;     //表示是一个中断控制器
    #interrupt-cells = <3>;   //pin中断属性需要配置的参数个数,不支持中断可以删除
    #size-cells = <0>;        //没有使用
    #gpio-cells = <6>;        //gpio属性需要配置的参数个数,对于linux-5.4为3
    /* takes the debounce time in usec as argument */
}

3.2.2 board.dts 板级配置

board.dts 用于保存每个板级平台的设备信息 (如 demo 板、demo2.0 板等等),以 demo 板为例,board.dts 路径如下:

/device/config/chips/{CHIP}/configs/demo/board.dts

在 board.dts 中的配置信息如果在 *.dtsi 中 (如 sun50iw9p1.dtsi 等) 存在,则会存在以下覆盖规则:

相同属性和结点,board.dts 的配置信息会覆盖 *.dtsi 中的配置信息。

新增加的属性和结点,会追加到最终生成的 dtb 文件中。

linux-4.9 上面 pinctrl 中一些模块使用 board.dts 的简单配置如下:

pio: pinctrl@0300b000 {
    input-debounce = <0 0 0 0 0 0 0>; /*配置中断采样频率,每个对应一个支持中断的bank,单位us*/
    
    spi0_pins_a: spi0@0 {
        allwinner,pins = "PC0", "PC2", "PC4"; 
        allwinner,pname = "spi0_sclk", "spi0_mosi", "spi0_miso"; 
        allwinner,function = "spi0"; 
    };
};

对于 linux-5.4,不建议采用上面的覆盖方式,而是修改驱动 pinctrl-0 引用的节点。

linux-5.4 上面 board.dts 的配置如下:

&pio{
    input-debounce = <0 0 0 0 1 0 0 0 0>; //配置中断采样频率,每个对应一个支持中断的bank,单位us
    vcc-pe-supply = <&reg_pio1_8>; //配置IO口耐压值,例如这里的含义是将pe口设置成1.8v耐压值 
};

4 模块接口说明

4.1 pinctrl 接口说明

4.1.1 pin4ctrl_get

函数原型:struct pinctrl *pinctrl_get(struct device *dev);

作用:获取设备的 pin 操作句柄,所有 pin 操作必须基于此 pinctrl 句柄。

参数:

  • dev: 指向申请 pin 操作句柄的设备句柄。

返回:

  • 成功,返回 pinctrl 句柄。

  • 失败,返回 NULL。

4.1.2 pinctrl_put

函数原型:void pinctrl_put(struct pinctrl *p)

作用:释放 pinctrl 句柄,必须与 pinctrl_get 配对使用。

参数:

p: 指向释放的 pinctrl 句柄。

返回:

没有返回值。

! 警告

必须与 pinctrl_get 配对使用。

4.1.3 devm_pinctrl_get

函数原型:struct pinctrl *devm_pinctrl_get(struct device *dev)

作用:根据设备获取 pin 操作句柄,所有 pin 操作必须基于此 pinctrl 句柄,与 pinctrl_get功能完全一样,只是 devm_pinctrl_get 会将申请到的 pinctrl 句柄做记录,绑定到设备句柄信息中。设备驱动申请 pin 资源,推荐优先使用 devm_pinctrl_get 接口。

参数:

dev: 指向申请 pin 操作句柄的设备句柄。

返回:

成功,返回 pinctrl 句柄。

失败,返回 NULL。

4.1.4 devm_pinctrl_put

函数原型:void devm_pinctrl_put(struct pinctrl *p)

作用:释放 pinctrl 句柄,必须与 devm_pinctrl_get 配对使用。

参数:

p: 指向释放的 pinctrl 句柄。

返回:

没有返回值。

! 警告

必须与 devm_pinctrl_get 配对使用,可以不显式的调用该接口。

4.1.5 pinctrl_lookup_state

函数原型:struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)

作用:根据 pin 操作句柄,查找 state 状态句柄。

参数:

p: 指向要操作的 pinctrl 句柄。

name: 指向状态名称,如 “default”、“sleep” 等。

返回:

成功,返回执行 pin 状态的句柄 struct pinctrl_state *。

失败,返回 NULL。

4.1.6 pinctrl_select_state

函数原型:int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s)

作用:将 pin 句柄对应的 pinctrl 设置为 state 句柄对应的状态。

参数:

p: 指向要操作的 pinctrl 句柄。

s: 指向 state 句柄。

返回:

成功,返回 0。

失败,返回错误码。

4.1.7 devm_pinctrl_get_select

函数原型:struct pinctrl *devm_pinctrl_get_select(struct device *dev, const char *name)

作用:获取设备的 pin 操作句柄,并将句柄设定为指定状态。

参数:

dev: 指向管理 pin 操作句柄的设备句柄。

name: 要设置的 state 名称,如 “default”、“sleep” 等。

返回:

成功,返回 pinctrl 句柄。

失败,返回 NULL。

4.1.8 devm_pinctrl_get_select_default

函数原型:struct pinctrl *devm_pinctrl_get_select_default(struct device *dev)

作用:获取设备的 pin 操作句柄,并将句柄设定为默认状态。

参数:

dev: 指向管理 pin 操作句柄的设备句柄。

返回:

成功,返回 pinctrl 句柄。

失败,返回 NULL。

4.1.9 pin_config_get

作用:获取指定 pin 的属性。

参数:

dev_name: 指向 pinctrl 设备。

name: 指向 pin 名称。

config: 保存 pin 的配置信息。

返回:

成功,返回 pin 编号。

失败,返回错误码。

! 警告

该接口在 linux-5.4 已经移除。

4.1.10 pin_config_set

作用:设置指定 pin 的属性。

参数:

dev_name: 指向 pinctrl 设备。

name: 指向 pin 名称。

config:pin 的配置信息。

返回:

成功,返回 0。

失败,返回错误码。

! 警告

该接口在 linux-5.4 已经移除。

4.2 gpio 接口说明

4.2.1 gpio_request

函数原型:int gpio_request(unsigned gpio, const char *label)

作用:申请 gpio,获取 gpio 的访问权。

参数:

gpio:gpio 编号。

label:gpio 名称,可以为 NULL。

返回:

成功,返回 0。

失败,返回错误码。

4.2.2 gpio_free

函数原型:void gpio_free(unsigned gpio)

作用:释放 gpio。

参数:

gpio:gpio 编号。

返回:

无返回值。

4.2.3 gpio_direction_input

函数原型:int gpio_direction_input(unsigned gpio)

作用:设置 gpio 为 input。

参数:

gpio:gpio 编号。

返回:

成功,返回 0。

失败,返回错误码。

4.2.5 __gpio_get_value

函数原型:int __gpio_get_value(unsigned gpio)

作用:获取 gpio 电平值 (gpio 已为 input/output 状态)。

参数:

gpio:gpio 编号。

返回:

返回 gpio 对应的电平逻辑,1 表示高, 0 表示低。

4.2.6 __gpio_set_value

函数原型:void __gpio_set_value(unsigned gpio, int value)

作用:设置 gpio 电平值 (gpio 已为 input/output 状态)。

参数:

gpio:gpio 编号。

value: 期望设置的 gpio 电平值,非 0 表示高, 0 表示低。

返回:

无返回值

4.2.7 of_get_named_gpio

函数原型:int of_get_named_gpio(struct device_node *np, const char *propname, int index)

作用:通过名称从 dts 解析 gpio 属性并返回 gpio 编号。

参数:

np: 指向使用 gpio 的设备结点。

propname:dts 中属性的名称。

index:dts 中属性的索引值。

返回:

成功,返回 gpio 编号。

失败,返回错误码。

4.2.8 of_get_named_gpio_flags

函数原型:int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index,

enum of_gpio_flags *flags)

作用:通过名称从 dts 解析 gpio 属性并返回 gpio 编号。

参数:

np: 指向使用 gpio 的设备结点。

propname:dts 中属性的名称。

index:dts 中属性的索引值

flags: 在 sunxi 平台上,必须定义为 struct gpio_config * 类型变量,因为 sunxi pinctrl的 pin 支持上下拉, 驱动能力等信息,而内核 enum of_gpio_flags * 类型变量只能包含输入、输出信息,后续 sunxi 平台 需要标准化该接口。

返回:

成功,返回 gpio 编号。

失败,返回错误码。

! 警告

该接口的 flags 参数,在 sunxi linux-4.9 及以前的平台上,必须定义为 struct gpio_config 类型变量。linux-5.4 已经标准化该接口,直接采用 enum of_gpio_flags 的定义。

5 使用示例

5.1 使用 pin 的驱动 dts 配置示例

对于使用 pin 的驱动来说,驱动主要设置 pin 的常用的几种功能,列举如下:

驱动使用者只配置通用 GPIO, 即用来做输入、输出和中断的

驱动使用者设置 pin 的 pin mux,如 uart 设备的 pin,lcd 设备的 pin 等,用于特殊功能

驱动使用者既要配置 pin 的通用功能,也要配置 pin 的特性

下面对常见使用场景进行分别介绍。

5.1.1 配置通用 GPIO 功能/中断功能

用法一:配置 GPIO,中断,device tree 配置 demo 如下所示:

soc{
    ...
    gpiokey {
        device_type = "gpiokey"; 
        compatible = "gpio-keys";
        
        ok_key {
            device_type = "ok_key";
            label = "ok_key";
            gpios = <&r_pio PL 0x4 0x0 0x1 0x0 0x1>; //如果是linux-5.4,则应该为gpios = <&r_pio 0 4 GPIO_ACTIVE_HIGH>;
            linux,input-type = "1>";
            linux,code = <0x1c>;
            wakeup-source = <0x1>;
            };
        };
    ...
};

说明

说明:gpio in/gpio out/ interrupt采用dts的配置方法,配置参数解释如下:
对于linux-4.9:
gpios = <&r_pio PL 0x4 0x0 0x1 0x0 0x1>;
            |    |  |   |   |   |   `---输出电平,只有output才有效
            |    |  |   |   |   `-------驱动能力,值为0x0时采用默认值
            |    |  |   |   `-----------上下拉,值为0x1时采用默认值
            |    |  |   `---------------复用类型
            |    |  `-------------------当前bank中哪个引脚
            |    `-----------------------哪个bank
            `---------------------------指向哪个pio,属于cpus要用&r_pio
使用上述方式配置gpio时,需要驱动调用以下接口解析dts的配置参数:
int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index,
enum of_gpio_flags *flags)
拿到gpio的配置信息后(保存在flags参数中,见4.2.8.小节),在根据需要调用相应的标准接口实现自己的功能
对于linux-5.4:
gpios = <&r_pio 0 4 GPIO_ACTIVE_HIGH>;
            |   |      |
            |   |      `-------------------gpio active时状态,如果需要上下拉,还可以或上
            GPIO_PULL_UP、GPIO_PULL_DOWN标志
            |   `-----------------------哪个bank
            `---------------------------指向哪个pio,属于cpus要用&r_pio

5.1.2 用法二

用法二:配置设备引脚,device tree 配置 demo 如下所示:

device tree对应配置
soc{
    pio: pinctrl@0300b000 {
        ...
        uart0_ph_pins_a: uart0-ph-pins-a {
            allwinner,pins = "PH7", "PH8"; 
            allwinner,function = "uart0"; 
            allwinner,muxsel = <3>;
            allwinner,drive = <0x1>;
            allwinner,pull = <0x1>;
        };
        /* 对于linux-5.4 请使用下面这种方式配置 */
        mmc2_ds_pin: mmc2-ds-pin {
            pins = "PC1";
            function = "mmc2";
            drive-strength = <30>;
            bias-pull-up;
        };
        ...
    };
    ...
    uart0: uart@05000000 {
        compatible = "allwinner,sun8i-uart";
        device_type = "uart0";
        reg = <0x0 0x05000000 0x0 0x400>;
        interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&clk_uart0>;
        pinctrl-names = "default", "sleep";
        pinctrl-0 = <&uart0_pins_a>;
        pinctrl-1 = <&uart0_pins_b>;
        uart0_regulator = "vcc-io";
        uart0_port = <0>;
        uart0_type = <2>;
    };
    ...
};

其中:

pinctrl-0 对应 pinctrl-names 中的 default,即模块正常工作模式下对应的 pin 配置

pinctrl-1 对应 pinctrl-names 中的 sleep,即模块休眠模式下对应的 pin 配置

5.2 接口使用示例

5.2.1 配置设备引脚

一般设备驱动只需要使用一个接口 devm_pinctrl_get_select_default 就可以申请到设备所有pin 资源。

static int sunxi_pin_req_demo(struct platform_device *pdev)
{ 
	struct pinctrl *pinctrl;
	/* request device pinctrl, set as default state */
	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
	if (IS_ERR_OR_NULL(pinctrl))
		return -EINVAL;

	return 0;
}

5.2.2 获取 GPIO 号

static int sunxi_pin_req_demo(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *np = dev->of_node;
    unsigned int gpio;
    
    #get gpio config in device node.
    gpio = of_get_named_gpio(np, "vdevice_3", 0);
    if (!gpio_is_valid(gpio)) {
    	if (gpio != -EPROBE_DEFER)
    		dev_err(dev, "Error getting vdevice_3\n");
		return gpio;
    }
}

5.2.3 GPIO 属性配置

通过 pin_config_set/pin_config_get/pin_config_group_set/pin_config_group_get 接口单独控制指定 pin 或 group 的相关属性。

static int pctrltest_request_all_resource(void)
{
    struct device *dev;
    struct device_node *node;
    struct pinctrl *pinctrl;
    struct sunxi_gpio_config *gpio_list = NULL;
    struct sunxi_gpio_config *gpio_cfg;
    unsigned gpio_count = 0;
    unsigned gpio_index;
    unsigned long config;
    int ret;

    dev = bus_find_device_by_name(&platform_bus_type, NULL, sunxi_ptest_data->dev_name);
    if (!dev) {
        pr_warn("find device [%s] failed...\n", sunxi_ptest_data->dev_name);
        return -EINVAL;
    }

    node = of_find_node_by_type(NULL, dev_name(dev));
    if (!node) {
        pr_warn("find node for device [%s] failed...\n", dev_name(dev));
        return -EINVAL;
    }
    dev->of_node = node;

    pr_warn("++++++++++++++++++++++++++++%s++++++++++++++++++++++++++++\n", __func__);
    pr_warn("device[%s] all pin resource we want to request\n", dev_name(dev));
    pr_warn("-----------------------------------------------\n");

    pr_warn("step1: request pin all resource.\n");
    pinctrl = devm_pinctrl_get_select_default(dev);
    if (IS_ERR_OR_NULL(pinctrl)) {
        pr_warn("request pinctrl handle for device [%s] failed...\n", dev_name(dev));
        return -EINVAL;
    }

    pr_warn("step2: get device[%s] pin count.\n", dev_name(dev));
    ret = dt_get_gpio_list(node, &gpio_list, &gpio_count);
    if (ret < 0 || gpio_count == 0) {
        pr_warn(" devices own 0 pin resource or look for main key failed!\n");
        return -EINVAL;
    }

    pr_warn("step3: get device[%s] pin configure and check.\n", dev_name(dev));
    for (gpio_index = 0; gpio_index < gpio_count; gpio_index++) {
        gpio_cfg = &gpio_list[gpio_index];

        /*check function config */
        config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0xFFFF);
        pin_config_get(SUNXI_PINCTRL, gpio_cfg->name, &config);
        if (gpio_cfg->mulsel != SUNXI_PINCFG_UNPACK_VALUE(config)) {
            pr_warn("failed! mul value isn't equal as dt.\n");
            return -EINVAL;
        }

        /*check pull config */
        if (gpio_cfg->pull != GPIO_PULL_DEFAULT) {
            config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0xFFFF);
            pin_config_get(SUNXI_PINCTRL, gpio_cfg->name, &config);
            if (gpio_cfg->pull != SUNXI_PINCFG_UNPACK_VALUE(config)) {
                pr_warn("failed! pull value isn't equal as dt.\n");
                return -EINVAL;
            }
        }

        /*check dlevel config */
        if (gpio_cfg->drive != GPIO_DRVLVL_DEFAULT) {
            config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DRV, 0XFFFF);
            pin_config_get(SUNXI_PINCTRL, gpio_cfg->name, &config);
            if (gpio_cfg->drive != SUNXI_PINCFG_UNPACK_VALUE(config)) {
                pr_warn("failed! dlevel value isn't equal as dt.\n");
                return -EINVAL;
            }
        }

        /*check data config */
        if (gpio_cfg->data != GPIO_DATA_DEFAULT) {
            config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
            pin_config_get(SUNXI_PINCTRL, gpio_cfg->name, &config);
            if (gpio_cfg->data != SUNXI_PINCFG_UNPACK_VALUE(config)) {
                pr_warn("failed! pin data value isn't equal as dt.\n");
                return -EINVAL;
            }
        }
    }

    pr_warn("-----------------------------------------------\n");
    pr_warn("test pinctrl request all resource success!\n");
    pr_warn("++++++++++++++++++++++++++++end++++++++++++++++++++++++++++\n\n");
    return 0;
}
注:需要注意,存在SUNXI_PINCTRL和SUNXI_R_PINCTRL两个pinctrl设备,cpus域的pin需要使用
SUNXI_R_PINCTRL

! 警告

linux5.4 中 使 用 pinctrl_gpio_set_config 配 置 gpio 属 性, 对 应 使 用pinconf_to_config_pack 生成 config 参数:

SUNXI_PINCFG_TYPE_FUNC 已不再生效,暂未支持 FUNC 配置(建议使用 pinctrl_select_state接口代替)

SUNXI_PINCFG_TYPE_PUD 更新为内核标准定义(PIN_CONFIG_BIAS_PULL_UP/PIN_CONFIG_BIAS_PULL_DOWN

SUNXI_PINCFG_TYPE_DRV 更新为内核标准定义(PIN_CONFIG_DRIVE_STRENGTH),相应的 val 对应关系为(4.9->5.4: 0->10, 1->20…

SUNXI_PINCFG_TYPE_DAT 已不再生效,暂未支持 DAT 配置(建议使用 gpio_direction_output或者 __gpio_set_value 设置电平值)

5.3 设备驱动使用 GPIO 中断功能

方式一:通过 gpio_to_irq 获取虚拟中断号,然后调用申请中断函数即可目前 sunxi-pinctrl 使用 irq-domain 为 gpio 中断实现虚拟 irq 的功能,使用 gpio 中断功能时,设备驱动只需要通过 gpio_to_irq 获取虚拟中断号后,其他均可以按标准 irq 接口操作。

static int sunxi_gpio_eint_demo(struct platform_device *pdev)
{ 
    struct device *dev = &pdev->dev;
    int virq;
    int ret;
    /* map the virq of gpio */
    virq = gpio_to_irq(GPIOA(0));
    if (IS_ERR_VALUE(virq)) {
	    pr_warn("map gpio [%d] to virq failed, errno = %d\n",
    											GPIOA(0), virq);
        return -EINVAL;
    }
    pr_debug("gpio [%d] map to virq [%d] ok\n", GPIOA(0), virq);
    /* request virq, set virq type to high level trigger */
    ret = devm_request_irq(dev, virq, sunxi_gpio_irq_test_handler,
                                IRQF_TRIGGER_HIGH, "PA0_EINT", NULL);
    if (IS_ERR_VALUE(ret)) {
        pr_warn("request virq %d failed, errno = %d\n", virq, ret);
        return -EINVAL;
    }
    
	return 0;
}

方式二:通过 dts 配置 gpio 中断,通过 dts 解析函数获取虚拟中断号,最后调用申请中断函数即可,demo 如下所示:

dts配置如下:
soc{
	...
    Vdevice: vdevice@0 {
        compatible = "allwinner,sun8i-vdevice";
        device_type = "Vdevice";
        interrupt-parent = <&pio>; /*依赖的中断控制器(带interrupt-controller属性的结 点)*/
        interrupts = < PD 3 IRQ_TYPE_LEVEL_HIGH>;
                        | |   `------------------中断触发条件、类型
                        | `-------------------------pin bank内偏移
                        `---------------------------哪个bank
        pinctrl-names = "default";
        pinctrl-0 = <&vdevice_pins_a>;
        test-gpios = <&pio PC 3 1 2 2 1>;
        status = "okay";
	};
	...
};

在驱动中,通过 platform_get_irq() 标准接口获取虚拟中断号,如下所示:

static int sunxi_pctrltest_probe(struct platform_device *pdev)
{ 
    struct device_node *np = pdev->dev.of_node;
    struct gpio_config config;
    int gpio, irq;
    int ret;

    if (np == NULL) {
        pr_err("Vdevice failed to get of_node\n");
        return -ENODEV;
    }
    ....
    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        printk("Get irq error!\n");
        return -EBUSY;
    }
	.....
	sunxi_ptest_data->irq = irq;
	......
	return ret;
}

//申请中断:
static int pctrltest_request_irq(void)
{
    int ret;
    int virq = sunxi_ptest_data->irq;
    int trigger = IRQF_TRIGGER_HIGH;

    reinit_completion(&sunxi_ptest_data->done);

    pr_warn("step1: request irq(%s level) for irq:%d.\n",
	    trigger == IRQF_TRIGGER_HIGH ? "high" : "low", virq);
	ret = request_irq(virq, sunxi_pinctrl_irq_handler_demo1,
			trigger, "PIN_EINT", NULL);
    if (IS_ERR_VALUE(ret)) {
        pr_warn("request irq failed !\n");
        return -EINVAL;
    }

    pr_warn("step2: wait for irq.\n");
    ret = wait_for_completion_timeout(&sunxi_ptest_data->done, HZ);
    
    if (ret == 0) {
        pr_warn("wait for irq timeout!\n");
        free_irq(virq, NULL);
        return -EINVAL;
    }

    free_irq(virq, NULL);

    pr_warn("-----------------------------------------------\n");
    pr_warn("test pin eint success !\n");
    pr_warn("+++++++++++++++++++++++++++end++++++++++++++++++++++++++++\n\n\n");

    return 0;
}

5.4 设备驱动设置中断 debounce 功能

方式一:通过 dts 配置每个中断 bank 的 debounce,以 pio 设备为例,如下所示:

&pio {
    /* takes the debounce time in usec as argument */
    input-debounce = <0 0 0 0 0 0 0>;
                      | | | | | | `----------PA bank
                      | | | | | `------------PC bank
                      | | | | `--------------PD bank
                      | | | `----------------PF bank
                      | | `------------------PG bank
                      | `--------------------PH bank
                      `----------------------PI bank
};

注意:input-debounce 的属性值中需把 pio 设备支持中断的 bank 都配上,如果缺少,会以bank 的顺序设置相应的属性值到 debounce 寄存器,缺少的 bank 对应的 debounce 应该是默认值(启动时没修改的情况)。sunxi linux-4.9 平台,中断采样频率最大是 24M, 最小 32k,debounce 的属性值只能为 0 或 1。对于 linux-5.4,debounce 取值范围是 0~1000000(单位 usec)。

方式二:驱动模块调用 gpio 相关接口设置中断 debounce

static inline int gpio_set_debounce(unsigned gpio, unsigned debounce);
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);

在驱动中,调用上面两个接口即可设置 gpio 对应的中断 debounce 寄存器,注意,debounce 是以 ms 为单位的 (linux-5.4 已经移除这个接口)。

6 FAQ

6.1 常用 debug 方法

6.1.1 利用 sunxi_dump 读写相应寄存器

需要开启 SUNXI_DUMP 模块:

make kernel_menuconfig
	---> Device Drivers
		---> dump reg driver for sunxi platform (选中)

使用方法:

cd /sys/class/sunxi_dump
1.查看一个寄存器
echo 0x0300b048 > dump ;cat dump
2.写值到寄存器上
echo 0x0300b058 0xfff > write ;cat write
3.查看一片连续寄存器
echo 0x0300b000,0x0300bfff > dump;cat dump

4.写一组寄存器的值
echo 0x0300b058 0xfff,0x0300b0a0 0xfff > write;cat write

通过上述方式,可以查看,修改相应gpio的寄存器,从而发现问题所在。

6.1.2 利用 sunxi_pinctrl 的 debug 节点

需要开启 DEBUG_FS:

make kernel_menuconfig
	---> Kernel hacking
		---> Compile-time checks and compiler options
			---> Debug Filesystem (选中)

挂载文件节点,并进入相应目录:

mount -t debugfs none /sys/kernel/debug
cd /sys/kernel/debug/sunxi_pinctrl

1.查看 pin 的配置:

echo PC2 > sunxi_pin
cat sunxi_pin_configure

结果如下图所示:

​ 图 6-1: 查看 pin 配置图

2.修改 pin 属性

每个 pin 都有四种属性,如复用 (function),数据 (data),驱动能力 (dlevel),上下拉 (pull),

修改 pin 属性的命令如下:

echo PC2 1 > pull;cat pull
cat sunxi_pin_configure  //查看修改情况

修改后结果如下图所示:

​ 图 6-2: 修改结果图

注意:在 sunxi 平台,目前多个 pinctrl 的设备,分别是 pio 和 r_pio 和 axpxxx-gpio,当操作 PL 之后的 pin 时,请通过以下命令切换 pin 的设备,否则操作失败,切换命令如下:

echo pio > /sys/kernel/debug/sunxi_pinctrl/dev_name //切换到pio设备 
cat /sys/kernel/debug/sunxi_pinctrl/dev_name
echo r_pio > /sys/kernel/debug/sunxi_pinctrl/dev_name //切换到r_pio设备 
cat /sys/kernel/debug/sunxi_pinctrl/dev_name

修改结果如下图所示:

​ 图 6-3: pin 设备图

6.1.3 利用 pinctrl core 的 debug 节点

mount -t debugfs none /sys/kernel/debug
cd /sys/kernel/debug/sunxi_pinctrl

1.查看 pin 的管理设备:

cat pinctrl-devices

结果如下图所示:

​ 图 6-4: pin 设备图

2.查看 pin 的状态和对应的使用设备

结果如下图 log 所示:

console:/sys/kernel/debug/pinctrl # ls
pinctrl-devices pinctrl-handles pinctrl-maps pio r_pio
console:/sys/kernel/debug/pinctrl # cat pinctrl-handles
Requested pin control handlers their pinmux maps:
device: twi3 current state: sleep
	state: default
        type: MUX_GROUP controller pio group: PA10 (10) function: twi3 (15)
        type: CONFIGS_GROUP controller pio group PA10 (10)config 00001409
config 00000005
        type: MUX_GROUP controller pio group: PA11 (11) function: twi3 (15)
        type: CONFIGS_GROUP controller pio group PA11 (11)config 00001409
config 00000005
	state: sleep
        type: MUX_GROUP controller pio group: PA10 (10) function: io_disabled (5)
        type: CONFIGS_GROUP controller pio group PA10 (10)config 00001409
config 00000001
        type: MUX_GROUP controller pio group: PA11 (11) function: io_disabled (5)
        type: CONFIGS_GROUP controller pio group PA11 (11)config 00001409
config 00000001
device: twi5 current state: default
    state: default
        type: MUX_GROUP controller r_pio group: PL0 (0) function: s_twi0 (3)
        type: CONFIGS_GROUP controller r_pio group PL0 (0)config 00001409
config 00000005
        type: MUX_GROUP controller r_pio group: PL1 (1) function: s_twi0 (3)
        type: CONFIGS_GROUP controller r_pio group PL1 (1)config 00001409
config 00000005
	state: sleep
        type: MUX_GROUP controller r_pio group: PL0 (0) function: io_disabled (4)
        type: CONFIGS_GROUP controller r_pio group PL0 (0)config 00001409
config 00000001
        type: MUX_GROUP controller r_pio group: PL1 (1) function: io_disabled (4)
        type: CONFIGS_GROUP controller r_pio group PL1 (1)config 00001409
config 00000001
device: soc@03000000:pwm5@0300a000 current state: active
	state: active
        type: MUX_GROUP controller pio group: PA12 (12) function: pwm5 (16)
        type: CONFIGS_GROUP controller pio group PA12 (12)config 00000001
config 00000000
config 00000000
	state: sleep
        type: MUX_GROUP controller pio group: PA12 (12) function: io_disabled (5)
        type: CONFIGS_GROUP controller pio group PA12 (12)config 00000001
config 00000000
config 00000000
device: uart0 current state: default
	state: default
	state: sleep
device: uart1 current state: default
	state: default
        type: MUX_GROUP controller pio group: PG6 (95) function: uart1 (37)
        type: CONFIGS_GROUP controller pio group PG6 (95)config 00001409
config 00000005
        type: MUX_GROUP controller pio group: PG7 (96) function: uart1 (37)
        type: CONFIGS_GROUP controller pio group PG7 (96)config 00001409
config 00000005
        type: MUX_GROUP controller pio group: PG8 (97) function: uart1 (37)
        type: CONFIGS_GROUP controller pio group PG8 (97)config 00001409
config 00000005
        type: MUX_GROUP controller pio group: PG9 (98) function: uart1 (37)
        type: CONFIGS_GROUP controller pio group PG9 (98)config 00001409
config 00000005
	state: sleep
        type: MUX_GROUP controller pio group: PG6 (95) function: io_disabled (5)
        type: CONFIGS_GROUP controller pio group PG6 (95)config 00001409
config 00000001
        type: MUX_GROUP controller pio group: PG7 (96) function: io_disabled (5)
        type: CONFIGS_GROUP controller pio group PG7 (96)config 00001409
config 00000001
        type: MUX_GROUP controller pio group: PG8 (97) function: io_disabled (5)
        type: CONFIGS_GROUP controller pio group PG8 (97)config 00001409
config 00000001
        type: MUX_GROUP controller pio group: PG9 (98) function: io_disabled (5)
        type: CONFIGS_GROUP controller pio group PG9 (98)config 00001409
....

从上面的部分 log 可以看到那些设备管理的 pin 以及 pin 当前的状态是否正确。以 twi3 设备为例,twi3 管理的 pin 有 PA10/PA11,分别有两组状态 sleep 和 default,default 状态表示使用状态,sleep 状态表示 pin 处于 io disabled 状态,表示 pin 不可正常使用,twi3 设备使用的 pin 当前状态处于 sleep 状态的。

6.1.4 GPIO 中断问题排查步骤

6.1.4.1 GPIO 中断一直响应
  1. 排查中断信号是否一直触发中断

  2. 利用 sunxi_dump 节点,确认中断 pending 位是否没有清 (参考 6.1.1 小节)

  3. 是否在 gpio 中断服务程序里对中断检测的 gpio 进行 pin mux 的切换,不允许这样切换,否则会导致中断异常

6.1.4.2 GPIO 检测不到中断
  1. 排查中断信号是否正常,若不正常,则排查硬件,若正常,则跳到步骤 2

  2. 利用 sunxi_dump 节点,查看 gpio 中断 pending 位是否置起,若已经置起,则跳到步骤5,否则跳到步骤 3

  3. 利用 sunxi_dump 节点,查看 gpio 的中断触发方式是否配置正确,若正确,则跳到步骤 4,否则跳到步骤 5

  4. 检查中断的采样时钟,默认应该是 32k,可以通过 sunxi_dump 节点,切换 gpio 中断采样时钟到 24M 进行实验

  5. 利用 sunxi_dump,确认中断是否使能

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

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

相关文章

Python计算 -- 内附蓝桥题:相乘

计算 ~~不定时更新&#x1f383;&#xff0c;上次更新&#xff1a;2023/02/23 &#x1f5e1;常用函数&#xff08;方法&#xff09; 1. 求一个整数的最末位 举个栗子&#x1f330; n int(input()) end n % 10蓝桥例题1 - 相乘✖️ 题目描述 本题为填空题&#xff0c;…

MySQL 11:MySQL锁

锁是一种机制&#xff0c;计算机通过这种机制协调多个进程或线程对资源的并发访问&#xff08;以避免争用&#xff09;。在数据库中&#xff0c;除了传统的计算资源&#xff08;如CPU、RAM、I/O等&#xff09;的争夺外&#xff0c;数据也是一种被众多用户共享的资源。如何保证并…

叠氮炔点击化学634926-63-9,Propargyl-PEG1-NHBoc,氨基叔丁酯PEG1丙炔基相关性质分享

●外观以及性质&#xff1a;Propargyl-PEG1-NHBoc产物呈固体或粘性液体&#xff0c;取决于PEG分子量&#xff0c;包含1个丙炔基和一个氨基叔丁酯&#xff0c;炔丙基可通过铜催化的叠氮炔点击化学与含叠氮化合物或生物分子反应&#xff0c;以产生稳定的三唑键&#xff0c;带有 P…

Windows下载安装Redis的详细步骤

目录 一、概述 1.redis的版本维护介绍 2.msi安装包和压缩包的优点和缺点 二、操作步骤 三、测试是否安装成功&#xff08;查看版本&#xff09; 四、获取资源 一、概述 1.redis的版本维护介绍 Redis的官网只提供Linux系统的下载。但是微软的技术团队长期开发和维护着这…

Tina_Linux_启动优化_开发指南

文章目录Tina_Linux_启动优化_开发指南1 概述2 启动速度优化简介2.1 启动流程2.2 测量方法2.2.1 printk time2.2.2 initcall_debug2.2.3 bootgraph.2.2.4 bootchart2.2.5 gpio 示波器.2.2.6 grabserial.2.3 优化方法2.3.1 boot0启动优化2.3.1.1 非安全启动.2.3.1.2 安全启动2.3…

jmeter接口自动化测试框架

接口测试可以分为两部分&#xff1a; 一是线上接口&#xff08;生产环境&#xff09;自动化测试&#xff0c;需要自动定时执行&#xff0c;每5分钟自动执行一次&#xff0c;相当于每5分钟就检查一遍线上的接口是否正常&#xff0c;有异常能够及时发现&#xff0c;不至于影响用…

易点易动助力企业固定资产信息化管理

对于生产制造或者互联网企业而言&#xff0c;固定资产比重较高&#xff0c;是企业资产的大头&#xff0c;一些办公设备、生产设备数量和金额都比较大。提升企业固定资产管理水平&#xff0c;是企业实现信息化建设的必要条件。 目前&#xff0c;国内的很多企业在固定资产管理中…

零售航母沃尔玛公布业绩:喜忧参半

2月21日美股盘前&#xff0c;零售巨无霸沃尔玛公布了截至1月的2023财年第四季度业绩报告。财报中不乏可圈可点之处&#xff0c;但是利润迎来六年首降&#xff0c;新财年的利润指引要也比预期低很多&#xff0c;可以说喜忧参半。 一、Q4业绩可圈可点 营收方面&#xff1a;在本…

漏斗分析法

一什么是漏斗分析&#xff1f; 漏斗分析是数据领域最常见的一种“程式化”数据分析方法&#xff0c;它能够科学地评估一种业务过程&#xff0c;从起点到终点&#xff0c;各个阶段的转化情况。通过可以量化的数据分析&#xff0c;帮助业务找到有问题的业务环节&#xff0c;并进…

插画网课平台排名

插画网课平台哪个好&#xff0c;插画网课排名靠前的有哪些&#xff0c;今天给大家梳理了国内5家专业的插画网课平台&#xff0c;各有优势和特色&#xff0c;给学插画的小伙伴提供选择&#xff0c;报插画网课一定要选择靠谱的&#xff0c;否则人钱两空泪两行&#xff01; 一&am…

使用 xshell 远程连接(使用 xftp 远程传输)

xshell 和 xftp的使用都基于ssh协议&#xff0c;我们需要先在远程服务端或者虚拟机上安装ssh服务&#xff0c;然后才能远程连接。 目录 1、什么是ssh协议&#xff1f; 2、安装 openssh (1) 安装 openssh 服务器 (2) 关闭服务器防火墙&#xff08;或者开放端口22&#xff09…

四个步骤在CRM系统中设置游戏化机制

长期高强度的单一工作会让销售人员逐渐失去对工作的兴趣&#xff0c;导致销售状态缺少动力和激情&#xff0c;工作开展愈加困难。不少企业通过CRM销售管理系统设置游戏化竞赛&#xff0c;调动销售人员的工作积极性。那么&#xff0c;如何在CRM系统中设置游戏化机制&#xff1f;…

深入浅出 MySQL 索引(一)

MySQL 索引&#xff08;基础篇&#xff09; 你好&#xff0c;我是悟空。 本文目录如下&#xff1a; 一、前言 最近在梳理 MySQL 核心知识&#xff0c;刚好梳理到了 MySQL 索引相关的知识&#xff0c;我的文章风格很多都是原理 实战的方式带你去了解知识点&#xff0c;所以…

Spring+MVC+MYbatis注解开发

Spring常见注解 注解一&#xff1a;Configuration 用在类上面&#xff0c;加上这个注解的类可以成为一个spring的xml配置文件&#xff0c;使用的是java代码的配置 注解二&#xff1a;ComponentScan 用在类上&#xff0c;加上注解可以指定扫描路径 注解三&#xff1a;创建对…

Uni-app使用vant和uview组件

目录 1.安装vant组件 1.1安装前需知 1.2.安装 1.3.创建uni-app项目 2.安装uview-ui组件 2.1官网 2.2安装 2.3安装成功 1.安装vant组件 1.1安装前需知 小程序能使用vant-weapp组件&#xff0c;且官网的安装是直接导入小程序中&#xff0c;不能直接导入uni-app框架中 V…

Python的标准模块介绍:sys、os、random和time

Python内置了许多标准模块&#xff0c;例如sys、os、random和time模块等&#xff0c;下面为大家介绍几个常用的标准模块。 1.sys模块 sys模块中提供了一系列与Python解释器交互的函数和变量&#xff0c;用于操控Python的运行时环境。sys模块中常用变量与函数如表1所示。 表1…

Apifox = Postman + Swagger + Mock + JMeter

目录 可视化API设计 高效 & 零学习成本 可复用的“数据模型” 遵循 OpenAPI(Swagger) 规范 可导入 Swagger 等 20 数据格式 具体使用尝鲜 多项目管理 支持多环境切换 支持IDEA、浏览器、桌面应用 Idea插件 公共API hub库 如题&#xff1a;一款非常好用的API管理测…

二叉树——最大二叉树

最大二叉树 链接 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums…

浅谈面向过程和面向对象的区别

面向过程和面向对象都是对软件分析、设计和开发的一种思想,它指导着人们以不同的方式去分析、设计和开发软件。 伴随着每个开发人员的职业生涯&#xff0c;或多或少都会接触到这两种软件设计思想。不仅是软件开发人员&#xff0c;其实这两种思想在我们平时的生活中也处处可见&a…

uniapp 超过2m无法上传代码!uni-module太大了,小程序无法上传怎么办?

问题描述&#xff1a; 虽然分包了。但是主包里还有很多uniui的组件&#xff0c;占了2m多&#xff0c;之前一直是点击运行&#xff0c;然后再上传代码。提示的超过2m无法上传。 解决办法&#xff1a; 点击发行&#xff0c;然后上传就可以了。 优化建议&#xff1a; 1、组件按…