[资料分享] 实时操作系统μC/OS-Ⅱ

Jacktang   2017-12-19 21:31 楼主
第3章 μC/OS-Ⅱ的中断和时钟
3.1 μC/OS-Ⅱ的中断过程
☆μC/OS-Ⅱ的中断过程:系统接收到中断请求后,如果这时CPU处于中断允许状态(即中断开放),系统会中止正在运行的当前任务,而按照中断向量的指向转而去运行中断服务子程序;当中断服务子程序的运行结束后,系统将会根据情况返回到被中止的任务继续运行,或者转向运行另一个具有更高优先级别的就绪任务。
☆可剥夺的μC/OS-Ⅱ内核,中断服务子程序运行结束后,系统根据情况进行任务调度,去运行优先级别最高的就绪任务,并不一定是被中断的任务。
☆幢系统允许中断嵌套,即高优先级别的中断请求可中断低优先级别的中断服务程序的运行。全局变量OSIntNeating记录中断嵌套的层数。
☆μC/OS-Ⅱ响应中断的过程示意图:

☆中断服务函数OSIntEnter()(把全局变量OSIntNesting加1,用以记录中断嵌套的层数),在中断服务程序保护被中断任务的断点数据之后,运行用户中断服务代码之前来调用:
void OSIntEnter(void)
{
if(OSRunning == TRUE)
{
if(OSIntNesting < 255)
{
OSIntNesting++; //中断嵌套层数计数器加1
}
}
}
☆退出中断服务函数OSIntExit()
流程图:

在中断嵌套层数计数器为0,调度器未被锁定且从任务就绪表中查找到的最高级就绪任务又不是被中断的任务的条件下将要进行任务切换,否则就返回被中断的服务子程序。
源代码:
void OSIntExit(void)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif

if(OSRunning == TRUE)
{
OS_ENTER_CRITICAL();
if(OSIntNesting > 0)
{
OSIntNesting--; //中断嵌套层数计数器减1
}
if((OSIntNesting == 0) && (OSLockNesting == 0))
{
OSIntExitY = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
if(OSPrioHighRdy != OSPrioCur)
{
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++;
OSIntCtxSw();
}
}
OS_EXIT_CRITICAL();
}
}
☆一个中断服务程序的流程:

☆中断级任务切换函数(函数在中断嵌套层数计数器为0,调度器未被锁定且从任务就绪表中查找到的最高级就绪任务又不是被中断的任务的条件下,将要运行任务切换,中断级任务切换函数就是中断服务程序中调用的负责任务切换工作的函数)
中断级任务切换函数OSIntCtxSw()示意代码如下:
OSIntCtxSw()
{
OSTCBCur = OSTCBHighRdy; //任务控制块的切换
OSPrioCur = OSPrioHighRdy;
SP = OSTCBHighRdy -> OSTCBStkPtr; //使SP指向待运行任务堆栈
用出栈指令把R1、R2、…弹入CPU的通用寄存器中;
RETI; //中断返回,使PC指向待运行任务
}
☆应用程序中的临界段:
临界段:在应用程序中不受任何干扰地连续运行的代码。为了在运行时不受中断所打断,在临界代码前必须用关中断指令使CPU屏蔽中断请求,而在临界段代码后必须开中断指令解除屏蔽,使得CPU可以响应中断请求。
☆为了增强μC/OS-Ⅱ的可移植性,μC/OS-Ⅱ用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()这两个宏来实现中断的开放和关闭,把系统硬件相关的关中断和开中断的指令分别封装在这两个宏中。
☆宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()的实现方法:
1.直接使用处理器的中断和关中断指令来实现,需令常数OS_CRITICAL_METHOD = 1。
代码如下:
#define OS_ENTER_CRITICAL() \
asm(“DI”) //关中断


#define OS_EXIT_CRITICAL() \
asm(“EI”) //开中断
2.在宏OS_ENTER_CRITICAL()中,把CPU的允许中断标志保存在堆栈中,然后再关闭中断,这样在临界段结束时,即在调用宏OS_EXIT_CRITICAL()时,只要把堆栈中保存的CPU允许中断状态恢复即可,可以保证CPU中断允许标志的状态在临界段前和后不发生变化。
代码如下:
#define OS_ENTER_CRITICAL() \
asm(“PUSH PSW”) \ //通过保存程序状态字来保存中断允许标志
asm(“DI”) //关中断


#define OS_EXIT_CRITICAL() \
asm(“POP PSW”) //恢复中断允许标志

