Linux驱动开发笔记(四)设备树进阶及GPIO、Pinctrl子系统

文章目录

  • 前言
  • 一、设备树的进阶知识
    • 1. 追加/修改节点内容
    • 2.chosen子节点
    • 3. 获取设备树节点信息
      • 3.1 of_find_node_by_path( )函数
      • 3.2 of_find_node_by_name( )函数
      • 3.3 of_find_node_by_type( )函数
      • 3.4 of_find_compatible_node( )函数
      • 3.5 of_find_matching_node_and_match( )函数
      • 3.6 of_get_parent( )函数
      • 3.7 of_get_next_child( )函数
    • 4. 提取属性值的of函数
      • 4.1 of_find_property( )函数
      • 4.2 of_property_read_uX_array( )函数
      • 4.3 of_property_read_uX( )函数
      • 4.4 of_property_read_string( )函数
      • 4.5 of_property_read_string_index( )函数
      • 4.6 of_property_read_bool( )函数
      • 4.7 of_iomap( )函数
  • 二、Pinctrl子系统
    • 2.1 使用pinctrl设置复用关系
      • 2.1.1 客户端(Client)
      • 2.1.2 服务端(Server)
    • 2.2 pinctrl相关结构体
  • 三、GPIO子系统
    • 1. 相关API
      • 1.1 of_get_named_gpio( )函数
      • 1.2 gpio_request( )函数
      • 1.3 gpio_direction_input( )函数
      • 1.4 gpio_direction_output( )函数
      • 1.5 gpio_set_value( )函数
      • 1.6 gpio_get_value( )函数
      • 1.7 gpio_free( )函数
    • 2.相关结构体
      • 2.1 gpio_device结构体
      • 2.2 gpio_chip结构体
      • 2.3 gpio_desc结构体
    • 3. GPIO设备树分析
      • 3.1 gpio-controller属性
      • 3.2 gpio-cells属性
      • 3.3 gpio-ranges属性


前言

  在早期笔者已经简单介绍过设备树的相关信息,本章将详细展开学习一下这部分内容。


一、设备树的进阶知识

1. 追加/修改节点内容

&cpu0 {
    cpu-supply = <&vdd_cpu>;
};

  这些源码并不包含在根节点“/{…}”内,它们不是一个新的节点,而是向原有节点追加内容。 以上方源码为例,“&cpu0”表示向“节点标签”为“cpu0”的节点追加数据, 这个节点可能定义在本文件也可能定义在本文件所包含的设备树文件中。

2.chosen子节点

chosen {
   bootargs = "earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=PARTUUID=614e0000-0000 rw rootwait";
};

  chosen子节点不代表实际硬件,它主要用于给内核传递参数。 此外这个节点还用作uboot向linux内核传递配置参数的“通道”, 我们在Uboot中设置的参数就是通过这个节点传递到内核的, 这部分内容是uboot和内核自动完成的,作为初学者我们不必深究。

3. 获取设备树节点信息

  这一小节我们就开始学习如何从设备树的设备节点获取我们想要的数据。 内核提供了一组函数用于从设备节点获取资源(设备节点中定义的属性)的函数,这些函数以of_开头,称为OF操作函数。 常用的OF函数介绍如下:

名称描述
of_find_node_by_path( )根据节点路径寻找节点函数
of_find_node_by_name( )根据节点名字寻找节点函数
of_find_node_by_type( )根据节点类型寻找节点函数
of_find_compatible_node( )根据节点类型和compatible属性寻找节点函数
of_find_matching_node_and_match( )根据匹配表寻找节点函数
of_get_parent( )寻找父节点函数
of_get_next_child( )寻找子节点函数

3.1 of_find_node_by_path( )函数

//根据节点路径寻找节点
struct device_node *of_find_node_by_path(const char *path)
  • 参数:
    • path: 指定节点在设备树中的路径。
  • 返回值:
    • device_node: 结构体指针,如果查找失败则返回NULL,否则返回device_node类型的结构体指针,它保存着设备节点的信息。
struct device_node {
    const char *name;  //节点名
    #const char *type;  //节点类型
    #phandle phandle; //唯一标识
    const char *full_name;  //节点全名
    #struct fwnode_handle fwnode; //用于支持不同设备
 
