zoukankan      html  css  js  c++  java
  • 中断管理

      uC/OS 系统中,中断相当于一个优先级最高的任务。中断一般用于处理比较紧急的事件,而且只做简单处理,例如标记该事件,带退出中断后再做详细处理。在使用 uC/OS系统时,一般建议使用信号量、消息或事件标志组等标志中断的发生,将这些内核对象发布给处理任务,处理任务再做详细处理。

      在使用 uC/OS 系统时,一般建议使用信号量、消息或事件标志组等标志事件的发生,将事件发布给处理任务,处理任务再做纤细处理。在使用 uC/OS 系统时,中断的处理一般是先在中断服务函数中通过发布信号量、消息或事件标志组等内核对象来标志中断的发生,
    等退出中断后再由相关处理任务详细处理中断。根据这些内核对象的发布大致可以分为两种情况,一种是在中断中直接发布,另一种是退出中断后再发布,也就是中断延迟发布。通过宏 OS_CFG_ISR_POST_DEFERRED_EN(位于“os_cfg.h”)可以使能或禁用中断延迟发布

    #define OS_CFG_ISR_POST_DEFERRED_EN     1u   //使能/禁用中断延迟发布

      使能中断延时发布,可以将中断级任务转换成任务级任务,而且在进入临界段时也可以使用锁调度器代替关中断,这就大大减小了关中断时间,有利于提高系统的实时性。在前面提到的 OSTimeTick()OSSemPost()OSQPost()OSFlagPost()OSTaskSemPost()OSTaskQPost()OSTaskSuspend()OSTaskResume() 这些函数,在使能中断延时发布后,如果在中断中调用这些函数,不会直接执行相关功能操作,而是先使用 OS_IntQPost() 函数(位于“os_int.c”)
    把这些事件发布到中断队列记录起来,待退出中断后,就会调度具有最高优先级的中断延迟提交任务 OS_IntQTask() 函数(位于“os_int.c”)真正处理这些事件。
      OS_IntQPost() 函数的定义位于“os_int.c

    void  OS_IntQPost (OS_OBJ_TYPE   type,        //内核对象类型
                       void         *p_obj,       //被发布的内核对象
                       void         *p_void,      //消息队列或任务消息
                       OS_MSG_SIZE   msg_size,    //消息的数目
                       OS_FLAGS      flags,       //事件标志组
                       OS_OPT        opt,         //发布内核对象时的选项
                       CPU_TS        ts,          //发布内核对象时的时间戳
                       OS_ERR       *p_err)       //返回错误类型
    {
        CPU_SR_ALLOC();  //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变
                         //量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR)
                         //,开中断时将该值还原。 
    
    #ifdef OS_SAFETY_CRITICAL               //如果使能(默认禁用)了安全检测
        if (p_err == (OS_ERR *)0) {         //如果错误类型实参为空
            OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数
            return;                         //返回,不继续执行
        }
    #endif
    
        CPU_CRITICAL_ENTER();                                   //关中断
        if (OSIntQNbrEntries < OSCfg_IntQSize) {                //如果中断队列未占满   
            OSIntQNbrEntries++;
    
            if (OSIntQNbrEntriesMax < OSIntQNbrEntries) {       //更新中断队列的最大使用数目的历史记录
                OSIntQNbrEntriesMax = OSIntQNbrEntries;
            }
            /* 将要重新提交的内核对象的信息放入到中断队列入口的信息记录块 */
            OSIntQInPtr->Type       = type;                     //保存内核对象类型
            OSIntQInPtr->ObjPtr     = p_obj;                    //保存被发布的内核对象
            OSIntQInPtr->MsgPtr     = p_void;                   //保存消息内容指针
            OSIntQInPtr->MsgSize    = msg_size;                 //保存消息大小
            OSIntQInPtr->Flags      = flags;                    //保存事件标志组
            OSIntQInPtr->Opt        = opt;                      //保存选项
            OSIntQInPtr->TS         = ts;                       //保存对象被发布的时间错
    
            OSIntQInPtr             =  OSIntQInPtr->NextPtr;    //指向下一个带处理成员
            /* 让中断队列管理任务 OSIntQTask 就绪 */
            OSRdyList[0].NbrEntries = (OS_OBJ_QTY)1;            //更新就绪列表上的优先级0的任务数为1个 
            OSRdyList[0].HeadPtr    = &OSIntQTaskTCB;           //该就绪列表的头指针指向 OSIntQTask 任务
            OSRdyList[0].TailPtr    = &OSIntQTaskTCB;           //该就绪列表的尾指针指向 OSIntQTask 任务
            OS_PrioInsert(0u);                                  //在优先级列表中增加优先级0
            if (OSPrioCur != 0) {                               //如果当前运行的不是 OSIntQTask 任务
                OSPrioSaved         = OSPrioCur;                //保存当前任务的优先级
            }
    
           *p_err                   = OS_ERR_NONE;              //返回错误类型为“无错误”
        } else {                                                //如果中断队列已占满
            OSIntQOvfCtr++;                                     //中断队列溢出数目加1
           *p_err                   = OS_ERR_INT_Q_FULL;        //返回错误类型为“无错误”
        }
        CPU_CRITICAL_EXIT();                                    //开中断
    }
    OS_IntQPost()

      OS_IntQTask () 函数的定义位于“os_int.c”:

    void  OS_IntQTask (void  *p_arg)
    {
        CPU_BOOLEAN  done;
        CPU_TS       ts_start;
        CPU_TS       ts_end;
        CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                        //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                        // SR(临界段关中断只需保存SR),开中断时将该值还原。
    
        p_arg = p_arg;                                          
        while (DEF_ON) {                                        //进入死循环
            done = DEF_FALSE;
            while (done == DEF_FALSE) {
                CPU_CRITICAL_ENTER();                           //关中断
                if (OSIntQNbrEntries == (OS_OBJ_QTY)0u) {       //如果中断队列里的内核对象发布完毕
                    OSRdyList[0].NbrEntries = (OS_OBJ_QTY)0u;   //从就绪列表移除中断队列管理任务 OS_IntQTask
                    OSRdyList[0].HeadPtr    = (OS_TCB   *)0;
                    OSRdyList[0].TailPtr    = (OS_TCB   *)0;
                    OS_PrioRemove(0u);                          //从优先级表格移除优先级0
                    CPU_CRITICAL_EXIT();                        //开中断
                    OSSched();                                  //任务调度
                    done = DEF_TRUE;                            //退出循环
                } else {                                        //如果中断队列里还有内核对象
                    CPU_CRITICAL_EXIT();                        //开中断
                    ts_start = OS_TS_GET();                     //获取时间戳
                    OS_IntQRePost();                            //发布中断队列里的内核对象
                    ts_end   = OS_TS_GET() - ts_start;          //计算该次发布时间
                    if (OSIntQTaskTimeMax < ts_end) {           //更新中断队列发布内核对象的最大时间的历史记录
                        OSIntQTaskTimeMax = ts_end;
                    }
                    CPU_CRITICAL_ENTER();                       //关中断
                    OSIntQOutPtr = OSIntQOutPtr->NextPtr;       //中断队列出口转至下一个
                    OSIntQNbrEntries--;                         //中断队列的成员数目减1
                    CPU_CRITICAL_EXIT();                        //开中断
                }
            }
        }
    }
    OS_IntQTask()

      在 执 行 任 务 OS_IntQTask () 时 真 正 起 到 提 交 作 用 的 是 OS_IntQRePost() 函 数 ,

      OS_IntQRePost() 函数的定义也位于“os_int.c”:

    void  OS_IntQRePost (void)
    {
        CPU_TS  ts;
        OS_ERR  err;
    
    
        switch (OSIntQOutPtr->Type) {   //根据内核对象类型分类处理
            case OS_OBJ_TYPE_FLAG:      //如果对象类型是事件标志
    #if OS_CFG_FLAG_EN > 0u             //如果使能了事件标志,则发布事件标志
                 (void)OS_FlagPost((OS_FLAG_GRP *) OSIntQOutPtr->ObjPtr,
                                   (OS_FLAGS     ) OSIntQOutPtr->Flags,
                                   (OS_OPT       ) OSIntQOutPtr->Opt,
                                   (CPU_TS       ) OSIntQOutPtr->TS,
                                   (OS_ERR      *)&err);
    #endif
                 break;                 //跳出
    
            case OS_OBJ_TYPE_Q:         //如果对象类型是消息队列
    #if OS_CFG_Q_EN > 0u                //如果使能了消息队列,则发布消息队列
                 OS_QPost((OS_Q      *) OSIntQOutPtr->ObjPtr,
                          (void      *) OSIntQOutPtr->MsgPtr,
                          (OS_MSG_SIZE) OSIntQOutPtr->MsgSize,
                          (OS_OPT     ) OSIntQOutPtr->Opt,
                          (CPU_TS     ) OSIntQOutPtr->TS,
                          (OS_ERR    *)&err);
    #endif
                 break;                 //跳出
    
            case OS_OBJ_TYPE_SEM:       //如果对象类型是多值信号量
    #if OS_CFG_SEM_EN > 0u              //如果使能了多值信号量,则发布多值信号量
                 (void)OS_SemPost((OS_SEM *) OSIntQOutPtr->ObjPtr,
                                  (OS_OPT  ) OSIntQOutPtr->Opt,
                                  (CPU_TS  ) OSIntQOutPtr->TS,
                                  (OS_ERR *)&err);
    #endif
                 break;                 //跳出
    
            case OS_OBJ_TYPE_TASK_MSG:  //如果对象类型是任务消息
    #if OS_CFG_TASK_Q_EN > 0u           //如果使能了任务消息,则发布任务消息
                 OS_TaskQPost((OS_TCB    *) OSIntQOutPtr->ObjPtr,
                              (void      *) OSIntQOutPtr->MsgPtr,
                              (OS_MSG_SIZE) OSIntQOutPtr->MsgSize,
                              (OS_OPT     ) OSIntQOutPtr->Opt,
                              (CPU_TS     ) OSIntQOutPtr->TS,
                              (OS_ERR    *)&err);
    #endif
                 break;                 //跳出
    
            case OS_OBJ_TYPE_TASK_RESUME://如果对象类型是恢复任务
    #if OS_CFG_TASK_SUSPEND_EN > 0u      //如果使能了函数OSTaskResume(),恢复该任务
                 (void)OS_TaskResume((OS_TCB *) OSIntQOutPtr->ObjPtr,
                                     (OS_ERR *)&err);
    #endif
                 break;                  //跳出
    
            case OS_OBJ_TYPE_TASK_SIGNAL://如果对象类型是任务信号量
                 (void)OS_TaskSemPost((OS_TCB *) OSIntQOutPtr->ObjPtr,//发布任务信号量
                                      (OS_OPT  ) OSIntQOutPtr->Opt,
                                      (CPU_TS  ) OSIntQOutPtr->TS,
                                      (OS_ERR *)&err);
                 break;                  //跳出
    
            case OS_OBJ_TYPE_TASK_SUSPEND://如果对象类型是挂起任务
    #if OS_CFG_TASK_SUSPEND_EN > 0u       //如果使能了函数 OSTaskSuspend(),挂起该任务
                 (void)OS_TaskSuspend((OS_TCB *) OSIntQOutPtr->ObjPtr,
                                      (OS_ERR *)&err);
    #endif
                 break;                   //跳出
    
            case OS_OBJ_TYPE_TICK:       //如果对象类型是时钟节拍
    #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u //如果使能了时间片轮转调度,
                 OS_SchedRoundRobin(&OSRdyList[OSPrioSaved]); //轮转调度进中断前优先级任务
    #endif
    
                 (void)OS_TaskSemPost((OS_TCB *)&OSTickTaskTCB, //发送信号量给时钟节拍任务
                                      (OS_OPT  ) OS_OPT_POST_NONE,
                                      (CPU_TS  ) OSIntQOutPtr->TS,
                                      (OS_ERR *)&err);
    #if OS_CFG_TMR_EN > 0u               //如果使能了软件定时器,发送信号量给定时器任务
                 OSTmrUpdateCtr--;
                 if (OSTmrUpdateCtr == (OS_CTR)0u) {
                     OSTmrUpdateCtr = OSTmrUpdateCnt;
                     ts             = OS_TS_GET();                             
                     (void)OS_TaskSemPost((OS_TCB *)&OSTmrTaskTCB,             
                                          (OS_OPT  ) OS_OPT_POST_NONE,
                                          (CPU_TS  ) ts,
                                          (OS_ERR *)&err);
                 }
    #endif
                 break;                  //跳出
    
            default:                     //如果内核对象类型超出预期
                 break;                  //直接跳出
        }
    }
    OS_IntQRePost()

    OSIntEnter ()
      任务在进入中断服务函数时需要首先调用 OSIntEnter () 函数标记进入中断,方便中断嵌套管理。OSIntEnter () 函数的信息如下表所示

      OSIntEnter () 函数的定义位于“os_core.c”:

    void  OSIntEnter (void)
    {
        if (OSRunning != OS_STATE_OS_RUNNING) {           //如果操作系统还没运行
            return;                                       //返回,停止执行
        }
        /* 如果操作系统已经运行 */
        if (OSIntNestingCtr >= (OS_NESTING_CTR)250u) {    //如果中断嵌套已经达到250层
            return;                                       //返回,停止执行
        }
        /* 如果中断嵌套尚未达到250层 */
        OSIntNestingCtr++;                                //中断嵌套数目加1
    }
    OSIntEnter()

    OSIntExit ()
      OSIntEnter () 函数相对应,任务在退出中断服务函数时需要首调用 OSIntExit () 函数标记退出中断,方便中断嵌套管理。OSIntExit () 函数的信息如下表所示

      OSIntExit () 函数的定义位于“os_core.c”:

    void  OSIntExit (void)
    {
        CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                        //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                        // SR(临界段关中断只需保存SR),开中断时将该值还原。
    
        if (OSRunning != OS_STATE_OS_RUNNING) {            //如果任务尚未运行
            return;                                        //返回,停止执行
        }
    
        CPU_INT_DIS();                                     //关中断
        if (OSIntNestingCtr == (OS_NESTING_CTR)0) {        //如果该函数不是在中断内
            CPU_INT_EN();                                  //开中断
            return;                                        //返回,停止执行
        }
        OSIntNestingCtr--;                                 //如果该函数在中断内,中断嵌套数减1
        if (OSIntNestingCtr > (OS_NESTING_CTR)0) {         //如果中断仍被嵌套
            CPU_INT_EN();                                  //开中断,执行上一层中断服务
            return;                                        //返回,停止执行
        }
        /* 如果中断已经完全解嵌(没有上一层中断) */
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {   //如果调度器被锁
            CPU_INT_EN();                                  //开中断
            return;                                        //返回,停止执行
        }
        /* 如果调度器未被锁 */
        OSPrioHighRdy   = OS_PrioGetHighest();             //获取就绪的最高优先级
        OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;//提取该优先级就绪列表的头个任务
        if (OSTCBHighRdyPtr == OSTCBCurPtr) {              //如果该任务是进中断前运行的任务
            CPU_INT_EN();                                  //开中断
            return;                                        //直接返回便会继续执行该任务
        }
        /* 如果该任务不是进中断前运行的任务 */
    #if OS_CFG_TASK_PROFILE_EN > 0u                        //如果使能了任务控制块的简况变量
        OSTCBHighRdyPtr->CtxSwCtr++;                       //该任务被切换的次数加1
    #endif
        OSTaskCtxSwCtr++;                                  //总的任务切换次数加1
    
    #if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
        OS_TLS_TaskSw();
    #endif
    
        OSIntCtxSw();                                      //中断级任务调度,调度该任务运行
        CPU_INT_EN();                                      //开中断
    }
    OSIntExit()

    CPU_IntDisMeasMaxGet ()
      关中断时间是嵌入式程序设计一个很重要的参数,uC/OS 系统也提供了测量关中断时间的机制。要使用测量关中断时间机制,必须事先使能该机制(位于“cpu_cfg.h”),如下所示

    #if 1          // Modified by fire (原是 0)                   //使能/禁用关中断时间测量
    #define  CPU_CFG_INT_DIS_MEAS_EN                                
    #endif

      要测量关中断时间,除了要使能测量关中断时间功能外,还须在程序初始化时调用CPU_Init() 函数,该函数里面包括用于初始化测量关中断时间的 CPU_IntDisMeasInit() 函数。CPU_Init() 函数一般在起始任务的初始化部分调用

      CPU_IntDisMeasMaxGet () 函数中调用了 CPU_IntDisMeasMaxCalc() 函数将测量的最大关中断减去测量时间,得到更加纯净的最大关中断时,

      CPU_IntDisMeasMaxCalc() 函数的定义也位于“cpu_core.c”。

    #ifdef  CPU_CFG_INT_DIS_MEAS_EN            //如果使能了关中断时间测量
    static  CPU_TS_TMR  CPU_IntDisMeasMaxCalc (CPU_TS_TMR  time_tot_cnts) 
    {
        CPU_TS_TMR  time_max_cnts;
    
        /* 将尚未处理的最大关中断时间(包括测量时间)减去测量时间 */
        time_max_cnts = time_tot_cnts;                    
        if (time_max_cnts >  CPU_IntDisMeasOvrhd_cnts) {           
            time_max_cnts -= CPU_IntDisMeasOvrhd_cnts;             
        } else {                                                    
            time_max_cnts  = 0u;                                    
        }
    
        return (time_max_cnts);
    }
    #endif
    CPU_IntDisMeasMaxCalc()

    CPU_IntDisMeasMaxCurGet () 

      CPU_IntDisMeasMaxCurReset () 函数相对应,CPU_IntDisMeasMaxCurGet() 函数是配合其完成测量程序段的最大关中断时间。CPU_IntDisMeasMaxCurGet() 函数用于结束程序段的最大关中断时间的测量,并返回测量的时间。CPU_IntDisMeasMaxCurGet() 函数的信息如下所示

      CPU_IntDisMeasMaxCurGet () 函数的定义位于“cpu_core.c :

    #ifdef  CPU_CFG_INT_DIS_MEAS_EN            //如果使能了关中断时间测量
    CPU_TS_TMR  CPU_IntDisMeasMaxCurGet (void) //获取测量的程序段的最大关中断时间
    {
        CPU_TS_TMR  time_tot_cnts;
        CPU_TS_TMR  time_max_cnts;
        CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                        //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                        // SR(临界段关中断只需保存SR),开中断时将该值还原。
        CPU_INT_DIS();                                       //关中断
        time_tot_cnts = CPU_IntDisMeasMaxCur_cnts;           //获取未处理的程序段最大关中断时间
        CPU_INT_EN();                                        //开中断
        time_max_cnts = CPU_IntDisMeasMaxCalc(time_tot_cnts);//获取减去测量时间后的最大关中断时间
    
        return (time_max_cnts);                               //返回程序段的最大关中断时间
    }
    #endif
    CPU_IntDisMeasMaxCurGet()

      CPU_IntDisMeasMaxCurGet () 函 数 跟 CPU_IntDisMeasMaxGet () 函 数 一 样 调 用 了CPU_IntDisMeasMaxCalc() 函数将测量的最大关中断减去测量时间,得到更加纯净的最大关中断时,

      CPU_IntDisMeasMaxCalc() 函数的定义位于“cpu_core.c”:

    #ifdef  CPU_CFG_INT_DIS_MEAS_EN            //如果使能了关中断时间测量
    static  CPU_TS_TMR  CPU_IntDisMeasMaxCalc (CPU_TS_TMR  time_tot_cnts) 
    {
        CPU_TS_TMR  time_max_cnts;
    
        /* 将尚未处理的最大关中断时间(包括测量时间)减去测量时间 */
        time_max_cnts = time_tot_cnts;                    
        if (time_max_cnts >  CPU_IntDisMeasOvrhd_cnts) {           
            time_max_cnts -= CPU_IntDisMeasOvrhd_cnts;             
        } else {                                                    
            time_max_cnts  = 0u;                                    
        }
    
        return (time_max_cnts);
    }
    #endif
    CPU_IntDisMeasMaxCalc()

     

  • 相关阅读:
    PDF文件中的Form保存问题
    Understanding IP Fragmentation
    tcp ip guide IPsec IKE
    Windows安全事件日志中的事件编号与描述
    Cisco PIX fix up and Juniper firewall FTP ALG
    很好的IPSec介绍,详细解释了IKE协商的2个阶段的作用
    virtualbox 下运行Ubuntu 8.10的分辨率和guest additions的问题。
    Fixing the ‘Do you want to display nonsecure items’ message
    windows xp 开始菜单里面所有项目右键不起作用。
    HP backup and recovery manager
  • 原文地址:https://www.cnblogs.com/tianxxl/p/10390940.html
Copyright © 2011-2022 走看看