嵌入式学习6--中断和等待队列

中断是指CPU处理在进行运作时,突发某种情况,使CPU暂停当前处理的事件,转向处理突发的事件,当突发事件处理完成以后,再回到暂停处继续执行未完的操作。

中断可分为外部中断和内部中断:

1.外部中断:指CPU外部器件发生变化时所触发的操作,典型的外部中断就是按键,当按键按下时,去执行相应的操作

2.内部中断:指CPU内部发生某种异常所触发的操作,如溢出、软中断指令等

中断机制将中断处理分为上半部和下半部

上半部:一般为不可打断的,处理紧急事件,要求处理时间短的

下半部:可被打断,处理不太紧急的事件,时间长的也在下半部处理,下半部的中断事件主要是在中断触发时,做一个登记,等到一个适当的时刻再来处理。

linux中中断的处理过程被封装成了接口函数,一般使用这些接口函数即可完成开发,当然也有例外。

下面介绍中断的接口函数

1.int gpio_to_irq(int gpio)//将gpio号转换为中断号并返回

2.int irq_to_gpio(int gpio)//将中断号转换为gpio号并返回

//申请一个中断

3.int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)

参数介绍

A.unsigned int irq -->中断号,比如IRQ_GPIO_A_START+1,代表的是GPIOA1

#define IRQ_PHY_MAX_COUNT               (74 + 32)
#define IRQ_GPIO_START            IRQ_PHY_MAX_COUNT

#define IRQ_GPIO_A_START        (IRQ_GPIO_START + PAD_GPIO_A)
#define IRQ_GPIO_B_START        (IRQ_GPIO_START + PAD_GPIO_B)
#define IRQ_GPIO_C_START        (IRQ_GPIO_START + PAD_GPIO_C)
#define IRQ_GPIO_D_START        (IRQ_GPIO_START + PAD_GPIO_D)
#define IRQ_GPIO_E_START        (IRQ_GPIO_START + PAD_GPIO_E)

B.irq_handler_t handler -->中断服务函数

irqreturn_t keys_isr(int irq, void *dev)
{
    ......
    return IRQ_HANDLED;
}

C.unsigned long flags -->中断标志,一般为上升沿、下降沿、双边沿、高电平、低电平

#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_TRIGGER_MASK    (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE    0x00000010

D.const char *name  -->中断的名字,可以自定义,可以使用命令 #cat /proc/interrupts 查看到

E.void *dev  -->传递给中断服务函数的参数

4.void free_irq(unsigned int irq, void *dev_id)

参数介绍

unsigned int irq -->中断号

void *dev_id  -->传递给中的参数

5.void disable_irq(unsigned int irq)  -->关闭中断,使用request_irq申请的中断属于激活状态,可以使用该函数关闭

6.void enable_irq(unsigned int irq)  -->开启中断

7.local_irq_save(flags) -->保存当前中断状态,并关闭所有中断

8.local_irq_restore(flags)  -->还原被关闭的中断

9.local_irq_disable() -->不保存中断状态,关闭所有中断

10.local_irq_enable() -->开启所有中断

等待队列:主要是为了实现同步

比如在食堂吃饭,你要先打饭,想打饭要先排队,等待队列就是为了完成这个事情,你要吃饭就得先排队打到饭才可以

1.定义一个等待队列

wait_queue_head_t  key_wait_queue;

2.初始化等待队列

init_waitqueue_head(key_wait_queue)

以上步骤可以合并为一个

DECLARE_WAIT_QUEUE_HEAD(key_wait_queue);//定义并初始化等待队列

3.定义一个等待队列标志位

int key_wait_queue_pressed_flag = 0; 

4.设置等待队列判断,阻塞在这里,这里的区别不太明白,需要继续研究,如果哪位明白,请告诉我,谢谢

wait_event(wq, condition) ; //不可被wake_up唤醒

wait_event_interruptible(key_wait_queue, key_wait_queue_pressed_flag); //可被wake_up唤醒

5.设置等待队列标志位并唤醒等待队列

key_wait_queue_pressed_flag = 1;
wake_up(&key_wait_queue);

下面代码为按键驱动,linux驱动需要从下向上看

#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <mach/devices.h>
#include <linux/wait.h>

#define MISCDEVICE_NAME "interrupt_drv"

typedef struct keys_irq_info{
    int key_irq;
    int key_dev;
    char *key_name;
}KEYS_IRQ_INFO;

char pressed_flag[4] = {0, 0, 0, 0};  //保存按键状态
static wait_queue_head_t key_wait_queue;  //定义等待队列
static int key_wait_queue_pressed_flag = 0; //等待队列标志位