    struct  property *properties;  //设备数属性键值对的链表
    #struct property *deadprops;    /* removed properties */ 链表头
    struct  device_node *parent; //父节点
    struct  device_node *child;  //子节点
    struct  device_node *sibling; //兄弟节点
#if defined(CONFIG_OF_KOBJ)
    #struct kobject kobj;//内核对象
#endif
    #unsigned long _flags;//状态标志
    #void   *data;//节点相关的私有数据
#if defined(CONFIG_SPARC)
    #const char *path_component_name;//表示设备节点路径的一部分
    #unsigned int unique_id;//唯一ID
    #struct of_irq_controller *irq_trans;//中断控制器结构体指针
#endif
};

3.2 of_find_node_by_name( )函数

//根据节点名字寻找节点
struct device_node *of_find_node_by_name(struct device_node *from,const char *name);
  • 参数:
    • from: 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找。
    • name: 要寻找的节点名。
  • 返回值:
    • device_node: 结构体指针,如果查找失败则返回NULL,否则返回device_node类型的结构体指针,它保存着设备节点的信息。

3.3 of_find_node_by_type( )函数

//根据节点类型寻找节点
struct device_node *of_find_node_by_type(struct device_node *from,const char *type)
  • 参数:
    • from: 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找。
    • type: 要查找节点的类型,这个类型就是device_node-> type。
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

3.4 of_find_compatible_node( )函数

// 根据节点类型和compatible属性寻找节点
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

  相比of_find_node_by_name函数增加了一个compatible属性作为筛选条件。

  • 参数:
    • from: 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找。
    • type: 要查找节点的类型,这个类型就是device_node-> type。
    • compatible: 要查找节点的compatible属性。
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

3.5 of_find_matching_node_and_match( )函数

//根据匹配表寻找节点
static inline struct device_node *of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)

  可以看到,该结构体包含了更多的匹配参数,也就是说相比前三个寻找节点函数,这个函数匹配的参数更多,对节点的筛选更细。参数match,查找得到的结果。

  • 参数:
    • from: 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找。
    • matches: 源匹配表,查找与该匹配表想匹配的设备节点。
    • of_device_id: 结构体如下:
struct of_device_id {
    char    name[32]; 		//节点中属性为name的值
    char    type[32];		//节点中属性为device_type的值
    char    compatible[128];//节点的名字,在device_node结构体后面放一个字符串,full_name指向它
    const void *data;		//链表,连接该节点的所有属性
};
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

3.6 of_get_parent( )函数

//寻找父节点
struct device_node *of_get_parent(const struct device_node *node)
  • 参数:
    • node: 指定谁(节点)要查找父节点。
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

3.7 of_get_next_child( )函数

//寻找子节点
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
  • 参数:
    • node: 指定谁(节点)要查找它的子节点。
    • prev: 前一个子节点,寻找的是prev节点之后的节点。这是一个迭代寻找过程,例如寻找第二个子节点,这里就要填第一个子节点。参数为NULL 表示寻找第一个子节点。
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

4. 提取属性值的of函数

名称描述
of_find_property( )查找节点属性函数
of_property_read_uX_array( )读取整型属性函数
of_property_read_uX( )简化后的读取整型属性函数,对读取整型属性函数的简单封装
of_property_read_string_index( )它用于指定读取属性值中第几个字符串,上位替代
of_property_read_bool( )读取布尔型属性函数
of_iomap( )自动完成物理地址到虚拟地址的转换
of_address_to_resource( )得到在设备树中设置的地址值

4.1 of_find_property( )函数

struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • name: 属性名。
    • lenp: 获取得到的属性值的大小,这个指针作为输出参数,这个参数“带回”的值是实际获取得到的属性大小。
  • 返回值:
    • property: 获取得到的属性。property结构体,我们把它称为节点属性结构体,如下所示。失败返回NULL。从这个结构体中我们就可以得到想要的属性值了。
struct property {
    char    *name;			//属性名
    int     length;			//属性长度
    void    *value;			//属性值
    struct property *next;	//下一个属性
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
    unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
    unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
    struct bin_attribute attr;
#endif
};

4.2 of_property_read_uX_array( )函数

//8位整数读取函数
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)

//16位整数读取函数
int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz)

//32位整数读取函数
int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)

//64位整数读取函数
int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)
  • 参数:
    • np: 指定要读取那个设备节点结构体,也就是说读取那个设备节点的数据。
    • propname: 指定要获取设备节点的哪个属性。
    • out_values: 这是一个输出参数,是函数的“返回值”,保存读取得到的数据。
    • sz: 这是一个输入参数,它用于设置读取的长度。
  • 返回值:
    • 返回值,成功返回0,错误返回错误状态码(非零值),-EINVAL(属性不存在),-ENODATA(没有要读取的数据),-EOVERFLOW(属性值列表太小)。

