字符设备驱动 — 3

字符设备驱动 — 3 GPIO和Pinctrl子系统

概述

目的:不再一味的去配置寄存器,简化 GPIO 驱动开发

image-20240529212705164

要想让 PIN_A 引脚用于 GPIO 或者 I2C,就需要通过 IOMUX 将它们连接到不同的模块

大多数的芯片,没有单独的 IOMUX 模块,引脚的复用、配置等等,就是在GPIO 模块内部实现的。在硬件上 GPIO 和 Pinctrl 是如此密切相关,在软件上它们的关系也非常密切。

pinctrl:引脚控制,用来配置比如引脚mux复用信息,引脚电器属性(比如上/下拉、速度、驱动能力等)信息。

gpio:控制gpio的输入输出,以及高低电平。

Pinctrl

—— 用于配置引脚,一般都在设备树中使用

两个重要的概念:pin controller、client device

1. pin controller:

芯片手册里你找不到 pin controller,它是一个软件上的概念。对应 IOMUX──用来复用引脚,还可以配置引脚(比如上下拉电阻等)(格式没有统一的标准,下面的 groups、function 只是示例)

2. client device:

“客户设备”,客户是指Pinctrl系统的客户,即使用Pinctrl系统的设备,使用引脚的设备

下图,左边为 pin controller,右边为 client device

image-20240530193232288

  1. pin state

对于一个"client device",如UART设备,它有多个“状态”:default、sleep等,那么对应的引脚也有这些状态。

比如,默认状态下,UART设备正常工作,那么所用的引脚就要复用为UART功能;
休眠状态下,为了省电,可以把这些引脚复用为GPIO功能;或者直接把它们配置输出高电平。

上图pinctrl-names定义2种状态:default,sleep。
第0种状态用到的引脚在pinctrl-0中定义,它是state_0_node_a,位于pincontroller节点中。
第1种状态用到的引脚在Pinctrl-1中定义,它是state_1_node_a,位于pincontroller节点中。

当UART设备处于default状态时,pinctrl子系统会自动根据上述信息将所用引脚复用为uart0功能。
当UART设备处于sleep状态时,pinctrl子系统会自动根据上述信息将所用引脚配置为高电平。

  1. groups和function

一个设备会用到一个或多个引脚,这些引脚可以归纳为一组(group);
这些引脚可以复用为某个功能:function,如I2C功能,SPI功能,GPIO功能等。当然:一个设备可以用到多组引脚,比如A1、A2两组引脚,A1组复用为F1功能,A2组复用为F2功能:

image-20240530193614336

imx6ull 中的使用示例:

image-20240530193901891

image-20240530193934688

GPIO

要操作 GPIO 引脚,需要先把所用引脚通过 pinctrl 子系统配置为 GPIO 功能,这是前提条件

使用流程:

  1. 在设备树中指定 GPIO 引脚
  2. 在驱动代码中使用 GPIO子系统的标准函数来获取 GPIO、设置 GPIO 方向、读取 / 设置 GPIO 的值

1. 在设备树中指定引脚:

所有 ARM 芯片的引脚都是分为组的,这通常由芯片厂家设置好,每一组引脚都是一个节点,这个节点含有 gpio-controller 属性,这些节点都写在 dtsi 文件中

image-20240530195938815

我们只需要关心两个属性 gpio-controller、gpio-cells

gpio-controller:表明这是一个引脚控制器,它下面有很多的引脚

gpio-cells:表示这个控制器下每一个引脚要用 2 个 32 位的数 ( cell ) 来描述(注意这是在去掉引脚引用的情况下)

普遍用法:第1个 cell 来表示是哪一个引脚,第二个 cell 来表示有效电平(通常)

eg: gpio1 中明确表示 gpio-cells = <2>; 所以 20 代表20号引脚,GPIO_ACTIVE_LOW 表示低电平有效

image-20240530200146985

​ 当然 第3个参数也可以设置为 IRQ_TYPE_EDGE_FALLING 表示这是一个中断引脚

image-20240530195915950

2. 在驱动中使用:

有两套接口:

  1. 基于描述符(descriptor_based),函数有前缀 “ gpiod_ ”,使用 gpio_desc 结构体来表示引脚
  2. 老的(legacy) ,函数有前缀 “ gpio_ ”,使用一个整数来表示一个引脚

操作一个引脚:首先要 get 引脚,然后设置方向,最后读取 / 设置 引脚的值

#include <linux/gpio/consumer.h> // descriptor-based
#include <linux/gpio.h> // legacy

image-20240530201021919

注意:” devm_ “ 表示设备资源管理,这是一种自动释放资源的机制,它的思想是 “ 资源是属于设备的,设备不存在时资源就可以自动释放 ”,比如在 Linux 开发过程中,先申请了 GPIO,再申请内存;如果内存申请失败,那么在返回之前就需要先释放 GPIO 资源。如果使用 devm 的相关函数,在内存申请失败时可以直接返回:设备的销毁函数会自动地释放已经申请了的 GPIO资源。

所以,建议使用 “ devm_ ” 版本的相关函数

使用示例:

设备树中指定引脚:

foo_device {
    compatible = "acme,foo";
    ...
    led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>,    /* red */
    			<&gpio 16 GPIO_ACTIVE_HIGH>,    /* green */
    			<&gpio 17 GPIO_ACTIVE_HIGH>;    /* blue */
    power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
};

驱动中使用 GPIO 子系统:

struct gpio_desc *red, *green, *blue, *power;
red   = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH);
green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH);
blue  = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH);
power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);

gpiod_set_value(power, 1);

注意:gpiod_set_value 函数所设置的值不是物理值,而是逻辑值

比如这里设置的是1,但是在设备树中,这个引脚是低电平有效,所以实际电路会输出低电平

  • 46
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值