中断管理

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

 

 

posted @ 2019-02-17 13:55  飞起的小田  阅读(702)  评论(0编辑  收藏  举报