4.3 of_property_read_uX( )函数

//8位整数读取函数
int of_property_read_u8 (const struct device_node *np, const char *propname,u8 *out_values)

//16位整数读取函数
int of_property_read_u16 (const struct device_node *np, const char *propname,u16 *out_values)

//32位整数读取函数
int of_property_read_u32 (const struct device_node *np, const char *propname,u32 *out_values)

//64位整数读取函数
int of_property_read_u64 (const struct device_node *np, const char *propname,u64 *out_values)

4.4 of_property_read_string( )函数

&wmsp; 在设备节点中存在很多字符串属性,例如compatible、status、type等等,这些属性可以使用查找节点属性函数of_find_property来获取,但是这样比较繁琐。内核提供了一组用于读取字符串属性的函数,介绍如下:

//读取字符串属性函数
int of_property_read_string(const struct device_node *np,const char *propname,const char **out_string)
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • propname: 属性名。
    • out_string: 获取得到字符串指针,这是一个“输出”参数,带回一个字符串指针。也就是字符串属性值的首地址。这个地址是“属性值”在内存中的真实位置,也就是说我们可以通过对地址操作获取整个字符串属性(一个字符串属性可能包含多个字符串,这些字符串在内存中连续存储,使用’0’分隔)。
  • 返回值:
    • 返回值:成功返回0,失败返回错误状态码。

4.5 of_property_read_string_index( )函数

int of_property_read_string_index(const struct device_node *np,const char *propname, int index,const char **out_string)

&wmsp; 相比前面的函数增加了参数index,它用于指定读取属性值中第几个字符串,index从零开始计数。 上一个函数只能得到属性值所在地址,也就是第一个字符串的地址,其他字符串需要我们手动修改移动地址,非常麻烦,推荐使用第本函数。

4.6 of_property_read_bool( )函数

&wmsp; 在设备节点中一些属性是BOOL型,当然内核会提供读取BOOL型属性的函数,介绍如下:

//读取布尔型属性
static inline bool of_property_read_bool(const struct device_node *np, const char *propname);
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • propname: 属性名。
  • 返回值:
    • 这个函数不按套路出牌,它不是读取某个布尔型属性的值,仅仅是读取这个属性存在或者不存在。如果想要或取值,可以使用之前讲解的“全能”函数查找节点属性函数of_find_property。

4.7 of_iomap( )函数

&wmsp; 在设备树的设备节点中大多会包含一些内存相关的属性,比如常用的reg属性。通常情况下,得到寄存器地址之后我们还要通过ioremap函数将物理地址转化为虚拟地址。现在内核提供了of函数,自动完成物理地址到虚拟地址的转换。介绍如下:

void __iomem *of_iomap(struct device_node *np, int index)
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • index: 通常情况下reg属性包含多段,index 用于指定映射那一段,标号从0开始。
  • 返回值:
    • 成功,得到转换得到的地址。失败返回NULL。

  内核也提供了常规获取地址的of函数,这些函数得到的值就是我们在设备树中设置的地址值。介绍如下:

int of_address_to_resource(struct device_node *dev, int index, struct resource *r)
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • index: 通常情况下reg属性包含多段,index 用于指定映射那一段,标号从0开始。
    • r: 这是一个resource结构体,是“输出参数”用于返回得到的地址信息。
struct resource {
    resource_size_t start;		//起始地址
    resource_size_t end;		//结束地址
    const char *name;			//属性名字
    unsigned long flags;
    unsigned long desc;
    struct resource *parent, *sibling, *child;
};
  • 返回值:
    • 成功返回0,失败返回错误状态码。

  这里介绍了三类常用的of函数,这些基本满足我们的需求,其他of函数后续如果使用到我们在详细介绍。

二、Pinctrl子系统

  在之前我们进行字符设备编辑的时候已经简单提到过引脚复用功能及其修改方法,接下来将讲述一下引脚复用相关的pinctrl子系统,其主要用于管理芯片的引脚,比如引脚的复用,引脚上下拉,驱动能力等。pinctrl核心层是内核抽象出来,向下为个SoC pin controler drvier提供底层通信接口的能力, 向上为其他驱动提供了控制pin的能力,比如pin复用、配置引脚的电气特性,同时也为GPIO子系统提供pin操作。而pin控制器驱动层,主要提供了操作pin的方法。
在这里插入图片描述

