您的位置:首页 > 其它

UCOS-II 中断与时钟问题总结(以UCOS2.25自带的X86例子)

2013-05-24 11:57 357 查看
这两天在看UCOS-II, uCOS中断和时钟问题做个小结,以UCOS2.25自带的X86例子、《嵌入式实时操作系统uCOS-II原理及应用》为基础,相信对网友们也有帮助,理解不对的地方,请高手指正:

UCOS-II是抢占式、实时操作系统,这是核心。

1. uCOS的系统有任务级任务调度和中断级任务调度:任务级任务调度靠常常在下面的代码中自动调用,

void main()

{

OSinit();

OSTaskCreate();

OSTaskCreate();

.......

OSStart();

}

当执行OSTaskCreate时,会将任务优先级较高的队列改变为就绪状态,同时OS_CORE.C中调度器OnSched()进行任务调度(内部调用ON_TASK_SW宏,该宏为INT 0x80,进行软件中断,调用在OS_CPU_A.ASM中需要实现的OSCtxSw(OSCtxSw是宏调用通常含有软中断指令,切换是靠中断级代码完成的))。OnSched只调用优先级最高的任务,可以通过进程同步类的函数来释放CPU资源,达到任务并行进行和通信,这些函数包括对任务状态和优先级改变的函数(OSTaskSuspend,
OSTaskResume, OSTaskChangePrio, OSTaskDel, OSTimeDly,ostimedlyhmsm,这几函数涉及到任务的状态迁移,见下面图), 信号量,消息队列,互斥信号量(用于防止任务优先级反转,很重要,可以另开一个话题)。







睡眠态:在ROM或RAM中,交给UCOS要调用下面两个函数之一:OSTaskCreate或者OSTaskCreateExt,调用之后告诉了UCOS任务的起始地址,优先级,要使用多少栈空间。

就绪态:建立了任务之后,就进入就绪态。如果是由任务建立的新任务且优先级更高,那么新建的任务将立即得到CPU使用权。通过OSTaskDel将一个任务返回到睡眠态。

运行态:调用OSStart可以启动多任务,该函数运行用户初始化代码中已建的优先级最高的任务。

等待态:正在运行的任务通过两个函数将自己延迟即进入等待状态。OSTimeDly或者OSTimeDlyHMSM。这两个函数将会立即执行强制任务切换,让下一个优先级最高且进入就绪态的任务运行。当延时时间到后,系统服务函数OSTimeTick将会让等待的任务进入就绪态。在运行中的任务如果调用OSFlagPend、OSSemPend、OSMutexPend、OSMboxPend或者OSQPend时时间并没有发生,那么该任务就进入等待态,此时最高优先级的就绪态任务进入运行态。当等待事件发生或者等待超时,该任务就从等待态进入就绪态。

中断服务态:正在运行的任务可以被中断。被中断了的任务进入中断服务态。响应中断时该任务被挂起,中断服务子程序可能报告多个事件发生,从而使得更高优先级的任务进入就绪态,当中断返回时会重新判断优先级,让最高优先级的任务运行,如果由更高优先级的任务那么先执行,直到被挂起的任务的优先级最高才会返回到被中断挂起的任务。

2. 下面讲重点,中断级任务调度:

当任务在运行的时候,执行完中断服务程序后,uCOS应该在任务列表中寻找已经就绪状态的任务(这些任务可能是因为调用等待资源或者调用OSTimeDly等函数进入等待状态,当资源已经具备或者OSTimeDlg时间超时),寻找最高优先级的任务执行。因此,在中断处理函数中要加入相应的代码,uCOS一般不提供中断注册的函数,中断服务程序需要实现的代码如下伪代码描述:

void isr_task()

{

保存全部CPU寄存器; (1)

调用OSIntEnter或OSIntNesting直接加1; (2)

执行用户代码做中断服务; (3)

调用OSIntExit(); (4)

}

3. 《嵌入式实时操作系统uCOS-II原理及应用》第3章中了很多这样一些(1)(2)代码,(3)中的是其他地方看到的:

(1) PC_Vectset(0x08, OSTickISR); //安装时钟服务程序, 完成各任务中执行了OSTimeDly等函数的时间到期提供处理程序

