Smart210学习记录-------linux驱动中断

发布者:温暖阳光最新更新时间:2025-01-20 来源: cnblogs关键字:Smart210  linux 手机看文章 扫描二维码
随时随地手机看文章

Linux中断

 
Linux 的中断处理分为两个半部,顶半部处理紧急的硬件操作,底半部处理不紧急的耗时操
作。tasklet 和工作队列都是调度中断底半部的良好机制,tasklet 基于软中断实现。内核定时器
依靠软中断实现。

1.申请和释放中断

申请中断 int request_irq(unsigned int irq, irq_handler_t handler,  unsigned long irqflags, const char *devname, void *dev_id)

irq 是要申请的硬件中断号。

handler 是向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生时,系统调用 这个函数,

dev_id 参数将被传递给它。

irqflags是中断处理的属性,可以指定中断的触发方式以及处理方式。在触发方式方面,可以是 IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_ LOW 等。在处理方式方面,若设置了 IRQF_DISABLED,表明中断处理程序是快速处理程序,快 速处理程序被调用时屏蔽所有中断,慢速处理程序则不会屏蔽其他设备的驱动;若设置了 IRQF_SHARED,则表示多个设备共享中断,

dev_id 在中断共享时会用到,一般设置为这个设备 的设备结构体或者 NULL。

request_irq()返回 0 表示成功,返回-EINVAL 表示中断号无效或处理函数指针为 NULL,返 回-EBUSY 表示中断已经被占用且不能共享。

 

顶半部 handler 的类型 irq_handler_t 定义为:
typedef irqreturn_t (*irq_handler_t)(int, void *);
typedef int irqreturn_t;     

 

释放中断            

void free_irq(unsigned int irq,void *dev_id);

2.使能和屏蔽中断

void disable_irq(int irq);

void disable_irq_nosync(int irq);

void enable_irq(int irq); disable_irq_nosync()与 disable_irq()的区别在于前者立即返回,而后者等待目前的中断处理完 成。由于 disable_irq()会等待指定的中断被处理完,因此如果在 n 号中断的顶半部调用 disable_irq(n),会引起系统的死锁,这种情况下,只能调用 disable_irq_nosync(n)。

 

下列两个函数(或宏,具体实现依赖于 CPU 体系结构)将屏蔽本 CPU 内的所有中断:

#define local_irq_save(flags) ...

void local_irq_disable(void);

与上述两个禁止中断对应的恢复中断的函数(或宏)是:

#define local_irq_restore(flags) ...

void local_irq_enable(void);

 

3.底半部机制
Linux 实现底半部的机制主要有 tasklet、工作队列和软中断。


(1)tasklet


tasklet 的使用较简单,我们只需要定义 tasklet 及其处理函数并将两者关联,例如:
void my_tasklet_func(unsigned long); /*定义一个处理函数*/
DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);
   /*定义一个 tasklet 结构 my_tasklet,与 my_tasklet_func(data)函数相关联 */


代码 DECLARE_TASKLET(my_tasklet,my_tasklet_func,data)实现了定义名称为 my_tasklet 的
tasklet 并将其与 my_tasklet_func()这个函数绑定,而传入这个函数的参数为 data。


在需要调度 tasklet 的时候引用一个 tasklet_schedule()函数就能使系统在适当的时候进行调度
运行:
tasklet_schedule(&my_tasklet);

 

 (2).工作队列 工作队列的使用方法和 tasklet 非常相似,下面的代码用于定义一个工作队列和一个底半部执 行函数:

struct work_struct my_wq; /*定义一个工作队列*/

void my_wq_func(unsigned long); /*定义一个处理函数*/

通过 INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定:

INIT_WORK(&my_wq, (void (*)(void *)) my_wq_func, NULL);  /*初始化工作队列并将其与处理函数绑定*/  

与 tasklet_schedule()对应的用于调度工作队列执行的函数为 schedule_work(),如:

schedule_work(&my_wq);/*调度工作队列执行*/

 

 (3).软中断
软中断(softirq)也是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候,asklet 是基于软中断实现的,因此也运行于软中断上下文。


在 Linux 内核中,用 softirq_action 结构体表征一个软中断,这个结构体中包含软中断处理函数指针和传递给该函数的参数。使用 open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。

软中断和 tasklet 运行于软中断上下文,仍然属于原子上下文的一种,而工作队列则运行于进
程上下文。因此,软中断和 tasklet 处理函数中不能睡眠,而工作队列处理函数中允许睡眠。

 