static KEYS_IRQ_INFO keys_irq[4] = {
    {
        .key_irq = IRQ_GPIO_A_START+28,
        .key_dev = 28,
        .key_name = "KEY_GPIOA_28",
    },{
        .key_irq = IRQ_GPIO_B_START+30,
        .key_dev = 30,
        .key_name = "KEY_GPIOB_30",
    },{
        .key_irq = IRQ_GPIO_B_START+31,
        .key_dev = 31,
        .key_name = "KEY_GPIOB_31",
    },{
        .key_irq = IRQ_GPIO_B_START+9,
        .key_dev = 9,
        .key_name = "KEY_GPIOB_9",
    }
};

irqreturn_t keys_isr(int irq, void *dev)  //中断服务函数
{
    int i = 0;
    
    for(i = 0; i < 4; i++){
        if(irq == keys_irq[i].key_irq){  //此处通过中断号判断是哪个按键触发的中断,可以通过传递的参数来判断
            pressed_flag[i] = 1;            //判断后将按键状态保存的标志位置1
            printk("<4>" "%s is pressed\n", keys_irq[i].key_name);
        }
    }
    
    key_wait_queue_pressed_flag = 1;   //将等到队列的唤醒标志置1
    wake_up(&key_wait_queue);             //通过这里唤醒read处阻塞的等待队列,唤醒后将read函数执行完
    
    return IRQ_HANDLED;
}

int interrupt_drv_open(struct inode *inode, struct file *filp)
{
    printk("<4>" "interrupt drv open\n");
    return 0;
}

ssize_t interrupt_drv_read(struct file *filp, char __user *usr_buf, size_t size, loff_t *offset)
{
    int i = 0;
    int ret = 0;
    
    if(size != 4)
        return -EINVAL;
    
    wait_event_interruptible(key_wait_queue, key_wait_queue_pressed_flag);  //当应用软件使用read读取按键状态时,会阻塞在                                                                                                                             //这里,直到按键按下后才会继续运行

    ret = copy_to_user(usr_buf, pressed_flag, size);  //等待队列在中断服务函数被唤醒后执行这里,将按键状态copy到应用层
    if(ret != 0)
        return -EINVAL;
        
    for(i = 0; i < 4; i++){      //将按键状态重置
        pressed_flag[i] = 0;
    }
    
    key_wait_queue_pressed_flag = 0;

    printk("<4>" "interrupt drv read\n");
    
    return ret;
}

int interrupt_drv_release(struct inode *inode, struct file *filp)
{
    printk("<4>" "interrupt drv close\n");
    return 0;
}


static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open  = interrupt_drv_open,
    .read  = interrupt_drv_read,
    .release = interrupt_drv_release,
};

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

static int __init interrupt_drv_init(void)
{
    int ret = 0;
    int i = 0;
    
    init_waitqueue_head(&key_wait_queue);   //初始化等待队列
    
    ret = misc_register(&misc);  //注册混杂设备
    if(ret)
        return -EBUSY;
    
    for(i = 0; i < 4; i++){
        ret = request_irq(keys_irq[i].key_irq,   //申请按键中断为下降沿触发
                            keys_isr,    //多个中断源共享一个中断服务函数,需要在函数中进行判断
                            IRQF_TRIGGER_FALLING,  //下降沿触发
                            keys_irq[i].key_name,   //中断名
                            &keys_irq[i].key_dev   //传递到中断服务函数中的参数
                        ); 
        if(ret < 0)
            goto request_irq_err;  //申请失败,进入失败处理
    }
    
    printk("<4>" "interrupt drv init\n");
    
    return 0;
    
request_irq_err:
    misc_deregister(&misc);  //取消注册混杂设备
    
    while(i--)
        free_irq(keys_irq[i].key_irq, &keys_irq[i].key_dev);  //释放中断
        return ret;
}

static void __exit interrupt_drv_exit(void) //驱动退出函数,rmmod命令使用时会运行此处,这里要重点关注,因为很多时候在驱              //动初始化时做了操作,但是在退出时没有做相应操作,会造成段错误,导致驱动退出失败或者下次安装失败
{
    int i = 4;
    
    misc_deregister(&misc);   //取消注册混杂设备
    
    while(i--)
        free_irq(keys_irq[i].key_irq, &keys_irq[i].key_dev);   //取消注册混杂设备
    
    printk(KERN_WARNING "interrupt drv exit\n");
}

module_init(interrupt_drv_init);
module_exit(interrupt_drv_exit);


MODULE_DESCRIPTION("interrupt drv");
MODULE_LICENSE("GPL");

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值