Linux GPIO驱动

1. 简介

        驱动程序中使用 GPIO 之前需要向 gpio 子系统申请,申请成功之后才可以使用,
例如设置 GPIO 的输入、输出方向,设置 GPIO 输出高或低电平、读取 GPIO 输入电平等等。

2.设备树描述

2.1 gpio设备定义,如下:

“ &gpio0”表示 led 引脚所使用的 IO 属于 gpio0

“ 7”表示 gpio0 的第 7 号 IO,

“ GPIO_ACTIVE_HIGH” 表示高电平有效

2.2 GPIO控制器在设备树中定义,如下:

         “ gpio-controller”表示 gpio0 节点是个 GPIO 控制器,表示这个节点对应的驱动程序是 gpio 驱动。

        #gpio-cells”属性和“ #address-cells”类似, 在 gpio0 节点中#gpio-cells的值等于 2,表示一共有两个 cell。

        大家可以这样理解,使用 gpio0 的时候,需要传递 2 个参数过去:

        第一个参数为 GPIO 编号,比如“ &gpio0 7”就表示 GPIO0_IO07。

        第二个参数表示 GPIO极性,如果为 0(GPIO_ACTIVE_HIGH)的话表示高电平有效,如果为 1(GPIO_ACTIVE_LOW)的话表示低电平有效。

3. gpio驱动介绍 

3.1 API函数

1 申请/释放

int gpio_request(unsigned gpio, const char *label)
void gpio_free(unsigned gpio)

gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信
息,此函数会返回这个 GPIO 的标号。
label:给 gpio 设置个名字。
返回值: 0,申请成功;其他值,申请失败。

 2.输入/输出

int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)

3.设置/获取GPIO的值

int gpio_get_value(unsigned gpio)
void gpio_set_value(unsigned gpio, int value)

注意:gpio设置为输出模式,不能通过gpio_get_value获取gpio状态!!!
同理,gpio设置为输入模式,通过gpio_set_value设置没有意义。

3.2 OF相关函数

1.获取设备树某个属性里面定义了几个 GPIO 信息

int of_gpio_named_count(struct device_node *np, const char *propname)
int of_gpio_count(struct device_node *np)

np:设备节点。
propname:要统计的 GPIO 属性。

返回值: 正值,统计到的 GPIO 数量;负值,失败。

 
of_gpio_named_count 统计任意属性的 GPIO 信息

of_gpio_count  统计的是“ gpios”这个属性的 GPIO 数量

举例:
gpios = <  0
                &gpio1 1 2
                0
                &gpio2 3 4>;

        “ gpios”节点一共定义了 4 个 GPIO,但是有 2 个是空的,没有实际的含义。

        通过 of_gpio_named_count 函数统计出来的 GPIO 数量就是 4 个。

 2.获取 GPIO 编号

int of_get_named_gpio(struct device_node *np, const char *propname, int index)

 np:设备节点

propname:包含要获取 GPIO 信息的属性名。
index: GPIO 索引,因为一个属性里面可能包含多个 GPIO,

        此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。

返回值: 正值,获取到的 GPIO 编号;负值,失败。

说明:此函数会将设备树中类似<&gpio0 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号。

Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号。

4.pinctrl子系统

根据imx6ull芯片讲解。

4.1作用

        ①、获取设备树中 pin 信息。
        ②、根据获取到的 pin 信息来设置 pin 的复用功能
        ③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。

4.2 设备树中描述

 说明:

        compatible 属性值为“fsl,imx6ul-iomuxc”,在 Linux 内核源码中全局搜索字符串“fsl,imx6ul-iomuxc”就会找到 I.MX6ULL 这颗 SOC 的 pinctrl 驱动文件。

UART1_RTS_B 的配置信息如下:
        MX6UL_PAD_UART1_RTS_B__GPIO1_IO19  0x17059

0x17059 就是 conf_reg 寄存器值

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 的宏定义内容如下:

#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0

0x0090 0x031C 0x0000 0x5 0x0,表示为:
<mux_reg conf_reg input_reg mux_mode input_val>

0x0090: mux_reg 寄存器偏移地址

0x031C: conf_reg 寄存器偏移地址

0x0000: input_reg 寄存器偏移地址
0x5 : mux_reg 寄 存 器 值
0x0: input_reg 寄存器值,在这里无效。
 

5.GPIO驱动-led实验

5.1 修改设备树


	led {
		compatible = "m0mo,led";
		status = "okay";
		default-state = "on";
		led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>;
	};

修改后,编译设备树:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- system-top.dtb

 5.2 gpio驱动demo

  
    /*gpio操作:1.获取gpio; 2.申请使用; 3.设置输入输出 4.设置初始值*/
    gpioled.gpio =  of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    printk("of_get_named_gpio\r\n");
    ret = gpio_request(gpioled.gpio, "led-gpio");
    ret = gpio_direction_output(gpioled.gpio, 1);
    gpio_set_value(gpioled.gpio, 0); 

5.3 led-gpio驱动demo

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>

struct gpioled_dev {
    struct cdev cdev;
    struct class *class;
    struct device *device; 
    dev_t devid;
    int major;
    int minor;

    struct device_node *nd;
    int gpio;
};
 
static struct gpioled_dev gpioled;
 

static ssize_t gpio_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offset)
{
    char kernelbuf[1] = {0};
    copy_from_user(kernelbuf,buf,1);

    printk("kernelbuf[0];%d\r\n",kernelbuf[0]);
    gpio_set_value(gpioled.gpio,kernelbuf[0]); 
    return 0;
}

static struct file_operations m_fops = {
    .owner = THIS_MODULE,
    .write = gpio_write,
};
 
//驱动入口
static int __init xxx_init(void)
{
    const char *str;
    //找到led节点    
    gpioled.nd = of_find_node_by_path("/led");
    //读取属性
    int ret = of_property_read_string(gpioled.nd,"status",&str);
    if(ret == 0)
    {
        if(strcmp("okay",str) != 0)
        {
            return -EINVAL;
        }
    }
    
    /*gpio操作:1.获取gpio; 2.申请使用; 3.设置输入输出 4.设置初始值*/
    gpioled.gpio =  of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    printk("of_get_named_gpio\r\n");
    ret = gpio_request(gpioled.gpio, "led-gpio");
    ret = gpio_direction_output(gpioled.gpio, 1);
    gpio_set_value(gpioled.gpio, 0); 


    //1.创建设备号
    if(gpioled.major){ 
        gpioled.devid = MKDEV(gpioled.major,0); 
        register_chrdev_region(gpioled.devid,1,"test");
    }else{
        alloc_chrdev_region(&gpioled.devid,0,1,"test");
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
    }
    printk("newcheled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
 
    //2.初始化cdev
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev,&m_fops);
 
    //3.添加cdev
    cdev_add(&gpioled.cdev,gpioled.devid,1);
 
    //4.创建类
    gpioled.class = class_create(THIS_MODULE,"xxx");
    
    //5.创建设备
    gpioled.device = device_create(gpioled.class,NULL,gpioled.devid,NULL,"xxx");    
 
 
    return 0;
}
 
//驱动退出
static void __exit xxx_exit(void)
{

    gpio_free(gpioled.gpio);

    //删除设备
    device_destroy(gpioled.class,gpioled.devid);
    
    //删除类
    class_destroy(gpioled.class);
   
    //删除cdev
    cdev_del(&gpioled.cdev); 
    
    //注销
    unregister_chrdev_region(gpioled.devid,1);
}
 
module_init(xxx_init);
module_exit(xxx_exit);
 
 
MODULE_AUTHOR("m0mo");
MODULE_DESCRIPTION("New Char Device Driver");
MODULE_LICENSE("GPL");

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值