(2) PC_SetTickRate(OS_TICKS_PER_SEC); // 设置时钟频率,需要根据CPU,写汇编程序完成定时器的寄存器初始化工作

(3) PC_Vectset(0x80, OSCtxSw); // 安装软件中断时候的任务切换工作,因为OSCtxSW重要执行寄存器的操作,常常使用进入软件中断模式特权模式。

(1): 跟踪了一下PC_VectSet(0x08, OSTickISR)发现,这个函数为:

void PC_VectSet (INT8U vect, void (*isr)(void))

{

#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */

OS_CPU_SR cpu_sr;

#endif

INT16U *pvect;

pvect = (INT16U *)MK_FP(0x0000, vect * 4); /* Point into IVT at desired vector location */

OS_ENTER_CRITICAL();

*pvect++ = (INT16U)FP_OFF(isr); /* Store ISR offset */

*pvect = (INT16U)FP_SEG(isr); /* Store ISR segment */

OS_EXIT_CRITICAL();

}

分析了一下,该函数只适合于80X86 CPU,因为 8 *4 = 32,正好为80X86定时器0的中断向量入口地址,和ARM不一样,Arm跳转后是执行这里的指令,而X86是执行该处32位值所指向地址的指令,因此,该函数不适合与其他CPU,如果要使用需要更改。

另外,OSTickISR在移植时,一般需要在OS_CPU_A.asm中根据CPU不同用汇编实现,完成的功能主要有:

void OSTickISR(void)

{

保存全部CPU寄存器; (1)

调用OSIntEnter或OSIntNesting直接加1; (2)

if(OSintNesting == 1)

{

OSTCBCur->OSStkPtr = SP; //在任务TCB中保存堆栈指针 (3)

}

调用OnTimeTick() ; 调用节拍处理函数该函数在OS_CORE.C中已经实现了,不需要自己实现(注意:与OS_CORE.C中同一级目录中的所有源程序都与CPU无关),节拍处理函数中自动完成了(1) OSTime ++; (2) 遍历任务控制块链表中所有控制块,把用来存放任务延时时限的OSTCBDly减1,如果OSTCBDly
== 0唤醒suspend等等待线程,将其转为就绪态 (4)

调用OSIntExit(); (4)

恢复所有CPU寄存器; (5)

执行中断返回指令; (6)

}

再论,因为OS_CORE.C中OnTimeTick函数有如下一段代码:

void OSTimeTick (void)

{

#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */

OS_CPU_SR cpu_sr;

#endif

OS_TCB *ptcb;

OSTimeTickHook(); // 在OS_CPU_C.C定义,该文件不在SOURCE自带的目录下,需要自己创建钩子函数

。。。。

}

在uCOS-II \ Ix86L \ BC45 \ OS_CPU_S.C文件中定义如下,可以自己定义钩子函数

#if OS_CPU_HOOKS_EN > 0

void OSTimeTickHook (void)

{

}

#endif

(2):PC_SetTickRate(OS_TICKS_PER_SEC); 用户设置时钟频率,需要根据CPU,写汇编程序完成定时器的寄存器初始化工作。

(3): PC_Vectset(0x80, OSCtxSw); 用于任务调度切换的注册,80为X86 CPU的软件中断入口,如果是ARM,需要相应的改变,同时OSCtxSW需要在OS_CPU.A.ASM中根据处理器来操作寄存器进行实现。 OSCTxSW完成的任务有(以X86的这个例子为例,其他的类似):

<1>把当前CPU软件中断前的PC指针、PSW(程序状态字)保存到当前任务控制块的栈中,压栈操作(与<6>相对应);

<2>把当前CPU各寄存器压栈保存到当前任务控制块的栈中,压栈操作。(与<5>对应)

<3> 在任务链中取得优先级最高,且就绪的任务控制块,复制到OSTCBCur中;

<4> 把该任务优先级别赋值给OSPrioCur中;

<5> 得到OSTCBCur中栈中所保存的上一次任务切换时CPU寄存器数据,恢复到相应的寄存器中,此过程出栈数据顺序与2>中正好相反,且对应

