Linux_kernel驱动之GPIO子系统

前言:
gpio子系统的内容在drivers/gpio文件夹下,主要文件有:

  1. devres.c :devres.c是针对gpio api增加的devres机制的支持
  2. gpiolib.c :gpiolib.c是gpio子系统的核心实现
  3. gpiolib-of.c :gpiolib-of.c是对设备树的支持
  4. gpiolib-acpi.c :
  5. gpio-xxx.c :

目录:
一、驱动流程步骤+函数分析
二、驱动讲解
三、驱动伪代码实现
四、驱动思想分析
**

正文;

一、驱动流程步骤
GPIO子系统驱动流程分为以下几个步骤:
1、请求一个GPIO组
2、设置GPIO方向输出\输入
3、将GPIO通过System导出,应用层可以通过文件操作gpio
4、如果设置为输出模式(设置值),输入模式(获取值)
5、将GPIO转为对应的IRQ,然后注册改IRQ的中断handler
6、释放请求的一个或者一个组GPIO

二、函数分析;
a、申请一个GPIO组:

int gpio_request(unsigned gpio, const char *label):
参数1:gpio号
参数2:这个数据保留到pin数据结构们勇于debug
返回值:零正常,负数错误
经过拓展的函数:(gpio_request_one),(gpio_request_array)

b、释放一个GPIO组:

void gpio_free(unsigned gpio)
参数1:GPIO编号
返回值:无

c、配置GPIO对应的方向:
(设置输入方向函数)

int gpio_direction_input(unsigned gpio); 
参数1:GPIO口的编号
返回值:零正常,负数错误

(设置输出方向函数)

int gpio_direction_output(unsigned gpio, int value); 
参数1:GPIO口的编号
参数2:GPIO口默认端口输出0还是1
返回值:零正常,负数错误

d、 测试GPIO是否合法:

 int gpio_is_valid(int number)
参数;GPIO编号
返回值:零正常,负数错误

e、获取GPIO的引脚值或者设置GPIO引脚值
(输出状态)

 void gpio_set_value(unsigned gpio, int value); 
参数1:GPIO的编号
参数2:设置对应的输出值(0/1)
返回值:无

(输入状态)

  int gpio_get_value(unsigned gpio);
参数1:GPIO编号
返回值:对应的GPIO引脚状态(0/1),负数错误

f、获取对应引脚的GPIO编号

int gpio_to_irq(unsigned gpio); 
 参数1:GPIO编号
返回值:中断编号
拓展:将对应的中断编号给request_irq()函数进行注册,实现中断服务函数
释放对应的中断资源fee_irq(),两个函数的形参都必须要中断编号

g、将GPIO端口导出到用户空间、
(GPIO导入函数)

 int gpio_export(unsigned gpio, bool direction_may_change); 
参数1、GPIO编号
参数2、表示用户程序是否允许修改gpio的方向
假如可以则参数为为真:direction_may_change
拓展:分析这个函数的本身就是创建system(文件子系统),生成文件可以在以  	
            路径找到( /sys/class/gpio*  )
export/unexport文件
             gpioN指代具体的gpio引脚
             gpio_chipN指代gpio控制器

(撤销GPIO导入)

void gpio_unexport(unsigned gpio)
参数1:GPIO编号
返回值:无

驱动工程师需要实现的步骤
要注意的是对应的内核版本有没有对应的设备树。一般来说3.0一下版本的内核是没有带设备树
作为一种情况,在3.10以上的内核是带有对应的设备树所以要注意;帮助手册一般是(gpio.txt)

没有带设备树的
通过GPIO.h头文件就可以获取得到。

带设备树的
更新一下设备树:make ARCH=arm exynos4_defconfig
make ARCH=arm dtbs

一、首先需要去到对应芯片的设备树的下面添加一个设备树节点

led_noe{
gpiox_1_led = <&gpx1 0 0 >   
}
<&gpx1 0 0 >分析;
参数1:继承对应的GPIO
参数2:具体的GPIO号
参数3:默认的GPIO电平状态

首先在对应的设备树下做好一个节点:要注意的是这个节点是在设备树根目录下。

