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()

     

  • 相关阅读:
    焦点的相关属性和方法
    laravel 环境配置
    fetch body里数据为ReadableStream 解决办法
    解决NodeJS+Express模块的跨域访问控制问题:Access-Control-Allow-Origin
    mongo启动
    react-native android 打包发布
    delphi 还原窗口
    窗口还原
    款式修改窗口,开发调整过窗口格局保存功能,关了窗口重新打开还是按关闭前的格局.
    希尔排序算法
  • 原文地址:https://www.cnblogs.com/tianxxl/p/10390940.html
Copyright © 2011-2022 走看看