精华内容
下载资源
问答
  • Linux驱动中断函数参数详解

    千次阅读 2017-06-15 19:49:42
    中断函数参数分析总结 Int request_irq (unsigned int irq, irq_handler_t handler, unsigned long flags, const char *devname, void *dev_id)   A.先来分析形参:   第一个参数irq: 表示要分配的中断号。对于...

    中断函数参数分析总结

    Int request_irq (unsigned int irq, irq_handler_t handler, unsigned long flags, const char *devname, void *dev_id)
     
    A.先来分析形参:
     
    第一个参数irq: 表示要分配的中断号。对于一些设备(系统时钟或键盘)它的值是预先固定的,而对于大多数设备来说,这个值是动态确定的。
     
    第二个参数 handler: 表示要挂入到中断请求对列中的中断服务例程, 这个中断服务函数的原型是static irqreturn_t handler(int , void *);
     
    中断处理程序的前缀为static,因为它从来不会被别的文件中的代码直接调用。
     
    第三个参数flags:为标志位。可以取IRQF_DISABLED、IRQF_SHARED和IRQF_SAMPLE_RANDOM之一。在本实例程序中取 IRQF_SHARED,该标志表示多个中断处理程序共享irq中断线。一般某个中断线上的中断服务程序在执行时会屏蔽请求该线的其他中断,如果取 IRQF_DISABLED标志,则在执行该中断服务程序时会屏蔽所有其他的中断。取IRQF_SAMPLE_RANDOM则表示设备可以被看作是事件随见的发生源。
     
    第四个参数devname:是请求中断的设备的名称。当你加载模块成功后可以在/proc/interrupts中查看到具体设备的名称,与此同时也可以看到这个设备对应的中断号以及请求次数。
     
    第五个参数dev_id:为一个指针型变量。注意该参数为void型,也就是说通过强制转换可以转换为任意类型。dev_id主要用于共享中断线,对每个注册的中断处理程序来说,( Dev_id must be globally unique. Normally the address of the  device data structure is used as the cookie.)dev_id参数必须唯一(指向任一设备结构的指针就可以满足此要求,选择设备结构因为它是唯一的,而且中断处理程序可能会用到它)如果无需共享中断线,则将该参数赋值为NULL。
     
    B:函数返回值
     
    requset_irq()函数成功执行后返回0。如果返回非0值,就表示错误发生。此时,指定的中断处理程序不会被注册。
    这里面有几个疑问:
     
    为什么要注册中断函数?????
     
    共享中断线的概念,参数dev_id的作用是什么????
     
     
    看一个图进行说明:
    1>由图可知:有16个中断线。要使用中断线,就要进行中断线的申请也常把申请一条中断线称为申请一个中断号,这就与request_irq()函数中的第一个形参 irq 有关系 。
     
    2>Linux有256个中断向量,而外部中中断向量只有16个(32~47)。由于硬件上的限制,很多外部设备不得不共享中断线。
     
    (例如:一些PC机所用的网卡和图形卡可以把它们分配到一条中断线上)
     
    让每个中断源独自占用一条中断线是不实现的。
     
    3>共享中断线的话虽然解决了中断资源的问题,但是,此时引出了另一个问题( 任何事物都有其两面性 ),此时仅仅用中断描述符并不能提供中断产生的所有信息。为了解决这个问题,内核必须对中断线给出近一步的描述,所以在Linux设计中,为每个中断请求IRQ设置了一个专用队列(中断请求队列) 。
    4>中断服务程序和中断处理程序的区别:
    a.中断服务例程(interrupt service routine):
     
    Linux中,15条中断线对应15个中断处理程序,依次命名是IRQ0x00_interrupt(),IRQ0x01_interrupt()..... IRQ0X0f_interrupt().
     
    中断处理程序相当于某个中断向量的总处理程序。
    eg:IRQ0X05_interupt()是5号中断(向量为37)的总处理程序。
    中断向量:中断服务程序的入口地址
    b.中断服务例程是针对一个具体设备的中断。
     
    5>.注册中断服务例程:
    在IDT表完成初始化时,每个中断服务队列还为空。此时即使打开中断且某个外设的中断真的发生了,也得不到实际的服务。因为CPU虽然通过中断门进入了某个中断向量的总处理程序。但是,具体的中断服务例程还没有挂入中断请求队列。所以,在设备驱动程序的初始化阶段,必须通过request_irq()函数将响应的中断服务例程挂入中断请求队列,也就是进行注册。
     
    6>分析一下中断服务程序,即request_irq()函数中第二个参数所对应的函数
     
    Static irqreturn_t myirq_handler (int irq, void *dev_id)
    {
     
             Printk ("ISR is Working\n");
             Return IRQ_HANDLED;
     
    }
     
    中断服务例程的形参:
     
    A. int irq :中断号。
    B. void *dev_id :与request_irq () 的参数dev_id一致,可以根据这个设备id号得到相应设备的数据结构,进而得到相应设备的信息和相关数据。
    C.返回值:中断程序的返回值是一个特殊类型 rqreturn_t。但是中断程序的返回值却只有两个值IRQ_NONE和IRQ_HANDLED。
    IRQ_NONE:中断程序接收到中断信号后发现这并不是注册时指定的中断源发出的中断信号。
    IRQ_HANDLED:接收到了准确的中断信号,并且作了相应正确的处理。
     
    一般 中断处理程序要做什么service,主要取决于产生的设备和该设备为什么要发送中断。
     
    1.当一个给定的中断处理程序正在执行时,这条中断线上的其它中断都会被屏蔽。but,所有其他中断线上的中断都是打开的。因此这些不同中断线上的其他中断都能被处理。
     
    2.request_irq()函数可能会睡眠,所以,不能在中断上下文或其它不允许阻塞的代码中调用该函数。
     
    4.在深入分析request_irq()函数之前,先来看几个重要的数据结构。
     
     
    A.      irqaction的数据结构(用irqaction结构体来描述一个具体的中断服务例程)
    113  struct irqaction { 
    114                             irq_handler_t handler; 
    115                             unsigned long flags; 
    116                             const char *name; 
    117                             void *dev_id; 
    118                             struct irqaction *next; 
    119                             int irq; 
    120                             struct proc_dir_entry *dir; 
    121                             irq_handler_t thread_fn; 
    122                             struct task_struct *thread; 
    123                             unsigned long thread_flags; 
    124                              };   
     
    1>handler:指向具体的一个中断服务例程。
     
    2>flags:表示中断标志位,对应于request_irq()函数中所传递的第三个参数,可取IRQF_DISABLED、IRQF_SAMPLE_RANDOM和IRQF_SHARED其中之一。
     
    3>name:请求中断的设备名称,对应request_irq()函数中所传递的第四个参数
     
    4>dev_id: 共享中断时有用。 对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。
     
    5>struct irqaction *next:指向irqaction描述符的下一个元素。用一条链表将共享同一条中断线上的中断服务例程链接起来。
     
    6>irq:所申请的中断号
     
    7>dir:指向proc/irq/NN/name entry
     
    8>thread_fn:指向具体的一个线程化的中断。
     
    9>thread:指向线程中断的指针。
     
    10>thread_flags:线程中断的标志。
     
    B.      irq_desc的数据结构体
     
    每个中断向量都有它自己的irq_desc 描述符。即用irq_desc来描述中断向量。所有的这些中断描述符组织在一起就形成了irq_desc irq_desc[NR_IRQS]数组
     
    175struct irq_desc { 
    176        unsigned int            irq; 
    177        struct timer_rand_state *timer_rand_state; 
    178        unsigned int            *kstat_irqs; 
    179#ifdef CONFIG_INTR_REMAP 
    180        struct irq_2_iommu      *irq_2_iommu; 
    181#endif 
    182        irq_flow_handler_t      handle_irq; 
    183        struct irq_chip         *chip; 
    184        struct msi_desc         *msi_desc; 
    185        void                    *handler_data; 
    186        void                    *chip_data; 
    187        struct irqaction        *action;       
    188        unsigned int            status;        
    189 
    190        unsigned int            depth;         
    191        unsigned int            wake_depth;    
    192        unsigned int            irq_count;     
    193        unsigned long           last_unhandled;
    194        unsigned int            irqs_unhandled; 
    195        raw_spinlock_t          lock; 
    196#ifdef CONFIG_SMP 
    197        cpumask_var_t           affinity; 
    198        const struct cpumask    *affinity_hint; 
    199        unsigned int            node; 
    200#ifdef CONFIG_GENERIC_PENDING_IRQ 
    201        cpumask_var_t           pending_mask; 
    202#endif 
    203#endif 
    204        atomic_t                threads_active; 
    205        wait_queue_head_t       wait_for_threads; 
    206#ifdef CONFIG_PROC_FS 
    207        struct proc_dir_entry   *dir; 
    208#endif 
    209        const char              *name; 
    210} ____cacheline_internodealigned_in_smp; 
    211 
    212extern void arch_init_copy_chip_data (struct irq_desc *old_desc, 
    213                                        struct irq_desc *desc, int node); 
    214extern void arch_free_chip_data (struct irq_desc *old_desc, struct irq_desc *desc); 
    215 
    216#ifndef CONFIG_SPARSE_IRQ 
    217extern struct irq_desc irq_desc[NR_IRQS]; 
     
    1>irq:表示这个描述符所对应的中断号。
     
    2>handle_irq:指向该IRQ线的公共服务程序(即该IRQ所对应的中断处理程序。
     
    3>chip:它是一个struct irq_chip类型的指针,是中断控制器的描述符 。在2.6以前的版本中它是hw_irq_controller。
     
    4>handler_data:是handler_irq的参数。
     
    5>chip_data:是指向irq_chip的指针。
     
    6>action:一个struct irqaction类型的指针,它指向一个单链表。该链表是由该中断线上所有中断服务例程链接起来的。
     
    7>status:表示中断线当前的状态。
     
    8>depth:中断线被激活时,值为0;当值为正数时,表示被禁止的次数

    9>irq_count:表示该中断线上发生中断的次数
     
    10>irqs_unhandled:该IRQ线上未处理中断发生的次数
     
    11>name:申请中断设备的名字。
     
    C.      struct irq_chip结构体:
     
    struct irq_chip是一个中断控制器的描述符。Linux支持N种可编程中断控制器PIC(中断控制器),通常不同的体系结构就有一套自己的中断处理方式。内核为了统一的处理中断,提供了底层的中断处理抽象接口,对于每个平台都需要实现底层的接口函数。这样对于上层的中断通用处理程序就无需任何改动。
     
    struct irq_chip的具体代码如下:
     
    111struct irq_chip { 
    112        const char      *name; 
    113        unsigned int    (*startup)(unsigned int irq); 
    114        void            (*shutdown)(unsigned int irq); 
    115        void            (*enable)(unsigned int irq); 
    116        void            (*disable)(unsigned int irq); 
    117 
    118        void            (*ack)(unsigned int irq); 
    119        void            (*mask)(unsigned int irq); 
    120        void            (*mask_ack)(unsigned int irq); 
    121        void            (*unmask)(unsigned int irq); 
    122        void            (*eoi)(unsigned int irq); 
    123 
    124        void            (*end)(unsigned int irq); 
    125        int             (*set_affinity)(unsigned int irq, 
    126                                        const struct cpumask *dest); 
    127        int             (*retrigger)(unsigned int irq); 
    128        int             (*set_type)(unsigned int irq, unsigned int flow_type); 
    129        int             (*set_wake)(unsigned int irq, unsigned int on); 
    130 
    131        void            (*bus_lock)(unsigned int irq); 
    132        void            (*bus_sync_unlock)(unsigned int irq); 
    133 
    134       
    135#ifdef CONFIG_IRQ_RELEASE_METHOD 
    136        void            (*release)(unsigned int irq, void *dev_id); 
    137#endif 
    138       
    142        const char      *typename; 
    143};  
    name:中断控制器的名字;
    Startup:启动中断线;
    Shutdown:关闭中断线;
    Enable:允许中断;
    Disable:禁止中断;
     
    分析了struct irq_desc, struct irq_chip和irqaction的数据结构之后我们来看看他们之间的关系 。
     
    现在深入分析request_irq()内部是如何实现的。
     
    135request_irq (unsigned int irq, irq_handler_t handler, unsigned long flags, 
    136            const char *name, void *dev) 
    137{ 
    138        return request_threaded_irq (irq, handler, NULL, flags, name, dev); 
    139}  
    可以看到request_irq()函数里面有封装了request_threaded_irq(irq, handler, NULL, flags, name, dev)函数。
     
    5.首先分析request_threaded_irq()函数中的各个形参
    1>:irq:表示申请的中断号。
    2>:handler:表示中断服务例程
    3.> thread_fn:中断线程化,此处传递的是NULL。NULL表示没有中断线程化。
    此参数是最新版本中才出现的。为什么要提出中断线程化?
    在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。but,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。
    4>.irqflags:表示中断标志位。
    5>.devname:表示请求中断的设备的名称。
     
    6>.dev_id: 对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。 共享中断时所用。
     
    现在继续迭代深入 request_threaded_irq()内部是如何实现的。
     
    1049int request_threaded_irq(unsigned int irq, irq_handler_t handler, 
    1050                         irq_handler_t thread_fn, unsigned long irqflags, 
    1051                         const char *devname, void *dev_id) 
    1052{ 
    1053        struct irqaction *action; 
    1054        struct irq_desc *desc; 
    1055        int retval; 
    1056 
    1057       
    1063        if ((irqflags & IRQF_SHARED) &&! dev_id) 
    1064                return -EINVAL; 
    1065 
    1066        desc = irq_to_desc (irq); 
    1067        if (! desc) 
    1068                return -EINVAL; 
    1069 
    1070        if (desc->status & IRQ_NOREQUEST) 
    1071                return -EINVAL; 
    1072 
    1073        if (! handler) { 
    1074                if (! thread_fn) 
    1075                        return -EINVAL; 
    1076                handler = irq_default_primary_handler; 
    1077        } 
    1078 
    1079        action = kzalloc (sizeof (struct irqaction), GFP_KERNEL); 
    1080        if (! action) 
    1081                return -ENOMEM; 
    1082 
    1083        action->handler = handler; 
    1084        action->thread_fn = thread_fn; 
    1085        action->flags = irqflags; 
    1086        action->name = devname; 
    1087        action->dev_id = dev_id; 
    1088 
    1089        chip_bus_lock (irq, desc); 
    1090        retval = __setup_irq (irq, desc, action); 
    1091        chip_bus_sync_unlock (irq, desc); 
    1092 
    1093        if (retval) 
    1094                kfree (action); 
    1095 
    1096#ifdef CONFIG_DEBUG_SHIRQ 
    1097        if (! retval && (irqflags & IRQF_SHARED)) { 
    1098               
    1104                unsigned long flags; 
    1105 
    1106                disable_irq (irq); 
    1107                local_irq_save (flags); 
    1108 
    1109                handler (irq, dev_id); 
    1110 
    1111                local_irq_restore(flags); 
    1112                enable_irq (irq); 
    1113        } 
    1114#endif 
    1115        return retval; 
    1116}
    1049int request_threaded_irq(unsigned int irq, irq_handler_t handler,
    1050                         irq_handler_t thread_fn, unsigned long irqflags,
    1051                         const char *devname, void *dev_id)
    1052{
    1053        struct irqaction *action;
    1054        struct irq_desc *desc;
    1055        int retval;
    1056
    1057       
    1063        if ((irqflags & IRQF_SHARED) && !dev_id)
    1064                return -EINVAL;
    1065
    1066        desc = irq_to_desc(irq);
    1067        if (!desc)
    1068                return -EINVAL;
    1069
    1070        if (desc->status & IRQ_NOREQUEST)
    1071                return -EINVAL;
    1072
    1073        if (!handler) {
    1074                if (!thread_fn)
    1075                        return -EINVAL;
    1076                handler = irq_default_primary_handler;
    1077        }
    1078
    1079        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
    1080        if (!action)
    1081                return -ENOMEM;
    1082
    1083        action->handler = handler;
    1084        action->thread_fn = thread_fn;
    1085        action->flags = irqflags;
    1086        action->name = devname;
    1087        action->dev_id = dev_id;
    1088
    1089        chip_bus_lock(irq, desc);
    1090        retval = __setup_irq(irq, desc, action);
    1091        chip_bus_sync_unlock(irq, desc);
    1092
    1093        if (retval)
    1094                kfree(action);
    1095
    1096#ifdef CONFIG_DEBUG_SHIRQ
    1097        if (!retval && (irqflags & IRQF_SHARED)) {
    1098               
    1104                unsigned long flags;
    1105
    1106                disable_irq(irq);
    1107                local_irq_save(flags);
    1108
    1109                handler(irq, dev_id);
    1110
    1111                local_irq_restore(flags);
    1112                enable_irq(irq);
    1113        }
    1114#endif
    1115        return retval;
    1116}
     
    程序的第一行和第二行分别定义了:
     
    (1) struct irqaction *action;
     
    (2)2struct irq_desc *desc;
     
    两个指针action和desc,它们分别指向了结构体irqaction和 irq_desc。
     
    (3)    If ((irqflags & IRQF_SHARED) &&! dev_id)
                  Return -EINVAL;
     
    作用是:判断中断标志位,如果是共享中断的话就必须要有一个唯一的dev_id,否则返回一个错误。
     
    (4)      Desc = irq_to_desc (irq);
     
    irq_to_desc(irq):根据中断号irq在 irq_desc[NR_IRQS]数组中 返回一个具体的irq_desc。即根据irq找到它的中断处理程序。
     
    (5)    if (!desc)
     
            return -EINVAL;
     
    当返回一个空值时返回一个错误。说明申请中断号失败。
     
    (6)if (desc->status & IRQ_NOREQUEST)
                   return -EINVAL;
     
    判断中断线的状态,若为IRQ_NOREQUEST时( IRQ_NOREQUEST表示 IRQ 不能被申请)
     
    (7)        if (!handler) {
                            if (!thread_fn)
                            return -EINVAL;
                   handler = irq_default_primary_handler;
                  }
     
    判断中断服务例程是否为空,如果handler为空,则判断线程中断服务例程,若线程中断服务例程也为空,则返回一个错误值。否则中断服务例程指向: rq_default_primary_handler。
     
    (8)
     
    1079        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
    1080        if (!action)
    1081                return -ENOMEM;
    1082
    1083        action->handler = handler;
    1084        action->thread_fn = thread_fn;
    1085        action->flags = irqflags;
    1086        action->name = devname;
    1087        action->dev_id = dev_id;
     
    从1079~1087:根据requst_irq()函数中传递的参数生成一个irqaction.
     
    1097        if (!retval && (irqflags & IRQF_SHARED)) {
    1098               
    1104                unsigned long flags;
    1105
    1106                disable_irq(irq);
    1107                local_irq_save(flags);
    1108
    1109                handler(irq, dev_id);
    1110
    1111                local_irq_restore(flags);
    1112                enable_irq(irq);
    1113        }
     
    1097~1113:如果为共享中断的话,在执行中断服务例程之前,要先把这条中断线上的中断屏蔽,让后在执行,执行完之后打开中断。
     
    6.有注册中断服务函数,那必然有相应的释放中断函数。
     
    可以调用void free_irq(unsigned int irq, void *dev_id)来释放我们申请的中断线。
     
    函数形参:
     
    1>unsigned int riq:表示申请的中断号与request_irq()函数中的第一个形参对应。
     
    2>void *dev_id:与request_irq()函数中的最后一个形参含义和用法相同,在此不再说明。
     
    函数功能:
     
    如果指定的中断线不是共享的,那么,该函数删除处理程序的同时将禁用这条中断线。如果中断线是共享的,则仅删除dev_id所对应的处理程序,而这条中断线本身只有在删除了最后一个处理程序时才会被禁止。
     
    切记:This function must not be called from interrupt context
     
    freee_irq()函数不能在中断上下文中被调用。
     
    3>深入分析下free_irq()函数内部是如何实现的
     
     993void free_irq(unsigned int irq, void *dev_id) 
     994{ 
     995        struct irq_desc *desc = irq_to_desc(irq); 
     996 
     997        if (!desc) 
     998                return; 
     999 
    1000        chip_bus_lock(irq, desc); 
    1001        kfree(__free_irq(irq, dev_id)); 
    1002        chip_bus_sync_unlock(irq, desc); 
    1003}
     993void free_irq(unsigned int irq, void *dev_id)
     994{
     995        struct irq_desc *desc = irq_to_desc(irq);
     996
     997        if (!desc)
     998                return;
     999
    1000        chip_bus_lock(irq, desc);
    1001        kfree(__free_irq(irq, dev_id));
    1002        chip_bus_sync_unlock(irq, desc);
    1003}
     
    可以看到free_irq()函数了封装了_free_irq(irq,dev_id)函数。
     
    free_irq()调用_free_irq()把每一个具体的中断服务例程()释放。
    展开全文
  • 中断函数参数深入分析总结

    千次阅读 2014-10-21 21:51:40
    中断函数参数分析总结 Int request_irq (unsigned int irq, irq_handler_t handler, unsigned long flags, const char *devname, void *dev_id)   A.先来分析形参:   第一个参数irq: ...

    2012.12.29笔记

    中断函数参数分析总结

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

     

    A.先来分析形参:

     

    第一个参数irq: 表示要分配的中断号。对于一些设备(系统时钟或键盘)它的值是预先固定的,而对于大多数设备来说,这个值是动态确定的。

     

    第二个参数 handler 表示要挂入到中断请求对列中的中断服务例程 这个中断服务函数的原型是static irqreturn_t handler(int , void *);

     

    中断处理程序的前缀为static,因为它从来不会被别的文件中的代码直接调用。

     

    第三个参数flags:为标志位。可以取IRQF_DISABLEDIRQF_SHAREDIRQF_SAMPLE_RANDOM之一。在本实例程序中取 IRQF_SHARED,该标志表示多个中断处理程序共享irq中断线。一般某个中断线上的中断服务程序在执行时会屏蔽请求该线的其他中断,如果取 IRQF_DISABLED标志,则在执行该中断服务程序时会屏蔽所有其他的中断IRQF_SAMPLE_RANDOM则表示设备可以被看作是事件随见的发生源。

     

    第四个参数devname:是请求中断的设备的名称。当你加载模块成功后可以在/proc/interrupts中查看到具体设备的名称,与此同时也可以看到这个设备对应的中断号以及请求次数。

     

    第五个参数dev_id:为一个指针型变量。注意该参数为void,也就是说通过强制转换可以转换为任意类型dev_id主要用于共享中断线,对每个注册的中断处理程序来说,( Dev_id must be globally unique. Normally the address of the  device data structure is used as the cookie.)dev_id参数必须唯一(指向任一设备结构的指针就可以满足此要求,选择设备结构因为它是唯一的,而且中断处理程序可能会用到它)如果无需共享中断线,则将该参数赋值为NULL

     

    B:函数返回值

     

    requset_irq()函数成功执行后返回0。如果返回非0值,就表示错误发生。此时,指定的中断处理程序不会被注册。

    这里面有几个疑问:

     

    为什么要注册中断函数?????

     

    共享中断线的概念,参数dev_id的作用是什么????

     

     

    看一个图进行说明:

    1>由图可知:有16个中断线。要使用中断线,就要进行中断线的申请也常把申请一条中断线称为申请一个中断号,这就与request_irq()函数中的第一个形参 irq 有关系 

     

    2>Linux256个中断向量,而外部中中断向量只有16个(3247。由于硬件上的限制,很多外部设备不得不共享中断线。

     

    (例如:一些PC机所用的网卡和图形卡可以把它们分配到一条中断线上)

     

    让每个中断源独自占用一条中断线是不实现的。

     

    3>共享中断线的话虽然解决了中断资源的问题,但是,此时引出了另一个问题( 任何事物都有其两面性,此时仅仅用中断描述符并不能提供中断产生的所有信息。为了解决这个问题,内核必须对中断线给出近一步的描述,所以在Linux设计中,为每个中断请求IRQ设置了一个专用队列(中断请求队列) 

    4>中断服务程序和中断处理程序的区别:

    a.中断服务例程(interrupt service routine)

     

    Linux中,15条中断线对应15个中断处理程序,依次命名是IRQ0x00_interrupt(),IRQ0x01_interrupt()..... IRQ0X0f_interrupt().

     

    中断处理程序相当于某个中断向量的总处理程序。

    eg:IRQ0X05_interupt()5号中断(向量为37)的总处理程序。

    中断向量:中断服务程序的入口地址

    b.中断服务例程是针对一个具体设备的中断。

     

    5>.注册中断服务例程:

    IDT表完成初始化时,每个中断服务队列还为空。此时即使打开中断且某个外设的中断真的发生了,也得不到实际的服务。因为CPU虽然通过中断门进入了某个中断向量的总处理程序。但是,具体的中断服务例程还没有挂入中断请求队列。所以,在设备驱动程序的初始化阶段,必须通过request_irq()函数将响应的中断服务例程挂入中断请求队列,也就是进行注册。

     

    6>分析一下中断服务程序,即request_irq()函数中第二个参数所对应的函数

     

    Static irqreturn_t myirq_handler (int irq, void *dev_id)

    {

     

             Printk ("ISR is Working\n");

             Return IRQ_HANDLED;

     

    }

     

    中断服务例程的形参:

     

    A. int irq :中断号。

    B. void *dev_id request_irq () 的参数dev_id一致,可以根据这个设备id号得到相应设备的数据结构,进而得到相应设备的信息和相关数据。

    C.返回值:中断程序的返回值是一个特殊类型 rqreturn_t。但是中断程序的返回值却只有两个值IRQ_NONEIRQ_HANDLED

    IRQ_NONE:中断程序接收到中断信号后发现这并不是注册时指定的中断源发出的中断信号

    IRQ_HANDLED:接收到了准确的中断信号,并且作了相应正确的处理。

     

    一般 中断处理程序要做什么service,主要取决于产生的设备和该设备为什么要发送中断。

     

    1.当一个给定的中断处理程序正在执行时,这条中断线上的其它中断都会被屏蔽but,所有其他中断线上的中断都是打开的。因此这些不同中断线上的其他中断都能被处理

     

    2.request_irq()函数可能会睡眠,所以,不能在中断上下文或其它不允许阻塞的代码中调用该函数。

     

    4.在深入分析request_irq()函数之前,先来看几个重要的数据结构。

     

     

    A.      irqaction的数据结构(irqaction结构体来描述一个具体的中断服务例程

    113  struct irqaction { 

    114                             irq_handler_t handler; 

    115                             unsigned long flags; 

    116                             const char *name; 

    117                             void *dev_id; 

    118                             struct irqaction *next; 

    119                             int irq; 

    120                             struct proc_dir_entry *dir; 

    121                             irq_handler_t thread_fn; 

    122                             struct task_struct *thread; 

    123                             unsigned long thread_flags; 

    124                              };   

     

    1>handler:指向具体的一个中断服务例程。

     

    2>flags:表示中断标志位,对应于request_irq()函数中所传递的第三个参数,可取IRQF_DISABLEDIRQF_SAMPLE_RANDOMIRQF_SHARED其中之一。

     

    3>name:请求中断的设备名称,对应request_irq()函数中所传递的第四个参数

     

    4>dev_id: 共享中断时有用。 对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。

     

    5>struct irqaction *next:指向irqaction描述符的下一个元素。用一条链表将共享同一条中断线上的中断服务例程链接起来。

     

    6>irq:所申请的中断号

     

    7>dir:指向proc/irq/NN/name entry

     

    8>thread_fn:指向具体的一个线程化的中断。

     

    9>thread:指向线程中断的指针。

     

    10>thread_flags:线程中断的标志。

     

    B.      irq_desc的数据结构体

     

    每个中断向量都有它自己的irq_desc 描述符。即用irq_desc来描述中断向量所有的这些中断描述符组织在一起就形成了irq_desc irq_desc[NR_IRQS]数组

     

    175struct irq_desc { 

    176        unsigned int            irq; 

    177        struct timer_rand_state *timer_rand_state; 

    178        unsigned int            *kstat_irqs; 

    179#ifdef CONFIG_INTR_REMAP 

    180        struct irq_2_iommu      *irq_2_iommu; 

    181#endif 

    182        irq_flow_handler_t      handle_irq; 

    183        struct irq_chip         *chip; 

    184        struct msi_desc         *msi_desc; 

    185        void                    *handler_data; 

    186        void                    *chip_data; 

    187        struct irqaction        *action;       

    188        unsigned int            status;        

    189 

    190        unsigned int            depth;         

    191        unsigned int            wake_depth;    

    192        unsigned int            irq_count;     

    193        unsigned long           last_unhandled;

    194        unsigned int            irqs_unhandled; 

    195        raw_spinlock_t          lock; 

    196#ifdef CONFIG_SMP 

    197        cpumask_var_t           affinity; 

    198        const struct cpumask    *affinity_hint; 

    199        unsigned int            node; 

    200#ifdef CONFIG_GENERIC_PENDING_IRQ 

    201        cpumask_var_t           pending_mask; 

    202#endif 

    203#endif 

    204        atomic_t                threads_active; 

    205        wait_queue_head_t       wait_for_threads; 

    206#ifdef CONFIG_PROC_FS 

    207        struct proc_dir_entry   *dir; 

    208#endif 

    209        const char              *name; 

    210} ____cacheline_internodealigned_in_smp; 

    211 

    212extern void arch_init_copy_chip_data (struct irq_desc *old_desc, 

    213                                        struct irq_desc *desc, int node); 

    214extern void arch_free_chip_data (struct irq_desc *old_desc, struct irq_desc *desc); 

    215 

    216#ifndef CONFIG_SPARSE_IRQ 

    217extern struct irq_desc irq_desc[NR_IRQS]; 

     

    1>irq:表示这个描述符所对应的中断号。

     

    2>handle_irq:指向该IRQ线的公共服务程序(即该IRQ所对应的中断处理程序。

     

    3>chip:它是一个struct irq_chip类型的指针,是中断控制器的描述符 。在2.6以前的版本中它是hw_irq_controller

     

    4>handler_data:handler_irq的参数。

     

    5>chip_data:是指向irq_chip的指针。

     

    6>action:一个struct irqaction类型的指针,它指向一个单链表。该链表是由该中断线上所有中断服务例程链接起来的。

     

    7>status:表示中断线当前的状态。

     

    8>depth:中断线被激活时,值为0;当值为正数时,表示被禁止的次数

    9>irq_count:表示该中断线上发生中断的次数

     

    10>irqs_unhandled:IRQ线上未处理中断发生的次数

     

    11>name:申请中断设备的名字。

     

    C.      struct irq_chip结构体

     

    struct irq_chip是一个中断控制器的描述符。Linux支持N种可编程中断控制器PIC(中断控制器),通常不同的体系结构就有一套自己的中断处理方式。内核为了统一的处理中断,提供了底层的中断处理抽象接口,对于每个平台都需要实现底层的接口函数。这样对于上层的中断通用处理程序就无需任何改动。

     

    struct irq_chip的具体代码如下:

     

    111struct irq_chip { 

    112        const char      *name; 

    113        unsigned int    (*startup)(unsigned int irq); 

    114        void            (*shutdown)(unsigned int irq); 

    115        void            (*enable)(unsigned int irq); 

    116        void            (*disable)(unsigned int irq); 

    117 

    118        void            (*ack)(unsigned int irq); 

    119        void            (*mask)(unsigned int irq); 

    120        void            (*mask_ack)(unsigned int irq); 

    121        void            (*unmask)(unsigned int irq); 

    122        void            (*eoi)(unsigned int irq); 

    123 

    124        void            (*end)(unsigned int irq); 

    125        int             (*set_affinity)(unsigned int irq, 

    126                                        const struct cpumask *dest); 

    127        int             (*retrigger)(unsigned int irq); 

    128        int             (*set_type)(unsigned int irq, unsigned int flow_type); 

    129        int             (*set_wake)(unsigned int irq, unsigned int on); 

    130 

    131        void            (*bus_lock)(unsigned int irq); 

    132        void            (*bus_sync_unlock)(unsigned int irq); 

    133 

    134       

    135#ifdef CONFIG_IRQ_RELEASE_METHOD 

    136        void            (*release)(unsigned int irq, void *dev_id); 

    137#endif 

    138       

    142        const char      *typename; 

    143};  

    name:中断控制器的名字;

    Startup:启动中断线;

    Shutdown:关闭中断线;

    Enable:允许中断;

    Disable:禁止中断;

     

    分析了struct irq_desc, struct irq_chipirqaction的数据结构之后我们来看看他们之间的关系 

     

    现在深入分析request_irq()内部是如何实现的。

     

    135request_irq (unsigned int irq, irq_handler_t handler, unsigned long flags, 

    136            const char *name, void *dev) 

    137{ 

    138        return request_threaded_irq (irq, handler, NULL, flags, name, dev); 

    139}  

    可以看到request_irq()函数里面有封装了request_threaded_irq(irq, handler, NULL, flags, name, dev)函数。

     

    5.首先分析request_threaded_irq()函数中的各个形参

    1>irq:表示申请的中断号。

    2>handler:表示中断服务例程

    3.> thread_fn:中断线程化,此处传递的是NULLNULL表示没有中断线程化。

    此参数是最新版本中才出现的。为什么要提出中断线程化?

     Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。but,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。

    4>.irqflags:表示中断标志位。

    5>.devname:表示请求中断的设备的名称。

     

    6>.dev_id: 对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。 共享中断时所用。

     

    现在继续迭代深入 request_threaded_irq()内部是如何实现的。

     

    1049int request_threaded_irq(unsigned int irq, irq_handler_t handler, 

    1050                         irq_handler_t thread_fn, unsigned long irqflags, 

    1051                         const char *devname, void *dev_id) 

    1052{ 

    1053        struct irqaction *action; 

    1054        struct irq_desc *desc; 

    1055        int retval; 

    1056 

    1057       

    1063        if ((irqflags & IRQF_SHARED) &&! dev_id) 

    1064                return -EINVAL; 

    1065 

    1066        desc = irq_to_desc (irq); 

    1067        if (! desc) 

    1068                return -EINVAL; 

    1069 

    1070        if (desc->status & IRQ_NOREQUEST) 

    1071                return -EINVAL; 

    1072 

    1073        if (! handler) { 

    1074                if (! thread_fn) 

    1075                        return -EINVAL; 

    1076                handler = irq_default_primary_handler; 

    1077        } 

    1078 

    1079        action = kzalloc (sizeof (struct irqaction), GFP_KERNEL); 

    1080        if (! action) 

    1081                return -ENOMEM; 

    1082 

    1083        action->handler = handler; 

    1084        action->thread_fn = thread_fn; 

    1085        action->flags = irqflags; 

    1086        action->name = devname; 

    1087        action->dev_id = dev_id; 

    1088 

    1089        chip_bus_lock (irq, desc); 

    1090        retval = __setup_irq (irq, desc, action); 

    1091        chip_bus_sync_unlock (irq, desc); 

    1092 

    1093        if (retval) 

    1094                kfree (action); 

    1095 

    1096#ifdef CONFIG_DEBUG_SHIRQ 

    1097        if (! retval && (irqflags & IRQF_SHARED)) { 

    1098               

    1104                unsigned long flags; 

    1105 

    1106                disable_irq (irq); 

    1107                local_irq_save (flags); 

    1108 

    1109                handler (irq, dev_id); 

    1110 

    1111                local_irq_restore(flags); 

    1112                enable_irq (irq); 

    1113        } 

    1114#endif 

    1115        return retval; 

    1116}

    1049int request_threaded_irq(unsigned int irq, irq_handler_t handler,

    1050                         irq_handler_t thread_fn, unsigned long irqflags,

    1051                         const char *devname, void *dev_id)

    1052{

    1053        struct irqaction *action;

    1054        struct irq_desc *desc;

    1055        int retval;

    1056

    1057       

    1063        if ((irqflags & IRQF_SHARED) && !dev_id)

    1064                return -EINVAL;

    1065

    1066        desc = irq_to_desc(irq);

    1067        if (!desc)

    1068                return -EINVAL;

    1069

    1070        if (desc->status & IRQ_NOREQUEST)

    1071                return -EINVAL;

    1072

    1073        if (!handler) {

    1074                if (!thread_fn)

    1075                        return -EINVAL;

    1076                handler = irq_default_primary_handler;

    1077        }

    1078

    1079        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);

    1080        if (!action)

    1081                return -ENOMEM;

    1082

    1083        action->handler = handler;

    1084        action->thread_fn = thread_fn;

    1085        action->flags = irqflags;

    1086        action->name = devname;

    1087        action->dev_id = dev_id;

    1088

    1089        chip_bus_lock(irq, desc);

    1090        retval = __setup_irq(irq, desc, action);

    1091        chip_bus_sync_unlock(irq, desc);

    1092

    1093        if (retval)

    1094                kfree(action);

    1095

    1096#ifdef CONFIG_DEBUG_SHIRQ

    1097        if (!retval && (irqflags & IRQF_SHARED)) {

    1098               

    1104                unsigned long flags;

    1105

    1106                disable_irq(irq);

    1107                local_irq_save(flags);

    1108

    1109                handler(irq, dev_id);

    1110

    1111                local_irq_restore(flags);

    1112                enable_irq(irq);

    1113        }

    1114#endif

    1115        return retval;

    1116}

     

    程序的第一行和第二行分别定义了:

     

    (1) struct irqaction *action;

     

    (2)2struct irq_desc *desc;

     

    两个指针actiondesc,它们分别指向了结构体irqaction irq_desc

     

    (3)    If ((irqflags & IRQF_SHARED) &&! dev_id)

                  Return -EINVAL;

     

    作用是:判断中断标志位,如果是共享中断的话就必须要有一个唯一的dev_id,否则返回一个错误。

     

    (4)      Desc = irq_to_desc (irq);

     

    irq_to_desc(irq):根据中断号irq irq_desc[NR_IRQS]数组中 返回一个具体的irq_desc。即根据irq找到它的中断处理程序。

     

    (5)    if (!desc)

     

            return -EINVAL;

     

    当返回一个空值时返回一个错误。说明申请中断号失败。

     

    (6)if (desc->status & IRQ_NOREQUEST)

                   return -EINVAL;

     

    判断中断线的状态,若为IRQ_NOREQUEST( IRQ_NOREQUEST表示 IRQ 不能被申请)

     

    (7)        if (!handler) {

                            if (!thread_fn)

                            return -EINVAL;

                   handler = irq_default_primary_handler;

                  }

     

    判断中断服务例程是否为空,如果handler为空,则判断线程中断服务例程,若线程中断服务例程也为空,则返回一个错误值。否则中断服务例程指向: rq_default_primary_handler

     

    8

     

    1079        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);

    1080        if (!action)

    1081                return -ENOMEM;

    1082

    1083        action->handler = handler;

    1084        action->thread_fn = thread_fn;

    1085        action->flags = irqflags;

    1086        action->name = devname;

    1087        action->dev_id = dev_id;

     

    10791087:根据requst_irq()函数中传递的参数生成一个irqaction.

     

    1097        if (!retval && (irqflags & IRQF_SHARED)) {

    1098               

    1104                unsigned long flags;

    1105

    1106                disable_irq(irq);

    1107                local_irq_save(flags);

    1108

    1109                handler(irq, dev_id);

    1110

    1111                local_irq_restore(flags);

    1112                enable_irq(irq);

    1113        }

     

    1097~1113:如果为共享中断的话,在执行中断服务例程之前,要先把这条中断线上的中断屏蔽,让后在执行,执行完之后打开中断。

     

    6.有注册中断服务函数,那必然有相应的释放中断函数。

     

    可以调用void free_irq(unsigned int irq, void *dev_id)来释放我们申请的中断线。

     

    函数形参:

     

    1>unsigned int riq:表示申请的中断号与request_irq()函数中的第一个形参对应。

     

    2>void *dev_id:request_irq()函数中的最后一个形参含义和用法相同,在此不再说明。

     

    函数功能:

     

    如果指定的中断线不是共享的,那么,该函数删除处理程序的同时将禁用这条中断线。如果中断线是共享的,则仅删除dev_id所对应的处理程序,而这条中断线本身只有在删除了最后一个处理程序时才会被禁止。

     

    切记:This function must not be called from interrupt context

     

    freee_irq()函数不能在中断上下文中被调用。

     

    3>深入分析下free_irq()函数内部是如何实现的

     

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

     994{ 

     995        struct irq_desc *desc = irq_to_desc(irq); 

     996 

     997        if (!desc) 

     998                return; 

     999 

    1000        chip_bus_lock(irq, desc); 

    1001        kfree(__free_irq(irq, dev_id)); 

    1002        chip_bus_sync_unlock(irq, desc); 

    1003}

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

     994{

     995        struct irq_desc *desc = irq_to_desc(irq);

     996

     997        if (!desc)

     998                return;

     999

    1000        chip_bus_lock(irq, desc);

    1001        kfree(__free_irq(irq, dev_id));

    1002        chip_bus_sync_unlock(irq, desc);

    1003}

     

    可以看到free_irq()函数了封装了_free_irq(irq,dev_id)函数。

     

    free_irq()调用_free_irq()把每一个具体的中断服务例程()释放。

    http://blog.sina.com.cn/s/blog_aafd61a201018r0h.html

    展开全文
  • 中断函数

    千次阅读 2014-10-22 12:44:15
     一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?答案是允许的,不过只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个ret语句,...

    C51的中断函数的格式为:void FuncIr(void) interrupt x [using y]

    以下是梦游的一些分析:
           一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?答案是允许的,不过只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个ret语句,相对效率会高一些。
           二、using的用法,using可以修饰任何函数,不过个人建议只用来修饰中断函数;简单的说,“using”会指定工作寄存器组,由于中断函数一般都是比较紧急的事情,有时一条语句都会斤斤计较,所以使用using切换寄存器组可以省去一些压栈的动作,由于51只有两级中断,同级中断不能被打断,因此,我们可以同级中断设成同样的寄存器组,从某种意义上来说,有一组寄存器是多余的。同时个人建议中断函数应该使用using这个关键字。
           三、中断中调用函数,首先要讨论中断函数中调用函数的必要性,前天在论坛上我和别人争论过这个问题,现在我还是这个观点:有些情况中断中调用函数还是必要的,这个时候是不是该调用函数,其实和普通函数差不多,首先是这个函数如果调用多次,或者要带一些参数什么的就更加必要的;前天有人跟我叫劲,说假如只调用一次且无参数无返回的函数要直接写,因为如果用函数,至少会增加CALL和RET两条语句,我不敢苟同,我是实际调试发现的,当你程序比较复杂时,你将那部单独拉出来做成函数,可能代码和时间都会更好。
           四、中断中调用的函数最好不要被中断外的其它函数调用,因为会出现“重复调用”的警告,有时这种调用是很致命的,有人说这个函数可以用reentrant来修饰,是的,的确可以这样解决,不过个人不建议这么做,也许这样会跟你减少很多堆栈空间,并且整个程序的优化要差很多,个人建议出现这种情况就把这个函数写两遍,分成两个函数分别调用。
            五、中断调用了函数,会出现一些莫名其妙的问题,一些数据不对(我现在遇到这个问题其实一般是因为汇编中使用了绝对寄存器引起的,有人说中断函数使用那个寄存器组,被中断调用的函数就使用哪个寄存器组(我认为好参考C51.PDF:Functions called from an interrupt procedure must function with the same register bank as the interrupt procedure. When the NOAREGS directive is not explicitly specified, the compiler may generate absolute register accesses using the register bank selected (by the using attribute or by the REGISTERBANK control) for that function. Unpredictable results may occur when a function assumes a register bank other than the one currently selected. Refer to “Register Bank Access” on page 124 for more information.),我认为这样不好:
           这样会增加额外的消耗,使用using会增加一下语句:
           PUSH PSW
           MOV PSW, #XX
            ....
            POP PSW
           更重要的是,使用using的函数不能有返回值(这个地方有问题,应该可以有返回值,下文说是不能不能返回 bit 类型的值),这是致命伤(所以这不是致命伤,可以使用using解决这个问题
           个人推荐的方法有两种:
           1、使用“#pragma NOAREGS”禁止使用绝对寄存器
           2、使用“#pragme RB(x)”来指定本文件的工作寄存器组
           六、一般说来,要求中断函数尽可能的短,但也有特殊情况,有些前/后台的系统中,就会把很多相对重要的事情放到定时中断(这个定时中断类似实时操作系统中的时钟节拍)去做,而且程序很长。我单独提出来这点是想告诉大家,中断函数也是一个函数而已,只要系统有必要,可以做一些看似不合理的事情,该出手时就出手,就像goto语句一样。

    转自http://www.ednchina.com/blog/hotchip/,请大家去他的博客中支持他,里面有不错的文章。括号中是我的理解

    关于using:
    举个例子来说:
    定义一个函数
    void func(unsigned char i) {
    ...
    }
    有如下一个中断函数
    void int_0(void) interrupt 0 using 1 {
    ....
    }
    在默认状态下,func使用寄存器组0(BANK0),那么当int_0调用func时是否存在当传递参数时会造成参数传递错误?

    如果在中断服务函数 ISR 中使用寄存器,那么必须处理好 using 的使用问题:
    1、中断服务函数使用 using 指定与主函数不同的寄存器组(主函数一般使用 Register bank 0)。
    2、中断优先级相同的ISR 可用 using 指定相同的寄存器组,但优先级不同的 ISR 必须使用不同的寄存器组,在 ISR 中被调用的函数也要使用 using 指定与中断函数相同的寄存器组。(应该是这样的)
    3、如果不用 using 指定,在 ISR 的入口,C51 默认选择寄存器组0,这相当于中断服务程序的入口首先执行指令:
    MOV PSW #0
    这点保证了,没使用 using 指定的高优先级中断。可以中断使用不同的寄存器组的低优先级中断。
    4、使用 using 关键字给中断指定寄存器组,这样直接切换寄存器组而不必进行大量的 PUSH 和 POP 操作,可以节省RAM空间,加速 MCU 执行时间。寄存器组的切换,总的来说比较容易出错,要对内存的使用情况有比较清晰的认识,其正确性要由你自己来保证。特别在程序中有直接地址访问的时候,一定要小心谨慎!至于“什么时候要用到寄存器组切换”,一种情况是:当你试图让两个(或以上)作业同时运行,而且它们的现场需要一些隔离的时候,就会用上了。 ISR 或使用实时操作系统 RTOS 中,寄存器非常有用。
    寄存器组使用的原则:
    1、8051 的最低32 个字节分成 4 组 8 寄存器。分别为寄存器R0 到R7。寄存器组由PSW 的低两位选择。在 ISR 中,MCU 可以切换到一个不同的寄存器组。对寄存器组的访问不可位寻址,C51 编译器规定使用 using 或 禁止中断的函数(#pragma disable )均不能返回 bit 类型的值。
    2、主程序(main函数)使用一组,如 bank 0;低中断优先级的所有中断均使用第二组,如 bank 1;高中断优先级的所有中断均使用再另外一组,如 bank 2。显然,同级别的中断使用同一组寄存器不会有问题,因为不会发生中断嵌套;而高优先级的中断则要使用与低优先级中断不同的一组,因为有可能出现在低优先级中断中发生高优先级中断的情况。编译器会自动判断何时可使用绝对寄存器存取。
    3、在 ISR 中调用其它函数,必须和中断使用相同的寄存器组。当没用 NOAREGS 命令做明确的声明,编译器将使用绝对寄存器寻址方式访问函数选定(即用 using 或 REGISTERBANK 指定)的寄存器组,当函数假定的和实际所选的寄存器组不同时,将产生不可预知的结果,从而可能出现参数传递错误,返回值可能会在错误的寄存器组中。
    举一例子:当需要在中断内和中断外调用同一个函数,假定按照程序的流程控制,不会出现函数的递归调用现象,这样的调用会不会出现问题?若确定不会发生重入情况,则有以下两种情况:
    1、如果 ISR 和主程序使用同一寄存器组(主程序缺省使用BANK 0,若 ISR 没有使用 using 为其指定寄存器区,则缺省也使用 BANK 0),则不需其他设置。
    2、如果 ISR 和主程序使用不同的寄存器组(主程序缺省使用BANK 0,ISR 使用 using 指定了其他 BANK),则被调用函数必须放在:
    #pragma NOAREGS
    #pragma AREGS
    控制参数对中,指定编译器不要对该函数使用绝对寄存器寻址方式;或者也可在 Options->C51,选中“Don''t use absolute register accesses”,使所有代码均不使用绝对寄存器寻址方式(这样,执行效率将稍有降低)。不论以上的哪一种情况,编译器均会给出重入警告,需手工更改 OVERLAY 参数,做重入说明。
    3、还有一种办法:如果被调用函数的代码不是很长,还是将该函数复制一份,用不同的函数名代替,这种情况适合ROM有足够多余的空间。
    因此,对using关键字的使用,如果没把握,宁可不用,交给编译系统自己去处理好了。
    详细使用可参见C51.PDF文件,以上供参考

     

    http://blog.sina.com.cn/s/blog_4c5da5d8010009tu.html

    展开全文
  • 关于C51的中断函数要注意的几个问题(高手绕行)  最近在虾潭逛,发现一些小虾米对C51中断函数有些不了解,今天周末,抽空发个技术帖子,希望对小虾米有所帮助,如 有错误之处,还请指正,就当抛砖引玉吧! ...

    关于C51的中断函数要注意的几个问题(高手绕行)

        最近在虾潭逛,发现一些小虾米对C51中断函数有些不了解,今天周末,抽空发个技术帖子,希望对小虾米有所帮助,如

    有错误之处,还请指正,就当抛砖引玉吧!

        C51的中断函数的格式为:

        void FuncIr(void) interrupt x [using y]

        以下是梦游的一些分析:

        一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?答案是允许的,不过

    只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个ret语句,相对效率会高一

    些。

        二、using的用法,using可以修饰任何函数,不过个人建议只用来修饰中断函数;简单的说,“using”会指定工作寄存

    器组,由于中断函数一般都是比较紧急的事情,有时一条语句都会斤斤计较,所以使用using切换寄存器组可以省去一些压栈

    的动作,由于51只有两级中断,同级中断不能被打断,因此,我们可以同级中断设成同样的寄存器组,从某种意义上来说,

    有一组寄存器是多余的。同时个人建议中断函数应该使用using这个关键字。

        三、中断中调用函数,首先要讨论中断函数中调用函数的必要性,前天在论坛上我和别人争论过这个问题,现在我还是

    这个观点:有些情况中断中调用函数还是必要的,这个时候是不是该调用函数,其实和普通函数差不多,首先是这个函数如

    果调用多次,或者要带一些参数什么的就更加必要的;前天有人跟我叫劲,说假如只调用一次且无参数无返回的函数要直接

    写,因为如果用函数,至少会增加CALL和RET两条语句,我不敢苟同,我是实际调试发现的,当你程序比较复杂时,你将那部

    分单独拉出来做成函数,可能代码和时间都会更好

        四、中断中调用的函数最好不要被中断外的其它函数调用,因为会出现“重复调用”的警告,有时这种调用是很致命

    的,有人说这个函数可以用reentrant来修饰,是的,的确可以这样解决,不过个人不建议这么做,也许这样会跟你减少很多

    堆栈空间,并且整个程序的优化要差很多,个人建议出现这种情况就把这个函数写两遍,分成两个函数分别调用。

        五,中断调用了函数,会出现一些莫名其妙的问题,一些数据不对。其实一般是因为汇编中使用了绝对寄存器引起的,有人说中断函数使用那个寄存器组,被中断调用的

    函数就使用哪个寄存器组,我认为这样不好:

        这样会增加额外的消耗,使用using会增加一下语句:

        PUSH PSW

        MOV PSW, #XX

        ....

        POP PSW

       更重要的是,使用using的函数不能有返回值,这是致命伤

       个人推荐的方法有两种:

       1、使用“#pragma NOAREGS”禁止使用绝对寄存器

       2、使用“#pragme RB(x)”来指定本文件的工作寄存器组

       六、一般说来,要求中断函数尽可能的短,但也有特殊情况,有些前/后台的系统中,就会把很多相对重要的事情放到定

    时中断(这个定时中断类似实时操作系统中的时钟节拍)去做,而且程序很长。我单独提出来这点是想告诉大家,中断函数

    也是一个函数而已,只要系统有必要,可以做一些看似不合理的事情,该出手时就出手,就像goto语句一样。

    51单片机的中断函数

    中断使用interrupt 关键字和中断编号0-4来实现:

    返回值 函数名 interrupt n

    n对应中断源的编号,中断编号告诉中断器中断程序的入口地址,它对应着IE寄存器中的使能位,即IE寄存器中的0位对应着外部中断0。

    8051单片机的中断源以及终端编号如下:

    中断编号   中断源                             入口地址

     0            外部中断0                          0003H 

    1             定时器/计数器0 溢出         000BH 

    2             外部中断1                           0013H 

    3             定时器/计数器1 溢出         001BH 

    4              串行口中断                  0023H

    在51系列单片机中,有的单片机多达32个中断源,所以中断编号是0-31.

    当正在执行一个特定的任务时,可能有更紧急的事情需要CPU处理,这就涉及到了中断优先级。高优先级中断可以中断正在处理的低优先级中断程序,因而最好给每种优先级程序分配不同的寄存器组。在C51中可使用using指定寄存器组,using后的变量为0-3的常整数,分别表示51单片机内的4哥寄存器组。中断函数的完整语法如下:

    返回值 函数名([参数])[模式][重入]interrupt n[usingn]

    unsigned int interruptcnt;

    unsigned char second;

    void timer0(void) interrupt 1 using 2

    if(++interruptcnt==4000) // 计数到4000

    second++; //另一个计数器

    interruptcnt=0; //计数器清零


    转自:http://blog.csdn.net/caoweiyan/article/details/8191483



    展开全文
  • 中断函数和中断处理函数

    万次阅读 2014-10-22 13:52:37
    (1)当你在做一个计算机(嵌入式)系统时,在为系统做初始化时往往会有设置中断向量的操作。 当你设置好某个特定的外部事件(比如定时器超时)的中断向量后,当你允许(使能)了该设备(定时器),那么等到特定...
  • 中断服务函数

    千次阅读 2020-01-17 14:44:45
    中断问题也是面试嵌入式岗位时,面试官比较喜欢拷问的问题之一,作为一个嵌入式开发人员,自然少不了与中断打交道,裸机程序中...4.不要在中断函数中使用printf函数,会带来重入和性能问题 中断并不是程序一开始...
  • C51中断函数格式

    千次阅读 2017-07-14 23:24:30
    C51 Keil 编译器中断函数语法定义:void 函数名() interrupt n using m C51编译器允许0~31个中断,C51控制器所提供的中断及中断地址如下: 中断号 中断源 中断地址 0 EXTERNAL 0 0003H 1 TIMER/COUNTER 0 ...
  • 中断服务函数中,如果设置标志位,然后其他函数判断标志位,需要设置全局变量来实现。全局变量的声明中需要添加修饰符volatile,防止编译器对变量进行优化,导致程序出错。 下面是volatile的用法说明: ...
  • 在开始写中断函数之前,我们来一起回顾一下,单片机的中断系统。  中断的意思(学习过微机原理与接口技术的同学,没学过单片机,也应该知道),我们在这里就不讲了,首先来回忆下中断系统涉及到哪些问题。  (1)...
  • stm32端口、中断初始化及中断函数

    千次阅读 2019-07-24 19:38:39
    对于stm32端口、中断初始化及中断函数的调用 端口初始化 首先来看下官方提供的端口初始化库函数(以stm32f4xx为例) ps:很多时候看官方提供的函数比较困难,所以结合例子进行分析,部分官方代码未能展示,可在keil...
  • CC2530中断初始化和中断函数

    千次阅读 2020-07-21 20:37:06
    设置中断的触发(开始)条件 例如:PICTL |= 0x01 设置端口P0_7中断触发方式为下降沿触发 PICTL &=~0x02 设置端口P1_2中断触发方式为上升沿触发 2)初始化中断使能端口寄存器(PxIEN) 设置Px端口中断使能 例如:P0...
  • ZYNQ 中断函数配置——个人笔记

    千次阅读 2020-05-25 21:42:08
    //入口参数:SCU GIC实例,中断ID,定义中断处理函数中断 s32 XScuGic_Connect (XScuGic *InstancePtr, u32 Int_Id, Xil_InterruptHandler Handler, void *CallBackRef); //断开连接:中断服务程序不再连接//...
  • 2.9中断函数集 Intrpt(Interrupt)函数集是提供具有输入中断信息的进程并可预设输出中断的核心函数的集合。   1. op_intrpt_schedule_self (time, code) 此核心函数的作用是为调用进程预设一个中断,其参数说明...
  • 在开始写中断函数之前,我们来一起回顾一下,单片机的中断系统。  中断的意思(学习过微机原理与接口技术的同学,没学过单片机,也应该知道),我们在这里就不讲了,首先来回忆下中断系统涉及到哪些问题。  ...
  • stm32中断函数改变全局变量值

    千次阅读 2017-02-22 11:39:00
     当检测到有按键按下时,实现PA.0led闪亮,函数肯定不能在中断服务程序里写,就想着在进入中断函数后改变变量biaozhi的值(0变成1),然后main中while循环一直检测biaozhi的值若为1,则调用函数;  这里只说下...
  • 中断系统流程解析: asm_do_IRQ(unsigned int irq, struct pt_regs *regs) handle_IRQ(irq, regs); generic_handle_irq(irq);/*进行一般的中断处理*/ struct irq_desc *desc = irq_to_desc(irq); /*#define irq...
  • 关于单片机 C中断函数

    千次阅读 2010-08-25 12:19:00
    定义中断服务函数 <br /> 函数类型 函数名 (形式参数) interrupt n [using n] 中断号 中断源 中断向量  0 外部中断0 0003H  1 定时器/计数器0 000BH  2 外部中断1 ...
  • 嵌入式里的中断解读以及中断服务函数

    千次阅读 多人点赞 2018-11-26 15:27:16
    从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。如果中断的线是激活的,中断控制器就把电信号发送处理器的某个特定引脚。处理器于是立即停止自己...
  • 关于C51的中断函数要注意的几个问题(高手绕行)  最近在虾潭逛,发现一些小虾米对C51中断函数有些不了解,今天周末,抽空发个技术帖子,希望对小虾米有所帮助,如 有错误之处,还请指正,就当抛砖引玉吧! ...
  • 中断处理函数注意事项

    千次阅读 2020-05-09 11:49:24
    中断服务函数由硬件触发,因此不能获得参数,也无法返回值;另一方面,在中断服务函数中使用不可重入的函数,往往会导致问题。
  • STM32之中断函数介绍篇

    千次阅读 2016-10-11 16:37:14
    NVIC驱动有很多的用途,如使能或者失能IRQ(中断请求),或者...2.NVIC)PriorityGroupConfig函数的功能是设置优先级分组:先占优先级和从优先级,且有一个输入参数是优先级分组的长度 例:定义先占优先级1,从优先级3
  • ① 将io口选择为GPIO_EXIT ② 在配置中修改一些参数  ③ GPIO:配置为上升沿触发,下拉...以上为中断函数的io口的配置过程。       void EXTIx_IRQHandler() {  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);//中
  • uCOS-II中断相关函数介绍

    千次阅读 2015-09-19 21:54:20
    uCOS-ii中断函数介绍
  • 面试题之二:中断服务函数

    千次阅读 2019-08-05 22:54:25
    中断问题也是面试嵌入式岗位时,面试官比较喜欢拷问的问题之一,作为一个嵌入式开发人员,自然少不了与中断打交道...4.不要在中断函数中使用printf函数,会带来重入和性能问题 中断并不是程序一开始就判断好会在那...
  • SysTick_Handler(void)这个定时器中断处理函数是怎么定义的?根据以往的经验,我感觉在底层应该会有一个类似于函数声明的东西的存在,但是往下跟程序的话,一直都没有找到相应的定义或者声明,那么究竟SysTick_...
  • linux中断编程函数

    千次阅读 2012-06-27 09:14:11
    总结下linux中断编程的函数,方便下次使用时查看。 1.中断申请和释放 int request_irq( unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void *dev_id ) irq---中断号...
  • 嵌入式中断服务函数的一些特点

    千次阅读 2016-08-23 19:39:07
    中断服务程序需要满足如下要求:  (1)不能返回值;  (2)不能向ISR传递参数;  (3) ISR应该尽可能的短小精悍;  (4) printf(char * lpFormatString,…)函数会带来重入和性能问题,不能在ISR中采用。
  • GPIO及中断API函数

    千次阅读 2016-01-19 16:07:51
    #include // 标准 GPIO_API ... port 的使用权,由参数 gpio 指定具体 port,非空的lables指针有助于诊断。主要是告诉内核这地址被占用了。当其它地方调用同一地址的gpio_request就会报告错误,该地址已被
  • 定时器及其“中断函数

    千次阅读 2014-10-21 09:48:10
    3)定时“中断函数”(回调函数): void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime); 注意:回调函数的写法一定要参照MSDN的提示规则编写,否则是无效的 由MFC响应的函数为 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 253,243
精华内容 101,297
关键字:

给中断函数设置参数