新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > μC/OS-II在51单片机上的移植

μC/OS-II在51单片机上的移植

作者:时间:2012-04-27来源:网络收藏

4.1 任务堆栈初始化函数OSTaskStkInit()
此函数是在任务创建函数OSTaskCreat()或OSTaskCreatExt()中调用的。因为系统为每个任务申请了一个数组作为栈,当一个任务运行时,就把堆栈指针指向本任务的栈,任务堆栈初始化函数就是在任务创建时将要创建任务的堆栈进行初始化。但C51的堆栈指针SP是8位的,只能在片内RAM的256个字节内寻址。因其寻址空间有限且SP唯一,不能像DSP或ARM那样为每一段程序或每一种模式定义堆栈,需小心管理堆栈空间。为了适应上述情况,需要换一种思路,不是让SP去指向各任务堆栈空间,而是把各任务堆栈空间的内容复制到系统栈中。至于堆栈数组空间要有多大以及堆栈数组空间里放些什么内容,可以借鉴keil中中断函数的压栈情况,当中断函数不指定寄存器组时,编译器一般将PC、ACC、B、DPTR、PSW、R0~R7寄存器入栈,其中PC和DPTR是双字节的,其它都是单字节的,一共15个字节,所以把堆栈数组设计成至少15个字节的,以保证任务所用的寄存器都在堆栈数组中包含着。因为每个数组里放的是寄存器的值,在此就把这每个任务的堆栈数组叫做寄存器数组,暂且把寄存器数组设计成15个字节,依次存放PC、ACC、B、DPTR、PSW、R0~R7。
函数OSTaskStkInit()传递4个参数,第1个参数task是所创建任务的起始地址,这个参数须保存到PC在寄存器数组的对应位置,第2个参数ppdata是所创建任务的参数,C51规则中用R1~R3来传递参数指针,这个参数须存放到R1~R3在寄存器数组中的对应位置。第3个参数ptos是栈底指针,从当前地址开始初始化堆栈指针,第4个参数opt是附加参数,一般不用。
4.2 运行等待任务中优先级最高任务函数OSStartHighRdy()
此函数在启动操作系统函数OSStart()的最后一行调用,且此函数不返回,经过此函数后μC/OS接管系统。OSStartHighRdy()不是去调用用户任务函数,而是让PC指针指向任务函数首地址。且任务函数的传递参数只有一个,若此参数正确,则可保证任务函数运行正确。在调用OSStartHighRdy()之前OSStart()已经把最高优先级任务的任务表准备好了,只要把最高优先级任务表的数据恢复到堆栈中,再执行返回指令即可,以上最关键的是如何让其返回到最高优先级任务中而不是返回到被调函数中。
当函数OSStart()调用函数OSStartHighRdy()时,断点地址入栈;当OSStartHighRdy()执行完之后,返回断点。在OSStartHighRdy()中把SP及SP-1的值改为最高优先级任务的地址,这样OSStartHighRdy()就会返回到最高优先级任务中去运行。
4.3 任务级的任务切换函数OSCtxSw()
此函数是保存当前任务的状态,然后运行处于就绪态中的最高优先级任务。前面介绍过不是更改SP去指向寄存器数组,而是把寄存器数组的数复制到堆栈中。先看下一般的情况,在用户任务MyTask(void*ppdtat)中调用TimeDly(),TimeDly()中调用OSSched(),在OSSched()中有一个宏OS_TASK_SW(),这个宏的目的是让程序进人函数OSCtxSw()。参看图1,就如Fun4为OSCtxSw(),Fun3为OSSched(),Fun2为TimeDly(),Fun1为MyTask()。ADD_D存的是OSSched()的断点,ADD_C为TimeDly()的断点,ADD_B为MyTask()的断点。如果进行任务切换,应该把高优先级任务的地址值赋给ADD_B(即SP-4与SP-5)。
以上考虑的是最简单的情况,当任务比较复杂时,可能更改了ACC、PSW、DPTR或R0~R7的值,在进入高优先任务时,寄存器并不是此任务的寄存器值,运行的结果可能不正确。
在上述情况下如何保证CPU寄存器的值正确,要分两个阶段。第一个阶段是把CPU寄存器值保存到要挂起任务的寄存器数组中,当刚进入OSCtxSw()时,CPU寄存器的值是要挂起任务的寄存器值,所以一开始就要锁定CPU寄存器的值。如果OS_TASK_SW()定义为中断的话,在进入OSCtxSw()时,CPU寄存器的值被自动压栈;如果把OS_TASK_SW()定义为函数时,在进入函数时使用内嵌汇编的方法把CPU寄存器入栈。这时堆栈中又压入了13个字节,就如在图1的ADD_D上又压入了13个字节的数据,然后从堆栈中把值取出来放到相应任务的寄存器数组中。第二个阶段是把将要执行任务的寄存器数组的值复制到堆栈中。此时PC指针在堆栈中对应的位置是SP-17与SP-18,SP到SP-12的13个字节对应ACC、B、DPTR、PSW、R0~R7。
4.4 中断级的任务切换函数OSIntCtxSw()
此函数和上一个函数基本思想一致,都要保存当前任务的状态,运行处于就绪态中的优先级最高的任务。二者的不同在于,上个函数的堆栈中SP-17与SP-18是PC值的位置,SP到SP-12是13个寄存器的位置。当中断来时,在中断中调用函数OSIntExit(),函数OSIntExit()调用函数OSIntCtxSw(),在OSIntCtxSw()中实现任务切换。在进入函数OSIntExit()之前寄存器的值已经入栈,所以运行到本函数时堆栈中SP-17与SP-18是PC值的位置。SP-4到SP-16是13个寄存器的位置。在图1上,上个函数的13个寄存器的值被压入ADD_D上面的13个字节中,而本函数是在ADD_B于ADD_C之间压入的这13个寄存器。
4.5 周期节拍中断函数OSTickISR()
这个函数是给系统提供一个节拍,一般每秒10~100次。如果节拍频率太高,μC/OS系统会占用大量硬件资源;如果太低,任务间的切换又会很慢。
此函数首先要保证产生一个周期性的中断,可以使用硬件定时器,也可以从交流电中获得50/60Hz的时钟频率。这个函数至少要做3件事:1)进入中断时,把中断嵌套层数计数器加1,说明又进入一次中断,也可以直接调用OSIntEnter()函数;2)调用时钟节拍函数OSTimeTick(),告知系统又经过了一个节拍;3)调用OSIntExit()函数,说明要退出中断了,此函数会自动处理。

5 结束语
文中阐述了在堆栈空间有限的51运行μC/系统的过程,利用系统栈SP作为数据交换的枢纽。在实际应用中,如果用系统栈来,只需根据文中的基本思想进行适当的改写,即可运行于其他处理器上。如果处理器的堆栈指针寻址空间足够大,也可以为每个任务开辟一个栈,通过改变堆栈指针指向不同任务的栈空间,来实现任务调度。
通过在51的运行,可以看出μC/也能在堆栈空间比较少的CPU上运行。

本文引用地址:http://www.eepw.com.cn/article/171557.htm

上一页 1 2 3 下一页

关键词: 移植 机上 单片 OS-II

评论


相关推荐

技术专区

关闭