裸板中中断异常处理,linux中断异常处理 ,linux系统中断处理的API,中断处理函数的要求,内核中登记底半部的方式

1、linux系统中的中断处理
  1.0裸板中中断异常是如何处理的?
     以s5p6818+按键为例
     
     1)按键中断的触发
        中断源级配置
            管脚功能
            中断使能
            中断触发方式
        (如果中断源是uart/i2c 只需要配置中断使能)
        中断级级设置
            中断优先级设置
            中断使能
        ARMcore级设置
            cpsr.I=0
     2) 中断异常触发 硬件自动做4件事
        备份CPSR
        修改CPSR
        保存返回地址到LR
        PC=VECTOR_BASE + 0x18

#include "../../global.h"

#include <linux/interrupt.h>
#include <mach/platform.h>
#include <linux/delay.h> typedef struct btn_desc
{
int irq;//中断号
char *name;//名称
}btn_desc_t; btn_desc_t buttons[]=
{
{IRQ_GPIO_A_START+, "up"},
{IRQ_GPIO_B_START+, "down"},
{IRQ_GPIO_B_START+, "left"},
{IRQ_GPIO_B_START+, "right"}, };
/*1 定义变量*/
struct tasklet_struct btn_tasklet; irqreturn_t btn_isr(int irq, void *dev)
{
btn_desc_t *pdata = dev;
printk("<1>" "enter %s: do emergent things ...\n",__func__);
printk("<1>" "%s key is pressed!\n", pdata->name); /*登记底半部*/
printk("<1>" "register bottom half\n");
tasklet_schedule(&btn_tasklet);
printk("<1>" "exit from top half\n");
return IRQ_HANDLED;
}
int mydata = 0x100; void btn_tasklet_func(unsigned long data)
{
int *pdata = (int *)data; printk("<1>" "enter %s: do no emergent things ...\n",__func__);
printk("<1>" "mydata=%#x\n", *pdata);
(*pdata)++;
}
int __init btn_drv_init(void)
{
int ret = ;
int i =;
for(; i<ARRAY_SIZE(buttons); i++)
{
ret = request_irq(buttons[i].irq, btn_isr,
IRQF_TRIGGER_FALLING,
buttons[i].name,
&(buttons[i]));
if(ret)
{
printk("<1>" "request_irq failed!\n");
while(i)
{
i--;
free_irq(buttons[i].irq, buttons+i);
}
return ret;
}
}
/*2 初始化变量*/
tasklet_init(&btn_tasklet, btn_tasklet_func,
(unsigned long)(&mydata));
return ;
}
void __exit btn_drv_exit(void)
{
int i = ;
for(i=; i<ARRAY_SIZE(buttons); i++)
{
free_irq(buttons[i].irq, buttons+i);
}
}
module_init(btn_drv_init);
module_exit(btn_drv_exit);

3) 软件处理异常
        异常向量表
          0x18: 跳转指令 ldr pc, =irq_handler
          
        irq_handler:
           保护现场//压栈寄存器
           bl c_irq_handler
           恢复现场
        
        c_irq_handler:
           判断哪个硬件中断源触发的中断
           调用对应硬件处理函数hardware_isr
           清空中断源、中断控制器中对应的pending
        hardware_isr:
           执行相应的硬件操作
  1.1 linux中断异常处理
     linux中断异常处理过程和裸板中是一致的
     
     但是linux内核中将大部分的处理代码已经实现完毕了
     实际驱动编程过程中只需要实现   
        1)中断触发方式的配置
        2)hardware_isr
        
     内核中实现的这部分代码 被称作linux中断子系统
     
     百度搜: linux中断子系统   arm
     
  1.3 linux系统中提供的关于中断处理的API
      
      //中断注册
      int __must_check
      request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
              const char *name, void *dev)
         irq, 中断号
              s5p6818中所有中断源的编号s5p6818_irq.h//BSP
         handler, 对应硬件处理函数
                  handler的类型为
                       irqreturn_t (*)(int, void *)
         flags, 中断触发方式的配置 常用的取值
                IRQF_TRIGGER_RISING
                IRQF_TRIGGER_FALLING
                IRQF_TRIGGER_HIGH
                IRQF_TRIGGER_LOW
         name, 名称
         dev,  内核调用handler时传递给handler第二个参数值
         返回值,0 注册成功
                 非0 注册失败
         作用:和内核约定
               1)irq号中断源硬件什么情况下触发中断
               2)irq号中断产生了调用内核handler函数
               3)当内核调用handler函数时传递的第二个参数值
                       
      //中断注销    
      void free_irq(unsigned int irq, void *dev)
         irq, 中断号
         dev, 特别注意 和注册时request_irq(irq, ...,dev)
              给定的最后一个参数保持一致
              否则注销失败
         作用:
              取消和内核的约定    
     
     实验时将内核中自带的按键驱动程序裁剪掉
        cd /home/tarena/driver/kernel
        make menuconfig
           Device Drivers  --->
              Input device support  --->
                  [*]   Keyboards  --->
                      < >   SLsiAP push Keypad support
        make uImage
        
        让开发板使用新内核
        cp arch/arm/boot/uImage /tftpboot
        ...
        
        insmod btn_drv.ko
        cat /proc/interrupts
            
            中断号   中断产生次数  中断来源   name
            134:          0         GPIO      up
        尝试按几次按键
        cat /proc/interrupts
        rmmod btn_drv
        cat /proc/interrupts
   4按键按下触发中断,打印不同的信息出来
      up
      down
      left
      right
   
   注意:xxx_init,只要返回0 就意味着模块安装成功
                           非0 模块安装失败   
   
   16道经典嵌入式笔试题 + 高质量的c/c++笔试题  
       
     判断一个函数是否具有可重入性的标准:
         就是看该函数是否使用了全局变量
         如果全局变量,那么它就是不可重入的
         
   中断处理函数的特点:
       1)最最重要的特点就是执行速度要非常快(裸板/linux/vxworks ...)
       2) linux系统中,中断处理函数工作于中断上下文
          其中不能调用引起阻塞或者睡眠的函数,(中断因为不能参与进程调度,睡眠之后就无法得到再次调用)
       3) 当执行中断处理函数时使用了独立的栈空间
          该栈一般很小 通常为一个物理内存页(4KB)
          在中断处理函数中不能静态分配大数组  
    
   linux系统中要求中断处理执行非常快
   但是往往有些硬件中断处理起来就是耗时
   针对这种情况,
   linux内核人为的将整个中断过程分为两个半部:
      顶半部:top half
              做那些最为紧急的工作
              往往就是一些特殊功能寄存器的读写操作
              中断退出之前,要向内核登记底半部
      底半部: bottom half     
              不紧急且耗时的工作放在底半部中完成
              
2、内核中登记底半部的方式
           中断处理程序包括上半部硬件中断处理程序,下半部处理机制,包括软中断、tasklet、workqueue、中断线程化。
  2.1 软中断机制
      通过软中机制登记底半部得去修改内核源码
      不能以模块的形式完成底半部的登记
      使用起来不灵活
      硬件驱动编程时很少使用
      
  2.2 tasklet
      如何使用tasklet登记半部?
      
      核心数据结构
          struct tasklet_struct
          {
              //记录底半部函数的地址
              void (*func)(unsigned long);
              //当内核调用func时传递给func的参数值
          unsigned long data;
          ...
          }
      1)定义tasklet变量
         struct tasklet_struct btn_tasklet;
      2) 初始化tasklet变量
         void tasklet_init(struct tasklet_struct *t,
          void (*func)(unsigned long), unsigned long data)
      3)使用tasklet变量完成底半部的登记
         void tasklet_schedule(struct tasklet_struct *t)

#include "../../global.h"

#include <linux/interrupt.h>
#include <mach/platform.h>
#include <linux/delay.h> typedef struct btn_desc
{
int irq;//中断号
char *name;//名称
}btn_desc_t; btn_desc_t buttons[]=
{
{IRQ_GPIO_A_START+, "up"},
{IRQ_GPIO_B_START+, "down"},
{IRQ_GPIO_B_START+, "left"},
{IRQ_GPIO_B_START+, "right"}, };
/*1 定义变量*/
struct tasklet_struct btn_tasklet; irqreturn_t btn_isr(int irq, void *dev)
{
btn_desc_t *pdata = dev;
  //printk("<1>" "enter %s: do emergent things ...\n", (btn_desc_t *)dev->name); //将报错不能这样在printk中做强转???
printk("<1>" "enter %s: do emergent things ...\n",__func__);
printk("<1>" "%s key is pressed!\n", pdata->name); /*登记底半部*/
printk("<1>" "register bottom half\n");
tasklet_schedule(&btn_tasklet);
printk("<1>" "exit from top half\n");
return IRQ_HANDLED;
}
int mydata = 0x100; void btn_tasklet_func(unsigned long data)
{
int *pdata = (int *)data;
printk("<1>" "enter %s: do no emergent things ...\n",__func__);
printk("<1>" "mydata=%#x\n", *pdata);
(*pdata)++;
}
int __init btn_drv_init(void)
{
int ret = ;
int i =;
for(; i<ARRAY_SIZE(buttons); i++)
{
ret = request_irq(buttons[i].irq, btn_isr,
IRQF_TRIGGER_FALLING,
buttons[i].name,
&(buttons[i]));
if(ret)
{
printk("<1>" "request_irq failed!\n");
while(i)
{
i--;
free_irq(buttons[i].irq, buttons+i);
}
return ret;
}
}
/*2 初始化变量*/
tasklet_init(&btn_tasklet, btn_tasklet_func,
(unsigned long)(&mydata));
return ;
}
void __exit btn_drv_exit(void)
{
int i = ;
for(i=; i<ARRAY_SIZE(buttons); i++)
{
free_irq(buttons[i].irq, buttons+i);
}
}
module_init(btn_drv_init);
module_exit(btn_drv_exit);
上一篇:Vue数据绑定


下一篇:Java8之旅(六) -- 使用lambda实现尾递归