当前位置: 首页 > news >正文

网站网络推广能优化企业官网建站步骤

网站网络推广能优化,企业官网建站步骤,做网站搞笑口号,给网站做seo的价格中断也是一种异常#xff0c;之所以把它单独的列出来#xff0c;是因为中断的处理与具体的开发板密切相关#xff0c;除一些必须、共用的中断(比如系统时钟中断、片内外设UART中断)外#xff0c;必须由驱动开发者提供处理函数。内核提炼出中断处理的共性#xff0c;搭建一…中断也是一种异常之所以把它单独的列出来是因为中断的处理与具体的开发板密切相关除一些必须、共用的中断(比如系统时钟中断、片内外设UART中断)外必须由驱动开发者提供处理函数。内核提炼出中断处理的共性搭建一个非常容易扩充的中断处理体系。init_IRQ函数(代码在arch/arm/kernel/irq.c中)被用来初始化中断和处理框架设置各种中断的默认处理函数。当发生中断时中断总入口函数asm_do_IRQ就可以调用这些函数进行下一步处理。Linux中断处理体系结构分析(二)(2010-06-10 21:08)1.中断处理的体系结构我们知道编写设备驱动程序一定要用到中断处理函数这在驱动程序的编写中占据很重要的一部分。在响应一个特定的中断的时候内核会执行一个函数该函数叫做中断处理程序(interrupt handler)或中断服务例程(interrupt service routine,ISP).产生中断的每个设备都有一个相应的中断处理程序中断处理程序通常不和特定的设备关联而是和特定的中断关联的也就是说如果一个设备可以产生多种不同的中断那么该就可以对应多个中断处理程序相应的该设备的驱动程序也就要准备多个这样的函数。在Linux内核中处理中断是分为上半部(top half)和下半部(bottomhalf)之分的。上半部只做有严格时限的工作例如对接收到的中断进行应答或复位硬件这些工作是在所有的中断被禁止的情况下完成的能够被允许稍后完成的工作会推迟到下半部去。要想了解上半部和下半部的机制可以阅读一下《Linux内核设计与实现》的第七章的内容。Linux内核将所有的中断统一编号使用一个irq_desc结构数组来描述这些中断;每个数组项对应一个中断也可能是一组中断它们共用相同的中断号里面记录了中断的名称、中断状态、中断标记(比如中断类型、是否共享中断等)并提供了中断的低层硬件访问函数(清除、屏蔽、使能中断)提供了这个中断的处理函数入口通过它可以调用用户注册的中断处理函数。通过irq_desc结构数组就可以了解中断处理体系结构irq_desc结构的数据类型include/linux/irq.h中定义struct irq_desc {unsigned int        irq;struct timer_rand_state *timer_rand_state;unsigned int *kstat_irqs;#ifdef CONFIG_INTR_REMAPstruct irq_2_iommu *irq_2_iommu;#endifirq_flow_handler_t    handle_irq; // 当前中断的处理函数入口struct irq_chip        *chip; //低层的硬件访问struct msi_desc        *msi_desc;void            *handler_data;void            *chip_data;struct irqaction    *action;    // 用户提供的中断处理函数链表unsigned int        status;        //IRQ状态........const char        *name; //中断的名称} ____cacheline_internodealigned_in_smp;handle_irq是这个或这组中断的处理函数入口。发生中断时总入口函数asm_do_IRQ将根据中断号调用相应irq_desc数组项中handle_irq.handle_irq使用chip结构中的函数清除、屏蔽或者重新使能中断还要调用用户在action链表中注册的中断处理函数。irq_chip结构类型也是在include/linux/irq.h中定义其中的成员大多用于操作底层硬件比如设置寄存器以屏蔽中断使能中断清除中断等。struct irq_chip {const char    *name;unsigned int    (*startup)(unsigned int irq);//启动中断如果不设置缺省为“enablevoid        (*shutdown)(unsigned int irq);/*关闭中断如果不设置缺省为disable*/void        (*enable)(unsigned int irq);// 使用中断如果不设置缺省为unmaskvoid        (*disable)(unsigned int irq);//禁止中断如果不设置缺省为“mask”void        (*ack)(unsigned int irq);/*响应中断通常是清除当前中断使得可以接收下一个中断*/void        (*mask)(unsigned int irq); //屏蔽中断源void        (*mask_ack)(unsigned int irq);//屏蔽和响应中断void        (*unmask)(unsigned int irq);//开启中断源void        (*eoi)(unsigned int irq);........const char    *typename;};irq_desc结构中的irqaction结构类型在include/linux/iterrupt.h中定义。用户注册的每个中断处理函数用一个irqaction结构来表示一个中断比如共享中断可以有多个处理函数它们的irqaction结构链接成一个链表以action为表头。irqation结构定义如下struct irqaction {irq_handler_t handler; //用户注册的中断处理函数unsigned long flags; //中断标志比如是否共享中断电平触发还是边沿触发const char *name; //用户注册的中断名字void *dev_id; //用户传给上面的handler的参数还可以用来区分共享中断struct irqaction *next; //指向下一个用户注册函数的指针int irq; //中断号struct proc_dir_entry *dir;irq_handler_t thread_fn;struct task_struct *thread;unsigned long thread_flags;};   irq_desc结构数组、它的成员“struct irq_chip *chip” struct irqaction *action,这3种数据结构构成了中断处理体系的框架。下图中描述了Linxu中断处理体系结构的关系图中断处理流程如下(1)发生中断时CPU执行异常向量vector_irq的代码(2)在vector_irq里面最终会调用中断处理的总入口函数asm_do_IRQ(3)asm_do_IRQ根据中断号调用irq_desc数组项中的handle_irq。(4)handle_irq会使用chip成员中的函数来设置硬件比如清除中断、禁止中断、重新使能中断等(5)handle_irq逐个调用用户在aciton链表中注册的处理函数中断体系结构的初始化就是构造这些数据结构比如irq_desc数组项中的handle_irq、chip等成员用户注册中断时就是构造action链表用户卸载中断时就是从action链表中去除不需要的项。2.中断处理体系结构的初始化init_IRQ函数被用来初始化中断处理体系结构代码在arch/arm/kernel/irq.c中153 void __init init_IRQ(void)154 {155 int irq;156157 for (irq 0; irq NR_IRQS; irq)158 irq_desc[irq].status | IRQ_NOREQUEST | IRQ_NOPROBE;159160 init_arch_irq();161 }157~~158行 初始化irq_desc结构数组中每一项的中断状态第160行调用架构相关的中断初始化函数。对于S3C2440开发板这个函数就是s3c24xx_init_irq,移植machine_desc结构中的init_irq成员就指向这个函数s3c24xx_init_irq函数在arch/arm/plat-s3c24xx/irq.c中定义它为所有中断设置了芯片相关的数据结构(irq_desc[irq].chip)设置了处理函数入口(irq_desc[irq].handle_irq)。以外部中断EINT4-EINT23为例用来设置它们的代码如下void __init s3c24xx_init_irq(void)534 {535 unsigned long pend;536 unsigned long last;537 int irqno;538 int i;........637 for (irqno IRQ_EINT4; irqno IRQ_EINT23; irqno) {638 irqdbf(registering irq %d (extended s3c irq)\n, irqno);639 set_irq_chip(irqno, s3c_irqext_chip);640 set_irq_handler(irqno, handle_edge_irq);641 set_irq_flags(irqno, IRQF_VALID);...............655 for (irqno IRQ_S3CUART_RX1; irqno IRQ_S3CUART_ERR1; irqno) {656 irqdbf(registering irq %d (s3c uart1 irq)\n, irqno);657 set_irq_chip(irqno, s3c_irq_uart1);658 set_irq_handler(irqno, handle_level_irq);659 set_irq_flags(irqno, IRQF_VALID);660 }..........676 irqdbf(s3c2410: registered interrupt handlers\n);677 }678在639行set_irq_chip函数的作用就是“irq_desc[irno].chip s3c_irqext_chip”,以后就可能通过irq_desc[irqno].chip结构中的函数指针设置这些外部中断的触发方式(电平触发边沿触发)使能中断禁止中断。在640行设置这些中断的处理函数入口为handle_edge_irq即“irq_desc[irqno].handle_irqhandle_edge_irq”.发生中断时handle_edge_irq函数会调用用户注册的具体处理函数 在641行设置中断标志为“IRQF_VALID”表示可以使用它们。init_IRQ函数执行完后irq_desc数组项的chip,handl_irq成员都被设置2.2 用户注册中断处理函数的过程用户驱动程序通过request_irq函数向内核注册中断处理函数request_irq函数根据中断号找到irq_desc数组项然后在它的action链表添加一个表项。原先的内核中requset_irq函数在kernel/irq/manage.c中定义而现在2.6.32版本中进行了改变可以看这篇文章http://eeek.borgchat.net/lists/newbies/msg39146.html这里解释了在2.6.32内核中我们可以看到找不到了request_irq函数的实现而是用request_threaded_irq()函数给替换了。我们可以在inclue/linux/interrupt.h中找到这个函数的原型。110 #ifdef CONFIG_GENERIC_HARDIRQS111 extern int __must_check112 request_threaded_irq(unsigned int irq, irq_handler_t handler,113 irq_handler_t thread_fn,114 unsigned long flags, const char *name, void *dev);115116 static inline int __must_check117 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,118 const char *name, void *dev)119 {120 return request_threaded_irq(irq, handler, NULL, flags, name, dev);121 }123 extern void exit_irq_thread(void);124 #else126 extern int __must_check127 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,128 const char *name, void *dev);136 static inline int __must_check137 request_threaded_irq(unsigned int irq, irq_handler_t handler,138 irq_handler_t thread_fn,139 unsigned long flags, const char *name, void *dev)140 {141 return request_irq(irq, handler, flags, name, dev);142 }143144 static inline void exit_irq_thread(void) { }145 #endif其实具体的实现在request_threaded_irq函数中也是在/kernel/irq/manage.c中定义requset_threaded_irq函数首先使用这4个参数构造一个irqaction结构然后调用setup_irq函数将它链入链表中1003 int request_threaded_irq(unsigned int irq, irq_handler_t handler,1004 irq_handler_t thread_fn, unsigned long irqflags,1005                          const char *devname, void *dev_id).............1056 action-handler handler;1057 action-thread_fn thread_fn;1058 action-flags irqflags;1059 action-name devname;1060 action-dev_id dev_id;10611062 chip_bus_lock(irq, desc);1084 local_irq_restore(flags);1085 enable_irq(irq);...........1088 return retval;1089 }1090 EXPORT_SYMBOL(request_threaded_irq);setup_irq函数也是在kernel/irq.manage.c中定义它完成如下3个主要功能(1)将新建的irqaction结构链入irq_desc[irq]结构的action链表中这有两种可能。如果action链表为空则直接链入,否则先判断新建的irqaction结构和链表中的irqaction结构所表示的中断类型是否一致即是否都声明为可共享的(IRQF_SHARED)、是否都使用相同的触发方式如果一致则将新建的irqation结构链入(2)设置irq_desc[irq]结构中chip成员的还没设置的指针让它们指向一些默认函数chip成员在init_IRQ函数初始化中断体系结构的时候已经设置了这里只是设置其中还没设置的指针这通过irq_chip_set_defaults函数来完成它在kernel/irq/chip.c中定义296 void irq_chip_set_defaults(struct irq_chip *chip)297 {298 if (!chip-enable)299 chip-enable default_enable;//调用chip-unmask300 if (!chip-disable)301 chip-disable default_disable;//此函数为空302 if (!chip-startup)303 chip-startup default_startup;//调用chip-enable310 if (!chip-shutdown)311 chip-shutdown chip-disable ! default_disable ?312 chip-disable : default_shutdown;313 if (!chip-name)314 chip-name chip-typename;315 if (!chip-end)316 chip-end dummy_irq_chip.end;317 }(4)启动中断如果irq_desc[irq]结构中status成员没有被指明IRQ_NOAUTOEN(表示注册中断时不要使用中断)还要调用chip-startup或chip-enable来启动中断所谓启动中断通常就是使用中断。一般情况下只有那些“可以自动使能的”中断对应的irq_desc[irq].status才会被指明为IRQ_NOAUTOEN所以无论哪种情况执行request_irq注册中断之后这个中断就已经被使能了。总结一下request_irq函数注册(1)irq_des[irq]结构中的action链表中已经链入了用户注册的中断处理函数(2)中断的触发方式已经被设好(3)中断已经被使能2.3 中断的处理过程asm_do_IRQ是中断的C语言总入口函数它在/arch/arm/kernel/irq.c中定义106 asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)107 {108 struct pt_regs *old_regs set_irq_regs(regs);109110 irq_enter();111112 /*113 * Some hardware gives randomly wrong interrupts. Rather114 * than crashing, do something sensible.115 */116 if (unlikely(irq NR_IRQS)) {117 if (printk_ratelimit())118 printk(KERN_WARNING Bad IRQ%u\n, irq);119 ack_bad_irq(irq);120 } else {121 generic_handle_irq(irq);122 }123124 /* AT91 specific workaround */125 irq_finish(irq);126127 irq_exit();128 set_irq_regs(old_regs);129 }desc_hand_irq函数直接调用desc结构中的hand_irq成员函数它就是irq_desc[irq].handle.irqasm_do_IRQ函数中参数irq的取值范围为IRQ_EINT0~(IRQ_EINT0 31),只有32个取值。它可能是一个实际的中断号也可能是一组中断的中断号。这里有S3C2440的芯片特性决定的发生中断时INTPND寄存器的某一位被置1INTOFFSET寄存器中记录了是哪一位(0--31)中断向量调用asm_do_IRQ之前要把INTOFFSET寄存器的值确定irq参数。每一个实际的中断在irq_desc数组中都有一项与它对应它们的数目不止32.当asm_do_IRQ函数参数irq表示的是“一组”中断时irq_desc[irq].handle_irq成员函数还需要先分辨出是哪一个中断然后调用irq_desc[irqno].handle_irq来进一步处理。以外部中断EINT8—EINT23为例它们通常是边沿触发(1)它们被触发里INTOFFSET寄存器中的值都是5asm_do_IRQ函数中参数irq的值为(IRQ_EINTO5),即IRQ_EINT8t23(2)irq_desc[IRQ_EINT8t23].handle_irq在前面init_IRQ函数初始化中断体系结构的时候被设为s3c_irq_demux_extint8.(3)s3c_irq_demux_extint8函数的代码在arch/arm/plat-s3c24xx/irq.c中它首先读取EINTPEND、EINTMASK寄存器确定发生了哪些中断重新计算它们的中断号然后调用irq_desc数组项中的handle_irq成员函数453 s3c_irq_demux_extint8(unsigned int irq,454 struct irq_desc *desc)455 {456 unsigned long eintpnd __raw_readl(S3C24XX_EINTPEND); //EINT8-EINT23 发生时相应位被置1457 unsigned long eintmsk __raw_readl(S3C24XX_EINTMASK);//屏蔽寄存器458459 eintpnd ~eintmsk; //清除被屏蔽的位460 eintpnd ~0xff; /* 清除低8位(EINT8对应位8)ignore lower irqs */461462 /* 循环处理所有子中断*/463464 while (eintpnd) {465 irq __ffs(eintpnd); //确定eintpnd中为1的最高位466 eintpnd ~(1467468 irq (IRQ_EINT4 - 4);//重新计算中断号前面计算出irq等于8时中断号为IRQ_EINT8469 generic_handle_irq(irq);//调用这中断的真正的处理函数470 }471472 }void(4)IRQ_EINT8--IRQ_EINT23这几个中断的处理函数入口在init_IRQ函数初始化中断体系结构的时候已经被设置为handle_edge_irq函数desc_handle_irq(irq,irq_descirq)就是调用这个函数它在kernel/irq/chip.c中定义它用来处理边沿触发的中断中断发生的次数统计531 handle_edge_irq(unsigned int irq, struct irq_desc *desc)532 {533 spin_lock(desc-lock);534535 desc-status ~(IRQ_REPLAY | IRQ_WAITING);536537 /*538 * If were currently running this IRQ, or its disabled,539 * we shouldnt process the IRQ. Mark it pending, handle540 * the necessary masking and go out541 */542 if (unlikely((desc-status (IRQ_INPROGRESS | IRQ_DISABLED)) ||543 !desc-action)) {544 desc-status | (IRQ_PENDING | IRQ_MASKED);545 mask_ack_irq(desc, irq);546 goto out_unlock;547 }548 kstat_incr_irqs_this_cpu(irq, desc);549550 /* Start handling the irq */551 if (desc-chip-ack)552 desc-chip-ack(irq);553554 /* Mark the IRQ currently in progress.*/555 desc-status | IRQ_INPROGRESS;556557 do {558 struct irqaction *action desc-action;559 irqreturn_t action_ret;560561 if (unlikely(!action)) {562 desc-chip-mask(irq);563 goto out_unlock;564 }565566 /*567 * When another irq arrived while we were handling568 * one, we could have masked the irq.569 * Renable it, if it was not disabled in meantime.570 */571 if (unlikely((desc-status 572 (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) 573 (IRQ_PENDING | IRQ_MASKED))) {574 desc-chip-unmask(irq);575 desc-status ~IRQ_MASKED;576 }577578 desc-status ~IRQ_PENDING;579 spin_unlock(desc-lock);580 action_ret handle_IRQ_event(irq, action);581 if (!noirqdebug)582 note_interrupt(irq, desc, action_ret);583 spin_lock(desc-lock);584585 } while ((desc-status (IRQ_PENDING | IRQ_DISABLED)) IRQ_PENDING);586587 desc-status ~IRQ_INPROGRESS;588 out_unlock:589 spin_unlock(desc-lock);590 }591响应中断通常是清除当前中断使得可以接收下一个中断对于IRQ_EINT8~IRQ_EINT23这几个中断desc-chip在前面init_IRQ函数初始化中断体系结构的时候被设为s3c_irqext_chip.desc-chip-ack就是s3c_irqext_ack函数(arch/armplat-s3c24xx/irq.c)它用来清除中断handle_IRQ_event函数来逐个执行action链表中用户注册的中断处理函数它在kernel/irq/handle.c中定义。do {379 trace_irq_handler_entry(irq, action);380 ret action-handler(irq, action-dev_id);//执行用户注册的中断处理函数381 trace_irq_handler_exit(irq, action, ret);382383 switch (ret) {384 case IRQ_WAKE_THREAD:385 /*386 * Set result to handled so the spurious check387 * does not trigger.388 */389 ret IRQ_HANDLED;390391 /*392 * Catch drivers which return WAKE_THREAD but393 * did not set up a thread function394 */395 if (unlikely(!action-thread_fn)) {396 warn_no_thread(irq, action);397 break;398 }399400 /*408 if (likely(!test_bit(IRQTF_DIED,409 action-thread_flags))) {410 set_bit(IRQTF_RUNTHREAD, action-thread_flags);411 wake_up_process(action-thread);412 }413414 /* Fall through to add to randomness */415 case IRQ_HANDLED:416 status | action-flags;417 break;418419 default:420 break;421 }422423 retval | ret;424 action action-next; //下一个425 } while (action);用户注册的中断处理函数的参数为中断号irq,action-dev_id。后一个参数是通过request_irq函数注册中断时传入的dev_id参数它由用户自己指定、自己使用可以为空当这个中断是“共享中断”时除外。对于电平触发的中断它们的irq_desc[irq].handle_irq通常是handle_level_irq函数。它也是在kernel/irq/chip.c中定义其功能与上述handle_edge_irq函数相似对于handle_level_irq函数已经清除了中断但是它只限于清除SoC内部的的信号如果外设输入到SoC的中断信号仍然有效这就会导致当前中断处理完成后会误认为再次发生了中断对于这种情况需要用户注册的中断处理函数中清除中断先清除外设的中断然后再清除SoC内部的中断号。中断的处理流程可以总结如下(1)中断向量调用总入口函数asm_do_IRQ传入根据中断号irq(2)asm_do_IRQ函数根据中断号irq调用irq_desc[irq].handle_irq它是这个中断的处理函数入口对于电平触发的中断这个入口函数通常为handle_level_irq,对于边沿触发的中断这个入口通常为handle_edge_irq(3)入口函数首先清除中断入口函数是handle_level_irq时还要屏蔽中断(4)逐个调用用户在irq_desc[irq].aciton链表中注册的中断处理函数(5)入口函数是handle_level_irq时还要重新开启中断卸载中断处理函数这通过free_irq函数来实现它与request_irq一样也是在kernel/irq/mangage.c中定义。它需要用到两个参数irq和dev_id,它们与通过request_irq注册中断函数时使用的参数一样使用中断号irq定位action链表再使用dev_id在action链表中找到要卸载的表项。同一个中断的不同中断处理函数必须使用不同的dev_id来区分在注册共享中断时参数dev_id必惟一。free_irq函数的处理过程与request_irq函数相反(1)根据中断号irqdev_id从action链表中找到表项将它移除(2)如果它是惟一的表项还要调用IRQ_DESC[IRQ].CHIP-SHUTDOWN或IRQ_DESC[IRQ].CHIP-DISABLW来关闭中断。在响应一个特定的中断的时候内核会执行一个函数该函数叫做中断处理程序(interrupt handler)或中断服务例程(interrupt service routine ,ISP).产生中断的每个设备都有一个相应的中断处理程序中断处理程序通常不和特定的设备关联而是和特定的中断关联的也就是说如果一个设备可以产生多种不同的中断那么该就可以对应多个中断处理程序相应的该设备的驱动程序也就要准备多个这样的函数。在Linux内核中处理中断是分为上半部(tophalf)和下半部(bottomhalf)之分的。上半部只做有严格时限的工作例如对接收到的中断进行应答或复位硬件这些工作是在所有的中断被禁止的情况下完成的能够被允许稍后完成的工作会推迟到下半部去。要想了解上半部和下半部的机制可以阅读一下《Linux内核设计与实现》这里主要是仿照《嵌入式Linux开发完全手册》上的例子写的只是增加了别外两个按按键。在我的mini2440开发板上有6个按键。在上两篇文章中主要分析了驱动中的整体的流程现在来看一个具体的例子是如何使用中断的。1. 模块的初始化函数和卸载函数/* 执行insmod mini2440_buttons.ko命令时就会调用这个函数*/static int __init mini2440_buttons_init (void){int ret;/*这里主要是注册设备驱动程序参数为主设备号如果BUTTON_MAJOR设为0表示由内核自动分配主设备号设备的名字file_operations结构操作主调和号为BUTTON_MAJOR的设备文件时就会调用mini2440_buttons_fops中的相关成员函数*/ret register_chrdev(BUTTON_MAJOR,DEVICE_NAME,mini2440_buttons_fops);if(ret 0){printk(DEVICE_NAME cant register major number\n);return ret ;}printk(DEVICE_NAMEinitialized\n);return 0;}/* 执行 rmmod mini2440_buttons.ko0 命令时就会调用这个函数 */static void __exit mini2440_buttons_exit(void){//卸载驱动程序unregister_chrdev(BUTTON_MAJOR,DEVICE_NAME);}//指定驱动程序的初始化函数和卸载函数module_init(mini2440_buttons_init);module_exit(mini2440_buttons_exit);下面这个结构体是每一个字符驱动程序都是要用到的。这里定义了应用程序可以使用的设备操作函数只有在这个结构体中的函数在应用程序中才可以使用在下面的驱动程序中要实现下面的函数。/* 这个结构是字符设备驱动程序的核心当应用程序操作设备文件时所调用的open,read,write等函数最终会调用这个结构中的对应函数*/static struct file_operations mini2440_buttons_fops {.owner THIS_MODULE, //这是 个宏指向编译模块时自动创建的_this_module变量.open mini2440_buttons_open,.release mini2440_buttons_close,.read mini2440_buttons_read,};2. mini2440_buttons_open函数在应用程序执行“open(/dev/buttons,..)系统调用时mini2440_buttons_open函数将被调用。这用来注册6个按键的中断处理程序static int mini2440_buttons_open(struct inode *inode,struct file *file){int i;int err;for (i0;i{ //注册中断处理函数 一共六个err request_irq(button_irqs[i].irq,buttons_interrupt,button_irqs[i].flags,button_irqs[i].name,(void *)press_cnt[i]);if (err)break;}if(err) //出错处理函数如果出错释放已经注册的中断{i--;for(;i0;i--)free_irq(button_irqs[i].irq,(void *)press_cnt[i]);return -EBUSY;}return 0;}requst_irq函数执行成功后这6个按键所用的GPIO引脚的功能被设为外部中断触发方式为下降沿触发中断处理函数为buttons_interrupt.最后一个参数“(void *)press_cnt[i]”将在buttons_interrupt函数中用到它用来存储按键被按下的次数。参数button_irqs的定义如下struct button_irq_desc{int irq;//中断号unsigned long flags; //中断标志用来定义中断的触发方式char *name; //中断名称};static struct button_irq_desc button_irqs[] {  //下面是按键对应的外部的中断号触发方式名称{IRQ_EINT8,IRQF_TRIGGER_FALLING,KEY0},{IRQ_EINT11,IRQF_TRIGGER_FALLING,KEY1},{IRQ_EINT13,IRQF_TRIGGER_FALLING,KEY2},{IRQ_EINT14,IRQF_TRIGGER_FALLING,KEY3},{IRQ_EINT15,IRQF_TRIGGER_FALLING,KEY4},{IRQ_EINT19,IRQF_TRIGGER_FALLING,KEY5},};3. mini2440_buttons_close函数mini2440_buttons_close函数的作用是用来卸载6个按键的中断处理函数代码如下/* 应用程序对设备文件/dev/buttons执行close(...)时。就会调用mini2440_buttons_close函数*/static int mini2440_buttons_close(struct inode *inode,struct file *file){int i;for(i0;i{ //释放已注册的函数free_irq(button_irqs[i].irq,(void *)press_cnt[i]);}return 0;}4. mini2440_buttons_read函数中断处理函数会在press_cnt数组中记录按键被按下的次数。mini_buttons_read函数首先判断是否按键再次按下如果没有则休眠否则读取press_cnt数组的数据/*等待队列当没有按键被按下时如果有进程调用mini2440_buttons_read函数它将休眠*/static DECLARE_WAIT_QUEUE_HEAD(button_waitq);/*中断事件标志中断服务程序将它置1mini2440_buttons_read将它清0*/static volatile int ev_press 0;/*应用程序对设备文件/dev/buttons执行read(...)时就会调用mini2440_buttons_read函数*/static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp){unsigned long err;//如果ev_press等于0休眠wait_event_interruptible(button_waitq,ev_press);ev_press 0;// 执行到这里是ev_press肯定是1将它清0//将按键状态复制给用户并清0err copy_to_user(buff,(const void *)press_cnt,min(sizeof(press_cnt),count));memset((void *)press_cnt,0,sizeof(press_cnt));return err ? -EFAULT:0;}   wait_event_interruptible首先会判断ev_press是否为0如果为0才会令当前进程进入休眠否则向下继续执行它的第一个参数button_waitq是一个等待的队列在前面定义过第二个参数ev_press用来表示中断是否已经发生中断服务程序将它置1如果ev_press为0当前进程进入休眠中断发生时中断处理函数buttons_interrupt会把它唤醒。将press_cnt数组的内容复制到用户空间buff参数表示的缓冲区位于用户空间使用copy_to_user向它赋值。5.中断处理函数buttons_interruptstatic irqreturn_t buttons_interrupt(int irq,void *dev_id){volatile int *press_cnt (volatile int *)dev_id;*press_cnt *press_cnt 1; //按键计数加1ev_press 1; //表示中断发生wake_up_interruptible(button_waitq); //唤醒休眠的进程return IRQ_RETVAL (IRQ_HANDLED);}buttons_interrupt函数被调用时第一个参数irq表示发生的中断号第二个参数dev_id就是前面使用request_irq注册中断时传入的“pres_cnt[i]”.将mini2440_buttons.c放到内核源码目录drivers/char下在drivers/char目录下生成可加载模块mini2440_buttons.ko把它放开开发板根文件系统的/lib/modules/2.6.22.6目录下就可以使用insmodmini2440_buttons、“rmmod mini2440_buttons.ko”命令进行加载卸载了。6.测试程序编写的测试程序buttons_test.c,编译后生成可执行文件然后把它放到开发板根文件系统/usr/bin目录下。在开发板根文件系统中建立设备文件。#mknod /dev/buttons c 232 0然后使用“insmod mini2440_buttons”命令加载模块。执行完这条命令后可以看到在控制台中执行“cat /proc/devices”[rootFrankzfz 2.6.32.2-FriendlyARM]$cat /proc/devicesCharacter devices:1 mem2 pty3 ttyp4 /dev/vc/04 tty5 /dev/tty5 /dev/console5 /dev/ptmx7 vcs10 misc13 input14 sound29 fb89 i2c90 mtd116 alsa128 ptm136 pts153 spi180 usb189 usb_device204 s3c2410_serial232 buttons  //主设备号 刚注册的设备名252 hidraw253 ttySDIO254 rtcBlock devices:1 ramdisk256 rfd259 blkext31 mtdblock44 ftl93 nftl96 inftl179 mmc运行测试程序button_test后/dev/buttons设备就会被打开可以使用“cat /proc/interrupts”命令看到注册的6个中断了。[rootFrankzfz /mnt]$cat /proc/interruptsCPU030: 157894 s3c S3C2410 Timer Tick42: 0 s3c ohci_hcd:usb143: 8 s3c s3c2440-i2c51: 9751 s3c-ext eth052: 3 s3c-ext KEY055: 1 s3c-ext KEY157: 2 s3c-ext KEY258: 15 s3c-ext KEY359: 75 s3c-ext KEY463: 10 s3c-ext KEY5 70: 848 s3c-uart0 s3c2440-uart71: 1476 s3c-uart0 s3c2440-uart83: 0 - s3c2410-wdtErr: 0第一列表示中断号第二列表示这个中断发生的次数第三列的文字表示这个中断的硬件访问结构“struct irq_chip *chip”的名字。它在初始化中断体系结构时指定第四列文字表示中断的名称它在request_irq中指定。测试程序buttons_test.c如下#include #include #include int main(int argc, char **argv){int i;int ret;int fd;int press_cnt[4];fd open(/dev/buttons,0); // 打开设备if (fd 0) {printf(Cant open /dev/buttons\n);return -1;}// 这是个无限循环进程有可能在read函数中休眠当有按键被按下时它才返回while (1) {// 读出按键被按下的次数ret read(fd, press_cnt, sizeof(press_cnt));if (ret 0) {printf(read err!\n);continue;}for (i 0; i sizeof(press_cnt)/sizeof(press_cnt[0]); i) {// 如果被按下的次数不为0打印出来if (press_cnt[i])}}close(fd);return 0;}在运行button_test时可以以后台运行在button_test 。在开发板上按下不同的按键可以在串口上看到以下的信息。$K1 has been pressed 1K2 has been pressed 1K2 has been pressed 1K3 has been pressed 1K3 has been pressed 5K3 has been pressed 2K6 has been pressed 1K4 has been pressed 1K4 has been pressed 1K4 has been pressed 1K4 has been pressed 3K4 has been pressed 7K4 has been pressed 2K4 has been pressed 5K4 has been pressed 10K4 has been pressed 5K5 has been pressed 1K5 has been pressed 1K5 has been pressed 1K5 has been pressed 1[3] Done(255) ./button_test   这只是一处简单的测试程序当有时按下一次时也可能出现说是按下了10次没有很精确。如果以前没有按键被按下则进行休眠状态。
http://www.sadfv.cn/news/434832/