<6>中断返回,读出OSTCBCur中栈中所保存PC指针数据和程序状态字PSW,此过程出栈数据顺序与1>中正好相反,且对应

4. 2中讲述的 在UCOS中断处理程序中,OSIntExit (OS_CORE.C中)调用的时候会触发 OSIntCtxSw()的调用,我们一般需要在OS_CPU_A.asm中实现针对具体CPU的汇编代码,完成如下中断任务级的调度,需要完成的和上面的OSCtxSw类似,不过少了(1)(2)动作,因为在中断的前部分已经执行了。

5. 要想移植一个实时操作系统,3中描述的PC_VectSet(0x08, OSTickISR)、PC_SetTickRate(OS_TICKS_PER_SEC)涉及到的时钟的几个函数需要针对CPU来具体实现,包括PC_SetTickRate中的时钟源和中断相关寄存器的设置, PC_VectSet针对不同的CPU的C语言实现,这里可以做一个通用的中断注册函数,当然写成汇编,在引导程序.s中也可以做,不过比较麻烦,另外,OSTickISR的具体实现,需要实现什么上面都有介绍。为了完成任务级的调度,还得实现ON_TASK_SW宏(默认在OS_CPU.h中定义),通常采用软件中断的方式,实现OSCtxSW在OS_CPU_A.asm的汇编代码,如果不是,改掉相应地方的代码。为了实现硬件中断级调度,在中断处理程序中所有的地方都需要按照
void isr_task()中的风格来做,同时需要加入通用的OSIntCtwSw在OS_CPU.asm中的汇编代码。

6. 因此,要想移植一个最简单的UCOS,需要在OS_CPU_A.asm中实现:

<1> OSCtxSw()

<2> OSIntCtxSw()

<3> OSTickISR() 以及针对该函数的时钟初始化



<4> OSStartHighRdy;本节没有介绍,主要在OSStart函数中被调用,应该实现如下的功能:

(1): 并关闭IRQ,FIQ中断

(2): 调用OSTaskSwHook(),这是钩子函数,一般为空,用户可以视需要自己添加内容。

(4):把OSRunning标志置1。OSRunning是UCOS系统己经启动的一个标识,它在调用UCOS中的OSStart时被置为1。

(5):SP指向最高优先级任务的堆栈。

(6):把最高优先级任务堆栈的内容保存到寄存器中

(7):开始执行最高优先级任务。
<4> 在其他地方,如OS_CPU_C.C 中,需要实现OSTaskStkInit函数,完成任务的堆栈初始化函数;类似;
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)

{

INT16U *stk;

opt = opt; /* 'opt' is not used, prevent warning */

stk = (INT16U *)ptos; /* Load stack pointer */

*stk-- = (INT16U)FP_SEG(pdata); /* Simulate call to function with argument */

*stk-- = (INT16U)FP_OFF(pdata);

*stk-- = (INT16U)FP_SEG(task);

*stk-- = (INT16U)FP_OFF(task);

*stk-- = (INT16U)0x0202; /* SW = Interrupts enabled */

*stk-- = (INT16U)FP_SEG(task); /* Put pointer to task on top of stack */

*stk-- = (INT16U)FP_OFF(task);

*stk-- = (INT16U)0xAAAA; /* AX = 0xAAAA */

*stk-- = (INT16U)0xCCCC; /* CX = 0xCCCC */

*stk-- = (INT16U)0xDDDD; /* DX = 0xDDDD */

*stk-- = (INT16U)0xBBBB; /* BX = 0xBBBB */

*stk-- = (INT16U)0x0000; /* SP = 0x0000 */

*stk-- = (INT16U)0x1111; /* BP = 0x1111 */

*stk-- = (INT16U)0x2222; /* SI = 0x2222 */

*stk-- = (INT16U)0x3333; /* DI = 0x3333 */

*stk-- = (INT16U)0x4444; /* ES = 0x4444 */

*stk = _DS; /* DS = Current value of DS */

return ((OS_STK *)stk);

}

7.以上主要涉及到中断部分,还有一些函数需要具体实现,例如X86例子中的OS_ENTER_CRITICAL()

ON_EXIT_CRITICAL()开中断与关中断的实现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: