Keil ARTX(Advanced Real-Time eXecutive) 是keil 为ARM 系列所提供的一个小型实时操作系统,整合于其UV3 开发环境之中. 一,简介 与以前大家使用keil for 51 时所熟悉RTX51 实时操作系统类似,整个OS 为keil IDE 定制,面向特定的处理器,以库的形式提供,这就屏蔽了底层的操作,使得用户可以专注于应用的开发,但同时也使得用户无法接触到操作系统底层. ARTX 提供的基本功能包括任务的建立,运行,删除,可以给任务指定优先级,对任务进行切换,具体可以参考keil 的官方文档ARARM.chm, 位于UV3 的安装目录. ARTX 为任务间通信和共享资源的保护提供如下机制: *事件标志 *信号量 *互斥信号量 *邮箱 ARTX 的特征如下:
任务数量 | 最大256 | 邮箱数量 | 软件无限制,取决于硬件资源 | 信号量数量 | 软件无限制 | 互斥信号量数量 | 软件无限制 | 信号数量 | 每任务16 个事件标志 | 用户定时器 | 软件无限制 | RAM 空间需求 | 最小500 字节 | CODE 空间需求 | 小于5kB | 硬件要求 | 一个片上定时器 | 任务优先级 | 1~255 | 上下文切换时间 | 在60MHz,0 等待时小于5us | 中断锁定时间 | 60MHz,0 等待时为1.8us |
由于ARTX 是keil 为UV3 所定制,所以使用UV3 可以方便地建立基于ARTX 的应用.简单的说,只需要引用一个头文件,并于连接时连接ARTX 库. 下面例子来自keil(位于Keil/ARM/ARTX/Examples/Artx_ex1). 假定当前有两个任务,称作”do-this”与”do-that”.这些必须重复运行,例如间隔时 间为50ms.两者都运行完成后会暂停一段时间,而”do-that”会在”do-this”运行后运行, 并暂停20ms. 按照如下步骤建立应用. 首先利用关键字__task 建立两个任务: void task1 (void) __task {
.... code of task 1 placed here .... } void task2 (void) __task {
.... code of task 2 placed here .... }
任务必须建立于运行之前,可通过在main 函数中调用os_sys_init() 来启动任务的运行. 如下例中,task1 最先启动,然后其通过调用os_tsk_create 来启动task2. void task1 (void) __task { os_tsk_create (task2, 0); .... code of task 1 placed here ....
} void task2 (void) __task {
.... code of task 2 placed here .... } void main (void) {
os_sys_init (task1); }
完成最初所设定的应用任务的源代码如下: /* Include type and function declarations for ARTX */ #include "ARTX.H"
/* id1, id2 will contain task identifications at run-time */ OS_TID id1, id2;
/* Forward reference. */ void task1 (void) __task; void task2 (void) __task;
void task1 (void) __task { /* Obtain own system task identification number */ id1 = os_tsk_self (); /* Assign system identification number of task2 to id2 */ id2 = os_tsk_create (task2, 0); for (;;) { /* do-this */
/* Indicate to task2 completion of do-this */ os_evt_set (0x0004, id2); /* Wait for completion of do-that (0xffff means no time-out)*/ os_evt_wait_or (0x0004, 0xffff); /* Wait now for 50 ms */ os_dly_wait (5);
} }
void task2 (void) __task { for (;;) { /* Wait for completion of do-this (0xffff means no time-out) */ os_evt_wait_or (0x0004, 0xffff); /* do-that */
/* Pause for 20 ms until signaling event to task1 */ os_dly_wait (2); /* Indicate to task1 completion of do-that */ os_evt_set (0x0004, id1); } }
void main (void) { os_sys_init (task1); }
os_dly_wait() 用于将任务暂停一定的系统定时周期. os_evt_wait_or() 用于os_evt_set() 等待和设置任务的事件标志. 二,机制 1. 计时中断 ARTX 需要使用一个硬件的定时器产生周期性的中断.使用一个硬件定时器来实现,可在ARTX_Config.c 中配置. 2. 系统定时任务 系统定时任务(系统时钟任务),在每一个系统定时中断产生时被执行,其具有最高的优先级且不可被抢占.此任务基本可以说是一个任务切换器. ARTX 给每个任务分配一个时间片(time slice),其时间的长短可以在ARTX_Config.c 中进行配置,由于时间片很短(默认为10ms),使得任务看上去像是在同时运行.
任务在自己分得的时间片内运行时,可通过os_tsk_pass() 或一些wait 函数放弃CPU 的控制权.然后ARTX 将切换到下一个就绪任务运行. os_clock_demon() 作为系统定时任务管理用户任务.它处理任务的延时, 使等待的任务进入休眠态,当有时间发生时,将相应的任务唤醒并进入就绪态. 这也就是其处于最高优先级的原因. 3. 任务管理 任务可处于下列状态: RUNNING, READY, WAIT_DLY, WAIT_ITV, WAIT_OR, WAIT_AND, WAIT_SEM, WAIT_MUT, WAIT_MBX, INACTIVE
4. 空闲任务 当无任务运行时,ARTX 调度任务os_idle_demon() 进入运行状态,可在该任务添加代码中使得系统休眠,从而降低系统功耗,具体可以在ARTX_Config.c 中配置. 5. 系统资源 ARTX 的任务有任务控制(TCB).这是一个可动态分配的内存块,保存着所有任务的控制和状态变量.TCB 可通过os_tsk_create() 或os_tsk_create_user() 于运行时分配. TCB 内存池的大小可以在ARTX_Config.c 中根据系统中任务的状况进行配置,不仅是任务的数量,还有实际任务的实例数,原因在于ARTX 支持一个任务(函数)的多个实例.
任务有其自己的堆栈,也于运行时被分配,然后栈指针被记录到任务的TCB 中. 6. 协同任务调度 如果禁止了轮转(Round-Robin)方式的任务调度,则用户必须使得任务可以协同调度,比如,调用函数os_dly_wait() 或os_tsk_pass() 来通知ARTX 进行任务的切换. 如下例: #include <ARTX.h> int counter1; int counter2; void task1 (void) __task; void task2 (void) __task; void task1 (void) __task { os_tsk_create (task2, 0); /* Create task 2 and mark it as ready */ for (;;) { | /* loop forever */ | counter1++; | /* update the counter */ | os_tsk_pass (); | /* switch to 'task2' */ | } | | } | | void task2 (void) __task { | | | for (;;) { | /* loop forever */ | | counter2++; | /* update the counter */ | | os_tsk_pass (); | /* switch to 'task1' */ | | } | | } | | | void main (void) { | | | os_sys_init(task1); | /* Initialize ARTX Kernel and start task 1 */ | | for (;;); | | } | | |
wait 系统调用与os_tsk_pass () 的区别在于前者使得任务等待一个事件,而后者直接切换到下一就绪任务运行. 7. 轮转任务调度 ARTX 可配置为轮转调度方式,任务将在一个分配的时间片内运行. 任务可连续运行时间片长度的时间(除非任务本身放弃时间片),然后,ARTX 将会切换到下一个就绪且优先级相同的任务运行.如果没有相同优先级的任务就绪,则当前任务会继续运行. 如下例: #include <ARTX.h> int counter1; int counter2;
void job1 (void) __task; | void job2 (void) __task; | void job1 (void) __task { | os_tsk_create (job2, 0); | /* Create task 2 and mark it as ready */ | while (1) { | /* loop forever */ | counter1++; | /* update the counter */ | } | | } | |
void job2 (void) __task { while (1) { /* loop forever */ counter2++; /* update the counter */ } } void main (void) { os_sys_init (job1); /* Initialize ARTX Kernel and start task 1 */ for (;;); } 注意: 不必等到时间片用完,任务就可以通过wait 系统调用或者os_tsk_pass () 来通知ARTX 可以切换到下一个就绪任务.wait 系统调用将使当前任务挂起(wait XXX 状态),等待一个特定的事件发生(然后其进入ready 状态).在这段等待的时间,其它的任务得以运行. 8. 抢占式任务调度 ARTX 是一个抢占式的多任务调度系统.如果一个高优先级的任务就绪,它将打断当前任务的运行,而使得高优先级任务可以立即运行. 抢占式任务切换发生于: *在系统定时中断中,如果某一个高优先级任务的延时时间到达,将使得当前任务被挂起而高优先级任务被运行 *如果挂起中的高优先级的任务接收到当前任务或中断发来的特定事件, 将会挂起当前任务,切换到高优先级任务运行 *高优先级任务等待到了所需的信号量 *一个高优先级任务正在等待的互斥信号量被释放 *一个高优先级任务等待的消息被发往信箱 *信箱已满,而一个高优先级的任务等待往信箱发送一个消息.则当前任务或中断从信箱中取走一个消息时,将使得该高优先级任务被激活,并立即投入运行
*当前任务的优先级下降,而有相对高优先级任务就绪时请参考下例: #include <ARTX.h> OS_TID tsk1,tsk2; int cnt1,cnt2; void job1 (void) __task; void job2 (void) __task; void job1 (void) __task { os_tsk_prio (2); os_tsk_create (job2, 1); while (1) { os_evt_wait_or (0x0001, 0xffff); cnt1++; } } void job2 (void) __task { while (1) { os_evt_set (0x0001, job1); cnt2++;
} } void main (void) { os_sys_init (job1); while (1); } 任务job1 比任务job2 的优先级高. 当job1 开始运行, 将创建任务job2 然后进入os_evt_wait_or() . 然后当前任务挂起并开始运行任务job2. 一旦任务job2 为任务job1 发送一个事件标志, 它将被挂起然后job1 继续运行. 任务job1 增加计数变量然后再次通过os_evt_wait_or() 调用挂起. 任务job2 将被继续运行, 增加计数器变量 cnt2 ,然后再次发送事件… 9. 用户定时器 用户定时器是基于系统定时的简单简单定时器模块.可在运行时动态创建与删除用户定时器.如果在用户定时器溢出前未被删除,该定时器的溢出将会调用用户定义的回调os_tmr_call() 然后删除这个定时器. 用户定时器超时的值可以在调用os_tmr_create() 创建该定时器时指定. os_tmr_call() 可在ARTX_Config.c 中配置. 10.中断函数 ARTX 支持中断平行处理,但是仍建议不要使用中断嵌套.最好使用短的中断函数,发时间标志到RTOS 的任务,这样一来,中断嵌套也就不必要了. 如下例: #define EVT_KEY 0x0001 OS_TID pr_task; int num_ints; /*----------------------------------------------------------------------------
* External 0 Interrupt Service Routine *---------------------------------------------------------------------------*/ void ext0_int (void) __irq | { | | isr_evt_set (EVT_KEY, pr_task); | /* Send event to 'process_task' | */ | | EXTINT | = 0x01; | | /* Acknowledge Interrupt | */ | | VICVectAddr = 0; | | | | } | | | | | | | | | | |
/*---------------------------------------------------------------------------- * Task 'process_task' *---------------------------------------------------------------------------*/ void process_task (void) __task { num_ints = 0; while (1) { os_evt_wait_or (EVT_KEY, 0xffff); num_ints++; } } /*---------------------------------------------------------------------------- * Task 'init_task' *---------------------------------------------------------------------------*/ void init_task (void) __task { PINSEL1 &= ~0x00000003; /* Enable EINT0 */ PINSEL1 |= 0x00000001; EXTMODE = 0x03; /* Edge triggered lo->hi transition */ EXTPOLAR = 0x03; pr_task = os_tsk_create (process_task, 100); VICVectAddr14 = (U32)eint0_int; /* Task started, Enable interrupts */ VICVectCntl14 = 0x20 | 14; os_tsk_delete_self (); /* Terminate this task */ } 中断函数的写法与没有ARTX 的ARM 系统相同. 注意: FIQ 不会被ARTX 禁止 FIQ 中禁止调用isr_这已系列的函数.
|