arm GPIO访问规则

1、简介

开发板裸机开发需要自己按照数据手册自己计算每个gpio的偏移,以实现相应功能,如上拉,第二功能等。
linux为开发者提供了一套统一的方法,方便开发。

1.1 gpio申请与释放函数

想要使用gpio需要先申请,申请成功才能使用。这些头文件定义在内核文件的”include/linux/gpio.h”中。

//申请io 成功返回0 失败返回错误码
static inline int gpio_request(unsigned gpio, const char *label)
gpio:需要申请的gpio号,一般是gpio的宏定义
label:为申请的gpio取的名字,方便阅读
//释放gpio
static inline void gpio_free(unsigned gpio)
gpio:申请成功的gpio 
//检测此gpio口是否有效                           
static inline bool gpio_is_valid(int number) 

//设置为输入
int gpio_direction_input(unsigned gpio);       
//设置为输出,并初始化值为value.               
int gpio_direction_output(unsigned gpio, int value);   

获取/设置gpio值:    int gpio_cansleep(unsigned gpio); 
a.不可睡眠: 
//返回value
gpio_get_value(unsigned gpio);   
//设置值                           
gpio_set_value(unsigned gpio, int value);                    
b.可睡眠:(对于有些挂载在I2C,SPI总线上的扩展GPIO,读写操作可能会导致睡眠,因此不能在中断函数中使用。使用下面的函数以区别于正常的GPIO)
//输入端口:返回零或非零,可能睡眠
int gpio_get_value_cansleep(unsigned gpio);   
//输出端口:可能睡眠               
void gpio_set_value_cansleep(unsigned gpio, int value); 

批量初始化方法:
//申请:
err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
//释放:
gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));

Gpio设置中断:
//首先应该设置此gpio为输入状态,然后获取对应的中断号(或错误码)。返回编号调用:request_irq()和free_irq()。
static inline int gpio_to_irq(unsigned gpio)
//返回gpio编号,再调用gpio_get_value()获取相应的值。(避免使用反向映射,不支持)
static inline int irq_to_gpio(unsigned irq)

1.2 参数宏定义

GPIO 宏定义文件都是由原厂提供,一般位置在”arch/arm(cpu架构)/mach-exynos(cpu型号)/include/mach/gpio-exynos4.h(具体的文件)”

主要用宏定义了gpio的地址
如:
gpioA的地址为 0xE0031080
使用宏定义成了 GPA 以后直接使用GPA即可

1.3 配置函数

主要是对gpio口的配置操作,如上拉,中断触发条件等。
位置定义在”arch/arm(cpu架构)/plat-samsung(平台)/include/plat/gpio-cfg.h”

//设置上拉、下拉
int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull);
//配置管脚是输入、输出、第二功能
int s3c_gpio_cfgpin(unsigned int pin, unsigned int to);
//配置为中断
int s5p_register_gpio_interrupt(int pin);

1.4 中断

主要是外部中断的申请与释放

#include 
//申请中断
static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
irq:中断号,与平台架构相关;可以通过gpio_to_irq映射
handler:用户中断处理函数;
flags:一个或多个中断标记
IRQF_DISABLED, 用于保证中断不被打断和嵌套
IRQF_SHARED, 申请子中断时,共享中断源
IRQF_SAMPLE_RANDOM, 表示对系统熵有贡献,对系统获取随
机数有好处
devname:中断名字,可以通过 cat /proc/interrupts 查看;
dev_id:在 free_irq 中有用,也用做区分中断处理函数

//中断处理函数
irqreturn_t (*handler)(int, void *, struct pt_regs *)

//设置中断触发方式(外部中断)
int set_irq_type(int irq, int edge);
irq:中断号
edge:外部中断触发方式定义在linux/irq.h
IRQ_TYPE_LEVEL_LOW,
IRQ_TYPE_LEVEL_HIGH,
IRQ_TYPE_EDGE_FALLING,
IRQ_TYPE_EDGE_RISING,
IRQ_TYPE_EDGE_BOTH

//释放中断
void free_irq(unsigned int irq, void *dev_id)
irq:中断号,与 request_irq 中的 irq 一致,用于定位 action 链表;
dev_id:用于在 action 链表中找到要卸载的表项;同一个中断的不同中断处理函数必须使用不同的dev_id来区分,这就要求在注册中断共享时参数 dev_id 必须唯一。

//使能中断
void enable_irq(unsigned int irq);

//关闭中断,并等待中断处理完成后返回
void disable_irq(unsigned int irq);
注意:当用户在中断处理函数中使用该函数会形成死锁

//关闭中断,立即返回
void disable_irq_nosync(unsigned int irq);

2、demo itop4412按键中断例子

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define DEMO_DEBUG
#ifdef  DEMO_DEBUG
#define dem_dbg(fmt, arg...)        printk(KERN_WARNING fmt, ##arg)
#else
#define dem_dbg(fmt, arg...)        printk(KERN_DEBUG fmt, ##arg)
#endif
//中断回调
static irqreturn_t eint9_interrupt(int irq, void *dev_id) {
    printk("%s(%d)\n", __FUNCTION__, __LINE__);

    return IRQ_HANDLED;
}

static dev_t dev_id;
static unsigned int irq;
static int demo_open (struct inode *pnode, struct file *filp)
{
    int ret;

    dev_id = MKDEV(imajor(pnode), iminor(pnode));
    //申请gpio
    ret = gpio_request(EXYNOS4_GPX1(1), "EINT9");
    if (ret) {
        printk("request EINT9 failed, ret = %d", ret);
        return ret;
    }
    //设置gpio为第二功能引脚 即中断
    s3c_gpio_cfgpin(EXYNOS4_GPX1(1), S3C_GPIO_SFN(0xF));
    //上拉
    s3c_gpio_setpull(EXYNOS4_GPX1(1), S3C_GPIO_PULL_UP);
    //释放gpio
    gpio_free(EXYNOS4_GPX1(1));

    //将GPX1_1映射为中断
    irq = gpio_to_irq(EXYNOS4_GPX1(1));
    //申请中断
    ret = request_irq(irq, eint9_interrupt,
            IRQ_TYPE_EDGE_FALLING /*IRQF_TRIGGER_FALLING*/, "eint9", (void *)&dev_id);
    if (ret < 0) {
        printk("Request IRQ %d failed, %d\n", IRQ_EINT(9), ret);
        goto exit;
    }

exit:
    free_irq(IRQ_EINT(9), (void *)&dev_id);
    return 0;
}

static ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
    return 0;   
}

static int demo_release (struct inode *pnode, struct file *filp)
{
    printk("==>demo_release\n");
    free_irq(irq, (void *)&dev_id);
    return 0;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = demo_open,
    .read = demo_read,
    .release = demo_release,
};  

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .fops = &fops,
    .name = "demo0",
};

static int __init demo_init(void)
{   
    int res;
    dem_dbg("==>demo_init\n");

    res = misc_register(&misc);
    if(res < 0){
        dem_dbg("register misc device failed!\n");
        return -EFAULT;
    }

    return 0;
}

static void __exit demo_exit(void)
{
    dem_dbg("==>demo_exit\n");

    misc_deregister(&misc);

}

module_init(demo_init);
module_exit(demo_exit);


MODULE_LICENSE("Dual BSD/GPL");

你可能感兴趣的:(linux驱动)