Linux 的 pinctrl 和 gpio 子系统

目录

一、引言

二、pinctrl 子系统详解

1.pinctrl 子系统的功能与作用

2.pinctrl 子系统的架构

三、gpio 子系统详解 

1.gpio 子系统的功能与作用

2.gpio 子系统的架构

四、pinctrl 和 gpio 子系统的协同工作

五、应用实例

1.控制 LED 灯

2.读取按键状态  

六、总结 


 

一、引言

        在 Linux 系统的设备驱动开发领域,pinctrl 和 gpio 子系统是至关重要的组成部分。它们为硬件工程师和驱动开发者提供了便捷且高效的方式来管理和控制芯片的引脚资源,使得系统能够精准地与外部设备进行交互,无论是简单的 LED 控制、按键读取,还是复杂的传感器通信、外设接口配置,都离不开这两个子系统的支持。本文将深入剖析 pinctrl 和 gpio 子系统的工作原理、架构以及它们在实际应用中的使用方法,帮助读者更好地理解和掌握这两个关键技术。

二、pinctrl 子系统详解

1.pinctrl 子系统的功能与作用

        pinctrl 子系统的核心任务是对芯片引脚进行灵活的配置,包括引脚的功能复用和电气特性的设置。现代芯片的引脚通常具备多种功能,例如一个引脚既可以作为通用输入输出(GPIO)引脚,也可以配置为特定外设(如 SPI、I2C、UART 等)的通信引脚。通过 pinctrl 子系统,开发者可以根据系统的需求,在软件层面动态地选择引脚的具体功能,从而极大地提高了引脚资源的利用率和系统设计的灵活性。

        同时,pinctrl 子系统还能够精确地控制引脚的电气特性,如设置引脚的上下拉电阻、驱动强度、输出速度等。合理地配置这些电气参数对于确保系统的稳定性和可靠性至关重要。例如,在连接一个对信号上升沿和下降沿时间敏感的外部设备时,可以通过 pinctrl 子系统调整引脚的输出速度,以满足设备的通信要求;对于一些容易受到干扰的引脚,适当设置上下拉电阻可以有效地避免信号浮空,增强系统的抗干扰能力

2.pinctrl 子系统的架构

设备树(Device Tree)中的体现:在 Linux 设备树中,pinctrl 子系统的配置信息通过专门的节点进行描述。每个引脚组(pin group)都有对应的节点,这些节点包含了引脚功能复用和电气特性配置的详细信息。例如,以下是一个简单的设备树 pinctrl 节点示例,用于配置一个 GPIO 引脚:

pinctrl_gpio: gpio_pins {
    pinmux = <PIN_FUNCTION_GPIO>;
    drive-strength = <2>;
    bias-pull-up;
};

在这个示例中,pinmux指定了引脚的功能为 GPIO,drive - strength配置了引脚的驱动强度,bias - pull - up表示启用上拉电阻。设备树中的这些信息为 pinctrl 子系统提供了硬件描述的基础,使得系统能够在启动时根据这些配置来正确地初始化引脚。 

  • 核心层:pinctrl 子系统的核心层负责解析设备树中的 pinctrl 信息,并将这些信息转换为内核可以理解和操作的格式。它提供了统一的接口,使得不同的驱动程序可以方便地获取和应用引脚配置信息。核心层还承担着管理引脚资源的重要任务,确保不同的设备在使用引脚时不会发生冲突。例如,当两个设备都试图使用同一个引脚时,核心层会根据预先定义的优先级或规则来分配引脚资源,保障系统的稳定运行。
  • 驱动层:驱动层与芯片的硬件紧密相连,负责具体实现引脚的配置功能。它根据核心层传递过来的配置信息,通过操作芯片内部的寄存器来完成引脚功能复用和电气特性的设置。不同芯片厂商的 pinctrl 驱动实现可能会有所差异,但都遵循 pinctrl 子系统的整体框架和接口规范。例如,对于某些芯片,驱动层可能需要通过特定的寄存器写入序列来设置引脚的功能和电气参数,而对于其他芯片,可能有专门的配置模块来完成这些操作。

