zoukankan      html  css  js  c++  java
  • 裸板中中断异常处理,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+28, "up"},
        {IRQ_GPIO_B_START+30, "down"},
        {IRQ_GPIO_B_START+31, "left"},
        {IRQ_GPIO_B_START+9,  "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 ...
    ",__func__);
        printk("<1>" "%s key is pressed!
    ", pdata->name);
    
        /*登记底半部*/
        printk("<1>" "register bottom half
    ");
        tasklet_schedule(&btn_tasklet);
        printk("<1>" "exit from top half
    ");
        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 ...
    ",__func__);
        printk("<1>"  "mydata=%#x
    ", *pdata);
        (*pdata)++;
    }
    int __init btn_drv_init(void)
    {
        int ret = 0;
        int i =0;
        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!
    ");
                while(i)
                {
                    i--;
                    free_irq(buttons[i].irq, buttons+i);
                }
                return ret;
            }
        }
        /*2 初始化变量*/
        tasklet_init(&btn_tasklet, btn_tasklet_func, 
                     (unsigned long)(&mydata));
        return 0;
    }
    void __exit btn_drv_exit(void)
    {
        int i = 0;
        for(i=0; 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+28, "up"},
        {IRQ_GPIO_B_START+30, "down"},
        {IRQ_GPIO_B_START+31, "left"},
        {IRQ_GPIO_B_START+9,  "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 ... ", (btn_desc_t *)dev->name); //将报错不能这样在printk中做强转??? printk(
    "<1>" "enter %s: do emergent things ... ",__func__); printk("<1>" "%s key is pressed! ", pdata->name); /*登记底半部*/ printk("<1>" "register bottom half "); tasklet_schedule(&btn_tasklet); printk("<1>" "exit from top half "); 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 ... ",__func__); printk("<1>" "mydata=%#x ", *pdata); (*pdata)++; } int __init btn_drv_init(void) { int ret = 0; int i =0; 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! "); while(i) { i--; free_irq(buttons[i].irq, buttons+i); } return ret; } } /*2 初始化变量*/ tasklet_init(&btn_tasklet, btn_tasklet_func, (unsigned long)(&mydata)); return 0; } void __exit btn_drv_exit(void) { int i = 0; for(i=0; i<ARRAY_SIZE(buttons); i++) { free_irq(buttons[i].irq, buttons+i); } } module_init(btn_drv_init); module_exit(btn_drv_exit);
  • 相关阅读:
    现在SimpleMemory的CSS(by BNDong)
    I AK IOI
    最大半联通子图
    曾经SimpleMemory的CSS
    幼儿园战争
    炸掉卡西欧991CNX
    LuoguP1131选择客栈
    2019CSP-S2养成任务
    NOIP2013&NOIP2018&USACO 三倍经验铺路题巧妙解法
    NOIP2018D2T1 旅行
  • 原文地址:https://www.cnblogs.com/DXGG-Bond/p/11851608.html
Copyright © 2011-2022 走看看