GPIO子系统
GPIO(通用输入输出,General Purpose Input/Output)子系统
是Linux内核中用于控制和使用GPIO引脚的一组功能。
GPIO引脚是微控制器和计算机系统中用于控制硬件组件的简单接口。
GPIO引脚可以被配置为输入或输出,可以被用来连接按钮、灯、传感器等各种设备。
GPIO子系统的主要功能
输入/输出配置:GPIO引脚可以配置为输入模式(用于读取传感器的状态)或输出模式(用于控制LED、继电器等)。
电平读取和设置:GPIO系统允许驱动程序读取引脚的电平状态(高或低)以及设置引脚的电平。
中断处理:GPIO子系统支持通过中断方式响应引脚状态的变化(如按钮按下),使得系统能及时做出反应。
设备树支持:在多数系统中,GPIO引脚的配置可以通过设备树来描述,便于对硬件的抽象和兼容支持。
1. GPIO引脚请求与释放
int of_get_named_gpio(struct device_node *np, const char *propname, int index);功能:从设备树节点中获取指定索引的GPIO。
参数:
np:设备树节点。
propname:GPIO属性名称。
index:GPIO索引。
返回值:成功返回GPIO号码,失败时返回负的错误码。gpio_request
int gpio_request(unsigned int gpio, const char *label);功能:请求使用一个特定的GPIO引脚。
参数:
gpio:要请求的GPIO引脚号码。
label:用于描述请求使用的GPIO的标签字符串(用于调试和日志记录)。
返回值:成功返回0,失败时返回负的错误码。
gpio_free
void gpio_free(unsigned int gpio);功能:释放先前请求的GPIO引脚,使其可供其他模块使用。
参数:
gpio:要释放的GPIO引脚号码。
2. GPIO引脚设置与读取
gpio_direction_inputint gpio_direction_input(unsigned int gpio);功能:将GPIO引脚配置为输入模式。参数:gpio:要配置的GPIO引脚号码。返回值:成功返回0,失败时返回负的错误码。gpio_direction_outputint gpio_direction_output(unsigned int gpio, int value);功能:将GPIO引脚配置为输出模式,并设置初始电平。参数:gpio:要配置的GPIO引脚号码。value:输出的初始电平(0或1)。返回值:成功返回0,失败时返回负的错误码。gpio_get_valueint gpio_get_value(unsigned int gpio);功能:读取GPIO引脚的当前电平。参数:gpio:要读取的GPIO引脚号码。返回值:返回引脚的电平状态(0或1)。gpio_set_valuevoid gpio_set_value(unsigned int gpio, int value);功能:设置GPIO引脚的电平状态。参数:gpio:要设置的GPIO引脚号码。value:要设置的电平状态(0或1)。
- GPIO中断处理
gpio_to_irqint gpio_to_irq(unsigned int gpio);功能:将GPIO引脚转换为IRQ编号,以便为该引脚设置中断。参数:gpio:目标GPIO引脚的号码。返回值:返回对应的IRQ编号,失败时返回负的错误码。request_irqint request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *devname, void *dev_id);功能:请求特定的IRQ,并注册处理程序。参数:irq:IRQ编号。handler:中断处理函数。flags:用于中断处理的标志。devname:与设备相关的名称(用于调试)。dev_id:用于指定设备的ID,回调时将其传入。返回值:成功返回0,失败时返回负的错误码。free_irqvoid free_irq(unsigned int irq, void *dev_id);功能:释放先前请求的IRQ。参数:irq:要释放的IRQ编号。dev_id:与设备相关的ID。
- GPIO属性与管理
of_gpio_getint of_gpio_get(const struct device_node *np, int index);功能:从设备树节点中获取指定索引的GPIO。参数:np:设备树节点。index:GPIO索引。返回值:成功返回GPIO号码,失败时返回负的错误码。gpiochip_addint gpiochip_add(struct gpio_chip *chip);功能:注册一个GPIO控制器。参数:chip:指向GPIO控制器的结构体。返回值:成功返回0,失败时返回负的错误码。
5. 示例代码
myleds{core_leds{led1 = <&gpioz 5 0>;led2 = <&gpioz 6 0>;led3 = <&gpioz 7 0>;};extend_leds{led1 = <&gpioe 10 0>;led2 = <&gpiof 10 0>;led3 = <&gpioe 8 0>;};
};详细解释
myleds:这是设备树中的一个节点,代表一个名为 myleds 的设备或模块。
core_leds 和 extend_leds:这是 myleds 节点的两个子节点,分别描述了核心LED和扩展LED的配置。
led1, led2, led3:这些是子节点中的属性,描述了每个LED对应的GPIO引脚。
&gpioz 5 0:&gpioz 是一个引用,指向设备树中定义的 gpioz 节点。5 是GPIO引脚的编号,0 是默认的GPIO配置(通常表示默认配置)。
&gpioe 10 0:类似地,&gpioe 指向 gpioe 节点,10 是GPIO引脚的编号。led1 = <&gpioz 5 0>;这里的 0 是第三个参数,通常用于指示与该GPIO引脚相关的特定配置或属性。虽然具体含义在不同平台和驱动中可能有所不同,但通常来说,第三个参数可以表示以下几种情况之一:1. GPIO配置
在一些架构中,0 可能代表GPIO引脚的特定配置选项。例如:输出默认值:在某些情况下,第三个参数可能表示GPIO引脚的初始电平状态(如0表示低电平,1表示高电平)。
其他配置选项:一些GPIO控制器可能会使用这个参数来表示上拉/下拉配置或其它特定设置。
2. 无特定配置
在某些情况下,0 可能只是为了填充占位符,表示没有特定的配置需求。驱动程序在解析引脚时会忽略这个参数,只关注前面的两个参数。3. 特定于平台
不同的硬件平台和驱动程序可能会对这个参数有不同的解释。为了准确理解该参数的含义,你需要查阅具体硬件平台的数据手册或设备树文档,特别是与GPIO控制相关的部分。
myleds.h
#ifndef __MYLEDS_H__
#define __MYLEDS_H__// 定义ioctl命令,用于控制LED开/关
#define LED_ON _IOW('K',0,int) // LED打开命令
#define LED_OFF _IOW('K',1,int) // LED关闭命令// LED状态枚举定义
enum leds_status{OFF, // 关闭状态ON // 开启状态
};// LED编号枚举定义
enum leds_no{LED1, // 核心LED1LED2, // 核心LED2LED3, // 核心LED3LED4, // 扩展LED4LED5, // 扩展LED5LED6, // 扩展LED6
};#endif
myleds.c
#include "myleds.h"
#include <head.h>
#include <sys/ioctl.h>// LED闪烁函数
int led_set_blink(int fd, int which)
{if (ioctl(fd, LED_ON, &which)) // 发送打开LED的ioctl命令PRINT_ERR("ioctl error"); // 输出错误信息sleep(1); // 延时1秒if (ioctl(fd, LED_OFF, &which)) // 发送关闭LED的ioctl命令PRINT_ERR("ioctl error"); // 输出错误信息sleep(1); // 延时1秒return 0; // 成功
}// 主函数
int main(int argc, const char* argv[])
{int fd;// 打开设备if ((fd = open("/dev/myleds", O_RDWR)) == -1)PRINT_ERR("open error"); // 打开失败,输出错误信息while (1) {led_set_blink(fd, LED5); // 持续闪烁LED5}close(fd); // 关闭设备return 0; // 结束程序
}
#include "myleds.h"
#include <head.h>
#include <sys/ioctl.h>// LED闪烁函数
int led_set_blink(int fd, int which)
{if (ioctl(fd, LED_ON, &which)) // 发送打开LED的ioctl命令PRINT_ERR("ioctl error"); // 输出错误信息sleep(1); // 延时1秒if (ioctl(fd, LED_OFF, &which)) // 发送关闭LED的ioctl命令PRINT_ERR("ioctl error"); // 输出错误信息sleep(1); // 延时1秒return 0; // 成功
}// 主函数
int main(int argc, const char* argv[])
{int fd;// 打开设备if ((fd = open("/dev/myleds", O_RDWR)) == -1)PRINT_ERR("open error"); // 打开失败,输出错误信息while (1) {led_set_blink(fd, LED5); // 持续闪烁LED5}close(fd); // 关闭设备return 0; // 结束程序
}
GPIO子系统是怎么控制硬件的
在Linux设备模型中,每个GPIO引脚被抽象为一个设备。
这些设备通过设备树(Device Tree)进行描述,使得系统能够在引导时识别和配置这些引脚。
设备树包含了引脚的信息,包括引脚的编号、功能、连接的设备等。
-
GPIO引脚的初始化
当驱动程序加载时,GPIO子系统会依据设备树的信息进行初始化。引脚的初始化通常包括以下步骤:
查找设备节点:通过设备树API(如 of_find_node_by_path)查找特定的GPIO设备节点。
获取GPIO编号:使用API(如 of_get_named_gpio)从设备节点中获取特定引脚的GPIO编号。
请求GPIO:调用 gpio_request 函数请求使用该GPIO引脚,这样其他驱动就无法使用它。 -
配置GPIO方向
每个GPIO引脚可以配置为输入或输出:
输入模式:使用 gpio_direction_input 设置引脚为输入模式,驱动程序可以读取其电平状态(高或低)。
输出模式:使用 gpio_direction_output 设置引脚为输出模式,驱动程序可以控制引脚的电平输出(高或低)。
-
控制GPIO引脚的电平
设置输出电平:使用 gpio_set_value 函数来设置GPIO引脚的高(1)或低(0)电平。
读取输入电平:使用 gpio_get_value 函数来读取GPIO引脚的当前电平状态。 -
释放GPIO资源
当驱动程序不再需要使用GPIO引脚时,需要调用 gpio_free 函数来释放该引脚,以便其他驱动可以使用。 -
事件驱动
对于输入模式,GPIO引脚也可以配置为触发中断,当引脚状态发生变化时(例如按钮被按下),系统可以生成一个中断信号,通知相关的驱动程序进行处理。
例如,在一个LED驱动程序中,可能会执行以下操作:使用设备树查找与LED相关的GPIO引脚。
请求该GPIO引脚。
设置引脚为输出模式。
在需要时调用 gpio_set_value 控制LED的开启或关闭。
在模块卸载时释放GPIO引脚。
步骤
1. 定义设备树结构:首先,在设备树文件中定义LED的相关信息,包括GPIO编号。比如,你可能会在设备树的某个节点下定义LED的信息,如下例:myleds {core_leds {led1 = <&gpioz 5 0>;led2 = <&gpioz 6 0>;};extend_leds {led1 = <&gpioe 10 0>;led2 = <&gpiof 10 0>;};
};2. 在驱动程序中包含所需的头文件:确保包含必要的头文件来使用设备树相关的API。#include <linux/of.h>
#include <linux/of_gpio.h>3. 查找设备树节点:使用 of_find_node_by_path 或 of_find_node_by_name 函数来查找设备树中定义的LED节点。4. 获取GPIO编号:使用 of_get_named_gpio 函数从找到的节点中获取与LED相关的GPIO引脚编号。5. 请求GPIO引脚:使用 gpio_request 请求获得的GPIO引脚。