三、gpio 子系统详解 

1.gpio 子系统的功能与作用

        gpio 子系统为开发者提供了一种简单而高效的方式来操作芯片的 GPIO 引脚。它允许开发者将 GPIO 引脚作为输入或输出引脚使用,从而实现与外部设备的数字信号交互。通过 gpio 子系统,开发者可以轻松地实现诸如读取外部按键状态、控制 LED 亮灭、与简单传感器通信等功能。

        在实际应用中,gpio 子系统屏蔽了底层硬件的复杂性,使得驱动开发者可以专注于实现设备的功能逻辑,而无需深入了解芯片 GPIO 模块的具体硬件细节。这大大提高了开发效率,同时也增强了代码的可移植性,因为相同的 gpio 子系统 API 可以在不同的芯片平台上使用,只要芯片的 GPIO 模块能够被正确地驱动和支持。

2.gpio 子系统的架构

  • 设备树(Device Tree)中的表示:在设备树中,每个使用 GPIO 引脚的设备都有对应的节点,这些节点通过特定的属性来关联到相应的 GPIO 控制器和引脚。例如,一个连接到 GPIO 的 LED 设备节点可能有这样的属性:
gpios = <&gpio_controller 10 GPIO_ACTIVE_LOW>;

这表示该 LED 设备所使用的 GPIO 来自gpio_controller,引脚编号为 10,并且是低电平有效。通过这种方式,设备树清晰地描述了设备与 GPIO 引脚之间的连接关系,为 gpio 子系统提供了必要的硬件信息。 

  • 内核驱动框架
    • 核心层功能:gpio 子系统的核心层提供了统一的接口和管理机制。它负责解析设备树中的 GPIO 相关信息,将其转换为内核可以理解和操作的格式,并维护着 GPIO 资源的分配情况。核心层还提供了一系列的 API,供设备驱动程序使用,这些 API 包括gpio_request(用于请求一个 GPIO 引脚)、gpio_direction_input(设置 GPIO 为输入模式)、gpio_direction_output(设置 GPIO 为输出模式)、gpio_get_value(获取 GPIO 引脚的值)和gpio_set_value(设置 GPIO 引脚的值)等。通过这些 API,驱动开发者可以方便地操作 GPIO 引脚,实现设备的功能。
    • 设备驱动层操作:在设备驱动层,开发者通过调用 gpio 子系统提供的 API 来实现对 GPIO 引脚的具体操作。例如,以控制一个 LED 灯为例,在设备驱动的初始化函数中,可能会有如下代码:
struct gpio_desc *led_gpio;
int ret;
led_gpio = gpio_to_desc(10);
ret = gpio_request(led_gpio, "led");
if (ret) {
    printk(KERN_ERR "Failed to request GPIO for LED\n");
    return ret;
}
gpio_direction_output(led_gpio, 0);

这段代码首先通过gpio_to_desc函数获取 GPIO 描述符,然后使用gpio_request请求该 GPIO 引脚,最后将其设置为输出模式并初始化为低电平,从而关闭 LED 灯。通过这种方式,设备驱动程序可以方便地控制 GPIO 引脚的输入输出状态,实现与外部设备的交互。 

四、pinctrl 和 gpio 子系统的协同工作

在实际的 Linux 设备驱动开发中,pinctrl 和 gpio 子系统通常需要协同工作,以实现对芯片引脚的完整配置和控制。

首先,pinctrl 子系统在系统启动初期根据设备树的配置信息,对芯片的引脚进行功能复用和电气特性的初始化设置。例如,将需要作为 GPIO 使用的引脚配置为 GPIO 功能,并设置好合适的电气参数,如上下拉电阻、驱动强度等。

