UCOS操作系统
一、创建任务
UCOSIII 是多任务系统,那么肯定要能创建任务,创建任务就是将任务控制块、任务堆栈、任务代码等联系在一起,并且初始化任务控制块的相应字段。在 UCOSIII 中我们通过函数OSTaskCreate()来创建任务,OSTaskCreate()函数原型如下(在 os_task.c 中有定义)。调用OSTaskCreat()创建一个任务以后,刚创建的任务就会进入就绪态,注意!不能在中断服务程序中调用 OSTaskCreat()函数来创建任务。
void OSTaskCreate (OS_TCB *p_tcb,
CPU_CHAR *p_name,
OS_TASK_PTR p_task,
void *p_arg,
OS_PRIO prio,
CPU_STK *p_stk_base,
CPU_STK_SIZE stk_limit,
CPU_STK_SIZE stk_size,
OS_MSG_QTY q_size,
OS_TICK time_quanta,
void *p_ext,
OS_OPT opt,
OS_ERR *p_err)
*p_tcb: 指向任务的任务控制块 OS_TCB。
*p_name: 指向任务的名字,我们可以给每个任务取一个名字
p_task: 执行任务代码,也就是任务函数名字
*p_arg: 传递给任务的参数
prio: 任务优先级,数值越低优先级越高,用户不能使用系统任务使用的那些优先级!
*p_stk_base:指向任务堆栈的基地址。
stk_limit: 任务堆栈的堆栈深度,用来检测和确保堆栈不溢出。
stk_size: 任务堆栈大小
q_size: UCOSIII 中每个任务都有一个可选的内部消息队列,我们要定义宏
OS_CFG_TASK_Q_EN>0,这是才会使用这个内部消息队列。
time_quanta:在使能时间片轮转调度时用来设置任务的时间片长度,默认值为时钟节拍除以
10。
*p_ext: 指向用户补充的存储区。
opt: 包含任务的特定选项,有如下选项可以设置。
OS_OPT_TASK_NONE 表示没有任何选项
OS_OPT_TASK_STK_CHK 指定是否允许检测该任务的堆栈
OS_OPT_TASK_STK_CLR 指定是否清除该任务的堆栈
OS_OPT_TASK_SAVE_FP 指定是否存储浮点寄存器,CPU 需要有浮点
运算硬件并且有专用代码保存浮点寄存器。
*p_err: 用来保存调用该函数后返回的错误码。
相应的参数可以看这个图,这里第一个参数是任务控制块要与RTOS相区分
OS_CRITICAL_ENTER(); //½øÈëÁÙ½çÇø
//´´½¨¿ªÊ¼ÈÎÎñ
OSTaskCreate((OS_TCB * )&StartTaskTCB, //ÈÎÎñ¿ØÖÆ¿é
(CPU_CHAR * )"start task", //ÈÎÎñÃû×Ö
(OS_TASK_PTR )start_task, //ÈÎÎñº¯Êý
(void * )0, //´«µÝ¸øÈÎÎñº¯ÊýµÄ²ÎÊý
(OS_PRIO )START_TASK_PRIO, //ÈÎÎñÓÅÏȼ¶
(CPU_STK * )&START_TASK_STK[0], //ÈÎÎñ¶ÑÕ»»ùµØÖ·
(CPU_STK_SIZE)START_STK_SIZE/10, //ÈÎÎñ¶ÑÕ»Éî¶ÈÏÞλ
(CPU_STK_SIZE)START_STK_SIZE, //ÈÎÎñ¶ÑÕ»´óС
(OS_MSG_QTY )0, //ÈÎÎñÄÚ²¿ÏûÏ¢¶ÓÁÐÄܹ»½ÓÊÕµÄ×î´óÏûÏ¢ÊýÄ¿,Ϊ0ʱ½ûÖ¹½ÓÊÕÏûÏ¢
(OS_TICK )0, //µ±Ê¹ÄÜʱ¼äƬÂÖתʱµÄʱ¼äƬ³¤¶È£¬Îª0ʱΪĬÈϳ¤¶È£¬
(void * )0, //Óû§²¹³äµÄ´æ´¢Çø
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //ÈÎÎñÑ¡Ïî
(OS_ERR * )&err); //´æ·Å¸Ãº¯Êý´íÎóʱµÄ·µ»ØÖµ
OS_CRITICAL_EXIT(); //Í˳öÁÙ½çÇø
err是这个函数返回的错误码,在我们调试的时候是很方便的。
二、删除任务
OSTaskDel()函数用来删除任务,当一个任务不需要运行的话,我们就可以将其删除掉,删
除任务不是说删除任务代码,而是 UCOSIII 不再管理这个任务,在有些应用中我们只需要某个
任务只运行一次,运行完成后就将其删除掉,比如外设初始化任务,OSTaskDel()函数原型如下:
void OSTaskDel (OS_TCB *p_tcb,
OS_ERR *p_err)
ucos中的任务控制块相当于RTOS中的任务句柄
*p_tcb: 指向要删除的任务 TCB,也可以传递一个 NULL 指针来删除调用 OSTaskDel()函数的任务自身。
*p_err: 指向一个变量用来保存调用 OSTaskDel()函数后返回的错误码。
1、删除某个任务以后,它占用的OS_TCB和堆栈就可以再次利用来创建其他的任务。
2、尽管UCOSIII允许在系统运行中删除任务,但是应该尽量避免这种操作,如果这个任务可能占有与其他任务共享的资源,在删除此任务之前这个被占有的资源没有被释放就有可能导致奇怪的结果。
三、完整代码
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "includes.h"
//ÈÎÎñÓÅÏȼ¶
#define START_TASK_PRIO 3
//ÈÎÎñ¶ÑÕ»´óС
#define START_STK_SIZE 128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB StartTaskTCB;
//ÈÎÎñ¶ÑÕ»
CPU_STK START_TASK_STK[START_STK_SIZE];
//ÈÎÎñº¯Êý
void start_task(void *p_arg);
//ÈÎÎñÓÅÏȼ¶
#define TASK1_TASK_PRIO 4
//ÈÎÎñ¶ÑÕ»´óС
#define TASK1_STK_SIZE 128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB Task1_TaskTCB;
//ÈÎÎñ¶ÑÕ»
CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
void task1_task(void *p_arg);
//ÈÎÎñÓÅÏȼ¶
#define TASK2_TASK_PRIO 5
//ÈÎÎñ¶ÑÕ»´óС
#define TASK2_STK_SIZE 128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB Task2_TaskTCB;
//ÈÎÎñ¶ÑÕ»
CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE];
//ÈÎÎñº¯Êý
void task2_task(void *p_arg);
//LCDË¢ÆÁʱʹÓõÄÑÕÉ«
int lcd_discolor[14]={
WHITE, BLACK, BLUE, BRED,
GRED, GBLUE, RED, MAGENTA,
GREEN, CYAN, YELLOW,BROWN,
BRRED, GRAY };
//Ö÷º¯Êý
int main(void)
{
OS_ERR err;
CPU_SR_ALLOC();
delay_init(); //ʱÖÓ³õʼ»¯
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÖжϷÖ×éÅäÖÃ
uart_init(115200); //´®¿Ú³õʼ»¯
LED_Init(); //LED³õʼ»¯
LCD_Init(); //LCD³õʼ»¯
POINT_COLOR = RED;
OSInit(&err); //³õʼ»¯UCOSIII 初始化ucos
OS_CRITICAL_ENTER(); //½øÈëÁÙ½çÇø
//´´½¨¿ªÊ¼ÈÎÎñ
OSTaskCreate((OS_TCB * )&StartTaskTCB, //ÈÎÎñ¿ØÖÆ¿é
(CPU_CHAR * )"start task", //ÈÎÎñÃû×Ö
(OS_TASK_PTR )start_task, //ÈÎÎñº¯Êý
(void * )0, //´«µÝ¸øÈÎÎñº¯ÊýµÄ²ÎÊý
(OS_PRIO )START_TASK_PRIO, //ÈÎÎñÓÅÏȼ¶
(CPU_STK * )&START_TASK_STK[0], //ÈÎÎñ¶ÑÕ»»ùµØÖ·
(CPU_STK_SIZE)START_STK_SIZE/10, //ÈÎÎñ¶ÑÕ»Éî¶ÈÏÞλ
(CPU_STK_SIZE)START_STK_SIZE, //ÈÎÎñ¶ÑÕ»´óС
(OS_MSG_QTY )0, //ÈÎÎñÄÚ²¿ÏûÏ¢¶ÓÁÐÄܹ»½ÓÊÕµÄ×î´óÏûÏ¢ÊýÄ¿,Ϊ0ʱ½ûÖ¹½ÓÊÕÏûÏ¢
(OS_TICK )0, //µ±Ê¹ÄÜʱ¼äƬÂÖתʱµÄʱ¼äƬ³¤¶È£¬Îª0ʱΪĬÈϳ¤¶È£¬
(void * )0, //Óû§²¹³äµÄ´æ´¢Çø
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //ÈÎÎñÑ¡Ïî
(OS_ERR * )&err); //´æ·Å¸Ãº¯Êý´íÎóʱµÄ·µ»ØÖµ
OS_CRITICAL_EXIT(); //Í˳öÁÙ½çÇø
OSStart(&err); //¿ªÆôUCOSIII
}
//¿ªÊ¼ÈÎÎñÈÎÎñº¯Êý
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //ͳ¼ÆÈÎÎñ
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //Èç¹ûʹÄÜÁ˲âÁ¿ÖжϹرÕʱ¼ä
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //µ±Ê¹ÓÃʱ¼äƬÂÖתµÄʱºò
//ʹÄÜʱ¼äƬÂÖתµ÷¶È¹¦ÄÜ,ʱ¼äƬ³¤¶ÈΪ1¸öϵͳʱÖÓ½ÚÅÄ£¬¼È1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //½øÈëÁÙ½çÇø
//´´½¨TASK1ÈÎÎñ
OSTaskCreate((OS_TCB * )&Task1_TaskTCB,
(CPU_CHAR * )"Task1 task",
(OS_TASK_PTR )task1_task,
(void * )0,
(OS_PRIO )TASK1_TASK_PRIO,
(CPU_STK * )&TASK1_TASK_STK[0],
(CPU_STK_SIZE)TASK1_STK_SIZE/10,
(CPU_STK_SIZE)TASK1_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
//´´½¨TASK2ÈÎÎñ
OSTaskCreate((OS_TCB * )&Task2_TaskTCB,
(CPU_CHAR * )"task2 task",
(OS_TASK_PTR )task2_task,
(void * )0,
(OS_PRIO )TASK2_TASK_PRIO,
(CPU_STK * )&TASK2_TASK_STK[0],
(CPU_STK_SIZE)TASK2_STK_SIZE/10,
(CPU_STK_SIZE)TASK2_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
OS_CRITICAL_EXIT(); //Í˳öÁÙ½çÇø
OSTaskDel((OS_TCB*)0,&err); //ɾ³ýstart_taskÈÎÎñ×ÔÉí
}
//task1ÈÎÎñº¯Êý
void task1_task(void *p_arg)
{
u8 task1_num=0;
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
POINT_COLOR = BLACK;
OS_CRITICAL_ENTER();
LCD_DrawRectangle(5,110,115,314); //»Ò»¸ö¾ØÐÎ
LCD_DrawLine(5,130,115,130); //»Ïß
POINT_COLOR = BLUE;
LCD_ShowString(6,111,110,16,16,"Task1 Run:000");
OS_CRITICAL_EXIT();
while(1)
{
task1_num++; //ÈÎÎñÖ´1ÐдÎÊý¼Ó1 ×¢Òâtask1_num1¼Óµ½255µÄʱºò»áÇåÁ㣡£¡
LED0= ~LED0;
printf("ÈÎÎñ1ÒѾִÐУº%d´Î\r\n",task1_num);
if(task1_num==5)
{
OSTaskDel((OS_TCB*)&Task2_TaskTCB,&err); //ÈÎÎñ1Ö´ÐÐ5´Ëºóɾ³ýµôÈÎÎñ2
printf("ÈÎÎñ1ɾ³ýÁËÈÎÎñ2!\r\n");
}
LCD_Fill(6,131,114,313,lcd_discolor[task1_num%14]); //Ìî³äÇøÓò
LCD_ShowxNum(86,111,task1_num,3,16,0x80); //ÏÔʾÈÎÎñÖ´ÐдÎÊý
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //ÑÓʱ1s
}
}
//task2ÈÎÎñº¯Êý
void task2_task(void *p_arg)
{
u8 task2_num=0;
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
POINT_COLOR = BLACK;
OS_CRITICAL_ENTER();
LCD_DrawRectangle(125,110,234,314); //»Ò»¸ö¾ØÐÎ
LCD_DrawLine(125,130,234,130); //»Ïß
POINT_COLOR = BLUE;
LCD_ShowString(126,111,110,16,16,"Task2 Run:000");
OS_CRITICAL_EXIT();
while(1)
{
task2_num++; //ÈÎÎñ2Ö´ÐдÎÊý¼Ó1 ×¢Òâtask1_num2¼Óµ½255µÄʱºò»áÇåÁ㣡£¡
LED1=~LED1;
printf("ÈÎÎñ2ÒѾִÐУº%d´Î\r\n",task2_num);
LCD_ShowxNum(206,111,task2_num,3,16,0x80); //ÏÔʾÈÎÎñÖ´ÐдÎÊý
LCD_Fill(126,131,233,313,lcd_discolor[13-task2_num%14]); //Ìî³äÇøÓò
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //ÑÓʱ1s
}
}
开始任务 start_task 只是用来创建任务 task1_task 和 task2_task,那么这个任务肯定只需要执行一次,两个任务创建完成以后就可以删除掉 start_task 任务了,这里我们使用 OSTaskDel()
函数删除掉任务自身,这里传递给 OSTaskDel()函数参数 p_tcb 的值为 0,表示删除掉任务自身。根据要求我们在任务 1 执行 5 次后由任务 1 删除掉任务 2,这里通过调用 OSTaskDel()函数删除掉任务 2,注意这时我们传递给 OSTaskDel()中参数 p_tcb 的值为任务 2 的任务控制块Task2_TaskTCB 的地址,因此这里我们用了取址符号“&”。
调用函数 OSTimeDlyHMSM()延时 1s,调用 OSTimeDlyHMSM()函数以后就会发起一个任务切换。
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err)延时函数第一个代表小时,然后是分钟,然后是秒钟,再然后是毫秒,这里延迟1秒,写在秒钟的位置,延时的目的是为了任务切换。这个函数内置任务切换的函数。