µC_OS-II实验指导书
此实验的目的是让读者理解嵌入式操作系统中任务管理的基本原理,了解任务的各个基本状态及其变迁过程;掌握µC/OS-II中任务管理的基本方法(创建、启动、挂起和解挂任务);熟练使用µC/OS-II任务管理的基本系统调用。
通过此实验读者可以了解在基于抢占式嵌入式实时操作系统并有共享资源的应用中,出现优先级反转现象的原理。优先级反转发生在有多个任务共享资源的情况下,高优先级任务被低优先级任务阻塞,并等待低优先级任务执行的现象。
通过此实验读者可以了解嵌入式实时操作系统µC/OS-II解决优先级反转的策略——优先级继承的原理,以此解决低优先级任务在占用了共享资源的情况下,被高优先级任务抢占了CPU使用权而导致的优先级反转的问题。
通过经典的哲学家就餐应用,读者可以了解如何利用嵌入式实时操作系统µC/OS-II的信号量机制来对共享资源进行互斥访问。
通过此实验读者可以了解嵌入式实时操作系统µC/OS-II中的内存管理的原理,包括对内存的分配和回收。
通过此实验读者可以了解嵌入式实时操作系统µC/OS-II中,时钟中断的使用情况。
通过此实验读者可以了解嵌入式实时操作系统µC/OS-II中的消息队列机制。读者可以了解一个应用中的任务是如何进行通信的,如何能使它们相互协调工作。
✓ 理解任务管理的基本原理,了解任务的各个基本状态及其变迁过程;
✓ 掌握µC/OS-II中任务管理的基本方法(创建、启动、挂起、解挂任务);
✓ 熟练使用µC/OS-II任务管理的基本系统调用。
为了展现任务的各种基本状态及其变迁过程,本实验设计了Task0、Task1两个任务:任务Task0不断地挂起自己,再被任务Task1解挂,两个任务不断地切换执行。通过本实验,读者可以清晰地了解到任务在各个时刻的状态以及状态变迁的原因。
图1
注意:图中的栅格并不代表严格的时间刻度,而仅仅表现各任务启动和执行的相对先后关系。
整个应用的运行流程如图1所示,其描述如下:
(1)系统经历一系列的初始化过程后进入boot_card()函数,在其中调用ucBsp_init()进行板级初始化后,调用main()函数;
(2)main()函数调用OSInit()函数对µC/OS-II内核进行初始化,调用OSTaskCreate创建起始任务TaskStart;
(3)main()函数调用函数OSStart()启动µC/OS-II内核的运行,开始多任务的调度,执行当前优先级最高的就绪任务TaskStart;
(4)TaskStart完成如下工作:
a、安装时钟中断并初始化时钟,创建2个应用任务;
b、挂起自己(不再被其它任务唤醒),系统切换到当前优先级最高的就绪任务Task0。
之后整个系统的运行流程如下:
● t1时刻,Task0开始执行,它运行到t2时刻挂起自己;
● t2时刻,系统调度处于就绪状态的优先级最高任务Task1执行,它在t3时刻唤醒Task0,后者由于优先级较高而抢占CPU;
● Task0执行到t4时刻又挂起自己,内核调度Task1执行;
● Task1运行至t5时刻再度唤醒Task0;
● ……
一个任务通常是一个无限的循环 ,由于任务的执行是由操作系统内核调度的,因此任务是绝不会返回的,其返回参数必须定义成void。在μC/OS-Ⅱ中,当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的CPU使用权就会被抢占,高优先级任务会立刻得到CPU的控制权(在系统允许调度和任务切换的前提下)。μC/OS-Ⅱ可以管理多达64个任务,但目前版本的μC/OS-Ⅱ有两个任务已经被系统占用了(即空闲任务和统计任务)。必须给每个任务赋以不同的优先级,任务的优先级号就是任务编号(ID),优先级可以从0到OS_LOWEST_PR10-2。优先级号越低,任务的优先级越高。μC/OS-Ⅱ总是运行进入就绪态的优先级最高的任务。
操作系统配置的目的在于根据应用的需要,对操作系统的功能和规模进行设置,以便优化对系统存储空间的使用。配置的方法为修改uC_OS-II源代码目录中的OS_CFG.h文件:
#define OS_MAX_EVENTS 10 /*最多可以有10个事件*/
#define OS_MAX_FLAGS 5 /*最多可以有5个事件标志*/
#define OS_MAX_MEM_PART 5 /*最多可以划分5个内存块*/
#define OS_MAX_QS 2 /*最多可以使用2个队列*/
#define OS_MAX_TASKS 3 /*最多可以创建3个任务*/
#define OS_LOWEST_PRIO 14 /*任务优先级不可以大于14*/
#define OS_TASK_IDLE_STK_SIZE 1024 /*空闲任务堆栈大小*/
#define OS_TASK_STAT_EN 1 /*是否允许使用统计任务*/
#define OS_TASK_STAT_STK_SIZE 1024 /*统计任务堆栈大小*/
#define OS_FLAG_EN 0 /*是否允许使用事件标志功能*/
#define OS_FLAG_WAIT_CLR_EN 1 /*是否允许等待清除事件标志*/
#define OS_FLAG_ACCEPT_EN 1 /*是否允许使用OSFlagAccept()*/
#define OS_FLAG_DEL_EN 1 /*是否允许使用OSFlagDel()*/
#define OS_FLAG_QUERY_EN 1 /*是否允许使用OSFlagQuery()*/
#define OS_MBOX_EN 0 /*是否允许使用邮箱功能*/
#define OS_MBOX_ACCEPT_EN 1 /*是否允许使用 OSMboxAccept() */
#define OS_MBOX_DEL_EN 1 /*是否允许使用 OSMboxDel()*/
#define OS_MBOX_POST_EN 1 /*是否允许使用OSMboxPost()*/
#define OS_MBOX_POST_OPT_EN 1 /*是否允许使用OSMboxPostOpt() */
#define OS_MBOX_QUERY_EN 1 /*是否允许使用OSMboxQuery()*/
#define OS_MEM_EN 0 /*是否允许使用内存管理的功能*/
#define OS_MEM_QUERY_EN 1 /*是否允许使用OSMemQuery()*/
#define OS_MUTEX_EN 0 /*是否允许使用互斥信号量的功能*/
#define OS_MUTEX_ACCEPT_EN 1 /*是否允许使用OSMutexAccept()*/
#define OS_MUTEX_DEL_EN 1 /*是否允许使用OSMutexDel()*/
#define OS_MUTEX_QUERY_EN 1 /*是否允许使用OSMutexQuery()*/
#define OS_Q_EN 0 /*是否允许使用队列功能*/
#define OS_Q_ACCEPT_EN 1 /*是否允许使用OSQAccept()*/
#define OS_Q_DEL_EN 1 /*是否允许使用OSQDel()*/
#define OS_Q_FLUSH_EN 1 /*是否允许使用 OSQFlush()*/
#define OS_Q_POST_EN 1 /*是否允许使用 OSQPost()*/
#define OS_Q_POST_FRONT_EN 1 /*是否允许使用OSQPostFront()*/
#define OS_Q_POST_OPT_EN 1 /*是否允许使用OSQPostOpt()*/
#define OS_Q_QUERY_EN 1 /*是否允许使用OSQQuery()*/
#define OS_SEM_EN 0 /*是否允许使用信号量功能*/
#define OS_SEM_ACCEPT_EN 1 /*是否允许使用OSSemAccept()*/
#define OS_SEM_DEL_EN 1 /*是否允许使用 OSSemDel() */
#define OS_SEM_QUERY_EN 1 /*是否允许使用 OSSemQuery()*/
#define OS_TASK_CHANGE_PRIO_EN 0 /*是否允许使用 OSTaskChangePrio()*/
#define OS_TASK_CREATE_EN 1 /*是否允许使用OSTaskCreate()*/
#define OS_TASK_CREATE_EXT_EN 1 /*是否允许使用OSTaskCreateExt()*/
#define OS_TASK_DEL_EN 1 /*是否允许使用OSTaskDel()*/
#define OS_TASK_SUSPEND_EN 1 /*是否允许使用OSTaskSuspend() and OSTaskResume()*/
#define OS_TASK_QUERY_EN 1 /*是否允许使用OSTaskQuery()*/
#define OS_TIME_DLY_HMSM_EN 0 /*是否允许使用OSTimeDlyHMSM()*/
#define OS_TIME_DLY_RESUME_EN 1 /*是否允许使用OSTimeDlyResume()*/
#define OS_TIME_GET_SET_EN 1 /*是否允许使用OSTimeGet() 和 OSTimeSet()*/
#define OS_SCHED_LOCK_EN 1 /*是否允许使用OSSchedLock()和OSSchedUnlock()*/
#define OS_TICKS_PER_SEC 200 /*设置每秒之内的时钟节拍数目*/
系统启动后,经历一系列的初始化过程,进入main()函数,这是我们编写实现应用程序的起点。首先需要在main()函数里创建起始任务TaskStart:
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 4);
TaskStart任务
TaskStart任务负责安装操作系统的时钟中断服务例程、初始化操作系统时钟,并创建所有的应用任务:
ucos_x86_idt_set_handler(0x20,(void *)OSTickISR,0x8e00);
/* Install uC/OS-II's clock tick ISR */
ucos_timer_init(); /*Timer 初始化*/
TaskStartCreateTasks(); /* Create all the application tasks */
OSTaskSuspend(OS_PRIO_SELF);
具体负责应用任务创建的TaskStartCreateTasks函数代码如下,它创建了两个应用任务Task0和Task1:
static void TaskStartCreateTasks (void)
{
INT8U i;
for (i = 0; i < N_TASKS; i++) { /* Create N_TASKS identical tasks */
TaskData[i] = i; /* Each task will display its own letter */
}
OSTaskCreate(Task0, (void *)&TaskData[i], &TaskStk[i][TASK_STK_SIZE - 1], 5);
OSTaskCreate(Task1, (void *)&TaskData[i], &TaskStk[1][TASK_STK_SIZE - 1], 6);
}
TaskStart任务完成上述操作后将自己挂起,操作系统将调度当前优先级最高的应用任务Task0运行。
应用任务
应用任务Task0运行后将自己挂起,之后操作系统就会调度处于就绪状态的优先级最高的任务,具体代码如下:
void Task0 (void *pdata)
{
INT8U i;
INT8U err;
i=*(int *)pdata;
for (;;) {
…… /*此处为输出信息,显示任务运行的状态 */
err=OSTaskSuspend(5); /* suspend itself */
}
}
应用任务Task1运行后将Task0唤醒,使其进入到就绪队列中:
void Task1 (void *pdata)
{
INT8U i;
INT8U err;
i=*(int *)pdata;
for (;;) {
OSTimeDly(150);
…… /*此处为输出信息,显示任务运行的状态 */
OSTimeDly(150);
err=OSTaskResume(5); /* resume task0 */
}
}
按照本实验手册第一部分所描述的方法建立应用项目并完成构建,当我们在LambdaTOOL调试器的控制下运行构建好的程序后,将看到在μC/OS-Ⅱ内核的调度管理下,两个应用任务不断切换执行的情形:
T1时刻的截图
T2时刻的截图
T3时刻的截图
T4时刻的截图
建立一个新任务。任务的建立可以在多任务环境启动之前,也可以在正在运行的任务中建立。中断处理程序中不能建立任务。一个任务可以为无限循环的结构。
函数原型:INT8U OSTaskCreate(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio);
参数说明:task是指向任务代码首地址的指针。
Pdata指向一个数据结构,该结构用来在建立任务时向任务传递参数。
返回值:
OSTaskCreate()的返回值为下述之一:
● OS_NO_ERR:函数调用成功。
● OS_PRIO_EXIST:具有该优先级的任务已经存在。
● OS_PRIO_INVALID:参数指定的优先级大于OS_LOWEST_PRIO。
● OS_NO_MORE_TCB:系统中没有OS_TCB可以分配给任务了。
无条件挂起一个任务。调用此函数的任务也可以传递参数OS_PRIO_SELF,挂起调用任务本身。当前任务挂起后,只有其他任务才能唤醒被挂起的任务。任务挂起后,系统会重新进行任务调度,运行下一个优先级最高的就绪任务。唤醒挂起任务需要调用函数OSTaskResume()。
任务的挂起是可以叠加到其他操作上的。例如,任务被挂起时正在进行延时操作,那么任务的唤醒就需要两个条件:延时的结束以及其他任务的唤醒操作。又如,任务被挂起时正在等待信号量,当任务从信号量的等待对列中清除后也不能立即运行,而必须等到被唤醒后。
函数原型:INT8U OSTaskSuspend ( INT8U prio);
参数说明:prio为指定要获取挂起的任务优先级,也可以指定参数OS_PRIO_SELF,挂起任务本身。此时,下一个优先级最高的就绪任务将运行。
返回值:
OSTaskSuspend()的返回值为下述之一:
● OS_NO_ERR:函数调用成功。
● OS_TASK_ SUSPEND_IDLE:试图挂起μC/OS-II中的空闲任务(Idle task)。此为非法操作。
● OS_PRIO_INVALID:参数指定的优先级大于OS_LOWEST_PRIO或没有设定OS_PRIO_SELF的值。
● OS_TASK_ SUSPEND _PRIO:要挂起的任务不存在。
唤醒一个用OSTaskSuspend()函数挂起的任务。OSTaskResume()也是唯一能“解挂”挂起任务的函数。
函数原型:NT8U OSTaskResume ( INT8U prio);
参数说明:prio指定要唤醒任务的优先级。
返回值:
OSTaskResume ()的返回值为下述之一:
● OS_NO_ERR:函数调用成功。
● OS_TASK_RESUME_PRIO:要唤醒的任务不存在。
● OS_TASK_NOT_SUSPENDED:要唤醒的任务不在挂起状态。
● OS_PRIO_INVALID:参数指定的优先级大于或等于OS_LOWEST_PRIO。
掌握在基于优先级的可抢占嵌入式实时操作系统的应用中,出现优先级反转现象的原理。
在本实验中,要体现嵌入式实时内核的优先级抢占调度的策略,并显现由于共享资源的互斥访问而出现的优先级反转现象。
优先级反转发生在有多个任务需要使用共享资源的情况下,可能会出现高优先级任务被低优先级任务阻塞,并等待低优先级任务执行的现象。高优先级任务需要等待低优先级任务释放资源,而低优先级任务又正在等待中等优先级任务,这种现象就被称为优先级反转。两个任务都试图访问共享资源是出现优先级反转最通常的情况。为了保证一致性,这种访问应该是顺序进行的。如果高优先级任务首先访问共享资源,则会保持共享资源访问的合适的任务优先级顺序;但如果是低优先级任务首先获得共享资源的访问,然后高优先级任务请求对共享资源的访问,则高优先级任务被阻塞,直到低优先级任务完成对共享资源的访问。
1)设计了3个应用任务TA0~TA2,其优先级逐渐降低,任务TA0的优先级最高。
2)除任务TA1外,其它应用任务都要使用同一种资源,该资源必须被互斥使用。为此,创建一个二值信号量mutex来模拟该资源。虽然μC/OS-Ⅱ在创建信号量时可以选择采用防止优先级反转的策略,但在本实验中我们不使用这种策略。
3)应用任务的执行情况如图2-1所示:
任务2: 任务1: 任务0:
图2-1
注意:图中的栅格并不代表严格的时间刻度,而仅仅表现各个任务启动和执行的相对先后关系。
1) 系统初始化,之后进入main函数;
2) 在main函数中,首先创建一个二值的信号量mutex;
3) 在main函数中创建TaskStart任务,由TaskStart任务创建所有的应用任务(TA0、TA1、TA2)。优先级较高的任务TA0、TA1先延时若干个时钟节拍,以便低优先级任务TA2运行。
4) t1时刻,任务TA2运行并首先申请到信号量mutex;
5) t2时刻,任务TA1延时到期,任务TA1的优先级高于任务TA2的优先级,因此任务TA1立刻抢占TA2执行,任务TA2由执行态转为就绪态;
6) t3时刻,任务TA0延时到期,任务TA0的优先级高于任务TA1的优先级,所以任务TA0立刻抢占执行,任务TA1由执行态转为就绪态,任务TA0申请二值信号量mutex被阻赛;
7) t4时刻,任务TA1由就绪态转回为执行态;此时TA0在等待TA2保持的mutex , 而TA2又因为优先级低于TA1被阻塞。如果TA1一直执行而TA2没有机会被调度的话,那么TA2将一直等到TA1执行完后才能执行,而TA0更要等到TA2释放它所占有的信号量资源后才能执行,这样就出现了优先级高的TA0任务等待优先级低的TA1任务的现象;
8) t5时刻,任务TA1挂起自己,而TA0又因为申请二值信号量mutex而处于阻塞状态,所以任务TA2由就绪态转为执行态,任务TA2释放信号量mutex;
9) t6时刻,TA0获得信号量并立刻抢占执行,任务TA2由执行态转为就绪态;
10) t7时刻,任务TA0将自己延时一段时间,而TA1仍然处于挂起状态,TA2是当前最高优先级的就绪任务,它又转为执行状态,任务TA2因申请二值信号量mutex而阻塞;
11) t8时刻,任务TA1延时到期转为执行态,任务TA1又因等待一个事件而阻塞;
12) t9时刻,任务TA0延时到,释放二值信号量mutex,mutex被TA2得到后,内核自动切换任务;
13) t10时刻,在就绪队列中,TA0优先级最高,TA0执行,又因为任务TA0等待一事件而阻塞;
14) t11时刻,任务TA1延时到期,立刻抢占执行,又由于任务TA1等待一事件而阻塞;;
15) t12时刻,任务TA2执行,保持信号量mutex;以后系统再次出现优先级反转现象;
16) 系统如此周而复始地运行……
修改uC_OS-II/OS_CFG.h:
#define OS_MAX_EVENTS 10 /*最多可以有10个事件*/
#define OS_MAX_FLAGS 5 /*最多可以有5个事件标志*/
#define OS_MAX_MEM_PART 5 /*最多可以划分5个内存块*/
#define OS_MAX_QS 2 /*最多可以使用2个队列*/
#define OS_MAX_TASKS 9 /*最多可以创建9个任务*/
#define OS_LOWEST_PRIO 24 /*任务优先级不可以大于24*/
#define OS_TASK_IDLE_STK_SIZE 1024 /*空闲任务堆栈大小*/
#define OS_TASK_STAT_EN 1 /*是否允许使用统计任务*/
#define OS_TASK_STAT_STK_SIZE 1024 /*统计任务堆栈大小*/
#define OS_FLAG_EN 1 /*是否允许使用事件标志功能*/
#define OS_FLAG_WAIT_CLR_EN 1 /*是否允许等待清除事件标志*/
#define OS_FLAG_ACCEPT_EN 1 /*是否允许使用OSFlagAccept()*/
#define OS_FLAG_DEL_EN 1 /*是否允许使用OSFlagDel()*/
#define OS_FLAG_QUERY_EN 1 /*是否允许使用OSFlagQuery()*/
#define OS_MBOX_EN 0 /*是否允许使用邮箱功能*/
#define OS_MEM_EN 1 /*是否允许使用内存管理的功能*/
#define OS_MEM_QUERY_EN 1 /*是否允许使用OSMemQuery()*/
#define OS_MUTEX_EN 1 /*是否允许使用互斥信号量的功能*/
#define OS_MUTEX_ACCEPT_EN 1 /*是否允许使用OSMutexAccept()*/
#define OS_MUTEX_DEL_EN 1 /*是否允许使用OSMutexDel()*/
#define OS_MUTEX_QUERY_EN 1 /*是否允许使用OSMutexQuery()*/
#define OS_Q_EN 0 /*是否允许使用队列功能*/
#define OS_SEM_EN 1 /*是否允许使用信号量功能*/
#define OS_SEM_ACCEPT_EN 1 /*是否允许使用OSSemAccept()*/
#define OS_SEM_DEL_EN 1 /*是否允许使用 OSSemDel() */
#define OS_SEM_QUERY_EN 1 /*是否允许使用 OSSemQuery()*/
#define OS_TASK_CHANGE_PRIO_EN 1 /*是否允许使用 OSTaskChangePrio()*/
#define OS_TASK_CREATE_EN 1 /*是否允许使用OSTaskCreate()*/
#define OS_TASK_CREATE_EXT_EN 1 /*是否允许使用OSTaskCreateExt()*/
#define OS_TASK_DEL_EN 1 /*是否允许使用OSTaskDel()*/
#define OS_TASK_SUSPEND_EN 1 /*是否允许使用OSTaskSuspend() and OSTaskResume()*/
#define OS_TASK_QUERY_EN 1 /*是否允许使用OSTaskQuery()*/
#define OS_TIME_DLY_HMSM_EN 1 /*是否允许使用OSTimeDlyHMSM()*/
#define OS_TIME_DLY_RESUME_EN 1 /*是否允许使用OSTimeDlyResume()*/
#define OS_TIME_GET_SET_EN 1 /*是否允许使用OSTimeGet() 和 OSTimeSet()*/
#define OS_SCHED_LOCK_EN 1 /*是否允许使用OSSchedLock()和OSSchedUnlock()*/
#define OS_TICKS_PER_SEC 200 /*设置每秒之内的时钟节拍数目*/
首先,在main()函数中创建一个二值信号量:
mutex=OSSemCreate(1);
然后,在main()函数中创建TaskStart任务。
TaskStart任务
在TaskStart任务中创建并启动所有的应用任务TA0, TA1,TA2。
static void TaskStartCreateTasks (void)
{
INT8U i;
for (i = 0; i
TaskData[i] = i;
} /* Each task will pass its own id */
OSTaskCreate(Task0, (void *)&TaskData[0], &TaskStk[0][TASK_STK_SIZE - 1], 5);
OSTaskCreate(Task1, (void *)&TaskData[1], &TaskStk[1][TASK_STK_SIZE - 1], 6);
OSTaskCreate(Task2, (void *)&TaskData[2], &TaskStk[2][TASK_STK_SIZE - 1], 7);
}
任务TA0的优先级最高,它需要使用信号量mutex:
void Task0 (void *pdata)
{
INT8U err;
INT8U id;
INT16U value;
id=*(int *)pdata;
for (;;)
{
printk("Task %d is waitting a event.\n",id);
OSTimeDly(200); /* Delay 200 clock tick */
printk("The event of Task %d come.\n",id);
printk("Task %d is try to get mutex.\n",id);
OSSemPend(mutex,0,&err); /* Acquire mutex */
switch(err)
{
case OS_NO_ERR:
printk("Task %d has got the mutex.\n",id);
printk("\n");
break;
default:
printk("Task %d is suspended.\n",id);
printk("\n");
}
OSTimeDly(200); /* Delay 200 clock tick */
printk("Task %d release mutex.\n",id);
OSSemPost(mutex); /* Release mutex */
}
}
任务TA1具有中等优先级,它不使用信号量:
void Task1 (void *pdata)
{
INT8U err;
INT8U id;
int i;
id=*(int *)pdata;
for (;;) {
printk("Task %d is waitting a event.\n",id);
OSTimeDly(100); /* Delay 100 clock tick */
printk("The event of Task %d come.\n",id);
OSTimeDly(100);
}
}
任务TA2的优先级最低,和高优先级任务TA0共用信号量mutex:
void Task2 (void *pdata)
{
INT8U err;
INT8U id;
INT16U value;
id=*(int *)pdata;
int i;
for (;;)
{
printk("Task %d is trying to get mutex.\n",id);
OSSemPend(mutex,0,&err); /* Acquire mutex */
switch(err)
{
case OS_NO_ERR:
{
printk("\n");
printk("----------------------------------\n");
printk("Task %d has got the mutex.\n",id);
OSTimeDly(200); /* Delay 100 clock tick */
break;
}
default :
{
printk("Task %d is failed to get mutex.\n",id);
printk("\n");
OSTimeDly(200); /* Delay 100 clock tick */
break;
}
}
printk("Task %d release mutex.\n",id);
printk("\n");
OSSemPost(mutex); /* Release mutex */
}
}
该函数建立并初始化一个信号量,信号量的作用如下:
● 允许一个任务和其他任务或者中断同步
● 取得设备的使用权
● 标志事件的发生
函数原型:OSSemCreate( INT16U value);
参数说明:value 参数是所建立的信号量的初始值,可以取0到65535之间的任何值。
返回值:OSSemCreate()函数返回指向分配给所建立的信号量的控制块的指针。如果没有可用的控制块,OSSemCreate()函数返回空指针。
该函数用于任务试图取得设备的使用权、任务需要和其他任务或中断同步、任务需要等待特定事件的发生的场合。如果任务调用OSSemPend()函数时,信号量的值大于零,OSSemPend()函数递减该值并返回该值。如果调用时信号量值等于零,OSSemPend()函数将任务加入该信号量的等待队列。OSSemPend()函数挂起当前任务直到其他的任务或中断设置信号量或超出等待的预期时间。如果在预期的时钟节拍内信号量被设置,μC/OS-Ⅱ默认让最高优先级的任务取得信号量并回到就绪状态。一个被OSTaskSuspend()函数挂起的任务也可以接受信号量,但这个任务将一直保持挂起状态直到通过调用OSTaskResume()函数恢复该任务的运行。
函数原型:Void OSSemPend ( OS_EVNNT *pevent, INT16U timeout, int8u *err );
参数说明:
pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。(参考OSSemCreate()函数)。
Timeout 允许一个任务在经过了指定数目的时钟节拍后还没有得到需要的信号量时恢复就绪状态。如果该值为零表示任务将持续地等待信号量,最大的等待时间为65535个时钟节拍。这个时间长度并不是非常严格的,可能存在一个时钟节拍的误差。
Err 是指向包含错误码的变量的指针。
返回值:
OSSemPend()函数返回的错误码可能为下述几种:
● OS_NO_ERR :信号量不为零。
● OS_TIMEOUT :信号量没有在指定数目的时钟周期内被设置。
● OS_ERR_PEND_ISR :从中断调用该函数。虽然规定了不允许从中断调用该函数,但μC/OS-Ⅱ仍然包含了检测这种情况的功能。
● OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针。
该函数用于设置指定的信号量。如果指定的信号量是零或大于零,OSSemPost()函数递增该信号量的值并返回。如果有任何任务在等待该信号量,则最高优先级的任务将得到信号量并进入就绪状态。任务调度函数将进行任务调度,决定当前运行的任务是否仍然为最高优先级的就绪任务。
函数原型:INT8U OSSemPost(OS_EVENT *pevent);
参数说明:pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。(参考OSSemCreate()函数)。
返回值:
OSSemPost()函数的返回值为下述之一:
● OS_NO_ERR :信号量被成功地设置
● OS_SEM_OVF :信号量的值溢出
● OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针
该函数用于将一个任务延时若干个时钟节拍。如果延时时间大于0,系统将立即进行任务调度。延时时间的长度可从0到65535个时钟节拍。延时时间0表示不进行延时,函数将立即返回调用者。延时的具体时间依赖于系统每秒钟有多少个时钟节拍(由文件SO_CFG.H中的OS_TICKS_PER_SEC宏来设定)。
函数原型:void OSTimeDly ( INT16U ticks);
参数说明:ticks为要延时的时钟节拍数。
返回值:无
掌握嵌入式实时操作系统µC/OS-II解决优先级反转的策略——优先级继承的原理。
优先级继承的主要思想是:当高优先级任务因申请某共享资源失败被阻塞时,把当前拥有该资源的、且优先级较低的任务的优先级提升,提升的高度等于这个高优先级任务的优先级。在µC/OS-II中,在创建管理共享资源的互斥信号量时,可以指定一个PIP(优先级继承优先级),之后可以把拥有共享资源的任务优先级提升到这个高度。具体过程如下:
1. 当任务A申请共享资源S时,首先判断是否有别的任务正在占用资源S,若无,则任务A获得资源S并继续执行;
2. 如果任务A申请共享资源S时任务B正在使用该资源,则任务A被挂起,等待任务B释放该资源;同时判断任务B的优先级是否低于任务A的,若高于任务A,则维持任务B的优先级不变;
3. 如果任务B的优先级低于任务A的,则提升任务B的优先级到PIP,当任务B释放资源后,再恢复其原来的优先级。
在本实验中设计了处于不同优先级的应用任务,如下图所示:
任务TASK0: 任务TASK1: 任务TASK2 :
图3-1
注意:图中的栅格并不代表严格的时间刻度,而仅仅表现各个任务启动和执行的相对先后关系。
这3个应用任务因为要竞争同一互斥资源mutex而相互制约。其中,任务TASK0的原始优先级最低,任务TASK1的原始优先级中等,任务TASK2的原始优先级最高。在使用mutex时采用优先级继承策略,并指定各任务在使用mutex时的PIP(优先级继承优先级)为8。
TaskStart任务首先运行,由它创建其他3个应用任务:
void TaskStartCreateTasks (void)
{
INT8U i;
for (i = 0; i
TaskData[i] = i; /* Each task will pass its own id */
OSTaskCreate(Task, (void *)&TaskData[i], &TaskStk[i][TASK_STK_SIZE - 1], 12-i);
}
}
每个任务的代码均如下所示:
void Task (void *pdata)
{
INT8U err;
INT8U id;
id=*(int *)pdata;
for (;;)
{
printk("task %d try to get the mutex. \n", id);
OSMutexPend(mutex, 0, &err); /* Acquire mutex to get continue */
printk("task %d is getting the mutex. \n", id);
OSTimeDlyHMSM(0, 0, 0, 200); /* Wait 200 minisecond */
printk("task %d releases the mutex. \n", id);
OSMutexPost(mutex); /* Release mutex */
OSTimeDlyHMSM(0, 0, 0, (3-id)*150); /* Wait (3-id)*150 minisecond */
}
}
TaskStart任务挂起后,操作系统调度它所创建的3个应用任务,整个系统的运行流程如下:
(1)TASK2拥有最高优先级10,最先开始运行,它在t1时刻,执行OSMutexPend(mutex, 0, &err),成功获得互斥信号量mutex;
(2)t2时刻,TASK2执行延时函数OSTimeDlyHMSM(0, 0, 0, 200)被挂起200ms,操作系统调度优先级次之的TASK1运行;
(3)t3时刻,TASK1执行OSMutexPend(mutex, 0, &err)申请互斥信号量失败后被阻塞,操作系统调度优先级最低的TASK0运行。TASK0执行OSMutexPend(mutex, 0, &err)申请互斥信号量失败后也被阻塞。在TASK1和TASK0被阻塞的时候,由于当前拥有mutex的TASK2优先级最高,因此保持其优先级不变。当所有的应用任务都阻塞时,系统有一个空闲任务在运行,这个空闲任务是在初始化操作系统的时候创建的,它的优先级最低;
(4)t4时刻,TASK2延时到并运行,它执行OSMutexPost(mutex)释放互斥信号量并由TASK1获得此信号量。t5时刻TASK2将自己延时一段时间(150ms),任务回到运行状态;
(5)TASK1执行延时函数OSTimeDlyHMSM(0, 0, 0, 200)后被挂起200ms,空闲任务运行;在t6时刻TASK2因延时时间到恢复执行,它执行OSMutexPend(mutex, 0, &err)申请互斥信号量失败后被阻塞,空闲任务运行。此时操作系统发现当前拥有信号量的任务TASK1的优先级低于TASK2的,因此将TASK1的优先级提升到PIP,但TASK1此时仍处于延时状态未运行;
(7)t7时刻,TASK1延时到,它在高优先级(PIP)下继续运行,调用OSMutexPost(mutex)释放互斥信号量并由TASK2获得此信号量,TASK1的优先级恢复到原来的高度,而TASK2因优先级较高而抢占TASK1运行(在t8时刻);
(8)TASK2又将自己延时一段时间(200ms),任务TASK1恢复执行后也将自己延时一段时间(300ms),空闲任务运行;t9时刻TASK2延时时间先到,它执行OSMutexPost(mutex)释放互斥信号量,此时TASK0获得此信号量;
(9)t10时刻,TASK2延时(150ms),任务TASK0被调度运行;TASK0在打印输出信息后,将自己延时(200ms),空闲任务运行;
(10)t11时刻,TASK1延时到恢复运行。TASK1执行OSMutexPend(mutex, 0, &err)申请互斥信号量失败后被阻塞;此时操作系统发现当前拥有信号量的TASK0优先级低于TASK1的,因此提升TASK0的优先级到PIP,空闲任务运行;
(11)t12时刻,TASK2延时到恢复运行,它执行OSMutexPend(mutex, 0, &err)申请互斥信号量失败后被阻塞;此时拥有信号量的TASK0的优先级已经被提升到了PIP,且高于TASK2的优先级,操作系统就没有针对TASK0再做优先级提升的工作。之后空闲任务运行;
(12)t13时刻,TASK0延时到,在高优先级(PIP)下继续运行,它执行OSMutexPost(mutex)释放互斥信号量,其优先级恢复到原来的高度,并由TASK2获得此信号量,TASK2抢占TASK0运行;
系统如此周而复始地运行下去……
修改uC_OS-II/OS_CFG.h:
#define OS_MAX_EVENTS 10 /*最多可以有10个事件*/
#define OS_MAX_FLAGS 5 /*最多可以有5个事件标志*/
#define OS_MAX_MEM_PART 5 /*最多可以划分5个内存块*/
#define OS_MAX_QS 2 /*最多可以使用2个队列*/
#define OS_MAX_TASKS 6 /*最多可以创建6个任务*/
#define OS_LOWEST_PRIO 14 /*任务优先级不可以大于14*/
#define OS_TASK_IDLE_STK_SIZE 1024 /*空闲任务堆栈大小*/
#define OS_TASK_STAT_EN 1 /*是否允许使用统计任务*/
#define OS_TASK_STAT_STK_SIZE 1024 /*统计任务堆栈大小*/
#define OS_FLAG_EN 0 /*是否允许使用事件标志功能*/
#define OS_MBOX_EN 0 /*是否允许使用邮箱功能*/
#define OS_MEM_EN 1 /*是否允许使用内存管理的功能*/
#define OS_MEM_QUERY_EN 1 /*是否允许使用OSMemQuery()*/
#define OS_MUTEX_EN 1 /*是否允许使用互斥信号量的功能*/
#define OS_MUTEX_ACCEPT_EN 1 /*是否允许使用OSMutexAccept()*/
#define OS_MUTEX_DEL_EN 1 /*是否允许使用OSMutexDel()*/
#define OS_MUTEX_QUERY_EN 1 /*是否允许使用OSMutexQuery()*/
#define OS_Q_EN 0 /*是否允许使用队列功能*/
#define OS_SEM_EN 1 /*是否允许使用信号量功能*/
#define OS_SEM_ACCEPT_EN 1 /*是否允许使用OSSemAccept()*/
#define OS_SEM_DEL_EN 1 /*是否允许使用 OSSemDel() */
#define OS_SEM_QUERY_EN 1 /*是否允许使用 OSSemQuery()*/
#define OS_TASK_CHANGE_PRIO_EN 1 /*是否允许使用 OSTaskChangePrio()*/
#define OS_TASK_CREATE_EN 1 /*是否允许使用OSTaskCreate()*/
#define OS_TASK_CREATE_EXT_EN 1 /*是否允许使用OSTaskCreateExt()*/
#define OS_TASK_DEL_EN 1 /*是否允许使用OSTaskDel()*/
#define OS_TASK_SUSPEND_EN 1 /*是否允许使用OSTaskSuspend() and OSTaskResume()*/
#define OS_TASK_QUERY_EN 1 /*是否允许使用OSTaskQuery()*/
#define OS_TIME_DLY_HMSM_EN 1 /*是否允许使用OSTimeDlyHMSM()*/
#define OS_TIME_DLY_RESUME_EN 1 /*是否允许使用OSTimeDlyResume()*/
#define OS_TIME_GET_SET_EN 1 /*是否允许使用OSTimeGet() 和 OSTimeSet()*/
#define OS_SCHED_LOCK_EN 1 /*是否允许使用OSSchedLock()和OSSchedUnlock()*/
#define OS_TICKS_PER_SEC 200 /*设置每秒之内的时钟节拍数目*/
本实验运行后,从虚拟机的窗口中可以看到如下信息:
本实验通过调用OSMutexCreate(8,&err),设置了一个互斥信号量,其中8为PIP(优先级继承优先级)的值。
该函数用于获得互斥信号量,其具体执行过程如下(关键代码解释):
If ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE){}
判断互斥信号量是否被占用
pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; /*Yes, Acquire the resource */
pevent->OSEventCnt |= OSTCBCur->OSTCBPrio; /* Save priority of owning task */
pevent->OSEventPtr = (void *)OSTCBCur; /* Point to owning task's OS_TCB */
如果互斥信号量没有被占有,则获得互斥信号量,任务继续运行。
pip = (INT8U)(pevent->OSEventCnt >> 8); /* No, Get PIP from mutex */
mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* Get priority of mutex owner */
ptcb = (OS_TCB *)(pevent->OSEventPtr); /* Point to TCB of mutex owner */
if (ptcb->OSTCBPrio != pip && mprio > OSTCBCur->OSTCBPrio) { /* Need to promote prio of owner?*/
互斥信号量被占用,但是占有互斥信号量的任务的优先级高于请求互斥信号量的任务的优先级,则不改变占有互斥信号量的任务的优先级。
ptcb->OSTCBPrio = pip; /* Change owner task prio to PIP */
ptcb->OSTCBY = ptcb->OSTCBPrio >> 3;
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = ptcb->OSTCBPrio & 0x07;
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
if (rdy == TRUE) { /* If task was ready at owner's priority ...*/
OSRdyGrp |= ptcb->OSTCBBitY; /* ... make it ready at new priority. */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
OSTCBPrioTbl[pip] = (OS_TCB *)ptcb;
}
占有互斥信号量的任务的优先级低于请求互斥信号量的任务的优先级,改变占有互斥信号量的任务的优先级到PIP。
该函数用于释放互斥信号量,具体代码说明如下:
if (OSTCBCur->OSTCBPrio == pip) { /* Did we have to raise current task's priority? */
判断任务的优先级是否在申请获得互斥信号量的过程中被改变。
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBPrio = prio;
OSTCBCur->OSTCBY = prio >> 3;
OSTCBCur->OSTCBBitY = OSMapTbl[OSTCBCur->OSTCBY];
OSTCBCur->OSTCBX = prio & 0x07;
OSTCBCur->OSTCBBitX = OSMapTbl[OSTCBCur->OSTCBX];
OSRdyGrp |= OSTCBCur->OSTCBBitY;
OSRdyTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;
OSTCBPrioTbl[prio] = (OS_TCB *)OSTCBCur;
}
OSTCBPrioTbl[pip] = (OS_TCB *)1; /* Reserve table entry */
如果任务的优先级在申请获得互斥信量的过程中被改变,则还原其初始优先级。
在LambdaTOOL的调试界面中,在“优先级继承.c”文件中Task任务代码调用函数OSMutexPend()和OSMutexPost()处设置断点。
当程序运行至断点处时,选择“单步跳入”运行模式,可以进入到OS_MUTEX.c文件中,查看OSMutexPend()和OSMutexPost()函数的运行过程,深入了解µC/OS-II操作系统的内核代码。
掌握在基于嵌入式实时操作系统µC/OS-II的应用中,任务使用信号量的一般原理。通过经典的哲学家就餐实验,了解如何利用信号量来对共享资源进行互斥访问。
五个哲学家任务(ph1、ph2、ph3、ph4、ph5)主要有两种过程:思考(即睡眠一段时间)和就餐。每个哲学家任务在就餐前必须申请并获得一左一右两支筷子,就餐完毕后释放这两支筷子。五个哲学家围成一圈,每两人之间有一支筷子。一共有五支筷子,在该实验中用了五个互斥信号量来代表。如图4-1所示:
图4-1
每个任务的代码都一样,如下所示:
INT8U err;
INT8U i;
INT8U j;
i=*(int *)pdata;
j=(i+1) % 5;
for (;;) {
TASK_Thinking_To_Hungry(i); /*首先哲学家处于thinking状态,然后执行TASK_Thinking_To_Hungry()函数进入hungry状态*/
printk("\n");
/*申请信号量,在获得两个信号量后执行TASK_Eat()函数进入eating状态*/
OSSemPend(fork[i], 0, &err);
OSSemPend(fork[j], 0, &err); /* Acquire semaphores to eat */
TASK_Eat(i);
printk("\n");
/*释放信号量*/
OSSemPost(fork[j]);
OSSemPost(fork[i]); /* Release semaphore */
OSTimeDly(200); /* Delay 200 clock tick */
}
修改uC_OS-II/OS_CFG.h:
#define OS_MAX_EVENTS 10 /*最多可以有10个事件*/
#define OS_MAX_FLAGS 5 /*最多可以有5个事件标志*/
#define OS_MAX_MEM_PART 5 /*最多可以划分5个内存块*/
#define OS_MAX_QS 2 /*最多可以使用2个队列*/
#define OS_MAX_TASKS 8 /*最多可以创建8个任务*/
#define OS_LOWEST_PRIO 14 /*任务优先级不可以大于14*/
#define OS_TASK_IDLE_STK_SIZE 1024 /*空闲任务堆栈大小*/
#define OS_TASK_STAT_EN 1 /*是否允许使用统计任务*/
#define OS_TASK_STAT_STK_SIZE 1024 /*统计任务堆栈大小*/
#define OS_FLAG_EN 1 /*是否允许使用事件标志功能*/
#define OS_FLAG_WAIT_CLR_EN 1 /*是否允许等待清除事件标志*/
#define OS_FLAG_ACCEPT_EN 1 /*是否允许使用OSFlagAccept()*/
#define OS_FLAG_DEL_EN 1 /*是否允许使用OSFlagDel()*/
#define OS_FLAG_QUERY_EN 1 /*是否允许使用OSFlagQuery()*/
#define OS_MBOX_EN 0 /*是否允许使用邮箱功能*/
#define OS_MEM_EN 0 /*是否允许使用内存管理的功能*/
#define OS_MUTEX_EN 0 /*是否允许使用互斥信号量的功能*/
#define OS_Q_EN 0 /*是否允许使用队列功能*/
#define OS_SEM_EN 1 /*是否允许使用信号量功能*/
#define OS_SEM_ACCEPT_EN 1 /*是否允许使用OSSemAccept()*/
#define OS_SEM_DEL_EN 1 /*是否允许使用 OSSemDel() */
#define OS_SEM_QUERY_EN 1 /*是否允许使用 OSSemQuery()*/
#define OS_TASK_CHANGE_PRIO_EN 1 /*是否允许使用 OSTaskChangePrio()*/
#define OS_TASK_CREATE_EN 1 /*是否允许使用OSTaskCreate()*/
#define OS_TASK_CREATE_EXT_EN 1 /*是否允许使用OSTaskCreateExt()*/
#define OS_TASK_DEL_EN 1 /*是否允许使用OSTaskDel()*/
#define OS_TASK_SUSPEND_EN 1 /*是否允许使用OSTaskSuspend() and OSTaskResume()*/
#define OS_TASK_QUERY_EN 1 /*是否允许使用OSTaskQuery()*/
#define OS_TIME_DLY_HMSM_EN 1 /*是否允许使用OSTimeDlyHMSM()*/
#define OS_TIME_DLY_RESUME_EN 1 /*是否允许使用OSTimeDlyResume()*/
#define OS_TIME_GET_SET_EN 1 /*是否允许使用OSTimeGet() 和 OSTimeSet()*/
#define OS_SCHED_LOCK_EN 1 /*是否允许使用OSSchedLock()和OSSchedUnlock()*/
#define OS_TICKS_PER_SEC 200 /*设置每秒之内的时钟节拍数目*/
开始,所有的哲学家先处于thinking状态,然后都进入hungry状态:
之后首先获得两个信号量的1、3号哲学家开始eating,待他们释放相关信号量之后,哲学家2、5、4获得所需的信号量并eating:
应用如此这般地循环执行程序下去……
OSSemCreate()
OSSemPend()
OSSemPost()
OSTimeDly()
函数的具体介绍请参考实验“优先级继承”的相关内容。
掌握嵌入式实时操作系统µC/OS中中断的使用情况。
在本实验中,设计了三个任务Task1、Task2、Task3,创建了一个信号量InterruptSem。
整个系统的运行流程如下:
1) 系统初始化,在TaskStart任务中,创建并启动任务Task1、Task2、Task3,优先级分别为12,13,14。
2) 在TaskStart任务中创建一个信号量InterruptSem(初值为1)。
3) 任务在TaskStart任务中挂起自己,操作系统实施调度,进入Task1运行;
4) 任务Task1睡眠100ticks;
5) 任务Task2开始执行, 任务Task2获得信号量InterruptSem;
6) 任务Task2睡眠500tick,任务Task3投入运行,打印输出语句后延时,任务Task1睡眠时间到继续投入运行,它申请信号量InterruptSem失败被阻塞;
7) 任务Task3投入运行,循环地打印输出语句。期间时钟中断不断产生,在中断处理程序中对任务Task2的睡眠时间进行计数;
8) Task2睡眠时间到后恢复运行,并释放信号量InterruptSem;
9) Task1获得信号量InterruptSem后抢占Task2运行;
10) Task1使用完信号量InterruptSem后释放该信号量;
11) 系统从步骤4重复执行,一直运行下去……
程序代码如下:
void Task1 (void *pdata)
{
INT8U err;
pdata=pdata;
for (;;)
{
OSTimeDly(100);
printk("\nTask1is try to get semaphore.\n\n"); /*task1 delay 100 clock ticks */
OSSemPend(InterruptSem, 0, &err); /* Acquire semaphore to get into the room */
printk("Task1 has Succeed to obtain semaphore.\n");
printk("Task1 is delayed.\n\n");
OSTimeDly(200);
printk("\nThe delay of Task1 finished .\n");
printk("Task1 release semaphore.\n");
OSSemPost(InterruptSem); /* Release semaphore */
OSTimeDly(200);
}
}
void Task2 (void *pdata)
{ INT8U err;
pdata=pdata;
for (;;)
{
printk( "\nTask2 is try to get semaphore.\n");
OSSemPend(InterruptSem, 0, &err); /* Acquire semaphore to get into the room */
printk( "Task2 has Succeed to obtain semaphore.\n");
printk("Task2 is delayed.\n\n");
OSTimeDly(500); /*task2 delay 500 clock ticks */
printk("\nThe delay of Task2 finished .\n");
printk("Task2 release semaphore.\n");
OSSemPost(InterruptSem); /* Release semaphore */
OSTimeDly(200);
}
}
void Task3 (void *pdata)
{
pdata=pdata;
for (;;)
{
printk("Task3 has got the CPU:|||||||||||||||||||||||||||||||||||||\n");
OSTimeDly(100);
}
}
修改uC_OS-II/OS_CFG.h:
#define OS_MAX_EVENTS 10 /*最多可以有10个事件*/
#define OS_MAX_FLAGS 5 /*最多可以有5个事件标志*/
#define OS_MAX_MEM_PART 10 /*最多可以划分5个内存块*/
#define OS_MAX_QS 2 /*最多可以使用2个队列*/
#define OS_MAX_TASKS 9 /*最多可以创建9个任务*/
#define OS_LOWEST_PRIO 24 /*任务优先级不可以大于24*/
#define OS_TASK_IDLE_STK_SIZE 1024 /*空闲任务堆栈大小*/
#define OS_TASK_STAT_EN 1 /*是否允许使用统计任务*/
#define OS_TASK_STAT_STK_SIZE 1024 /*统计任务堆栈大小*/
#define OS_FLAG_EN 0 /*是否允许使用事件标志功能*/
#define OS_MBOX_EN 1 /*是否允许使用邮箱功能*/
#define OS_MBOX_ACCEPT_EN 1 /*是否允许使用 OSMboxAccept() */
#define OS_MBOX_DEL_EN 1 /*是否允许使用 OSMboxDel()*/
#define OS_MBOX_POST_EN 1 /*是否允许使用OSMboxPost()*/
#define OS_MBOX_POST_OPT_EN 1 /*是否允许使用OSMboxPostOpt() */
#define OS_MBOX_QUERY_EN 1 /*是否允许使用OSMboxQuery()*/
#define OS_MEM_EN 1 /*是否允许使用内存管理的功能*/
#define OS_MEM_QUERY_EN 1 /*是否允许使用OSMemQuery()*/
#define OS_MUTEX_EN 1 /*是否允许使用互斥信号量的功能*/
#define OS_MUTEX_ACCEPT_EN 1 /*是否允许使用OSMutexAccept()*/
#define OS_MUTEX_DEL_EN 1 /*是否允许使用OSMutexDel()*/
#define OS_MUTEX_QUERY_EN 1 /*是否允许使用OSMutexQuery()*/
#define OS_Q_EN 1 /*是否允许使用队列功能*/
#define OS_Q_ACCEPT_EN 1 /*是否允许使用OSQAccept()*/
#define OS_Q_DEL_EN 1 /*是否允许使用OSQDel()*/
#define OS_Q_FLUSH_EN 1 /*是否允许使用 OSQFlush()*/
#define OS_Q_POST_EN 1 /*是否允许使用 OSQPost()*/
#define OS_Q_POST_FRONT_EN 1 /*是否允许使用OSQPostFront()*/
#define OS_Q_POST_OPT_EN 1 /*是否允许使用OSQPostOpt()*/
#define OS_Q_QUERY_EN 1 /*是否允许使用OSQQuery()*/
#define OS_SEM_EN 1 /*是否允许使用信号量功能*/
#define OS_SEM_ACCEPT_EN 1 /*是否允许使用OSSemAccept()*/
#define OS_SEM_DEL_EN 1 /*是否允许使用 OSSemDel() */
#define OS_SEM_QUERY_EN 1 /*是否允许使用 OSSemQuery()*/
#define OS_TASK_CHANGE_PRIO_EN 1 /*是否允许使用 OSTaskChangePrio()*/
#define OS_TASK_CREATE_EN 1 /*是否允许使用OSTaskCreate()*/
#define OS_TASK_CREATE_EXT_EN 1 /*是否允许使用OSTaskCreateExt()*/
#define OS_TASK_DEL_EN 1 /*是否允许使用OSTaskDel()*/
#define OS_TASK_SUSPEND_EN 1 /*是否允许使用OSTaskSuspend() and OSTaskResume()*/
#define OS_TASK_QUERY_EN 1 /*是否允许使用OSTaskQuery()*/
#define OS_TIME_DLY_HMSM_EN 0 /*是否允许使用OSTimeDlyHMSM()*/
#define OS_TIME_DLY_RESUME_EN 1 /*是否允许使用OSTimeDlyResume()*/
#define OS_TIME_GET_SET_EN 1 /*是否允许使用OSTimeGet() 和 OSTimeSet()*/
#define OS_SCHED_LOCK_EN 1 /*是否允许使用OSSchedLock()和OSSchedUnlock()*/
#define OS_TICKS_PER_SEC 200 /*设置每秒之内的时钟节拍数目*/
OSSemCreate()
OSSemPend()
OSSemPost()
OSTimeDly()
函数的具体介绍请参考实验“优先级继承”的相关内容。
附件:BC3.1 安装指南
Borland C/C++ 3.1 是 Borland 公司于二十世纪九十年代初推出的优秀 x86 平台 C/C++ 开发工具,它不仅拥有功能强大的集成开发调试环境 IDE,还提供了通过串口即可实现远程 磁盘管理的工具 TDRF 以及远程调试工具 TD(Turbo Debugger),。选用 Borland C++3.1 这个优秀的开发平台,可以实现高效率、高质量的软件设计和开发。
下面介绍 Borland C++3.1 这个软件的安装过程。
1、客户可将所获得的软件工具光盘插入光盘驱动器,然后在 DOS 状态下进入工具光 盘所在的光盘驱动器,在本文的示例图片中,该光盘驱动器为计算机的 G 盘,如图 2-1。(或是在硬盘的Borland c++3.1的安装文件中找到INSTALL.EXE安装程序,运行它即可.)
图 2-1 在 DOS 状态下进入 BC 安装目录
2、工具光盘根目录下的 BC 子目录即为 Borland C++ 3.1 的安装目录。用 CD 命令进 入该目录后,输入 INSTALL 命令并回车,Borland C++3.1 即开始安装。
图 2-2 BC3.1 安装启动界面
从图 2-2 可见 Borland C++3.1 安装完毕需要 49M 的硬盘空间,另在安装过程中需要额 外的 5M 临时硬盘空间。继续回车进入下一步安装。
3、安装程序将询问您的安装光盘处于哪一个驱动器(如图 2-3),本安装示例中为 G 盘, 因此输入 G。客户则输入自己光驱所在的驱动器盘符。而后回车进入下一步。
图 2-3 BC3.1 安装界面
4、安装程序将询问 Borland C++3.1 安装程序位于哪一个子目录(如图 2-4),在客户 所获得的安装光盘中,该目录名称为\BC。设置完毕后,回车继续安装。
图 2-4 BC3.1 安装界面
5、进入设置目标目录(如图 2-5),Borland C++3.1 的安装缺省目录是当前驱动器下的 BORLANDC 目录,但我们一般不推荐采用这个目录名,我们推荐将 Borland C++3.1 安装 到系统的 C 盘下,采用 BC 作为目录名。先选中图 2-5 中的 Directories…这个选项。
图 2-5 BC3.1 安装界面
6、回车后即进入如图 2-6 所示的详细选单。在此我们选中 Borland Dir 这个选项。
图 2-6 BC3.1 目录选择界面
7、回车后,将弹出一个对话框(如图 2-7 所示)。在此对话框内填入 C:\BC。
图 2-7 设置 BC3.1 目录
8、再回车,所有的目录选单都发生了改变(如图 2-8 所示),系统目标目录被定向到
C:\BC。
图 2-8 修改后的 BC3.1 目录
9、按 ESC 键将退回到如图 2-9 所示的选单中。
图 2-9 修改后的 BC3.1 选项
10、按键盘的↑↓键选中 Start Installation 这一项。
图 2-10 选择 Start Installation
11、回车启动如图 2-11 所示的安装过程,文件将进行复制和安装,这个过程将持续几 十秒钟。
图 2-11 BC3.1 解压安装工程
12、文件复制完成,安装程序会出现如图 2-12 所示的提示框。
图 2-12 BC3.1 解压安装完毕
13、再按键盘上的任意键后,安装程序将显示 Borland C++3.1 的 README 文件,如 图 2-13 所示。至此,Borland C++3.1 的安装复制工作全部完成。再按 ESC 键,将完成 Borland C++3.1 的安装,并退回到 DOS 的命令行状态下。
图 2-13 BC31 的 README
本文来源:https://www.2haoxitong.net/k/doc/2e68580e804d2b160b4ec097.html
文档为doc格式