相关文章:

  • 网站建设初级工程师推广方式的英文
  • 软件开发网站开发学习长链接转化成短链接
  • 网站设计公司飞沐vs做的网站项目可以改名字吗
  • 帝国cms能做手机网站吗黄村网站建设一条龙
  • 怎么建立免费个人网站住房和城乡建设部网站职称查询
  • 长沙建网站公司最新网页游戏传奇
  • 产品免费推广网站有哪些康桥网站建设
  • 株洲 网站建设建筑设计专业大学排名
  • 做网站做注册登录的难点教育门户网站源码
  • 上海网站制作软件网站后台怎么做水印图片
  • 青岛网站设计公司价格建设个人你网站
  • 口碑好的徐州网站建设建筑网图
  • 男女做羞羞的视频网站怎样做的网站内网外网都能用
  • 科技公司网站设计服务网络推广费用一般多少
  • 北京 集团公司网站建设东营城乡建设信息网
  • 怎么制作营销网站谁可以做网站优化排名推广
  • 做网站多少钱 网络服务小说主题+wordpress
  • 莱州网站建设报价手机网站内容规划
  • 福州仓前网站建设东莞企创做网站怎么样
  • 网站建设和续费网站建设中的端口
  • 西宁做网站公司网页配色的技巧是什么
  • 怎么自己做网站加盟深夜禁用直播app软件
  • 科技类公司网站怎么设计1元二手已备案域名
  • iis6.1添加网站建个好网站
  • 网站建设维护 知乎获取网站访客qq代码
  • 广州网站app制作公司浙江响应式网站建设
  • 软件网站开发公司seo招聘
  • 完整企业网站模板wordpress 主题 ie6
  • 个人网站可以做商城吗云天下网站建设
  • 移动网站功能08系统iis信息管理器怎么建设网站