驱动中断编程

1.编写一个外部中断
1.获取外部中断编号

int gpio_to_irq(unsigned int gpio)

功能:通过Io口编号转换成对应的外部中断编号
参数:gpio 要获取的IO口编号
返回值:成功:中断编号; 失败:- EINVAL

2.注册中断函数

int request_irq(unsigned int irq,irq_handler_t handler, unsigned long flags, const char *name,void *dev_id); 

功能:向内核注册一个中断服务函数,当发生中断号为 irq 的中断时候,会执行 handler 指针函数。
参数:
irq:中断编号(每个中断源有惟一的编号),这里的中断不是看硬件手册,这一点和裸机编程不同。
handler:中断服务函数指针。 原型 typedef irqreturn_t (*irq_handler_t)(int, void *)。
flags:中断属性,如快速中断,共享中断,如果是外部分中断还有:上升沿,下降沿触发中断这类标志。

flags16进制意义
#define IRQF_TRIGGER_NONE0x00000000没有设置触发边沿
#define IRQF_TRIGGER_RISING0x00000001设置触发边沿为上升沿
#define IRQF_TRIGGER_FALLING0x00000002设置触发边沿为下降沿
#define IRQF_TRIGGER_HIGH0x00000004设置触发方式 为高电平
#define IRQF_TRIGGER_LOW0x00000008设置触发方式 为低电平
#define IRQF_TRIGGER_PROBE0x00000010快速中断标志
#define IRQF_DISABLED0x00000020指明该中断是独占中断,只能注册一次
#define IRQF_SHARED0x00000080指明该中断是共享中断,可以注册多次

name:中断名字, 注册后会出现/proc/irq/irq号/name文件夹出现。
dev_id: 这个参数是传递给中断服务函数。对共享中断来说, 这个参数一定有要,当注销共享中断中的其中一个时, 用这个来标识要注销哪一个。 对于有惟一入口的中断,可以传递 NULL,但是一般来说都会传递一个有意义指针,在中断程序中使用,以方便编程。
返回值:
0 表示成功
-EINVAL (无效参数22)表示中断号无效。
-EBUSY (设备或者资源忙16)表示中断已经被占用。

3.注销中断函数

void free_irq(unsigned int irq,void * dev_id)

功能:从内核中断链表上删除一个中断结构
参数:
irq:中断编号
dev_id: 要和注册中断函数的最后一个参数 dev_id 相同

4.使能中断函数

void enable_irq(unsigned int irq);

参数:irq,要使能的中断对应的编号;
说明:

5.禁止中断函数

void disable_irq_nosync(unsigned int irq);
void disable_irq(unsigned int irq);

参数:
irq,要禁止的中断对应的编号。
注意:在中断服务程序中不能使用 disable_irq 这个函数,否则内核崩溃,可以使用 disable_irq_nosync。
disable_irq:函数调用后,函数不会马上返回,而等待中断程序执行完成才返回,在中断调用会导致死锁。
disable_irq_nosync:调用后,函数马上返回

代码例子(编写按键中断驱动程序):
但是,这个东西有个缺点,就是按键会抖动,所以你按下一次,它会识别到很多次。

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>

//按键数量
#define BTN_SIZE   4键缓冲区,'0'表示没有按键,'1'表示按下了
static char keybuf[] = {"0000"};
/*把一个当成一个对象来看待,方便编程,定义一个描述按键结构*/
struct button_desc {
    int  gpio;   //存放io口编号
    int  number; //存放按键编号,根据自己需要设计,
    char *name;  //按键名字,随便,但是要有意义
};

/* 定义4个按键的信息 */
static struct button_desc buttons[] = {
    { EXYNOS4_GPX3(2), 0, "KEY0" },
    { EXYNOS4_GPX3(3), 1, "KEY1" },
    { EXYNOS4_GPX3(4), 2, "KEY2" },
    { EXYNOS4_GPX3(5), 3, "KEY3" },
};

//中断服务函数
irqreturn_t key_isr(int irq, void* dev)
{
    //存放按键状态
    int dn = 0;
    int index = 0;
    //这里进行还原原来的类型
    struct button_desc *bdata = (struct button_desc *)dev;
    //把读取到的结果取逻辑非,因为程序设计使用正逻辑。'1'表示按下。
    dn = !gpio_get_value(bdata->gpio);
    //取得当前中断对应 的按键编号
    index = bdata->number;
    //把按键状态更新到对应的按缓冲中
    keybuf[index] = dn + '0';
    //输出按键提示
    printk("%s %s\r\n", bdata->name, dn ? "down" : "up");
    return IRQ_HANDLED;
}

static ssize_t tiny4412_read (struct file *flp, char __user *buff,size_t count, loff_t * off)
{
    int ret = 0;
    //用户传递0,直接返回
    if(!count) {
        return 0;
    }
    //修正参数
    if(count > BTN_SIZE ) {
        count = BTN_SIZE;
    }
    //复制数据到用户空间
    ret = copy_to_user(buff, keybuf, count);
    if(ret) {
        printk("error:copy_to_user\r\n");
        return -EFAULT;
    }
    return count;
}

static const struct file_operations dev_fops = {
    .read   =   tiny4412_read,
    .owner  =   THIS_MODULE,
};

#define LEDS_MAJOR  255   //255
#define DEVICE_NAME  "mybtn"

static struct miscdevice misc = {
    .minor = LEDS_MAJOR, //次设备号
    .name  = DEVICE_NAME,//设备名
    .fops  = &dev_fops,  //文件操作方法
};

static int __init btn_init(void)
{
    int ret;
    int irq;
    int i;
    int flags;
    flags = IRQ_TYPE_EDGE_BOTH; //设置为双边触发
    for ( i = 0; i < 4 ; i++ ) {
        //得到中断号
        irq = gpio_to_irq(buttons[i].gpio); //keyX
        
        //注册中断
        ret = request_irq(irq, key_isr, flags, buttons[i].name, (void*)&buttons[i]);
        if(ret < 0) {
            break;
        }
    }

    //如果不是全部成功,则反向注销已经注册的中断
    if(ret < 0) {
        for ( --i; i; i-- ) {
            irq = gpio_to_irq(buttons[i].gpio); //keyX
            disable_irq(irq);
            free_irq(irq, (void*)&buttons[i]);
        }
        return ret;
    }
    //注册杂项设备
    ret = misc_register(&misc);       //注册混杂设备
    return ret;
}
static void __exit btn_exit(void)
{
    int i = 0;
    int irq;
    //注销中断
    for (i = 0; i < 4; i++) {
        irq = gpio_to_irq(buttons[i].gpio); //keyX
        disable_irq(irq);
        free_irq(irq, (void*)&buttons[i]);
    }

    //注销杂项设备
    misc_deregister(&misc);
    printk(KERN_EMERG "Goodbye,cruel world!, priority = 0\n");
}

module_init(btn_init);
module_exit(btn_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("jian");
MODULE_DESCRIPTION("button");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小坚学Linux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值