LINUX驱动之——GPIO的基本驱动

楼主在网上找了很多相关LINUX驱动开发的相关例程。发现基本的驱动开发都是有框架或者多种开发手段的,我们可以使用不同的开发方式来降低开发难度。有文件系统的开发和LINUX系统自带的内核函数来开发。这两者有什么区别呢?就像是单片机的库函数开发和寄存器开发一样。

先让我们来看一下GPIO子系统在linux内核中的结构吧。该文件目录在/sys/class/gpio/下。

struct gpio_chip {
  const char *label;
  struct device *dev;
  struct module *owner;
  int (*request)(struct gpio_chip *chip, unsigned offset);
  void (*free)(struct gpio_chip *chip, unsigned offset);
  int (*get_direction)(struct gpio_chip *chip, unsigned offset);
  int (*direction_input)(struct gpio_chip *chip, unsigned offset);
  int (*direction_output)(struct gpio_chip *chip, unsigned offset,
  int value);
  int (*get)(struct gpio_chip *chip,unsigned offset);
  void (*set)(struct gpio_chip *chip, unsigned offset, int value);
  void (*set_multiple)(struct gpio_chip *chip, unsigned long *mask,
  unsigned long *bits);
  int (*set_debounce)(struct gpio_chip *chip, unsigned offset,
  unsigned debounce);
  int (*to_irq)(struct gpio_chip *chip, unsigned offset);
  int base;
  u16 ngpio;
  const char *const *names;
  bool can_sleep;
  bool irq_not_threaded;
  bool exported;
#ifdef CONFIG_GPIOLIB_IRQCHIP
  /*
   * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip
   * inside the gpiolib to handle IRQs for most practical cases.
   */
  struct irq_chip *irqchip;
  struct irq_domain *irqdomain;
  unsigned int irq_base;
  irq_flow_handler_t irq_handler;
  unsigned int irq_default_type;
#endif
#if defined(CONFIG_OF_GPIO)
  /*
   * If CONFIG_OF is enabled, then all GPIO controllers described in the
    * device tree automatically may have an OF translation
   */
  struct device_node *of_node;
  int of_gpio_n_cells;
  int (*of_xlate)(struct gpio_chip *gc,
  const struct of_phandle_args *gpiospec, u32 *flags);
};

以下几个是主要的注意事项:

  • request 是特定芯片激活的可选回调函数。如果提供了,在调用gpio_request()或gpiod_get()时,它会在分配GPIO之前执行。

  • free 是一个可选的回调函数,用于特定芯片的释放。如果提供了,那么在调用gpiod_put()或gpio_free()时,它会在GPIO被释放之前执行。

  • get_direction 在您需要知道方向的时候执行GPIO偏移量。返回值应为0表示out, 1表示in(与GPIOF_DIR_XXX相同),或负错误。

  • direction_input 将信号偏移量offset配置为输入,否则返回错误。

  • get 返回GPIO offset 的值;对于输出信号,这将返回实际感知到的值或0。

  • set 指定一个输出值给GPIO offset。


有些的芯片运行的Linux系统里面自带了引脚的编号,如果要获取编号可能要在该芯片文档中用公式计算。以Orange-pi为例它的计算公式如图所示:

利用SYSFS设置GPIO

当然,我们也能查看已经被占用的GPIO端口。

cat /sys/kernel/debug/gpio

例如第一行的意思是编号为8的GPIO的名字是goodix-int,现在是输入模式并且是高电平。已经被占用的GPIO无法使用,除非修改其寄存器。


让我们来尝试的用命令行初始化一个GPIO吧。

echo 10 > export #添加一个GPIO并且设置编号

然后我们进入GPIO10的direction 查看其电平状态。

echo out > direction #改变为输出模式

同理可用次办法尝试设置LED的参数,现在如下图我的电位是低电平所以我的LED灯已经亮起。

现在再次查看设备,GPIO10已被添加并且被设置。


当然我们也能用代码来实现上面的操作,这里举个例子,在我更后面的博客里面我会详细说明。Linux一切皆文件!! 这使得一切都变得非常好理解。

int gpio_set_dir(unsigned int gpio, unsigned int out_flag)
{
    int fd, len;
    char buf[MAX_BUF];
 
    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR  "/gpio%d/direction", gpio);
 
    fd = open(buf, O_WRONLY);
    if (fd < 0) {
        perror("gpio/direction");
        return fd;
    }
 
    if (out_flag)
        write(fd, "out", 4);
    else
        write(fd, "in", 3);
 
    close(fd);
    return 0;
}

