Linux GPIO & IRQ Driver

本文主要描述了GPIO口的申请和高低电平输出、输入,以及中断的申请和调用等

Linux Kernel 中使用GPIO,不能直接引用和操作GPIO的物理地址,而需要把物理地址映射为内存中的虚拟地址,然后对映射的虚拟地址进行操作。

大致情况是这样的,以下通过实际代码分析linux GPIO的简单的驱动分析。

一、一种情况的GPIO驱动和IRQ中断服务申请

1.1、源码实例:

#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/serial.h>

#include <asm/irq.h>
#include <mach/hardware.h>
#include <plat/regs-serial.h>
#include "samsung.h"

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/clk.h>

#include <linux/io.h>
#include <mach/hardware.h>
#include <linux/delay.h>
#include <asm/irq.h>

#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#include <plat/s5pv210.h>
#include <plat/gpio-cfg.h>
#include <linux/gpio.h>
struct scan_button_irq_desc
{	
	int irq;
	int pin;
	int pin_setting;
//	int number;
	char *name;
};

static struct scan_button_irq_desc scan_button_irq[] =
{
	{IRQ_EINT7,S5PV210_GPH0(7),IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"scan_trig"},
};

static irqreturn_t  s3c_gpio_scan_isr(int irq,void* dev_id)
{

	unsigned int key_status;	
	u32 cfg;

	//void __iomem *base = S5PV210_GPG1DAT;
	cfg = readl(S5PV210_GPG1CON);
	cfg &= (0xf<<20); //clear gpg1_5 con
	cfg |= (1 << 20); //out put cfg
	writel(cfg,S5PV210_GPG1CON);
	printk("scanmodule pwr ctrl con: %d",readl(S5PV210_GPG1CON));
	
	cfg = readl(S5PV210_GPG1DAT);
	cfg |= (1 << 5);
	writel(cfg,S5PV210_GPG1DAT);
	printk("scanmodule pwr ctrl dat: %d",readl(S5PV210_GPG1DAT));

	key_status = (readl(S5PV210_GPH0DAT)) & (1 << 7);
	printk("scan_button,keyval:%d : %s\n", GPIO_KEY, key_status?"released":"pressed");
	
	return IRQ_HANDLED;
}

static int s3c_gpio_scan_isr_setup(void)
{
	int ret;
~~~~
	s3c_gpio_cfgpin(S5PV210_GPH0(7), S3C_GPIO_SPECIAL(0xf)); //配置gpio为中断输出,Eint7  
	s3c_gpio_setpull(S5PV210_GPH0(7), S3C_GPIO_PULL_UP);  //S3C_GPIO_PULL_NONE
	
	set_irq_wake(scan_button_irq[0].irq, 1);
	ret = request_irq(scan_button_irq[0].irq, s3c_gpio_scan_isr,  
		IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING,
		scan_button_irq[0].name, (void *)&scan_button_irq);
	if (ret) {
		printk("request IRQ for scan triger failed\n");
		ret = -EIO;
	}
~~~~
	return ret;
}

static int __init XXX_init(void)
{
~~~~
	//中断申请
	ret = s3c_gpio_scan_isr_setup();
	if(!ret)
		printk("IRQ request successfully\n");
	else
		printk("IRQ request failed");
~~~~
	return 0;
}

static void __exit XXX_exit(void)
{

~~~~
        //IRQ srv free
	free_irq(scan_button_irq[0].irq,(void *)&scan_button_irq);

~~~~

}

1.2、中断申请函数原型及分析:

1.2.1、原型

int request_irq(unsigned int irqirqreturn_t (*handler)(int, void *,struct pt_regs *),unsigned long flags, const char *dev_name, void *dev_id);
void free_irq(unsigned int irq, void *dev_id);

1.2.2、分析

1)、unsigned int irq:所要注册的中断号
2)、irqreturn_t (*handler)(int, void *, struct pt_regs *):中断服务程序的入口地址。
3)、unsigned long flags:与中断管理有关的位掩码选项,有三组值:
          SA_INTERRUPT : 快速中断处理程序,当使用它的是后处理器上所有的其他中断都被禁用;
          SA_SHIRQ :           该中断是在设备之间可共享的;
          SA_SAMPLE_RANDOM :   这个位表示产生的中断能够有贡献给 /dev/random和 /dev/urandom 使用的加密池。
4)、const char *dev_name:设备描述,表示那一个设备在使用这个中断。
5)、void *dev_id:用作共享中断线的指针. 它是一个独特的标识, 用在当释放中断线时以及可能还被驱动用来指向它自己的私有数据区(来标识哪个设备在中断) 。这个参数在真正的驱动程序中一般是指向设备数据结构的指针.在调用中断处理程序的时候它就会传递给中断处理程序的void *dev_id。(这是我的理解)如果中断没有被共享, dev_id 可以设置为 NULL, 但是使用这个项指向设备结构不管如何是个好主意. 我们将在"实现一个处理"一节中看到 dev_id 的一个实际应用。

中断号的查看可以使用下面的命令:“cat /proc/interrupts”。

