目录
系列文章目录
前言
一、中断
1:中断的概念
2:中断执行的过程
3:相关操作函数
二、时间管理
1:时钟节拍
2: 时间原理相关函数
3:何时启动时钟节拍器
第一章裸机系统与操作系统
第二章操作系统分类
第三章实时操作系统简介
第四章实时操作系统---内核结构
第五章实时操作系统---内核结构2
这篇文章我们主要讲解一下ucos中的中断和时间管理,中断室计算机系统处理异步事件的重要机制,当异步事件发生时,硬件首先会向CPU发起中断请求,CPU响应这个请求之后会立即运行中断服务程序来处理该事件。
为了处理任务延时,任务调度等与时间相关的事件,任何一个计算机系统都应该有个系统时钟,与其他系统一样,UCOS的系统时钟也是通过硬件定时器产生的定时中断来实现的。
中断:由于某种事件的发生而导致程序流程的改变。产生中断的事件称为中断源。
CPU响应中断的条件:至少有一个中断源向CPU发出中断信 号;系统允许中断,且对此中断信号未予屏蔽
任务在运行过程中,应内部或外部事件的请求中止当前任务而去处理异步事件所要求的任务的过程叫做中断。应中断请求二运行的程序叫做中断服务程序,中断服务程序的入口叫做中断向量
中断一旦被识别,CPU会保存部分(或全部)运行上下文(context,即寄存器的值),然后跳转到专门的子程序去处理此次事件,称为中断服务子程序(ISR)。
μC/OS-Ⅱ中,中断服务子程序要用汇编 语言来编写,然而,如果用户使用的C语 言编译器支持在线汇编语言的话,用户可 以直接将中断服务子程序代码放在C语言 的程序文件中。
大概的执行过程如下所示
(1)保存全部CPU寄存器的值;
(2)调用OSIntEnter(),或直接把全局变量 OSIntNesting(中断嵌套层次)加1;
(3)执行用户代码做中断服务;
(4)调用OSIntExit();
(5)恢复所有CPU寄存器;
(6)执行中断返回指令。
在中断服务程序中,有两个重要的函数,分别是OSIntEnter 和 OSIntExit。
其中OSIntEnter 通常发生才中断服务程序保护了断点数据之后运行中断服务程序之前,称之为进入中断服务函数,其作用就是把OSIntNesting变量加1,从而用它来记录中断嵌套的层数。源码如下
void OSIntEnter (void) { if (OSRunning == TRUE) { if (OSIntNesting < 255) { OSIntNesting++; } } }另一个函数OSIntExit叫做退出中断服务函数,这个函数调用发生在处理完中断流程,退出中断的时候,起作用就是在退出中断处理的时候判断要执行哪一个任务(执行最高优先级),该函数的作用可以用下面的图来表示。
void OSIntExit (void) { OS_ENTER_CRITICAL(); //关中断 if ((--OSIntNesting|OSLockNesting) == 0) //判 断嵌套是否为零 { //把高优先级任务装入 OSIntExitY = OSUnMapTbl[OSRdyGrp]; OSPrioHighRdy=(INT8U)((OSIntExitY<< 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); if (OSPrioHighRdy != OSPrioCur) { OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; OSIntCtxSw(); } } OS_EXIT_CRITICAL(); //开中断返回 }从上面的代码中我们可以看到在执行任务切换的时候我们是调用OSIntCtxSw函数的,那么为什么不调用OS_TASK_SW呢,原因有以下两点
一半的任务切换工作,即CPU寄存器入栈,已经在前面做完了; 需要保证所有被挂起任务的栈结构是一样的下面的图中描述了OSIntCtxSw的堆栈情况
任何系统都需要一个周期性的信号源,以供系统处理延时超时等与时间相关的事件,这个周期性的信号源就叫做时钟。
多数系统中采用硬件定时器产生一个周期性为MS级的周期性中断来实现系统时钟,这个系统时钟的最小时间单位就是两次中断到来之间的相间隔的时间,这个最小的时间间隔就是时钟节拍。
硬件定时器以时钟节拍为周期定时产生中断,该中断的服务函数叫做OSTickISR,系统通过调用该函数来实现在每个时钟节拍中断中需要执行的操作。
void OSTickISR(void) { (1)保存处理器寄存器的值; (2)调用OSIntEnter()或将OSIntNesting加1; (3)调用OSTimeTick(); /*检查每个任务的时间延时*/ (4)调用OSIntExit(); (5)恢复处理器寄存器的值; (6)执行中断返回指令; }上面的代码中我们可以看到在 OSTickISR函数中调用了OSIntExit,这个函数的主要作用就是给每个用户任务控制块OS_TCB 中的时间延时项OSTCBDly 减 1(如果该项不为零的话)。OSTimTick()从 OSTCBList 开始,沿着 OS_TCB 链表 做,一直做到空闲任务。当某任务的任务控制块中的时间延时项 OSTCBDly 减到 了零,这个任务就进入了就绪态。而确切被任务挂起的函数 OSTaskSuspend()挂 起的任务则不会进入就绪态。OSTimTick()的执行时间直接与应用程序中建立了 多少个任务成正比。
OSTimeDLY():任务延时函数,申请该服 务的任务可以延时一段时间;
调用OSTimeDLY后,任务进入等待状态;
使用方法void OSTimeDly (INT16U ticks); ticks表示需要延时的时间长度,用时钟节拍的个数来表示。
void OSTimeDly (INT16U ticks) { if (ticks > 0) { OS_ENTER_CRITICAL(); if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { OSRdyGrp &= ~OSTCBCur->OSTCBBitY; } OSTCBCur->OSTCBDly = ticks; OS_EXIT_CRITICAL(); OSSched(); } } OSTimeDlyHMSM() :OSTimeDly()的另一个 版本,即按时分秒延时函数; 使用方法 INT8U OSTimeDlyHMSM(INT8U hours, // 小时
INT8U minutes, // 分钟
INT8U seconds, // 秒
INT16U milli // 毫秒
); OSTimeDlyResume() :让处在延时期的任 务提前结束延时,进入就绪状态; 使用方法 INT8U OSTimeDlyResume (INT8U prio); prio表示需要提前结束延时的任务的优先级 任务ID OSTimeGet() :获得该计数器的当前值; INT32U OSTimeGet (void); OSTimeSet() :设置该计数器的值。 void OSTimeSet (INT32U ticks);每隔一个时钟节拍,发生一个时钟中断,将一 个32位的计数器OSTime加1; 该计数器在用户调用OSStart()初始化多任务和 4,294,967,295个节拍执行完一遍的时候从0开 始计数。若时钟节拍的频率等于100Hz,该计数 器每隔497天就重新开始计数;
用户必须在多任务系统启动以后再开启时钟节拍 器,也就是在调用OSStart()之后; 在调用OSStart()之后做的第一件事是初始化定时器中断
void main(void) { ... OSInit(); /* 初始化uC/OS-II*/ /* 应用程序初始化代码... */ /* 调用OSTaskCreate()创建至少一个任务*/ 允许时钟节拍中断; /* 错误!可能crash!*/ OSStart(); /* 开始多任务调度 */ }