【IMX6ULL驱动开发学习】03.Linux驱动开发之GPIO子系统、中断、定时器

目录

一、GPIO子系统

1.1 引脚编号

1.2 基于sysfs操作引脚

1.3 在驱动程序中调用GPIO子系统

二、中断

2.1 使用中断的流程

 2.2 函数细节

三、定时器

3.1 定时器函数

 3.2 使用定时器处理按键抖动

扫描二维码关注公众号,回复: 16515694 查看本文章

一、GPIO子系统

1.1 引脚编号

在硬件上如何确定GPIO引脚?它属于哪组GPIO?它是这组GPIO里的哪个引脚?需要2个参数。但是在Linux软件上,可以使用引脚编号来表示。以100ask_ imx6ull为例在开发板上执行如下命令查看已经在使用的GPIO状态:可以看到在Linux系统中可以使用编号来访问某个GPIO。

cat /sys/kernel/debug/gpio

1.2 基于sysfs操作引脚

以100ask_imx6ull为例,它有一个按键,原理图如下:

 那么GPIO5_3的号码是4*32 + 3 =131,可以如下操作读取按键值:

echo 131 > /sys/class/gpio/export              // gpio_request
echo in > /sys/class/gpio/gpio110/direction    // gpio_direction_input
cat /sys/class/gpio/gpio110/value              // gpio_get_value
echo 131 > /sys/class/gpio/unexport            // gpio_free

作用分别是:申请引脚、设置成输入、获取电平状态、释放引脚

1.3 在驱动程序中调用GPIO子系统

在设备树中指定了GPIO引脚,在驱动代码中如何使用?也就是GPIO子系统的接口函数是什么?GPIO子系统有两套接口:基于描述符的(descriptor-based)、老的(legacy)。前者的函数都有前缀“ gpiod_”,它使用 gpio_desc 结构体来表示一个引脚;后者的函数都有前缀“ gpio_”,它使用一个整数来表示一个引脚。要操作一个引脚,首先要get引脚,然后设置方向,读值、写值。

驱动程序中要包含头文件,选用一个即可:

include <linux/gpio/consumer.h>     // descriptor-based
include <linux/gpio.h>              // legacy

下表列出常用的函数:

二、中断

2.1 使用中断的流程

  • 确定中断号
  • 注册中断处理函数,函数原型如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev);
  • 在中断处理函数里:分辨中断、处理中断、清楚中断

 2.2 函数细节

request_irq函数的第1个参数是中断号,可以根据GPIO函数获得中断号:

int gpio_to_irq(unsigned int gpio); //使用该函数传入一个gpio的编码得到中断号

request_irq函数的第2个参数是函数指针: 中断函数会被内核调用

enum irqreturn {
	IRQ_NONE		= (0 << 0),
	IRQ_HANDLED		= (1 << 0),
	IRQ_WAKE_THREAD		= (1 << 1),
};
typedef enum irqreturn irqreturn_t;
typedef irqreturn_t (*irq_handler_t)(int irq, void *dev);

request_irq函数的第3个参数flag表示中断的触发类型:上升沿触发、下降沿触发、上升下降同时触发、高电平触发、低电平触发,有如下取值:

#define IRQF_TRIGGER_NONE	    0x00000000
#define IRQF_TRIGGER_RISING	    0x00000001
#define IRQF_TRIGGER_FALLING   	0x00000002
#define IRQF_TRIGGER_HIGH	    0x00000004
#define IRQF_TRIGGER_LOW	    0x00000008

#define IRQF_SHARED		        0x00000080

request_irq函数的第4个参数是中断的名字,可以在执行cat /proc/interrupts的结果里查看。

request_irq函数的第5个参数是给中断处理函数使用的(用户自行决定是否要传给中断函数的参数)。

三、定时器

定时器作用:超时后调用超时函数(相当于闹钟,时间到后你就要做某些事)。

两个要素:超时时间和超时函数。

应用:如按键消抖。

3.1 定时器函数

在内核中使用定时器很简单,涉及这些函数(参考内核源码include\linux\timer.h):

setup_timer(timer, fn, data);
void add_timer(struct timer_list *timer);
int mod_timer(struct timer_list *timer, unsigned long expires);
int del_timer(struct timer_list *timer);
  • 设置定时器:定时器结构体,定时器超时函数,传给超时函数的参数。
  • 向内核添加定时器。 timer->expires 表示超时时间。当超时时间到达,内核就会调用这个函数:timer->function(timer->data)。
  • 修改定时器的超时时间。
  • 删除定时器。

 3.2 使用定时器处理按键抖动

在实际的按键操作中,可能会有机械抖动:按下或松开一个按键,它的 GPIO 电平会反复变化,最后才稳定。一般是几十毫秒才会稳定。如果不处理抖动的话,用户只操作一次按键,中断程序可能会上报多个数据。怎么处理?

核心在于:在GPIO中断中并不立刻记录按键值,而是修改定时器超时时间,10ms 后再处理。

  • 如果 10ms 内又发生了 GPIO 中断,那就认为是抖动,这时再次修改超时时间为 10ms。
  • 只有 10ms 之内再无 GPIO 中断发生,那么定时器的函数才会被调用。在定时器函数中记录按键值。
     

猜你喜欢

转载自blog.csdn.net/qq_43460230/article/details/132177927