# cat /proc/interrupts                                                          
           CPU0                                                                 
 24:        109    s3c-uart  s5pv210-uart                                       
 26:        164    s3c-uart  s5pv210-uart                                       
 35:          0  s5p_vic_eint  key pgio                                         
 36:          0  s5p_vic_eint  pmic Tick                                        
 38:          1  s5p_vic_eint  rt5625 HP Hotplug                                
 39:         15  s5p_vic_eint  scan_trig                                        
 45:          0  s5p_vic_eint  hpd                                              
 58:          1         VIC  System timer                                       
 59:          0         VIC  s3c2410-wdt                                        
 61:     271883         VIC  rtc-tick                                           
 78:       1992         VIC  s3c2410-i2c.0                                      
 83:          0         VIC  s3c2410-i2c.2                                      
 87:          0         VIC  ehci_hcd:usb1                                      
 88:       5180         VIC  s3c-udc                                            
 90:       4528         VIC  mmc0                                               
 91:          0         VIC  mmc1                                               
 92:          0         VIC  mmc2                                               
 97:          1         VIC  s3cfb, s3cfb                                       
101:          0         VIC  s3c-fimc0                                          
102:          0         VIC  s3c-fimc1                                          
103:         90         VIC  s3c-fimc2                                          
104:          0         VIC  s3c-jpg                                            
105:          0         VIC  s5p-g2d                                            
106:        740         VIC  PowerVR                                            
107:          0         VIC  s5p-tvout                                          
108:          0         VIC  s5p-tvout                                          
109:         57         VIC  s3c2410-i2c.1                                      
110:          0         VIC  s3c-mfc                                            
111:          0         VIC  s5p-tvout                                          
121:          1         VIC  s3c-keypad                                         
130:         16         VIC  mmc3                                               
131:          0         VIC  s5p-cec                                            
137:          0         VIC  s3c_action                                         
138:          0         VIC  s3c_updown                                         
Err:          0                                                                 
#

/proc/stat 记录了几个关于系统活动的低级统计量, 包括(但是不限于)自系统启动以来收到的中断数. stat 的每一行以一个文本字串开始, 是该行的关键词; intr 标志是我们在找的.

第一个数是所有中断的总数, 而其他每一个代表一个单个 IRQ 线, 从中断 0 开始. 所有的计数跨系统中所有处理器而汇总的. 这个快照显示, 中断号 4 已使用 1 次, 尽管当前没有安装处理. 如果你在测试的驱动请求并释放中断在每个打开和关闭循环, 你可能发现 /proc/stat 比 /proc/interrupts 更加有用.


二、GPIO Driver 另外一种申请方法

~~~~

#include <linux/gpio.h>
#include <linux/spi/spi.h>

#include <plat/gpio-cfg.h>
#include <plat/regs-fb.h>

#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-mem.h>
#include <mach/regs-gpio.h>
#include <mach/gpio.h> //s5pv210 base address define
#include <mach/gpio-bank.h>

~~~~
	if(gpio_request(S5PV210_GPJ1(3), "GPJ1_3"))  //S5PV210_GPJ1(3)为GPIO的编号
		printk("S5PV210_GPJ1(3) ERR!\n");
	
	if(gpio_request(S5PV210_GPJ1(4), "GPJ1_4"))
		printk("S5PV210_GPJ1(4) ERR!\n");
	
	if(gpio_request(S5PV210_GPJ1(5), "GPJ1_5"))
		printk("S5PV210_GPJ1(5) ERR!\n");
	
	if(gpio_request(S5PV210_GPJ0(5), "GPJ0_5"))
		printk("S5PV210_GPJ0(5) ERR!\n");

	gpio_direction_output(S5PV210_GPJ1(3), 0); //直接设置为输出功能,并且直接输出对应的高低电平
	gpio_direction_output(S5PV210_GPJ1(4), 0);
	gpio_direction_output(S5PV210_GPJ1(5), 0);
	gpio_direction_output(S5PV210_GPJ0(5), 0);

~~~~

	gpio_free(S5PV210_GPJ1(3));
	gpio_free(S5PV210_GPJ1(4));
	gpio_free(S5PV210_GPJ1(5));
	gpio_free(S5PV210_GPJ0(5));	

~~~~

gpio_set_value(port_num,0/1) 一般只是在这个GPIO口的寄存器上写上某个值,至于这个端口是否设置为输出,它就管不了!而gpio_direction_output (port_num,0/1),在某个GPIO口写上某个值之后,还会把这个端口设置为输出模式。 因此,有人也许就会建议,把gpio_set_value这个函数直接去掉不用,是否可以,显然是可以的。 但是为什么系统还要用呢, 我个人分析是, 系统开发人员在要结合这两者来使用,以便提高效率。 一般某个端口设置好了输入与输出模式后,最好不要经常变动。 首先要调用gpio_direction_output(),以后要设置高低电平时,直接使用gpio_set_value()就可以了,这样可以省却再次调用设置输出模式的操作,从而提高运行效率!

发布了27 篇原创文章 · 获赞 6 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章