3.(前提:用户使用的C编译器只有扩展功能)用户可获得程序状态字的值,把该值保存在C语言函数的局部变量中,不必压到堆栈里。
代码如下:
#define OS_ENTER_CRITICAL() \
cpu_sr = get_processor_psw(); \ //通过保存程序状态字在全局变量sr中
disable_interrupts(); //关中断


#define OS_EXIT_CRITICAL() \
set_processor_psw(cpu_sr); //用sr恢复程序状态字
需令常数OS_CRITICAL_METHOD = 3。

3.2 μC/OS-Ⅱ的时钟
☆时钟:任何操作系统都要提供一个周期性的信号源,以供系统处理诸如延时、超时等与时间有关的时间。
☆利用硬件定时器产生一个周期是毫秒级的周期性中断来实现系统时钟,最小时钟单位为时钟节拍。
☆OSTickISR():硬件定时器以时钟节拍为周期定时产生中断的中断服务程序。完成系统在每个时钟节拍时所需的工作。
代码如下:
void OSTickISR(void)
{
保存CPU寄存器;
调用OSIntEnter(); //记录中断嵌套层数
if(OSIntNesting == 1)
{
OSTCBCur -> OSTCBStkPtr = SP; //在任务TCB中保存堆栈指针
}
调用OSTimeTick(); //节拍处理
清除中断;
开中断;
调用OSIntExit(); //中断嵌套层数减1
恢复CPU寄存器;
中断返回;
}
☆时钟节拍服务函数:在时钟中断服务程序中调用的OSTimeTick()。
代码如下:



OSTimeTick()的任务:在每个时钟节拍了解每个任务的延时状态,使其中已经到了延时时限的非挂起任务进入就绪状态。
3.3 时间管理
☆任务的延时:
函数OSTimeDly(),使当前任务的运行延时一段时间并进行一次任务调度,以让出CPU的使用权。
代码如下:
void OSTimeDly()
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
if(ticks > 0)
{
OS_ENTER_CRITICAL();
if((OSRdyTbl[OSTCBCur -> OSTCBY] &= ~OSTBCur -> OSTCBitX) == 0)
{
OSRdyGrp &= ~OSTCBCur -> OSTCBBitY; //取消当前任务的就绪状态
}
OSTCBCur -> OSTCBDly = ticks; //延时节拍数存入任务控制块
OST_EXIT_CRITICAL();

OS_Sched(); //调用调度函数
}
}
参数ticks是以时钟节拍数为单位的延时时间的。
☆用时、分、秒为参数的任务的延时函数OSTimeDlyHMSM(),原型如下:
INT8U OSTimeDlyHMSM(
INT8U hours, //小时
INT8U minutes, //分
INT8U seconds, //秒
INT16U milli //毫秒
);
☆调用函数OSTimeDly()和OSTimeDlyHMSM()的任务,当规定的延时时间期满,或有其他任务通过函数OSTimeDlyResume()取消了延时时,它会立即进入就绪状态。
☆取消任务的延时:
函数OSTimeDlyResume()
INT8U OSTimeDlyResume(INT8U prio);
参数prio为被取消延时任务的优先级别。
OSTimeDlyResume()函数的源代码:


☆获取和设置系统时间:
函数OSTimeGet()获取OSTime的值,OSTime在应用程序调用OSStart()时被初始化为0,以后每发生1个时钟节拍,OSTime的值就加1。
原型:
INT8U OSTimeGet(void);
函数OSTimeSet()可以设置OSTime的值。
原型:
void OSTimeSet(INT32U ticks);
3.4 小结
1.在μC/OS-Ⅱ中,中断服务子程序运行结束之后,系统将会根据情况进行一次中断级的任务调度去运行优先级别最高的就绪任务,而并不一定要接续运行被中断的任务。
2.μC/OS-Ⅱ的中断允许嵌套,用全局变量OSIntNesting来记录嵌套数。
3.μC/OS-Ⅱ的中断服务程序的工作通常是由中断激活的一个任务来完成的。
4.在任务中可以设置临界区的方法来屏蔽中断。设置临界区的宏有三种方式来实现。
5.μC/OS-ⅡI的时钟通常是一个由硬件计数器定时产生周期性中断信号来实现的,每一次中断叫做一个节拍,其中断服务程序叫做节拍服务程序。
6.μC/OS-Ⅱ在每一个节拍服务里都要遍历系统中全部任务的任务控制块,把其中记录任务延时时间的成员OSTCBDly减1,并使延时时间到的任务进入就绪状态。
7.μC/OS-Ⅱ有10个函数提供了钩子函数,应用程序设计人员可以再钩子函数中编写写自己的代码。
8.μC/OS-Ⅱ进行时间管理的函数中,最重要的是延时函数OSTimeDly()和OSTimeDlyHMSM().

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复