2.1 使用pinctrl设置复用关系

  在设备树中,pinctrl(引脚控制)使用客户端和服务端的概念来描述引脚控制的关系和配置,以便在系统启动时正确地初始化和管理硬件设备的引脚。这种划分提供了对引脚功能的抽象,使得设备驱动程序能够以一种标准化的方式使用这些引脚。

2.1.1 客户端(Client)

  pinctrl客户端可以指定引脚描述、引脚组描述和配置描述,以满足其特定的功能和需求。客户端通常与某个具体的硬件设备相关联,例如一个LED灯或者一个传感器。

node {
    pinctrl-names = "default", "wake up";
    pinctrl-0 = <&pinctrl_hog_1>;
    pinctrl-1 = <&pinctrl_hog_2>;
}

pinctrl-names 属性定义了两个状态名称:default 和 wake up。
pinctrl-0 属性指定了第一个状态 default 对应的引脚配置,引用了 pinctrl_hog_1 节点。
pinctrl-1 属性指定了第二个状态 wake up 对应的引脚配置,引用了 pinctrl_hog_2 节点。这意味着设备可以处于两个不同的状态之一,每个状态分别使用不同的引脚配置。

2.1.2 服务端(Server)

  服务端是设备树中定义引脚配置的部分。它包含引脚组和引脚描述符,为客户端提供引脚配置选择。服务端在设备树中定义了 pinctrl 节点,其中包含引脚组和引脚描述符的定义。以下是pinctrl节点下的描述形式:

&pinctrl {
    /*----------新添加的内容--------------*/
    led_test {
        led_test_pin: led_test_pin { //led_test_pin”节点标签
        	//“rockchip,pins”是固定的格式,后面的内容自定义的,我们将通过这个标签引用这个节点
            rockchip,pins = <0 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>;
        };
    };
};

  如上述的一个外设xxx,由其使用的引脚为GPIO0_C7,”RK_FUNC_GPIO”设置复用功能为GPIO,每个引脚可以复用的功能具体参考下手册,”&pcfg_pull_none”指定上下拉,这里的没有设置不用上下拉。
  之后我们再去编写leds下的设备节点:

my_led: led {
    compatible = "topeet,led";
    gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&rk_led_gpio>;
    };   

  此时我们就完成了GPIO的复用功能设置。

2.2 pinctrl相关结构体

  内核将pinctrl驱动抽象为pinctrl_desc对象,具体到soc厂商的pinctrl驱动便是该对象一个实例, 在驱动所有的pin信息以及对于pin的控制接口实例化成pinctrl_desc,并将pinctrl_desc注册到内核中,如下

struct pinctrl_desc {
   const char *name;
   const struct pinctrl_pin_desc *pins;   //描述一个pin控制器的引脚,
   unsigned int npins;                    //描述该控制器有多少个引脚
   const struct pinctrl_ops *pctlops;     //引脚操作函数,有描述引脚,获取引脚等,全局控制函数
   const struct pinmux_ops *pmxops;       //引脚复用相关的操作函数
   const struct pinconf_ops *confops;     //引脚配置相关
   struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
   unsigned int num_custom_params;
   const struct pinconf_generic_params *custom_params;
   const struct pin_config_item *custom_conf_items;
#endif
};

  一般控制器驱动匹配设备,调用probe,最后会调用pinctrl_register函数,向内核注册pinctrl,产生pinctrl_dev,该函数如下:

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
                                 struct device *dev, void *driver_data);

  描述一个引脚的结构体 struct pinctrl_pin_desc:

pinctrl_pin_desc
struct pinctrl_pin_desc {
   unsigned number;
   const char *name;
   void *drv_data;
};

  很多pin组合在一起,实现特定功能,使用struct group_desc:

group_desc
struct group_desc {
   const char *name;
   int *pins;
   int num_pins;
   void *data;
};

三、GPIO子系统

  在Linux系统中所有的设备树已经设置好了,我们只需要将GPIO口和控制器对接即可。GPIO子系统结构简单描述如下图:
在这里插入图片描述

1. 相关API

函数名描述
of_get_named_gpio( )获取GPIO编号
gpio_request( )申请GPIO
gpio_direction_input( )将GPIO配置为输入
gpio_direction_output( )将GPIO配置为输出
gpio_set_value( )GPIO电平输出
gpio_get_value( )获取电平状态
gpio_free( )释放GPIO资源

1.1 of_get_named_gpio( )函数

//获取gpio编号
static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index)
  • 参数:
    - np:结构体指针
    - propname:属性名
    - index:编号
  • 返回值:
    - 成功:返回gpio编号
    - 失败:返回错误码

1.2 gpio_request( )函数

//申请gpio
static inline int gpio_request(unsigned gpio, const char *label)
  • 参数:
    - gpio:GPIO编号
    - label: 标签 NULL
  • 返回值:
    - 成功: 返回0
    - 失败: 返回错误码

1.3 gpio_direction_input( )函数

//设置gpio为输入
static inline int gpio_direction_input(unsigned gpio)
  • 参数:
    - gpio:GPIO编号
  • 返回值:
    - 成功:返回0
    - 失败:返回错误码

1.4 gpio_direction_output( )函数

//设置gpio为输出
static inline int gpio_direction_output(unsigned gpio, int value)
  • 参数:
    - gpio:GPIO编号
    - value:输出电平(1高电平;0是低电平)
  • 返回值:
    - 成功:返回0
    - 失败:返回错误码

1.5 gpio_set_value( )函数

//设置gpio输出电平
gpio_set_value()static inline void gpio_set_value(unsigned int gpio, int value)
  • 参数:
    - gpio:GPIO编号
    - value:输出电平
  • 返回值:
    - 成功:返回0
    - 失败:返回错误码

1.6 gpio_get_value( )函数

//获取gpio电平状态
static inline int gpio_get_value(unsigned int gpio)
  • 参数:
    - gpio:GPIO编号
  • 返回值:
    - 1: 是高电平
    - 0: 是低电平

1.7 gpio_free( )函数

//释放gpio
static inline void gpio_free(unsigned gpio)
  • 参数:
    - gpio GPIO编号
  • 返回值:无

2.相关结构体

2.1 gpio_device结构体

  gpio_device结构体在Linux内核中用于表示一个GPIO控制器,它管理一个或多个gpio_chip和该组下的所有引脚(pin)。多个gpio_device结构体在内核中通常通过链表来组织和管理

struct gpio_device {
   int                       id;           //gpio控制器的id,也就是第几个
   struct device             dev;
   struct cdev               chrdev;
   struct device             *mockdev;
   struct module             *owner;
   struct gpio_chip  *chip;
   struct gpio_desc  *descs;
   int                       base;         gpio在内核中的编号,申请gpio口时就是根据这个编号来查找
   u16                       ngpio;        //该gpio控制器有多少个引脚
   const char                *label;    //标签
   void                      *data;
   struct list_head        list;

#ifdef CONFIG_PINCTRL
   /*
   * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
   * describe the actual pin range which they serve in an SoC. This
   * information would be used by pinctrl subsystem to configure
   * corresponding pins for gpio usage.
   */
   struct list_head pin_ranges;
#endif
};

id:表示这是系统中第几个GPIO控制器。
dev:通用设备结构体,包含了该设备的基本信息和操作函数。
chip:指向关联的gpio_chip结构体的指针,gpio_chip描述了该组GPIO引脚的具体操作方式。
descs:指向一个gpio_desc结构体的数组,用于描述该组GPIO控制器下的所有引脚。每个引脚都有一个对应的gpio_desc。
base:表示该GPIO控制器中引脚的起始编号,加上偏移量可以得到每个引脚的编号。
ngpio:表示该GPIO控制器管理的引脚数量。
label:用于标识该GPIO控制器的标签或名称。
data:私有数据指针,可以用于存储与该GPIO控制器相关的任何额外信息。
list:链表节点,用于将多个gpio_device结构体链接在一起。

2.2 gpio_chip结构体

  gpio_chip结构体是Linux GPIO子系统中描述GPIO控制器功能和操作的核心数据结构,它包含了控制GPIO引脚所需的所有信息和接口,包括相关操作函数和中断相关。

struct gpio_chip {
   const char                *label;       //GPIO端口的名字,标签
   struct device             *dev;
   struct module             *owner;
   
	//gpio在内核中的编号,申请gpio口时就是根据这个编号来查找
   int                       base;      
       
   u16                       ngpio;         //该控制器的GPIO数目
   const char                *const *names;
   unsigned         		 can_sleep;
   
   /*操作gpio口的方法*/
   int (*request)(struct gpio_chip *chip, unsigned offset);
   void (*free)(struct gpio_chip *chip, unsigned offset);
   int (*direction_input)(struct gpio_chip *chip, unsigned offset);
   int (*get)(struct gpio_chip *chip, unsigned offset);
   int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
   int (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce);
   void (*set)(struct gpio_chip *chip, unsigned offset, int value);
   int (*to_irq)(struct gpio_chip *chip, unsigned offset);
   /*......*/
};