按键中断驱动程序:

四个按键(对应的IO口为GPH2(0),GPH2(1),GPH2(2),GPH2(3))) 分别对应外部中断为EINT16,17,18,19

加入了poll机制和异步通知fasync


#include <linux/fs.h>

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include gpio.h>

#include

#include

#include

#include

#include


#define KEYNAME   'mykey'

static unsigned char  keymajor = 0;


static struct cdev key_cdev;


static struct class *key_class;

static struct device*key_device;

static struct fasync_struct *fasync_queue;



static DECLARE_WAIT_QUEUE_HEAD(my_key_waitq); /*DECLARE_WAIT_QUEUE_HEAD (name)声明且初始化*/

static unsigned char pressed = 0;


static unsigned char key_value = 0;


struct my_key_desc {

    unsigned int  irq;

    unsigned int  pin;

    unsigned char value;

    const char*   name;

};


static struct my_key_desc  key_desc[] = {

    {IRQ_EINT16_31, S5PV210_GPH2(0), 0x01, 'KEY1'},

    {IRQ_EINT16_31, S5PV210_GPH2(1), 0x02, 'KEY2'},    

    {IRQ_EINT16_31, S5PV210_GPH2(2), 0x03, 'KEY3'},

    {IRQ_EINT16_31, S5PV210_GPH2(3), 0x04, 'KEY4'},

};



static irqreturn_t my_key_irq(int irq, void* dev_id)

{

    volatile struct my_key_desc key_dr = *(volatile struct my_key_desc*)dev_id;

    int value;

    value = gpio_get_value(key_dr.pin);


    if(value == 0)  /*有键按下key_desc.value = 0x8x*/

        key_value = key_dr.value|0x80;

    else 

        key_value = key_dr.value ;


    pressed = 1;

    wake_up_interruptible(&my_key_waitq);


    kill_fasync(&fasync_queue, SIGIO, POLLIN | POLLOUT);

    return 0;

}



static int my_key_open(struct inode *inode, struct file *file)

{

    int ret;

    printk('request_irq settingn');

    ret = request_irq( IRQ_EINT(16),  my_key_irq, IRQ_TYPE_EDGE_BOTH, 'KEY1', (void*)&key_desc[0]);

    if(ret) {

            printk('ret is %dn', ret);

            return ret;

        }

    ret = request_irq( IRQ_EINT(17),  my_key_irq, IRQ_TYPE_EDGE_BOTH, 'KEY2', (void*)&key_desc[1]);

    if(ret)

        return ret;    

    ret = request_irq( IRQ_EINT(18),  my_key_irq, IRQ_TYPE_EDGE_BOTH, 'KEY3', (void*)&key_desc[2]);

    if(ret)

        return ret;

    ret = request_irq( IRQ_EINT(19),  my_key_irq, IRQ_TYPE_EDGE_BOTH, 'KEY4', (void*)&key_desc[3]);

    if(ret)

        return ret;    

    return 0;

}



/*static int my_key_open(struct inode * inode, struct file * file)

{

    int ret;

    unsigned char i;

    for(i = 0; i < 4; i++) {

//        s3c_gpio_cfg(key_desc[i].pin, EINT_MODE );  sizeof(key_desc) / sizeof(key_desc[0])

        ret = request_irq(key_desc[i].irq,  my_key_irq , IRQ_TYPE_EDGE_BOTH, key_desc[i].name,(void*)&key_desc[i]);

        if(ret) {

            printk(KERN_EMERG'request_irq error %dn' , i);

            break;

        }

    }

    if(ret) {

        i--;

        for(; i > 0; i--) {

            free_irq(key_desc[i].irq, (void*)&key_desc[i]);

            return -EBUSY;

        }

    }

    return 0;

}*/


static int my_key_fasync(int fd, struct file * file, int on){

        return fasync_helper(fd, file, on, &fasync_queue);

}



static ssize_t my_key_read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)

{

    int ret;

    if(n != 1) {

        printk(KERN_EMERG'The driver can only give one key value once!n');

        return -EINVAL;

    }


    if(filep->f_flags & O_NONBLOCK) {

        if(!pressed)

            return -EBUSY;

    } else {

        wait_event_interruptible(my_key_waitq, pressed);/*pressed为1时继续执行后面,否则休眠*/

        pressed = 0;


        ret = copy_to_user(outbuf, &key_value, 1);

        if(ret) {

            printk(KERN_EMERG'key copy_to_user errorn');

            return -ENOMEM;

        }

    }

    return 0;

}