内核提供一个函数通过设备树给我们获取到对应的GPIO号:

static inline int of_get_named_gpio(struct device_node *np,
                           const char *propname, int index)
功能:解析设备树的中的GPIO
参数1:设备树节点结构体 通过of_find_node_by_path()函数获取得到
参数2:设备树的键值堆   gpiox_1_led
参数3:索引号		(led_noe第几个的GPIO   从0开始)
返回值:成功返回GPIO号   失败错误码

上面函数的参数1,作为一个结构体,是通过下面这个函数获取回来的。

struct device_node *of_find_node_by_path(const char *path)
参数1:节点路径    (  #define    "/led_node"   ) 
返回值:地址,错误为NULL

二、通过获取回来的GPIO号,然后进行一个检查是否合法

 int gpio_is_valid(int number)
三、设置GPIO的输入或者输出方向

i

nt gpio_direction_input(unsigned gpio); 或者是
       int gpio_direction_output(unsigned gpio, int value); 
四、可以获取GPIO的状态或者设置GPIO的状态
当输出的状态下可以读取或者设备GPIO的状态
 void gpio_set_value(unsigned gpio, int value); 
 int gpio_get_value(unsigned gpio);

或者当时输入方向的时候可以读取对应的GPIO引脚状态

 int gpio_get_value(unsigned gpio);

五、要在退出函数哪里将申请的这些资源都退出

void gpio_unexport(unsigned gpio)
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的GPIO按键驱动程序,使用input子系统和按键消抖: ```c #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/delay.h> #define GPIO_KEY 17 #define KEY_NAME "gpio-key" static struct input_dev *input; static int irq_num; static irqreturn_t gpio_key_isr(int irq, void *dev_id) { int state = gpio_get_value(GPIO_KEY); input_report_key(input, KEY_ENTER, !state); input_sync(input); msleep(20); // 消抖 return IRQ_HANDLED; } static int gpio_key_init(void) { int ret; printk(KERN_INFO "gpio-key: initializing\n"); ret = gpio_request(GPIO_KEY, "gpio-key"); if (ret) { printk(KERN_ERR "gpio-key: unable to request GPIO %d\n", GPIO_KEY); return ret; } ret = gpio_direction_input(GPIO_KEY); if (ret) { printk(KERN_ERR "gpio-key: unable to set GPIO %d as input\n", GPIO_KEY); gpio_free(GPIO_KEY); return ret; } input = input_allocate_device(); if (!input) { printk(KERN_ERR "gpio-key: unable to allocate input device\n"); gpio_free(GPIO_KEY); return -ENOMEM; } input->name = KEY_NAME; input->evbit[0] = BIT_MASK(EV_KEY); input_set_capability(input, EV_KEY, KEY_ENTER); ret = input_register_device(input); if (ret) { printk(KERN_ERR "gpio-key: unable to register input device\n"); input_free_device(input); gpio_free(GPIO_KEY); return ret; } irq_num = gpio_to_irq(GPIO_KEY); ret = request_irq(irq_num, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, KEY_NAME, NULL); if (ret) { printk(KERN_ERR "gpio-key: unable to request IRQ for GPIO %d\n", GPIO_KEY); input_unregister_device(input); input_free_device(input); gpio_free(GPIO_KEY); return ret; } printk(KERN_INFO "gpio-key: initialized\n"); return 0; } static void gpio_key_exit(void) { printk(KERN_INFO "gpio-key: exiting\n"); free_irq(irq_num, NULL); input_unregister_device(input); input_free_device(input); gpio_free(GPIO_KEY); } module_init(gpio_key_init); module_exit(gpio_key_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("GPIO key driver with input subsystem and debounce"); ``` 在此驱动程序中,我们将GPIO17作为按键输入使用。当按键按下或释放时,我们会向输入子系统报告一个相应的键值。为了防止按键抖动,我们在中断处理程序中添加了20毫秒的延迟。 此驱动程序还定义了一个输入设备和一个中断处理程序。我们将输入设备与按键相关联,并在按键按下或释放时向输入子系统报告相应的事件。中断处理程序负责检测GPIO17的状态,并触发适当的事件。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值