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_input
int gpio_direction_input(unsigned int gpio);
功能:将GPIO引脚配置为输入模式。
参数:
gpio:要配置的GPIO引脚号码。
返回值:成功返回0,失败时返回负的错误码。
gpio_direction_output
int gpio_direction_output(unsigned int gpio, int value);
功能:将GPIO引脚配置为输出模式,并设置初始电平。
参数:
gpio:要配置的GPIO引脚号码。
value:输出的初始电平(0或1)。
返回值:成功返回0,失败时返回负的错误码。
gpio_get_value
int gpio_get_value(unsigned int gpio);
功能:读取GPIO引脚的当前电平。
参数:
gpio:要读取的GPIO引脚号码。
返回值:返回引脚的电平状态(0或1)。
gpio_set_value
void gpio_set_value(unsigned int gpio, int value);
功能:设置GPIO引脚的电平状态。
参数:
gpio:要设置的GPIO引脚号码。
value:要设置的电平状态(0或1)。
- GPIO中断处理
gpio_to_irq
int gpio_to_irq(unsigned int gpio);
功能:将GPIO引脚转换为IRQ编号,以便为该引脚设置中断。
参数:
gpio:目标GPIO引脚的号码。
返回值:返回对应的IRQ编号,失败时返回负的错误码。
request_irq
int 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_irq
void free_irq(unsigned int irq, void *dev_id);
功能:释放先前请求的IRQ。
参数:
irq:要释放的IRQ编号。
dev_id:与设备相关的ID。
- GPIO属性与管理
of_gpio_get
int of_gpio_get(const struct device_node *np, int index);
功能:从设备树节点中获取指定索引的GPIO。
参数:
np:设备树节点。
index:GPIO索引。
返回值:成功返回GPIO号码,失败时返回负的错误码。
gpiochip_add
int 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, // 核心LED1
LED2, // 核心LED2
LED3, // 核心LED3
LED4, // 扩展LED4
LED5, // 扩展LED5
LED6, // 扩展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引脚。