然后,gpio 子系统在设备驱动程序的运行过程中,根据应用程序的需求,对已经通过 pinctrl 子系统配置好的 GPIO 引脚进行具体的输入输出操作。例如,读取外部按键连接的 GPIO 引脚的电平状态,或者控制连接到 GPIO 引脚的 LED 灯的亮灭。

这种协同工作的方式使得开发者可以在不同的层次上对芯片引脚进行灵活的管理和控制,既保证了系统的硬件配置的准确性和稳定性,又方便了设备驱动程序的开发和实现。

五、应用实例

1.控制 LED 灯

假设我们有一个连接到芯片 GPIO 引脚的 LED 灯,通过 pinctrl 和 gpio 子系统来实现对它的控制。

首先,在设备树中,我们需要配置对应的 pinctrl 节点,将连接 LED 的引脚配置为 GPIO 功能,并设置合适的电气特性,如设置为推挽输出模式,适当的驱动强度等。例如:

然后,在 LED 设备节点中,通过gpios属性关联到对应的 GPIO 引脚,并指定相关参数,如:

my_led {
    gpios = <&gpio_controller 5 GPIO_ACTIVE_HIGH>;
    pinctrl - names = "default";
    pinctrl - 0 = <&pinctrl_led>;
};

在设备驱动程序中,我们可以使用 gpio 子系统的 API 来控制 LED 的亮灭。例如,在驱动的初始化函数中,首先获取 GPIO 引脚的描述符,并请求该引脚: 

struct gpio_desc *led_gpio;
int ret;
led_gpio = gpio_to_desc(5);
ret = gpio_request(led_gpio, "my_led");
if (ret) {
    printk(KERN_ERR "Failed to request GPIO for LED\n");
    return ret;
}

然后,在需要点亮 LED 的地方,使用gpio_set_value函数将引脚设置为高电平:

gpio_set_value(led_gpio, 1);

要关闭 LED,则将引脚设置为低电平:

gpio_set_value(led_gpio, 0);


2.读取按键状态  

对于一个连接到芯片 GPIO 引脚的按键,同样需要 pinctrl 和 gpio 子系统的配合。

在设备树中,配置按键连接引脚的 pinctrl 节点,将其设置为 GPIO 输入功能,并根据需要设置上下拉电阻,例如:

pinctrl_key: key_pins {
    pinmux = <PIN_FUNCTION_GPIO>;
    bias-pull-up;
};

在按键设备节点中,关联到相应的 GPIO 引脚和 pinctrl 节点: 

my_key {
    gpios = <&gpio_controller 8 GPIO_ACTIVE_LOW>;
    pinctrl - names = "default";
    pinctrl - 0 = <&pinctrl_key>;
};

在设备驱动程序中,首先获取 GPIO 引脚的描述符并请求该引脚:

truct gpio_desc *key_gpio;
int ret;
key_gpio = gpio_to_desc(8);
ret = gpio_request(key_gpio, "my_key");
if (ret) {
    printk(KERN_ERR "Failed to request GPIO for key\n");
    return ret;
}

然后,在需要读取按键状态的地方,使用gpio_get_value函数获取引脚的电平状态: 

int key_value = gpio_get_value(key_gpio);
if (key_value == 0) {
    // 按键按下
} else {
    // 按键松开
}

 

六、总结 

        通过对 Linux 的 pinctrl 和 gpio 子系统的深入探讨,我们可以看到它们在设备驱动开发中发挥着不可或缺的作用。pinctrl 子系统为芯片引脚的配置提供了强大的功能复用和电气特性控制能力,而 gpio 子系统则为开发者提供了便捷的 GPIO 引脚操作接口。两者的协同工作使得开发者能够高效地管理和控制芯片的引脚资源,实现与外部设备的稳定、可靠的通信和交互。在实际的开发过程中,深入理解和熟练掌握这两个子系统的工作原理和使用方法,将有助于我们开发出更加高效、稳定的设备驱动程序,为 Linux 系统在各种硬件平台上的应用提供有力的支持。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千千道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值