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_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:输出的初始电平(01)。
   返回值:成功返回0,失败时返回负的错误码。
   gpio_get_value
   int gpio_get_value(unsigned int gpio);
     
   功能:读取GPIO引脚的当前电平。
   参数:
   gpio:要读取的GPIO引脚号码。
   返回值:返回引脚的电平状态(01)。
   gpio_set_value
   void gpio_set_value(unsigned int gpio, int value);
     
   功能:设置GPIO引脚的电平状态。
   参数:
   gpio:要设置的GPIO引脚号码。
   value:要设置的电平状态(01)。
   
   
  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。
  1. 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)进行描述,使得系统能够在引导时识别和配置这些引脚。

设备树包含了引脚的信息,包括引脚的编号、功能、连接的设备等。

  1. GPIO引脚的初始化

    当驱动程序加载时,GPIO子系统会依据设备树的信息进行初始化。引脚的初始化通常包括以下步骤:

    查找设备节点:通过设备树API(如 of_find_node_by_path)查找特定的GPIO设备节点。
    获取GPIO编号:使用API(如 of_get_named_gpio)从设备节点中获取特定引脚的GPIO编号。
    请求GPIO:调用 gpio_request 函数请求使用该GPIO引脚,这样其他驱动就无法使用它。

  2. 配置GPIO方向

    每个GPIO引脚可以配置为输入或输出:

输入模式:使用 gpio_direction_input 设置引脚为输入模式,驱动程序可以读取其电平状态(高或低)。
输出模式:使用 gpio_direction_output 设置引脚为输出模式,驱动程序可以控制引脚的电平输出(高或低)。

  1. 控制GPIO引脚的电平

    设置输出电平:使用 gpio_set_value 函数来设置GPIO引脚的高(1)或低(0)电平。
    读取输入电平:使用 gpio_get_value 函数来读取GPIO引脚的当前电平状态。

  2. 释放GPIO资源
    当驱动程序不再需要使用GPIO引脚时,需要调用 gpio_free 函数来释放该引脚,以便其他驱动可以使用。

  3. 事件驱动
    对于输入模式,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引脚。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

可能只会写BUG

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

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

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

打赏作者

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

抵扣说明:

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

余额充值