嵌入式操作系统μcos-ii在c8051f040单片机中移植研究(编辑修改稿)内容摘要:
81。 C/OS 是向下兼容的, OSTaskCreateExt()是OSTaskCreate()的扩展版本,提供了一些附加的功能。 用两个函数中的任何一个都可以建立任务。 任务可以在多任务调度开始前建立,也可以在其它任务的执行过程中被建立。 在开始多任务调度 (即调用 id 参数为要建立的任务创建一个特殊 的标识符。 该参数在 181。 C/OS以后的升级版本中可能会用到,但在 181。 C/OSⅡ中还未使用。 这个标识符可以扩展 181。 C/OSⅡ功能,使 9 它可以执行的任务数超过目前的 64 个。 pbos 是指向任务的堆栈栈底的指针,用于堆栈的检验。 stk_size用于指定堆栈成员数目的容量。 也就是说,如果堆栈的入口宽度为 4字节宽,那么 stk_size为 10000是指堆栈有 40000个字节。 该参数与 pbos一样,也用于堆栈的检验。 pext是指向用户附加的数据域的指针,用来扩展任务的 OS_TCB。 每个任务都有自己的堆栈空间。 堆栈必须声明 为 OS_STK类型,并且由连续的内存空间组成。 用户可以静态分配堆栈空间 (在编译的时候分配 )也可以动态地分配堆栈空间 (在运行的时候分配 )。 用户可以用 C编译器提供的 malloc()函数来动态地分配堆栈空间,在动态分配中,用户要时刻注意内存碎片问题。 特别是当用户反复地建立和删除任务时,内存堆中可能会出现大量的内存碎片,导致没有足够大的一块连续内存区域可用作任务堆栈,这时 malloc()便无法成功地为任务分配堆栈空间181。 C/OSⅡ 支持的处理器的堆栈既可以从上 (高地址 )往下 (低地址 )长也可以从下往上长。 用户在调用 OSTaskCreate()或 OSTaskCreateExt()的时候必须知道堆栈是怎样长的,因为用户必须得把堆栈的栈顶传递给以上两个函数,当 OS_STK_GROWTH置为 0时,用户需要将堆栈的最低内存地址传递给任务创建函数。 任务所需的堆栈的容量是由应用程序指定的。 用户在指定堆栈大小的时候必须考虑任务所调用的所有函数的嵌套情况,任务所调用的所有函数会分配的局部变量的数目,以及所有可能的中断服务例程嵌套的堆栈需求。 另外,堆栈必须能储存所有的 CPU寄存器。 有时候决定任务实际所需的堆栈空间 大小是很有必要的。 因为这样用户就可以避免为任务分配过多的堆栈空间,从而减少自己的应用程序代码所需的 RAM(内存 )数量。 181。 C/OSⅡ提供的 OSTaskStkChk()函数可以提供这种有价值的信息。 为了使用 181。 C/OSⅡ的堆栈检验功能,必须要 做以下几件事情:在 OS_TASK_CREATE_EXT 为 1。 用 OSTaskCreateExt()建立任务,并给予任务比实际需要更多的 内 存 空 间。 在 OSTaskCreateExt() 中 , 将 参 数 opt 设 置 为OS_TASK_OPT_STK_CHK+OS_TASK_OPT_STK_CLR。 将想检验的任务的优先级作为OSTaskStkChk()的参数并调用之。 OSTaskStkChk()顺着堆栈的栈底开始计算空闲的堆栈空间大小,具体实现方法是统计储存值为 0的连续堆栈入口的数目,直到发现储存值不为 0的堆栈入口。 注意堆栈入口的储存值在进行检验时使用的是堆栈的数据类型。 有时候删除任务是很有必要的。 删除任务 ,是说任务将返回并处于休眠状态,并不是说任务的代码被删除了,只是任务的代码不再被 181。 C/OSⅡ调用。 任务的删除意味着:它的任务控制块从 OSTCBList链表中 移到 OSTCBFreeList,这样时钟节拍函数中就不会处理它了,这样调度把它置入就绪表的可能性就没了;如果它已经处于就绪表中,那么它将被移出,这样调度器函数就不会处理它,这样它被调度运行的机会就没了;在建立任务的时候会分配给任务一个优先级。 在程序运行期间,用户可以通过调用 OSTaskChangePrio()来改变任务的优先级。 换句话说,就是 181。 C/OSⅡ允许动态的改变任务的优先级。 用户不能改变空闲任务的优先级,但可以改变调用本函数的任务或者其它任务的优先级。 为了改变调用本函数的任务的优先级,可以指定该任 务当前的优先级或 OS_PRIO_SELF,OSTaskChangePrio()会决定该任务的优先级。 用户还必须指定任务的新优先级。 因为 181。 C/OSⅡ不允许多个任务具有相同的优先级,所以 OSTaskChangePrio()需要检验新优先级是否是合法的。 10 时间管理 181。 C/OSⅡ要求提供定时中断来实现延时与超时控制等功能。 这个定时中断叫做时钟节拍,它应该每秒发生 10至 100次。 时钟节拍的实际频率是由用户的应用程序决定的。 时钟节拍的频率越高,系统的负荷就越重。 时钟的 中断服务子程序和节时钟节函数 OSTimeTick—— 该函数用于通知 181。 C/OSⅡ发生了时钟节拍中断。 这里主要讲述五个与时钟节拍有关的系统服务: (1) OSTimeDly() (2) OSTimeDlyHMSM() (3) OSTimeDlyResume() (4) OSTimeGet() (5) OSTimeSet() 181。 C/OSⅡ提供了这样一个系统服务:申请该服务的任务可以延时一段时间,这段时间的长短是用时钟节拍的数目来确定的。 实现这个系统服务的函数叫做 OSTimeDly()。 调用该函数会使 181。 C/OSⅡ进行一次任务调度,并且执行下一 个优先级最高的就绪态任务。 任务调用OSTimeDly()后,一旦规定的时间期满或者有其它的任务通过调用 OSTimeDlyResume()取消了延时,它就会马上进入就绪状态。 只有当该任务在所有就绪任务中具有最高的优先级时,它才会立即运行。 OSTimeDly()虽然是一个非常有用的函数,但应用程序需要知道延时时间对应的时钟节拍的数目。 用户可以使用定义全局常数 OS_TICKS_PER_SEC 的方法将时间转换成时钟段,但这种方法有时显得比较愚笨。 如果增加了 OSTimeDlyHMSM()函数后,用户就可以按小时(H)、分 (M)、秒 (S)和毫秒 (m)来定义时间了,这样会显得更自然些。 与 OSTimeDly()一样,调用 OSTimeDlyHMSM()函数也会使 181。 C/OSⅡ进行一次任务调度,并且执行下一个优先级最高的就绪态任务。 任务调用 OSTimeDlyHMSM()后,一旦规定的时间期满或者有其它的任务通过调用 OSTimeDlyResume()取消了延时,它就会马上处于就绪态。 同样,只有当该任务在所有就绪态任务中具有最高的优先级时,它才会立即运行。 181。 C/OSⅡ允许用户结束延时正处于延时期的任务。 延时的任务可以不等待延时 期满,而是通过其它任务取消延时来使自己处于就绪态。 这可以通过调用 OSTimeDlyResume()和指定要恢复的任务的优先级来完成。 实际上, OSTimeDlyResume()也可以唤醒正在等待事件的任务,虽然这一点并没有提到过。 在这种情况下,等待事件发生的任务会考虑是否终止等待事件。 无论时钟节拍何时发生, 181。 C/OSⅡ都会将一个 32位的计数器加 1。 这个计数器在用户调用 OSStart()初始化多任务和 4,294,967,295个节拍执行完一遍的时候从 0开始计数。 在时钟节拍的频率等于 100Hz的时候,这个 32 位的计数器每隔 497天就重新开始计数。 用户可以通过调用 OSTimeGet()来获得该计数器的当前值。 也可以通过调用 OSTimeSet()来改变该计数器的值注意,在访问 OSTime 的时候中断是关掉的。 这是因为在大多数 8位处理器上增加和拷贝一个 32位的数都需要数条指令,这些指令一般都需要一次执行完毕,而不能被中断等因素打断。 11 任务间通信与同步 在 181。 C/OSⅡ 中,有多种方法可以保护任务之间的共享数据和提供任务之间的通讯。 这里只讲其中的两种:一是利用宏 OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL()来关闭中断和打开中断。 当两个任务或者一个任务和一个中断服务子程序共享某些数据时,可以采用这种方法。 二是利用函数 OSSchedLock()和 OSSchedUnlock()对 181。 C/OSII 中的任务调度函数上锁和开锁。 用这种方法也可以实现数据的共享。 图 22 事件控制块的使用 图。 一个任务或者中断服务子程序可以通过事件控制块 ECB( Event Control Blocks)来向另外的任务发信号。 这里,所有的信号都被看成是事件 ( Event)。 这也说明为什么上面把用于通讯的数据结构叫做事件控制块。 一个任务还可以等待另一个任务或中断服务子程序给它发送信号。 只有任务可以等待事件发生,中断服务子程序是不能这样做的。 对于处于等待状态的任务,还可以给它指定一个最长等待时间,以此来防止因为等待的事件没有发生而无限期地等下去。 12 多个任务可以同时等待同一个事件的发生。 在这种情况下,当该事件发生后,所有等待该事件的任务中,优先级最高的任务得到了该事件并进入就绪状态,准备执行。 上面讲到的事件,可以是信号量、邮箱或者消息队列等。 当事件控制块是一个信号 量时,任务可以等待它,也可以给它发送消息。 内存管理 在 ANSI C中可以用 malloc()和 free()两个函数动态地分配内存和释放内存。 但是,在嵌入式实时操作系统中,多次这样做会把原来很大的一块连续内存区域,逐渐地分割成许多非常小而且彼此又不相邻的内存区域,也就是内存碎片。 由于这些碎片的大量存在,使得程序到后来连非常小的内存也分配不到。 在任务堆栈中,讲过用 malloc()函数来分配堆栈时,也讨论过内存碎片的问题。 另外,由于内存管理算法的原因, malloc()和 free()函数执行时间是不确定的。 在 181。 C/OSⅡ 中,操作系统把连续的大块内存按分区来管理。 每个分区中包含有整数个大小相同的内存块。 利用这种机制, 181。 C/OSⅡ 对 malloc()和 free()函数进行了改进,使得它们可以分配和释放固定大小的内存块。 这样一来, malloc()和 free()函数的执行时间也是固定的了。 如图 ,在一个系统中可以有多个内存分区。 这样,用户的应用程序就可以从不同的内存分区中得到不同大小的内存块。 但是,特定的内存块在释放时必须重新放回它以前所属于的内存分区。 显然,采用 这样的内存管理算法,上面的内存碎片问题就得到了解决。 为了便于内存的管理,在 181。 C/OSⅡ 中使用内存控制块( memory control blocks)的数据结构来跟踪每一个内存分区,系统中的每个内存分区都有它自己的内存控制块。 图 23 内存分区 13 3 C8051F 系列单片机的特点 C8051F系列单片机的特点 Cygnal公司推出的 C8051F系列单片机将 80C51系列单片机从 MCU推向了 SOC(片上系列)时代,其主要特点是: ⑴ 指令运行速度大大提高。 C8051F系列采用 CIP51的 CPU模式,指令以时钟周期为运行单位。 与 8051相比,相同的时钟下,单周期指令运行速度为原来的 12倍;全指令集平均运行速度为原来的。 ⑵ I/O从固定方式到交叉开关配置。 C8051F系列采用开关网络以硬件方式实现 I/O 端口的灵活配置。 在这种通过交叉开关配置的 I/O 端口系统中,单片机外部为通用 I/O 口,内部。嵌入式操作系统μcos-ii在c8051f040单片机中移植研究(编辑修改稿)
阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。
用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。