例如上面就是设置一个IO输入输出的函数。也是打开文件系统然后往里面写参数就可以了。相信大家学过linux文件系统的都会很简单的理解。


方法2:用linux内核函数来初始化GPIO

话不多说,上框架。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
static int led_init(void)//驱动函数入口
{
}
static void led_exit(void)//驱动函数出口
{
}
module_init(led_init);//注册入口
module_exit(led_exit);//注册出口
MODULE_LICENSE("GPL");//模块的许可证声明这个一般都要有这句话,具体可以去百度

然后丰富函数内容。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/platform.h> //PAD_GPIO_C

static int led_init(void)
{
    gpio_request(10,"myled");
    gpio_direction_output(10, 0);
    printk("led init...\n");
    return 0;
}
static void led_exit(void)
{
    gpio_set_value(10, 1);
    gpio_free(10);
    printk("led exit...\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

这样,一个GPIO的LED驱动就完成啦。下面我将再次说明以下与GPIO相关的简单驱动内核函数。

  • fint gpio_request(unsigned gpio, const char *label)

函数功能:CPU的任何一个GPIO引脚硬件资源对于Linux内核来说都是一种宝贵的资源,如果某个内核程序要想访问这个GPIO引脚资源,首先必须想Linux内核申请资源(类似malloc)

  • void gpio_free(unsigned gpio)

函数功能:内核程序如果不再使用访问GPIO硬件资源记得要将硬件资源归还给linux内核,类似free

  • int gpio_direction_output(unsigned gpio, int value)

函数功能:配置GPIO引脚为输出功能,并且输出一个value值(1高电平/0低电平)

  • int gpio_direction_input(unsigned gpio)

函数功能:配置GPIO为输入功能

  • int gpio_set_value(unsigned gpio, int value)

函数功能:设置GPIO引脚的输出值为value(1:高/0:低),前提是必须首先将GPIO配置为输出功能

  • int gpio_get_value(unsigned gpio)

函数功能:获取GPIO引脚的电平状态,返回值就是引脚的电平状态(返回1:高电平;返回0:低电平),此引脚到底是输入还是输出没关系!


设备树

Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离。在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写。引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码。设备树源文件扩展名为.dts(device tree source),一般放置/arch/arm/boot/dts/目录内。设备树信息在根文件系统中/proc/device-tree目录下,包括根节点’/’的所有属性和子节点。

设备树的格式如下(例子):

    gpios {
        compatible = "gpio-user";                                                                                                                                                                                   
        status = "okay";
        /*input*/
        gpio0 {
            label = "in0";
            gpios = <&gpio1 1 0>;
            default-direction = "in";
         };
        ... ...
         /*output*/
         gpio17 {
            label = "out1";
            gpios = <&gpio3 3 0>;
            default-direction = "out";
        };
    };

为啥有时候要修改设备树呢?因为有时候你不想重新改动驱动代码,或者其他不需要的设备占用了你的IO,你想把它ban掉。有时候开发会涉及到设备树的修改,这里我引用一个大佬写的比较详细的博客:设备树的使用和说明 - 知乎 (zhihu.com)。(如有侵权亲联系我我会立刻删掉)


我们在编译这些文件的时候一般的会用makefile编译。不会写makefile的小伙伴可以去学CMake,足以应付一些不太复杂的文件编译。在这里我也推荐一篇大佬写的不错的博客:https://blog.csdn.net/weixin_44498318/article/details/106219135。(如有侵权亲联系我我会立刻删掉)

  • 3
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: GPIO是英文General Purpose Input/Output的缩写,翻译过来就是通用输入输出。Linux内核提供了GPIO驱动框架,可以通过该框架来控制硬件上的GPIO,实现对外设的控制。 在Linux内核中,GPIO驱动可以分为两类:基于平台的GPIO驱动和基于设备的GPIO驱动。基于平台的GPIO驱动是针对整个平台的GPIO控制,而基于设备的GPIO驱动则是针对单个设备的GPIO控制。 在使用GPIO驱动时,需要先找到所使用的GPIO引脚的编号,并将其映射到内存中的地址。然后通过读写内存中的寄存器来控制GPIO的状态。 对于GPIO的操作可以通过Linux内核提供的sysfs接口来实现。在sysfs中,每个GPIO都被表示为一个文件,可以通过读写文件来进行GPIO的操作。 需要注意的是,在使用GPIO驱动时,需要谨慎操作,避免对硬件造成损坏。同时,还需要了解所使用的硬件设备的特性和限制,以确保GPIO驱动的正确使用。补充说明: 在Linux内核中,GPIO驱动主要由GPIO子系统和GPIO控制器驱动两部分组成。GPIO子系统提供了一个通用的接口,用于操作GPIO控制器驱动,而GPIO控制器驱动则是实际控制硬件的部分。 GPIO子系统可以分为两个部分:GPIO框架和GPIO API。GPIO框架是一个通用的框架,用于管理GPIO控制器和GPIO设备,它定义了一些数据结构和函数接口,用于注册和管理GPIO控制器和GPIO设备。GPIO API是一个用户空间的API,提供了一些函数接口,用于操作GPIOGPIO控制器驱动是针对特定的GPIO控制器的驱动程序,它负责实际控制GPIO的硬件操作。在Linux内核中,每种GPIO控制器都有一个对应的GPIO控制器驱动程序。当使用GPIO时,首先需要通过GPIO子系统将GPIO控制器驱动注册到系统中,然后才能使用GPIO API对GPIO进行操作。 需要注意的是,在使用GPIO驱动时,需要注意GPIO的电气特性,避免对硬件造成损坏。同时,在进行GPIO操作时,还需要注意GPIO的并发访问和竞争问题,以确保系统的正确性和稳定性。 ### 回答2: Linux GPI驱动指的是Linux系统中通过General Purpose Input/Output(GPIO)接口与硬件设备进行交互的驱动程序。GPIO接口是一组通用的、可编程的多功能引脚,可用于连接各种外部设备,例如开关、LED、传感器、驱动器等。 Linux GPIO驱动可以实现对GPIO引脚的读写操作、中断处理等功能。它不仅可以与单片机等嵌入式设备进行通信,还可与各种外接硬件设备进行连接和通信。 在Linux系统中,用户可以通过/sys/class/gpio文件系统来访问GPIO引脚。在使用GPIO驱动时,用户需要首先加载相应的内核模块,然后使用GPIO API来对引脚进行读写操作或开启中断。 GPIO驱动程序需要实现以下功能: 1. 查询GPIO可用性及分配资源。通常,由于GPIO是多路的,因此设备需要分配资源共享GPIO。 2. 初始化GPIO引脚,包括定义方向及设置上下拉电阻等。 3. 实现GPIO引脚的读写操作。 4. 解除分配资源并释放相关资源。 正常情况下,GPIO驱动程序会提供一个设备文件,用户可以通过读写该文件实现GPIO引脚的操作。 总之,Linux GPIO驱动具有良好的可移植性和稳定性,可以方便地与其他硬件设备进行交互,因此被广泛应用于各种嵌入式设备和嵌入式系统中。 ### 回答3: Linux GPIO驱动是一种在嵌入式系统中实现通用输入输出(GPIO,General Purpose Input/Output)功能的软件驱动GPIO是一种非常有用的硬件资源,它可以连接到外部设备,例如LED灯、按键和触摸屏等。 Linux内核支持GPIO操作,当你的嵌入式系统上有GPIO设备时,你可以利用GPIO来读取或设置其状态。驱动程序能够将GPIO标记为输入或输出,并且它们可以在运行时进行配置。 在Linux中,一般有两种方式将GPIO驱动程序添加到内核中:一种是将其编译到内核中,另一种是将其作为模块加载。 GPIO驱动程序等价于操作系统提供的设备文件,例如/dev/gpiochip0,它允许用户空间应用程序访问GPIO。这些设备文件可用于读取或写入GPIO状态。例如,要控制一个LED,需要将GPIO设置为输出模式,然后将其电平设置为高或低即可。 除此之外,GPIO驱动程序也可以实现中断(interrupt)机制,GPIO的状态变化时可以产生中断事件,这常用于处理GPIO键盘或GPIO中断信号的应用场景。 总结来说,Linux内核支持GPIO驱动需要有以下几个步骤:配置GPIO硬件;添加驱动程序;编写用户空间应用程序,按需要读取或设置GPIO状态来和外设交互。GPIO驱动程序是嵌入式系统中非常必要的组成部分,它们能够随时提供接口以方便对外部设备的读写访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值