label:一个字符串,用于标识GPIO控制器的名称或标签。
base:表示该GPIO控制器管理的第一个GPIO引脚的编号。
ngpio:表示该GPIO控制器管理的GPIO引脚数量。
descs:指向gpio_desc结构体的数组,详细描述了该GPIO控制器下的每一个引脚的状态和信息。

2.3 gpio_desc结构体

  GPIO Controller中每一个引脚用gpio_desc表示。

struct gpio_desc {
   struct gpio_device        *gdev;      //gpio_device里面描述了gpio信息,
   unsigned long             flags;
   /* flag symbols are bit numbers */
   #define FLAG_REQUESTED    0
   #define FLAG_IS_OUT       1
   #define FLAG_EXPORT       2       /* protected by sysfs_lock */
   #define FLAG_SYSFS        3       /* exported via /sys/class/gpio/control */
   #define FLAG_ACTIVE_LOW   6       /* value has active low */
   #define FLAG_OPEN_DRAIN   7       /* Gpio is open drain type */
   #define FLAG_OPEN_SOURCE 8        /* Gpio is open source type */
   #define FLAG_USED_AS_IRQ 9        /* GPIO is connected to an IRQ */
   #define FLAG_IS_HOGGED    11      /* GPIO is hogged */
   #define FLAG_TRANSITORY 12        /* GPIO may lose value in sleep or reset */

   /* Connection label */
   const char                *label;
   /* Name of the GPIO */
   const char                *name;
};

gdev:指向gpio_device结构体的指针。
flags:标志位字段,用于指示当前GPIO引脚的状态。例如,当使用gpio_request函数申请GPIO资源时,这个字段会被置位;当使用gpio_free函数释放GPIO资源时,这个字段会被清零。
name:GPIO引脚的名称,通常用于调试和日志输出。
label:GPIO引脚的标签,用于在用户空间或其他内核模块中标识这个引脚。

3. GPIO设备树分析

  这里以GPIO3为例:

gpio3: gpio@fe760000 {//节点名
    compatible = "rockchip,gpio-bank"; //厂商名  设备名
    reg = <0x0 0xfe760000 0x0 0x100>; //地址  0x0 0xfe760000控制器地址   0x0 0x100地址范围
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;//中断
    clocks = <&cru PCLK_GPIO3>, <&cru DBCLK_GPIO3>;//时钟
 
    gpio-controller;//标识
    #gpio-cells = <2>;//描述子节点个数
    gpio-ranges = <&pinctrl 0 96 32>; //引脚范围 &pinctrl 引用pinctrl节点 0    96 GPIO起始序号  32引脚范围
    interrupt-controller;
    #interrupt-cells = <2>;//描述子节点个数
};

  在这个实例中,大部分的内容我们已经提前讲到过了,这里主要介绍一下gpio-controller、gpio-cells和gpio-ranges这两个部分:

3.1 gpio-controller属性

  gpio-controller用于标识一个设备节点作为GPIO控制器(GPIO控制器是负责管理和控制GPIO引脚的硬件模块或驱动程序),其通常作为设备节点的一个属性出现,位于设备节点的属性列表中。当一个设备节点被标识为GPIO控制器时,它通常会定义一组GPIO引脚,并提供相关的GPIO控制和配置功能。其他设备节点可以使用该GPIO控制器来控制和管理其GPIO引脚。
  通过使用gpio-controller属性,设备树可以明确标识出GPIO控制器设备节点,使系统可以正确识别和管理GPIO引脚的配置和控制。

3.2 gpio-cells属性

  gpio-cells用于指定GPIO引脚描述符的编码方式(GPIO引脚描述符是用于标识和配置GPIO引脚的一组值,例如引脚编号、引脚属性等),该属性的属性值是一个整数,表示用于编码GPIO引脚描述符的单元数。通常,这个值为2。
gpio-ranges属性是设备树中一个用于描述GPIO范围映射的属性。它通常用于描述具有大量GPIO引脚的GPIO控制器,以简化GPIO引脚的编码和访问。

3.3 gpio-ranges属性

  在设备树中,GPIO控制器的每个引脚都有一个本地编号,用于在控制器内部进行引脚寻址。然而,这些本地编号并不一定与外部引脚的物理编号或其他系统中使用的编号一致。为了解决这个问题,可以使用gpio-ranges属性将本地编号映射到实际的引脚编号。
  gpio-ranges属性是一个包含一系列整数值的列表,每个整数值对应于设备树中的一个GPIO控制器。列表中的每个整数值按照特定的顺序提供以下信息:
(1)外部引脚编号的起始值。
(2)GPIO控制器内部本地编号的起始值。
(3)引脚范围的大小(引脚数量)。

  例如将gpio-ranges属性的值设置为<&pinctrl 0 0 32>,其中<&pinctrl>表示引用了名为pinctrl的引脚控制器节点,0 0 32表示外部引脚从0开始,控制器本地编号从0开始,共映射了32个引脚。这样,gpio-ranges属性将GPIO控制器的本地编号直接映射到外部引脚编号,使得GPIO引脚的编码和访问更加简洁和直观。

免责声明:本文参考了野火和讯为的部分资料,仅供学习参考使用,若有侵权或勘误请联系笔者。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端_调试工具_Chrome Devtools

目录 一、上左侧菜单功能 1.选择功能 2.手机/电脑 显示切换功能 3.内存 4.元素 5.控制台 6.源代码/来源 7.网络 8.应用 9.性能 10.安全 11.Lighthouse 12.记录器 13.性能数据分析 二、上右侧菜单功能 1.警告 2.设置 3....更多功能 谷歌浏览器自带了调试工具C…

TPC-H建表语句(MySQL语法)

TPC-H测试集介绍 TPC-H&#xff08;Transaction Processing Performance Council, Standard Specification, Decision Support Benchmark, 简称TPC-H&#xff09;是一个非常权威数据库基准测试程序&#xff0c;由TPC组织制定。 TPC-H定义了一个包含8个表的模式&#xff08;Sc…

亮数据代理IP助力高效数据采集

文章目录 &#x1f4d1;前言一、爬虫数据采集痛点二、代理IP解决爬虫痛点2.1 为什么可以2.2 本篇采用的代理IP 四、零代码获取数据4.1 前置背景4.2 亮数据浏览器自动抓取数据4.3 使用步骤&#xff1a; 五、数据集5.1 免费样本5.2 定制数据集 &#x1f324;️个人小结 &#x1f…

力扣 503. 下一个更大元素 II

题目来源&#xff1a;https://leetcode.cn/problems/next-greater-element-ii/description/ C题解&#xff1a;因为是循环数组&#xff0c;所以对数组进行了两次遍历&#xff0c;相当于循环。使用了栈&#xff0c;一个存放元素&#xff0c;一个存放索引&#xff0c;用来更新res…

龙迅LT8712X TYPE-C或者DP转HDMI加VGA输出,内置MCU,只是IIS以及4K60HZ分辨率

龙迅LT8712X描述&#xff1a; LT8712X是一种高性能的Type-C/DP1.2到HDMI2.0和VGA转换器&#xff0c;设计用于将USB Type-C源或DP1.2源连接到HDMI2.0和VGA接收器。LT8712X集成了一个DP1.2兼容的接收器&#xff0c;一个HDMI2.0兼容的发射机和一个高速三角机窝视频DAC。此外&…

AppInventor2有没有删除后的撤销功能?

问&#xff1a;不小心删除了组件&#xff0c;能撤回吗&#xff1f; 答&#xff1a;界面&#xff08;组件&#xff09;设计界面&#xff0c;没有撤销功能。代码&#xff08;逻辑&#xff09;设计视图&#xff0c;可以使用 CtrlZ 撤销&#xff0c;CtrlY 反撤销。 界面设计没有撤…

关于2024中国海洋装备博览会(福州)的参展通知

2024中国航洋装备博览会 2024世界航海装备大会 2024中国船舶供应链大会 2024中国航洋装备博览会2024世界航海装备大会 时间地点、规模、主题、定位 1.时间&#xff1a;2024年11月15日至18日 2.地点&#xff1a;福州海峡国际会展中心、冠城大通游艇码头 3.规模&#xff1…

【ROS2大白话】三、给turtlebot3安装realsense深度相机

系列文章目录 【ROS2大白话】一、ROS2 humble及cartorgrapher安装 【ROS2大白话】二、turtlebot3安装 【ROS2大白话】三、给turtlebot3安装realsense深度相机 【ROS2大白话】四、ROS2非常简单的传参方式 文章目录 系列文章目录效果展示一、修改model.sdf文件1. 路径位置2. 修改…

Day25 首页待办事项及备忘录添加功能

​ 本章节,完成首页待办事项及备忘录添加功能 一.修改待办事项和备忘录逻辑处理类,即AddMemoViewModel和AddTodoViewModel 在 AddMemoViewModel逻辑处理类中,为了支持与其关联的View视图文件的数据绑定,需要定义一个与视图文件相匹配的实体类 Model。这个Model将包含 View中…

冯喜运:6.6外汇黄金原油晚间行情预测及独家操作建议

【黄金消息面分析】&#xff1a;周三&#xff08;6月5日&#xff09;&#xff0c;黄金价格继续区间波动并上涨&#xff0c;与周二的价格走势形成鲜明对比&#xff0c;此前美国公布的经济数据好坏参半&#xff0c;可能促使美联储降低借贷成本。美国国债收益率下降&#xff0c;美…

支付宝小众玩法 爱溜达的人不容错过

创建一个简单的程序来帮助用户管理他们的图片&#xff0c;例如筛选分辨率合适、尺寸适中的图片来准备上传&#xff0c;这是一个技术上合理且有益的方向。例如&#xff0c;一个Python脚本使用Pillow库来检查文件夹中图片的尺寸&#xff1a; from PIL import Image import os# 根…

深圳比创达EMC|EMC电磁兼容性行业:技术前沿与市场挑战

在当今高度信息化的社会&#xff0c;电磁兼容性&#xff08;EMC&#xff09;技术已成为各行各业不可或缺的一部分。随着电子设备的日益增多和复杂化&#xff0c;电磁环境日益复杂&#xff0c;电磁兼容性行业面临着前所未有的挑战和机遇。 一、EMC电磁兼容性行业的技术基础 电…

Qt图像处理技术十一:得到QImage图像的马赛克图像

效果图 指数5 指数15 指数40 原理 马赛克的原理很简单&#xff0c;就是取一个值&#xff0c;让这个值作为一个方格子的长宽&#xff0c;如40*40px的格子&#xff0c;取这个区域的平均R G B值&#xff0c;然后这个区域的所有像素点都是这个RGB值即可 源码 QImage applyM…

新能源管理系统主要包括哪些方面的功能?

随着全球对可持续发展和环境保护的日益重视&#xff0c;新能源管理系统已成为现代能源领域的核心组成部分。这一系统不仅涉及对新能源的收集、存储和管理&#xff0c;还包括对整个能源网络进行高效、智能的监控和控制。以下是新能源管理系统主要包含的几方面功能&#xff1a; 一…

STM32 HAL库USART的接收数据方法实现

目录 概述 1 使用STM32Cube生成项目 1.1 软件版本信息 1.2 配置串口相关参数 1.3 生成工程 2 问题描述 3 解决问题 3.1 修改代码 3.2 编写新的回调函数 4 测试 概述 本文主要介绍STM32 HAL库USART的接收数据方法实现&#xff0c;笔者使用的HAL库为STM32Cube_FW_F1_V1.…

鸿蒙开发 之 Stage模型

1.介绍 2.工程目录以及配置文件说明 配置文件 3.UIAbility创建流程 注意&#xff1a;退出应用先销毁的是windowStage之后是ability 4.页面及生命周期 5.UIAbility启动模式

【Mybatis】INSERT INTO 遇到NULL怎么处理?

目录标题 背景-使用Mybatis手写批量插入Insert方法测试核心代码&#xff0c;author字段为null&#xff0c;插入条件怎么写&#xff1f; MybatisPlus解决方案自动填充字段 Mybatis解决方案if标签处理 问题&#xff1a;如果不在工程里面设置默认值&#xff1f;如何直接使用数据库…

Qt窗口与对话框

目录 Qt窗口 1.菜单栏 2.工具栏 3.状态栏 4.滑动窗口 QT对话框 1.基础对话框QDiaog 创建新的ui文件 模态对话框与非模态对话框 2.消息对话框 QMessageBox 3.QColorDialog 4.QFileDialog文件对话框 5.QFontDialog 6.QInputDialog Qt窗口 前言&#xff1a;之前以上…

Java Logback 基本操作

一、环境搭建 配置 maven &#xff1a; JavaWeb开发中的Maven使用_javaweb项目中如何使用manven项目-CSDN博客 配置 eclipse &#xff1a; eclipse新建Maven web项目全过程-CSDN博客 二、下载Logback 下载地址&#xff1a; Maven Repository: ch.qos.logback logback-clas…

系统架构设计师【补充知识】: 应用数学 (核心总结)

24.1 图论之最小生成树 (1)定义: 在连通的带权图的所有生成树中&#xff0c;权值和最小的那棵生成树(包含图中所有顶点的树)&#xff0c;称作最小生成树。 (2)针对问题: 带权图的最短路径问题。 (3)最小生成树的解法有普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法&#xff0c;我…