uCOS中断管理相关函数理解

为什么退出中断会进行切换?为什么中断服务函数中要调用进入中断OSIntEnter()和退出中断OSIntExit()两个函数?首先任务在调度器下正常切换运行,中断来了,会打断当前任务,os自动保存现场后,进入中断服务函数中运行。虽然中断现场的保护和恢复是内核自行完成的,但是uCOS作为多任务系统,一般紧急重要的任务优先级较高,所以在中断最后,应该检查就绪列表中是否有优先级高于被中断打断的任务,进行任务的切换。又由于uCOS支持中断延迟发布,为了判断对象发布函数是否在中断中被调用,uCOS是用全局变量OSIntNestingCtr来判断的,其值大于0,说明处于中断中。所以uCOS封装了进入中断OSIntEnter()和退出中断OSIntExit()两个函数,进入中断函数主要是在中断服务程序的最开始被调用,在其中让OSIntNestingCt值加1,表明进入中断中;而退出中断函数主要是在中断服务函数的最后被调用,在其中让OSIntNestingCt值减1,并判断当前任务的优先级,决定要不要切换任务。

中断队列控制块

struct  os_int_q {

    OS_OBJ_TYPE   Type;  //用于发布的内核对象类型,例如是消息队列、信号量、事件等。

    OS_INT_Q    *NextPtr;   //指向下一个中断队列控制块

    void         *ObjPtr;   //指向内核对象变量指针

    void         *MsgPtr;   //指向消息内容的指针

    OS_MSG_SIZE   MsgSize;   //发布消息的大小

    OS_FLAGS      Flags;   //记录要设置事件的标志位

    OS_OPT        Opt;    //发布对象时的选项

    CPU_TS        TS;     //发布时的时间戳  

};

中断(延迟发布)队列(中断消息队列):中断队列和就绪列表很像,就绪列表每一个元素是一个结构体,每个结构体下又连接着相同优先级的多个任务,是一个双向列表。而中断队列也是一个结构数组,在os_cfg_app.c中定义,为OS_INT_Q  OSCfg_IntQ  [OS_CFG_INT_Q_SIZE],但是其数组元素不是TCB,而是中断队列控制块OS_INT_Q,OS_CFG_INT_Q_SIZE默认为10,并且在初始化时把这10个成员串成一条循环单向列表(所以中断队列控制块OS_INT_Q结构体中只有一个指向下一个中断队列控制块的指针)。

中断延迟发布相关函数

{

注意中断延迟发布函数的调用:中断延迟发布的相关函数都不需要我们自己调用。因为在之前学习内核对象:如信号量,消息队列,事件等和非内核对象:如任务信号量和任务消息队列,他们的释放(post)函数中都写有对中断中调用的判断代码,如果在中断中调用,其会自己调用中断中发布函数OS_IntQPost(),当然前提是使能中断延迟发布OS_CFG_ISR_POST_DEFERRED_EN,该宏默认值为0,是默认禁用中断延迟发布。

OS_IntQTaskInit():中断延迟发布任务初始化  

{

:该函数在系统初始化时即被调用。如果使能了中断延迟发布,在OSInit()中会调用OS_IntQTaskInit()进行中断延迟发布任务的初始化操作。看到初始化函数不要怕,都是初始化相关结构体元素,以及相关变量。在os_cfg_app.c中有中断延迟发布相关的变量定义,在os.h中有相关变量的声明(extern)。所以在os_int.c中,与中断相关的函数可以使用这些变量。

1个入口参数:返回的错误类型p_err。

函数过程:① 清空延迟提交过程中溢出的计数值OSIntQOvfCtr;延迟发布消息队列的基地址OSCfg_IntQBasePtr不应该为空,在uCOS中它指向的是一个大数组的第一个元素,也可以它指向大数组;延迟发布队列的成员OSCfg_IntQSize至少为2个,在uCOS默认为为10个;初始化延迟发布任务每次运行的最长时间记录变量OSIntQTaskTimeMax。② 将中断队列中成员串成循环单向列表并初始化,即将数组中每个OS_INT_Q结构体中的元素初始化并串成循环单向列表。所以在for循环中让OS_INT_Q结构体的其他元素清0/空,然后让元素NextPtr指向下一个中断队列控制块OS_INT_Q;因为一共10个元素,所以for循环也要循环10次才能将各个成员初始化;退出循环后让最后一个队列控制块指向第一个中断队列控制块。③ 为了方便管理中断队列,uCOS直接定义了两个统计中断队列中成员个数的变量:当前中断队列中成员个数OSIntQNbrEntries和OSIntQNbrEntriesMax中断队列中最多时的成员个数;这里都初始化为0。因为循环单链表找不到方向插入和删除,所以定义了两个全局变量指针:指向下一个要插入中断队列的指针OSIntQInPtr和指向要下一个从中断队列中提取的指针OSIntQOutPtr,即出队和入队指针;都初始化为指向中断队列中第一个元素。④ 因为要创建中断延迟发布任务,所以在这之前已经将创建任务函数OSTaskCreate()所需参数创建好(因为这不是用户自己在app.c中创建,这是uCOS官方设计该os考虑的事情,所以uCOS已经把所需参数定义好,但是这些参数用户可以修改,所以要检查重要参数的指向)。检查中断延迟发布任务堆栈基地址的指针OSCfg_IntQTaskStkBasePtr是否为空;并检查中断延迟发布任务的堆栈大小是否小于最小堆栈大小64字;最后调用OSTaskCreate()创建中断延迟发布任务OS_IntQTask。

}

OS_IntQTask:中断延迟发布任务函数

{

注(任务的作用):同其他的任务一样,是无限循环无返回的形式。uCOS 在中断中只是将要提交的内核对象的信息都暂时保存起来,当退出中断后,在中断延迟发布任务中执行中断队列中要释放的内核对象函数。

任务函数过程:① 考虑到任务切换的效率,如果中断队列中成员个数OSIntQNbrEntries等于0,表示中断队列中的内核对象发布完毕或者表示系统刚启动,中断队列中无对象(中断延迟发布任务OS_IntQTask的优先级是0,为最大,所以os首先调度的是该任务,此时不可能调用了中断延迟发布的函数),这个时候都应该将该任务从就绪列表中移除,并清空优先级列表的相应位,让os无须再调度该任务。然后调用OSSched()进行任务切换。② 如果中断队列中还存在未发布的对象就调用函数OS_IntQRePost()发布中断队列中的对象。并计算发布时的时间,更新中断队列发布内核对象的最大时间的历史记录OSIntQTaskTimeMax。然后指向下一个发布的对象,中断队列的成员数OSIntQNbrEntries减1。

需要注意的是因为中断延迟发布任务优先级最高,所以在该任务的循环中是会把所有中断队列中的对象都发布了的。全部发布完,中断队列的成员数OSIntQNbrEntries为0,然后该任务就被移出就绪列表除非在执行中断延迟任务的时候被中断打断

}

OS_IntQPost():中断延迟发布函数 

{

8个入口参数:发布的对象类型type;发布的对象p_obj;发布的消息p_void;发布的消息大小msg_size;设置的事件标志flags;选项opt;发布时的时间戳ts;返回的错误类型p_err。

一定注意:该函数只是保存中断中要发布的内核对象信息。真正的内核对象释放是在中断延迟发布任务OS_IntQTask中调用函数OS_IntQRePost()实现的!!!

函数过程:① 检查中断队列是否已满:就是看统计中断队列中元素个数的全局变量OSIntQNbrEntries是否小于中断队列大小OSCfg_IntQSize,小于则说明还能添加进中断队列,那么接下来就应该把需要中断延迟发布的对象添加进中断队列,OSIntQNbrEntries加1,同时更新中断队列中最大成员个数OSIntQNbrEntriesMax的值。② 如果中断队列未满,将需要中断延迟发布的对象添加进中断队列,就是插入到循环单向链表中,就是给空的中断队列控制块赋值。OSIntQInPtr是入队指针,所以就是给该指针指向的中断队列控制块赋值,包括对象,对象类型等等。然后让入队指针OSIntQInPtr指向下一个中断队列控制块。③ 因为中断延迟发布任务OS_IntQTask会把中断队列中所有对象发布完,并移出就绪列表;或者刚开始,中断队列中无发布的对象,移出就绪列表。所以在调用该中断延迟发布函数OS_IntQPost()后,表明中断队列中有要发布的对象,就应该将中断延迟发布任务重新插入就绪列表,并置位优先级表的相应位。果当前运行任务不是中断延迟发布任务,就保存当前任务的优先级。最后返回无错误。注意到没:如果在执行中断延迟发布任务函数时,被其他中断打断,在其他中断中又调用该函数进行中断延迟发布,那么该任务是不会移出就绪列表的。或者在一个中断中调用多次该函数中断延迟发布多个对象,就绪列表的插入这段代码就重复执行了。还有中断嵌套中调用该函数等等情况。这里uCOS并没有判断这些情况,任由这些情况存在,重复执行插入就绪列表和置位优先级表相应位的代码是uCOS应该改进的地方。④ 如果中断队列满了,将延迟提交过程中溢出的计数值OSIntQOvfCtr加1,并返回中断队列已满的错误。

}

OS_IntQRePost():发布中断队列中的对象

{

函数过程:因为中断队列可以保存多种不同的对象,所以需要根据对象类型调用不同post的函数。出队的指针是OSIntQOutPtr,通过switch (OSIntQOutPtr->Type)得到对象类型,在case下调用不同的post函数即可。

}

}

猜你喜欢

转载自blog.csdn.net/m0_43443861/article/details/126442423