static int my_key_write(struct file * filep,const char * buf,size_t n,loff_t * ppos)

{


    return 0;

}


 

static int my_key_close(struct inode * inode,struct file * file)

{

     free_irq(IRQ_EINT(16), (void*)&key_desc[0]);

     free_irq(IRQ_EINT(17), (void*)&key_desc[1]);

     free_irq(IRQ_EINT(18), (void*)&key_desc[2]);

     free_irq(IRQ_EINT(19), (void*)&key_desc[3]);

     my_key_fasync(-1, file, 0);

     return 0;

}


static unsigned int my_key_poll(struct file *filep, poll_table *wait)

{

    unsigned int mask = 0;

    poll_wait(filep, &my_key_waitq, wait);


    if(pressed) {

        mask |= POLLIN | POLLRDNORM;

    }


    return mask;

}




struct file_operations key_fops= {

    .owner    =   THIS_MODULE,

    .open     =   my_key_open,

    .read     =   my_key_read,

    .write    =   my_key_write,

    .release  =   my_key_close,

    .poll     =   my_key_poll,

    .fasync   =   my_key_fasync,

};




static int __init my_key_init(void)

{

    int ret;

    int deno;

    deno = MKDEV(keymajor, 0);

    cdev_init(&key_cdev, &key_fops);

    if (keymajor) {

        register_chrdev_region(deno, 1, KEYNAME);

        printk(KERN_EMERG'key major is %dn', keymajor);

    } else {

        alloc_chrdev_region(&deno, 0, 1, KEYNAME);

        keymajor = MAJOR(deno);

        printk(KERN_EMERG'key major is %dn', keymajor);

    }


    key_cdev.owner = THIS_MODULE;

    key_cdev.ops   = &key_fops;

    

    ret = cdev_add(&key_cdev, deno, 1);

    if(ret) {

        printk(KERN_EMERG'cdev_add errorn');

        goto add_error;

    }



    key_class= class_create(THIS_MODULE, KEYNAME);

    if(IS_ERR(key_class)) {

        printk(KERN_EMERG'class_create errorn');

[1] [2]
关键字:Smart210  linux 引用地址:Smart210学习记录-------linux驱动中断

上一篇:Smart210学习记录-----linux定时器
下一篇:Smart20学习记录----异步通知

推荐阅读最新更新时间:2026-03-25 10:43

Linux驱动之按键驱动编写(中断方式)
1、查看原理图,确定需要控制的IO端口 打开原理图,确定需要控制的IO端口为GPF0、GPF2、GPG3、GPG11。可以看到它的中断号为IRQ_EINT0、IRQ_EINT2、IRQ_EINT11、IRQ_EINT19 2、查看芯片手册,确定IO端口的寄存器地址,可以看到因为用了两组GPIO端口,所以它的基地址分别为0x56000050、0x56000060。中断方式的寄存器基地址为0x56000088、0x5600008c、0x56000090 3、编写驱动代码,编写驱动代码的步骤如下: 1)、编写出口、入口函数。代码如下,具体说明参考Linux驱动之LED驱动编写 static int second_
[单片机]
<font color='red'>Linux</font><font color='red'>驱动</font>之按键<font color='red'>驱动</font>编写(<font color='red'>中断</font>方式)
linux驱动中断管理(for mini2440)
2440处理器的中断组织成两层:主中断 和 次中断,一共可以处理60个中断源。 主中断 和 次中断 经过中断构架的抽象之后,编写驱动程序时,我们不用考虑这两层中断,直接使用 request_irq 注册 60个中断源的某个,就可以直接使用中断了。 err = request_irq(button_irqs .irq, buttons_interrupt, NULL, button_irqs .name, (void *)&button_irqs ); set_irq_type(button_irqs .irq, IRQT_BOTHEDGE); 像上面这样使用中断。 下面是主中断0~31共3
[单片机]
Smart210学习记录-------文件操作
一.linux文件操作(只能在linux系统上用) 创建: int creat(const char* filename, mode_t mode) filename 表示要创建的文件名,mode表示对该文件的读写权限 int umask(int newmask) 调用将 umask 设置为 newmask,然后返回旧的 umask,它只影响读、写和执行权限 S_IRUSR 用户可以读 S_IWUSR 用户可以写 S_IXUSR 用户可以执行 。。。等等 除了可以通过上述宏进行“或”逻辑产生标志以外,我们也可以自己用数字来表示,Linux 用 5 个数字来表示文件的各种权限:第一位表示设置用户 ID;第二位表示设置组 ID;第三位
[单片机]
Smart210学习记录----nand flash驱动
当读写文件请求到来的时候,流程如下   1.通过vfs进入文件系统,   2.文件系统把文件读写转换为块设备读写,其中有运用算法对读写操作进行合并,排序等,最后把块设备读写放进队列   3.循环从队列中取出读写要求,然后用处理函数(blk_init_queue设置)进行处理。    这个函数就是连接上层(IO调度)跟底层(硬件操作)的桥梁,当我们调用add_mtd_partitions的时候,就建立了上下层的联系。   4.对不同的处理要求,调用不同的nand的底层处理函数 nand flash驱动代码: #include linux/module.h #include linux/types.h #in
[单片机]
<font color='red'>Smart210</font>学习记录----nand flash<font color='red'>驱动</font>
[smart210] Nand Flash K9F4G08U0B 的配置与读写控制(二)
平台:smart210 CPU:s5pv210 目标:控制核心板上的Nand Flash,对其进行读写操作,本文为上文续篇,主要实现的是对nand flash进行读/写与块擦除操作 void nand_init(void) { // 1. config nandflash controller NFCONF = (TACLS 12)|(TWRPH0 8)|(TWRPH1 4)|(0 3)|(0 2)|(1 1)|(0 0); NFCONT = (0 18)|(0 17)|(0 16)|(0 10)|(0 9)|(0 8)|(0 7)|(0 6)|(0x3 1)|(1 0); // 2. config
[单片机]
[<font color='red'>smart210</font>] Nand Flash K9F4G08U0B 的配置与读写控制(二)
Smart210 USB ROOT HUB自动复位
现象: 使用usb camera 在lcd上显示图像时,偶尔发生USB ROOT HUB reset, 导致camera停止工作,然后发生usb camera自动断开,然后又重新识别,断开又识别。 如果不使用usb camera,就不会发生 usb reset。 分析: 1、怀疑USB供电问题,导致usb不稳定,但是,使用带外接电源的usb,没有任何改善; 2、分析log,是usb异常中断导致,但是仍然无法判断异常的来源; 3、怀疑是驱动问题,更换linux 内核版本,3.4.2、3.7、3.9.7、3.10、3.13几个版本都有同样问题; 4、使用官方kernel 3.0.8就没有问题,确认还是驱动配置问题。但是仍然找不到
[单片机]
【改进Poll定时查询】IMX257实现GPIO-IRQ中断按键获取键值驱动程序
按键驱动程序中,如果不使用read函数中使程序休眠的,而是还是使用查询方式的话,可以使用Poll函数,来控制一定时间内,如果有按键发生,则立即返回键值。 同时,poll也可以同时监控多个(比如说按键,鼠标,等)一旦发生事件则立即返回。 我们在linux查看帮助: 从帮助中的说明得知, poll, ppoll - wait for some event on a file descriptor poll就是监控某个设备的事件。 修改驱动程序 1.增加头文件 #include linux/poll.h 2.增加key_poll 方法 static unsigned key_poll(struct file *fil
[单片机]
【改进Poll定时查询】IMX257实现GPIO-IRQ<font color='red'>中断</font>按键获取键值<font color='red'>驱动</font>程序
基于STM32CUBEMX驱动TMOS模块STHS34PF80(4)----中断获取信号
概述 HS34PF80的数据准备信号提供了一种机制,允许设备在新的测量数据可读取时通知系统,并触发同步操作,通过正确配置相关寄存器,可以确保系统及时捕获和处理来自设备的新数据,从而提高整体性能和响应能力。 检测人体的存在和动作,并通过特定的通信接口发送检测结果。 最近在弄ST和瑞萨RA的课程,需要样片的可以加群申请:615061293 。 样品申请 https://www.wjx.top/vm/OhcKxJk.aspx# 视频教程 https://www.bilibili.com/video/BV1NF41117S6/ 参考Demo https://github.com/STMicroelectronics/STMems_S
[单片机]
基于STM32CUBEMX<font color='red'>驱动</font>TMOS模块STHS34PF80(4)----<font color='red'>中断</font>获取信号
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved