linux 中断与时钟

中断控制器

  1. 用途

一般CPU中断请求管脚只有一两根,所以需要通过中断控制器,扩展CPU的中断数目,类似于USB Hub对USB接口的扩展。

  1. 原理
  • 作用

CPU

 

图 1中断过程图

  1. 中断检测锁存

硬件设备需要请求CPU来为其服务时,硬件设备可以发送中断请求给中断控制器,中断控制器通过检测下面状态检测是否有中断请求: 

  • 电平状态(Level-sensitive)

高电平或低电平,从请求一直到CPU响应,请求管脚必须保持电平状态不变,否则中断请求被视为取消。

  • 边缘促发(Edge-triggered)

下降沿促发、上升沿促发,一旦检测出中断请求,就会被中断控制器锁存

中断控制器接收到中断请求后,需要锁存边缘促发的中断请求。

 

  1. 中断仲裁

中断控制器同时接收到两个以上设备的中断请求时,中断控制器会根据事先设置好的中断优先级,决定先响应哪个设备中断。

 

  1. 中断选择

ARM CPU提供irq和fiq,因此,在中断控制器为设备中断选择请求什么类型的中断?

 

  1. 提供中断源

CPU接收到中断请求后,需要中断控制器提供,哪个设备发生中断,然后调用相应的设备中断处理函数。

 

  1. 中断屏蔽

中断控制器,应该为每个中断提供开/关。

 

CPU

 

图 2中断过程图

  1. 中断检测锁存

和单CPU相同

 

  1. 中断仲裁

和单CPU相同

 

  1. 中断选择
  2. 选择CPU

对于私有中断,固定CPU处理

对于共有中断,根据中断优先级 高于 CPU的屏蔽优先级时,由那个CPU处理

  1. 选择中断类型

和单CPU相同

 

  1. 提供中断源

和单CPU相同

 

  1. 中断屏蔽

和单CPU相同

 

 

  • 实现

CPU

 

在很多芯片中,使用多功能管脚控制器和中断控制器共同完成中断控制器功能;一般多功能管脚控制器有中断识别锁存和中断屏蔽的功能;

 

CPU

 

 

  • ISR

中断服务例程

 

[1] 中断作用

    通知CPU发生某件事情,可以用于替代轮询方式(多任务的系统中):

    1. 降低CPU使用率

    2. 降低耗电量

    3. 降低发热量

   

[2] 中断工作

    1. 设置中断促发方式                                   设备驱动                            

    2. 清除中断控制器锁存                                

       2.1 中断控制器                                     OS

       2.1 设备内部                                       设备驱动

    3. 中断选择                                           OS

    4. 中断向量表                                         OS

    5. 中断处理程序                                      

       5.1 保护现场                                       OS

       5.2 获取中断源                                     OS

           共享中断                                       设备驱动 处理一部分                                

           (共享中断控制器上的一个中断号)

           (设备本身可以识别自己是否发生中断)

           非共享中断

       5.3 设备中断处理                                   设备驱动

       5.4 恢复现场                                       OS

    6. 设置中断优先级                                     OS

    7. 中断使能                                          

       7.1 设备开关                                       OS

       7.2 CPU开关                                        OS

       

[3] 设备中断(设备驱动的一部分)

    1. 实现设备的中断处理函数(ISR)

    2. ISR<--->中断号<--->中断促发方式

 

[4] 设备中断处理函数(ISR)

    1. 判断设备是否发生中断(共享中断)

    2. 中断处理

    3. 清除中断内部锁存

   

[5] 设备驱动实现

    1.  实现设备的中断服务程序(ISR)

       /*

        * @brief          处理设备中断

        * @param[in]      irq                     中断号

        * @param[in]      dev_id                  描述设备的结构体指针

        * @return         中断是否处理

        *                 @IRQ_HANDLED            中断已经处理

        *                 @IRQ_NONE               中断还未处理

        * @notes          在中断上下文中调用,在ARM处理器中在IRQ模式下执行

        */

       irqreturn_t irq_handler_t(int irq, void *dev_id);

       

   2.  注册申请中断

       /*

        * @brief          申请中断

        * @param[in]      irq                     中断号

        * @param[in]      handler                 中断处理程序

        * @param[in]      flags                   中断标志(中断触发极性、中断是否共享、中断是否可以嵌套......)

        *                                         每种标志占用一个bit,可以多个标志组合

        * @param[in]      name                    中断名,调试

        * @param[in]      dev_id                  描述设备的结构体指针,在调用中断服务程序时,传给它

        * @return         @li 0                   表示申请成功

        *                 @li < 0                 错误码

        * @notes          cat /proc/interrupts

        */

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

       

   3. 注销中断

       /*

        * @brief          注销中断

        * @param[in]      irq                     中断号

        * @param[in]      dev_id                  描述设备的结构体指针

        */

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

       

[6] 设备中断禁止/使能

   /*

    * @brief          禁止中断

    * @param[in]      irq                     中断号

    * @notes          如果调用本函数时,中断处理函数正在执行,函数会等待中断处理函数执行完成返回

    */

    void disable_irq(int irq);

       

   /*

    * @brief          禁止中断

    * @param[in]      irq                     中断号

    * @notes          如果调用本函数时,不会等待中断处理函数执行完成返回

    */

    void disable_irq_nosync(int irq);

   

   /*

    * @brief          使能中断

    * @param[in]      irq                     中断号

    */

    void enable_irq(int irq);

   

[7] 中断处理机制

    1. 中断处理划分--缩短cpu的中断处理时间,提高cpu利用率

       上半部: 中断处理函数

       下半部: 中断需要完成的任务,放在中断处理函数之外完成,下半部的执行一定要被上半部促发

       上半部和下半部一定要在同一个CPU上执行

       

    2. 内核中5种需要处理的任务

       (1) 中断处理程序

       (2) 软中断(软件促发、软件执行,可能在中断上下文中调用)

       (3) 工作者线程

       

       (4) 普通内核线程

       (5) 普通的应用程序进程(内核线程)

       优先级由高到低3-5优先级可以调整,应用进程会有固定的时间片

       

[8] tasklet

    1. 原理

       结合图《2.软中断和tasklet》理解:

       (1) 采用软中断机制实现

       (2) 将中断的下半部打包,排队

       

    2. 用法

       1. 描述对象

          (1) 属性

              tasklet_struct

             

          (2) 方法

              tasklet处理函数(实现中断的下半部)

             

       2. 创建对象(每个设备)

          (1) 分配内存

          (2) 初始化

         

       3. 调度(中断处理函数)

          将tasklet_struct对象,添加到本CPU的tasklet队列,同时促发tasklet软中断

       

    3. 适用

       1. 处理时间不能太长

       2. 不能调用可能引起休眠的函数

       

[9] 工作队列

    1. 原理

       见《3.工作队列.bmp》

       将中断的下半部打包,排队,等待工作者线程(OS为每个CPU的中断下半处理,开启的线程)处理

   

    2. 用法

       1. 描述对象

          (1) 属性

              work_struct

             

          (2) 方法

              work_struct处理函数(实现中断的下半部)

             

       2. 创建对象(每个设备)

          (1) 分配内存

          (2) 初始化

         

       3. 调度(中断处理函数)

          将work_struct对象,添加到本CPU的work_struct队列,同时唤醒工作者线程

         

    3. 适用

       用在tasklet不能使用的地方

     

    总结:如果tasklet和工作队列都可以使用,优先使用tasklet

     

[10] 定时器

     1. 原理

        见《软中断和定时器.bmp》和《定时器级联.bmp》图,理解:

        (1) 定时器采用软中断机制实现

        (2) 定时器级联的目的是确保短时间定时准确

        (3) 定时器本质是打包一个操作,在超时之后执行

       

     2. 基本概念

        jiffies       记录机器从开机到现在发生了多少次时钟中断的变量

        HZ 每秒钟发生时钟中断的次数

       

     3. 用法

        1. 描述对象

           (1) 属性

               timer_list

               

           (2) 方法

               void timer_func(unsigned long)

           

        2. 创建对象

           2.1 分配内存

               按键防抖动时,每个设备都需要一个定时器

               

           2.2 初始化

               init_timer

               

               .expires = jiffies + HZ / 100

               .function = timer_func

               .data = (void *)dev_id

               

        3. 使用对象

           添加定时器

           void add_timer(struct timer_list *timer)

           

           删除定时器

           int del_timer(struct timer_list *timer)

           

           修改定时时间

           int mod_timer(struct timer_list *timer, jiffies + x)

           

     4. 适用

        (1) 从现在开始,过多长时间以后,做某件事情(反复做某件事情)

       

        (2) 处理时间不能过长

        (3) 不能调用可能引起休眠的函数

 

[11] 定时函数

     (1) 指令延迟

         /*

          * @brief 微秒延迟

          * @param[in] 延迟的微秒数

          * @notes cpu忙等

          */

         void udelay(unsigned long usecs);

   

         /*

          * @brief 毫秒延迟

          * @param[in] 延迟的毫秒数

          * @notes cpu忙等

          */

         void mdelay(unsigned long msecs);

         

     (2) 利用jiffies延迟

         /*

          * @brief 判断a(tick数)是否在b(tick数)的后面

          * @param a 系统启动以来经过的tick数

          * @param b 系统启动以来经过的tick数

          * @return a在b后返回true,否则,返回false

          */

         int time_after(unsigned long a, unsigned long b);

   

        /*

         * @brief 判断a(tick数)是否在b(tick数)的前面

         * @param a 系统启动以来经过的tick数

         * @param b 系统启动以来经过的tick数

         * @return a在b前返回true,否则,返回false

         */

        int time_before(unsigned long a, unsigned long b);

       

        例:

        expire = jiffies/*取得当前时钟中断数(当前时间)*/ + HZ(定时时长);

        while (time_before(jiffies, expire)) {

          if (串口fifo字节数 > 48) break;

        }

        ...

       

---------------------------------------------------------------------------------

 

【CLK操作】

[1] 【获取CLK】

[2] 【使能CLK】

[3] 【禁止CLK】

[4] 【释放CLK】

 

[头文件]

#include <linux/clk.h>

 

【获取CLK】

/*

 * @brief 获取时钟

 * @param[in] dev 指定获取时钟的设备

 * @param[in] con_id 连接设备ID

 * @return 返回时钟结构

 * IS_ERR(返回值) 判断是否是错误码

 * PTR_ERR(返回值) 取得错误码

 */

struct clk *clk_get(struct device *dev, const char *con_id);

 

【使能CLK】

/*

 * @brief 使能时钟

 * @param[in] clk 时钟结构

 * @return 结果

 * @li 0 使能成功

 * @li !0 错误码

 */

int clk_enable(stuct clk *clk);

 

【获取时钟频率】

/*

 * @brief 获取时钟频率

 * @param[in] clk 时钟结构

 * @return 时钟频率

 */

unsigned long clk_get_rate(struct clk * clk);

 

/*

 * @brief 设置时钟频率

 * @param[in] clk 时钟结构

 * @param[in] 时钟频率

 */

int clk_set_rate(struct clk * clk, unsigned long rate);

 

【禁止CLK】

/*

 * @brief 禁止时钟

 * @param[in] clk 时钟结构

 */

 void clk_disable(struct clk *clk);

 

【释放CLK】

/*

 * @brief 释放时钟

 * @param[in] clk 时钟结构

 */

void clk_put(stuct clk *clk);

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值