任务是竞争系统资源的最小运行单元,每个任务都会去竞争一个属于自己的内存空间独立运行,享有独立栈空间,系统默认支持32个优先级,数值越小优先级越低。优先级一样的多个任务以时间片切换的方式共享CPU,
基于优先级的抢占式调度器,主要职责是找到处于就绪态的最高优先级任务,切入切出任务,同时保存任务上下文内容。高优先级的任务可打断低优先级的任务,低优先级必须在高优先级任务阻塞或结束后才继续调度。
就绪态:该状态任务处于就绪列表,等待调度器调用,新创建的任务会被初始化为该状态
运行态:表明任务正在执行,调度器永远选择处于最高优先级的就绪态任务运行
阻塞态:任务等待某个时序或外部中断,不处于就绪列表,无法得到调度器调用
退出态:任务运行结束,等待系统回收资源
创建任务—>就绪态:创建完毕立即进入就绪态,等待调度
就绪态—>运行态:任务切换时,最高优先级任务先被执行,该任务进入运行态,但仍处于就绪列表中
运行态—>就绪态:创建了更高优先级任务后,原先在就绪列表中运行的任务由运行态变为就绪态,此刻仍处于就绪列表,等待最高优先级任务执行完毕后继续执行
运行态—>阻塞态:正在运行的任务阻塞,该任务从就绪列表删除,任务变为阻塞态,发生任务切换,系统运行就绪列表中最高优先级的任务
阻塞态—>就绪态(运行态):阻塞的任务被恢复后,该任务加入就绪列表,变为就绪态,如果该任务优先级比未加入前正在就绪列表中运行的最高优先级任务的优先级高,则进入运行态
就绪态—>阻塞态:就绪时就被阻塞,从列表删除,直到任务被恢复为就绪态
运行态、阻塞态—>退出态:调用删除任务函数,无论哪一种状态的任务都会为退出态
介绍的任务函数全都是默认给出来的,不需要我们编译,只需要了解其是怎么使用的就可以了
以下为任务创建函数的源代码,调用任务创建函数前要提前定义好任务ID变量
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreate(UINT32 *puwTaskID, TSK_INIT_PARAM_S *pstInitParam)
{
UINT32 uwRet = LOS_OK;
UINTPTR uvIntSave;
//(1)定义新建任务的任务控制块结构体指针,用于保存新建任务的任务信息
LOS_TASK_CB *pstTaskCB;
//(2)调用下面的函数,创建并阻塞任务,第一个形参为用户定义的任务ID变量地址,第二个形参为任务配置
//这两个参数与传入这个总函数的参数一样,第二个形参的配置在调用函数前由用户定义的(见上一篇文章)
uwRet = LOS_TaskCreateOnly(puwTaskID, pstInitParam);
if (LOS_OK != uwRet)
{
return uwRet;
}
//(3)通过任务ID获取任务控制块信息
pstTaskCB = OS_TCB_FROM_TID(*puwTaskID);
uvIntSave = LOS_IntLock();
pstTaskCB->usTaskStatus &= (~OS_TASK_STATUS_SUSPEND);
//(4)将新建的任务从阻塞态解除,设置为就绪态
pstTaskCB->usTaskStatus |= OS_TASK_STATUS_READY;
#if (LOSCFG_BASE_CORE_CPUP == YES)
g_pstCpup[pstTaskCB->uwTaskID].uwID = pstTaskCB->uwTaskID;
g_pstCpup[pstTaskCB->uwTaskID].usStatus = pstTaskCB->usTaskStatus;
#endif
//(5)获取新建任务的优先级,并将任务按照优先级顺序由高到低插入任务就绪列表
osPriqueueEnqueue(&pstTaskCB->stPendList, pstTaskCB->usPriority);
g_stLosTask.pstNewTask = LOS_DL_LIST_ENTRY(osPriqueueTop(), LOS_TASK_CB, stPendList); /*lint !e413*/
if ((g_bTaskScheduled) && (g_usLosTaskLock == 0))
{
//(6)↑如果开启了任务调度,并且调度器没有闭锁,则进行第二次判断。
//如果新创建的任务优先级比当前任务的优先级更高↓,则在进行一次任务调度,否则返回任务创建成功即下面的(8)
if (g_stLosTask.pstRunTask != g_stLosTask.pstNewTask)
{
if (LOS_CHECK_SCHEDULE)
{
(VOID)LOS_IntRestore(uvIntSave);
//(7)如果满足了6的条件,进行任务调度
osSchedule();
return LOS_OK;
}
}
}
(VOID)LOS_IntRestore(uvIntSave);
//(8)返回值
return LOS_OK;
}
任务删除后,LiteOS会回收任务的相关资源,源代码如下
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskDelete(UINT32 uwTaskID)
{
UINTPTR uvIntSave;
LOS_TASK_CB *pstTaskCB;
UINT16 usTempStatus;
UINT32 uwErrRet = OS_ERROR;
CHECK_TASKID(uwTaskID);
uvIntSave = LOS_IntLock();
pstTaskCB = OS_TCB_FROM_TID(uwTaskID);
usTempStatus = pstTaskCB->usTaskStatus;
//(1)如果要删除的任务状态是OS_TASK_STATUS_UNUSED,表示任务还没创建,无法删除,返回错误代码
if (OS_TASK_STATUS_UNUSED & usTempStatus)
{
uwErrRet = LOS_ERRNO_TSK_NOT_CREATED;
OS_GOTO_ERREND();
}
//(2)如果任务正在运行且调度器被闭锁,系统会将任务解锁,g_usLosTaskLock设为0,并进行删除
if ((OS_TASK_STATUS_RUNNING & usTempStatus) && (g_usLosTaskLock != 0))
{
PRINT_INFO("In case of task lock, task deletion is not recommended\n");
g_usLosTaskLock = 0;
}
//(3)如果任务处于就绪态,将任务从就绪列表删除,取消任务的继续态
if (OS_TASK_STATUS_READY & usTempStatus)
{
osPriqueueDequeue(&pstTaskCB->stPendList);
pstTaskCB->usTaskStatus &= (~OS_TASK_STATUS_READY);
}//(4)如果处于阻塞态或在队列中被阻塞,将任务从阻塞列表中删除
else if ((OS_TASK_STATUS_PEND & usTempStatus) || (OS_TASK_STATUS_PEND_QUEUE & usTempStatus))
{
LOS_ListDelete(&pstTaskCB->stPendList);
}
if ((OS_TASK_STATUS_DELAY | OS_TASK_STATUS_TIMEOUT) & usTempStatus)
{
//(5)如果要删除的任务处于延时状态或等待信号量/事件等阻塞超时状态,将任务从延时列表删除
osTimerListDelete(pstTaskCB);
}
pstTaskCB->usTaskStatus &= (~(OS_TASK_STATUS_SUSPEND));
pstTaskCB->usTaskStatus |= OS_TASK_STATUS_UNUSED;
pstTaskCB->uwEvent.uwEventID = 0xFFFFFFFF;
pstTaskCB->uwEventMask = 0;
#if (LOSCFG_BASE_CORE_CPUP == YES)
(VOID)memset((VOID *)&g_pstCpup[pstTaskCB->uwTaskID], 0, sizeof(OS_CPUP_S));
#endif
//(6)系统重新在就绪列表中寻找最高优先级的任务执行,因为如果把下一个要切换的任务删了,删后系统无法正常进行任务切换
g_stLosTask.pstNewTask = LOS_DL_LIST_ENTRY(osPriqueueTop(), LOS_TASK_CB, stPendList); /*lint !e413*/
//(7)如果任务正在运行,不能立即删除(删后要调度新任务,调度过程需要旧任务参与)
//将任务添加到系统回收列表g_stTskRecyleList,创建任务时对回收列表任务进行回收
if (OS_TASK_STATUS_RUNNING & pstTaskCB->usTaskStatus)
{
LOS_ListTailInsert(&g_stTskRecyleList, &pstTaskCB->stPendList);
g_stLosTask.pstRunTask = &g_pstTaskCBArray[g_uwTskMaxNum];
g_stLosTask.pstRunTask->uwTaskID = uwTaskID;
g_stLosTask.pstRunTask->usTaskStatus = pstTaskCB->usTaskStatus;
g_stLosTask.pstRunTask->uwTopOfStack = pstTaskCB->uwTopOfStack;
g_stLosTask.pstRunTask->pcTaskName = pstTaskCB->pcTaskName;
pstTaskCB->usTaskStatus = OS_TASK_STATUS_UNUSED;
(VOID)LOS_IntRestore(uvIntSave);
osSchedule();
return LOS_OK;
}
else
{
//(8)如果被删除的任务不是当前任务,将任务状态变为未使用状态
pstTaskCB->usTaskStatus = OS_TASK_STATUS_UNUSED;
//(9)将任务控制块插入系统可用任务链表,就是把这个删除的任务的任务控制块返还给可用链表
//系统可用的任务控制块是有限的,系统初始化时已经分配好了,删完后将控制块返还
LOS_ListAdd(&g_stLosFreeTask, &pstTaskCB->stPendList);
//(10)任务控制块的内存释放,进行回收利用
(VOID)LOS_MemFree(m_aucSysMem0, (VOID *)pstTaskCB->uwTopOfStack);
//(11)将任务的栈顶指针指向NULL
pstTaskCB->uwTopOfStack = (UINT32)NULL;
}
//(12)删除成功则返回LOS_OK,否则返回错误代码
(VOID)LOS_IntRestore(uvIntSave);
return LOS_OK;
LOS_ERREND:
(VOID)LOS_IntRestore(uvIntSave);
//(13)删除成功则返回LOS_OK,否则返回错误代码
return uwErrRet;
}
调用该函数后任务就进入阻塞态了,放弃CPU使用权,其它优先级较低的任务就可以获得CPU使用权。每个任务的循环中必须要有阻塞的出现,否则比该任务优先级低的任务永远无法获得CPU使用权
LITE_OS_SEC_TEXT UINT32 LOS_TaskDelay(UINT32 uwTick)
{
UINTPTR uvIntSave;
if (OS_INT_ACTIVE)
{
//(1)如果在中断中进行延时,非法,返回错误代码,LiteOS不允许在中断中调用延时函数
return LOS_ERRNO_TSK_DELAY_IN_INT;
}
if (g_usLosTaskLock != 0)
{
//(2)如果在调度器被锁时进行延时,也是非法,因为延时操作依赖于调度器
return LOS_ERRNO_TSK_DELAY_IN_LOCK;
}
if (uwTick == 0)
{
//(3)进行0个Tick的延时,当前任务主动放弃CPU使用权,进行一次强制切换任务操作
return LOS_TaskYield();
}
else
{
uvIntSave = LOS_IntLock();
//(4)如果可以进行延时,将调用延时的任务从就绪列表中删除,任务就绪态解除,
//再将该任务添加到延时链表,并将任务变为阻塞态,延时时间到达后,任务变为就绪态
osPriqueueDequeue(&(g_stLosTask.pstRunTask->stPendList));
g_stLosTask.pstRunTask->usTaskStatus &= (~OS_TASK_STATUS_READY);
osTaskAdd2TimerList((LOS_TASK_CB *)g_stLosTask.pstRunTask, uwTick);
g_stLosTask.pstRunTask->usTaskStatus |= OS_TASK_STATUS_DELAY;
(VOID)LOS_IntRestore(uvIntSave);
//(5)进行任务切换
LOS_Schedule();
}
return LOS_OK;
}
即暂停任务,不管任务什么优先级,只要被指定挂起了,都不会得到CPU使用权,即使多次挂起同一个任务,只要调用一次任务恢复函数即可解除挂起状态
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskSuspend(UINT32 uwTaskID)
{
UINTPTR uvIntSave;
LOS_TASK_CB *pstTaskCB;
UINT16 usTempStatus;
UINT32 uwErrRet = OS_ERROR;
//(1)根据任务ID获取任务控制块
CHECK_TASKID(uwTaskID);
pstTaskCB = OS_TCB_FROM_TID(uwTaskID);
uvIntSave = LOS_IntLock();
usTempStatus = pstTaskCB->usTaskStatus;
//(2)如果任务处于未使用状态,返回错误代码
if (OS_TASK_STATUS_UNUSED & usTempStatus)
{
uwErrRet = LOS_ERRNO_TSK_NOT_CREATED;
OS_GOTO_ERREND();
}
//(3)如果任务已经被挂起了,无法继续挂起,返回错误
if (OS_TASK_STATUS_SUSPEND & usTempStatus)
{
uwErrRet = LOS_ERRNO_TSK_ALREADY_SUSPENDED;
OS_GOTO_ERREND();
}
//(4)如果任务正在运行并且调度器被闭锁,无法挂起,返回错误
if ((OS_TASK_STATUS_RUNNING & usTempStatus) && (g_usLosTaskLock != 0))
{
uwErrRet = LOS_ERRNO_TSK_SUSPEND_LOCKED;
OS_GOTO_ERREND();
}
//(5)如果任务处于就绪态,可以挂起
if (OS_TASK_STATUS_READY & usTempStatus)
{
//(6)将任务从就绪列表删除
osPriqueueDequeue(&pstTaskCB->stPendList);
//(7)将任务从就绪态解除
pstTaskCB->usTaskStatus &= (~OS_TASK_STATUS_READY);
}
//(8)将任务的状态变为挂起态
pstTaskCB->usTaskStatus |= OS_TASK_STATUS_SUSPEND;
if (uwTaskID == g_stLosTask.pstRunTask->uwTaskID)
{
(VOID)LOS_IntRestore(uvIntSave);
//(9)进行一次任务调度
LOS_Schedule();
return LOS_OK;
}
(VOID)LOS_IntRestore(uvIntSave);
return LOS_OK;
LOS_ERREND:
(VOID)LOS_IntRestore(uvIntSave);
return uwErrRet;
}
将挂起的函数重新进入就绪态
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskResume(UINT32 uwTaskID)
{
UINTPTR uvIntSave;
LOS_TASK_CB *pstTaskCB;
UINT16 usTempStatus;
UINT32 uwErrRet = OS_ERROR;
//(1)判断任务ID是否有效,无效返回错误
if (uwTaskID > LOSCFG_BASE_CORE_TSK_LIMIT)
{
return LOS_ERRNO_TSK_ID_INVALID;
}
//(2)根据任务ID获取任务控制块
pstTaskCB = OS_TCB_FROM_TID(uwTaskID);
uvIntSave = LOS_IntLock();
usTempStatus = pstTaskCB->usTaskStatus;
//(3)判断要恢复任务的状态,如果是未使用状态,返回错误代码
if (OS_TASK_STATUS_UNUSED & usTempStatus)
{
uwErrRet = LOS_ERRNO_TSK_NOT_CREATED;
OS_GOTO_ERREND();
}//(4)如果是未挂起状态,无需恢复,返回错误代码
else if (!(OS_TASK_STATUS_SUSPEND & usTempStatus))
{
uwErrRet = LOS_ERRNO_TSK_NOT_SUSPENDED;
OS_GOTO_ERREND();
}
//(5)从以上判断得出任务可挂起,将任务从阻塞态解除
pstTaskCB->usTaskStatus &= (~OS_TASK_STATUS_SUSPEND);
if (!(OS_CHECK_TASK_BLOCK & pstTaskCB->usTaskStatus) )
{
//(6)将任务变成就绪态
pstTaskCB->usTaskStatus |= OS_TASK_STATUS_READY;
osPriqueueEnqueue(&pstTaskCB->stPendList, pstTaskCB->usPriority);
//(7)将任务按照本身优先级数值添加到就绪列表
if (g_bTaskScheduled)
{
(VOID)LOS_IntRestore(uvIntSave);
//(8)如果调度器已经运行了,发起一次任务调度,寻找处于就绪态的最高优先级的任务,如果被恢复的刚好是,则立即运行该任务
LOS_Schedule();
return LOS_OK;
}
g_stLosTask.pstNewTask = LOS_DL_LIST_ENTRY(osPriqueueTop(), LOS_TASK_CB, stPendList); /*lint !e413*/
}
(VOID)LOS_IntRestore(uvIntSave);
return LOS_OK;
LOS_ERREND:
(VOID)LOS_IntRestore(uvIntSave);
return uwErrRet;
}
los_task.h文件中一开始就定义了各类错误的代码,如果有返回错误代码,具体详情打开查看即可
任务设计时需要考虑:中断服务程序、普通任务、空闲任务、任务的执行时间等
中断服务程序运行在非任务的执行环境,上下文环境中不能使用挂起、阻塞的操作,中断程序最好快进快出,程序中一般标记一下这个事情发生了就行,具体的处理就交给对应的任务处理函数即可,中断优先级高于任何任务的优先级
任务中不允许出现死循环,否则低优先级的任务无法运行。任务不能只有就绪态没有阻塞态,这样影响其他低优先级的任务运行。
空闲任务是系统不执行其他工作时自动进入的任务,可通过宏定义LOSCFG_KERNEL_TICKLESS和LOSCFG_KERNEL_RUNSTOP选择自己需要的功能设计,唯一一个不允许出现阻塞的任务,确保系统一直运行。
任务时间:1.任务从开始到结束的时间 2.任务的周期
处理时间更短的任务优先级应该设置的更高。
例如:事件A的服务任务TA,系统要求实时响应指标为10ms,TA的最大运行时间为1ms,所以TA的周期为10ms,TA的运行时间为1ms。如果此时有一个运行时间为20ms的事件B,优先级比A的高,把它抢占了,在TA的周期里面,执行完毕B后,TA已经错过对事件A的响应了。
/* LiteOS 头文件 */
#include "los_sys.h"
#include "los_task.ph"
/* 板级外设头文件 */
#include "bsp_usart.h"
#include "bsp_led.h"
#include "bsp_key.h"
/* 定义任务句柄 */
UINT32 LED_Task_Handle;
UINT32 Key_Task_Handle;
/* 函数声明 */
static UINT32 AppTaskCreate(void);
static UINT32 Creat_LED_Task(void);
static UINT32 Creat_Key_Task(void);
static void LED_Task(void);
static void Key_Task(void);
static void BSP_Init(void);
int main(void)
{
UINT32 uwRet = LOS_OK; //定义一个任务创建的返回值,默认为创建成功
/* 板载相关初始化 */
BSP_Init();
printf("按下KEY1挂起任务,按下KEY2恢复任务\r\n");
/* LiteOS 内核初始化 */
uwRet = LOS_KernelInit();
if (uwRet != LOS_OK)
{
printf("LiteOS 核心初始化失败!失败代码0x%X\n",uwRet);
return LOS_NOK;
}
uwRet = AppTaskCreate();
if (uwRet != LOS_OK)
{
printf("AppTaskCreate创建任务失败!失败代码0x%X\n",uwRet);
return LOS_NOK;
}
/* 开启LiteOS任务调度 */
LOS_Start();
//正常情况下不会执行到这里
while(1);
}
static UINT32 AppTaskCreate(void)
{
/* 定义一个返回类型变量,初始化为LOS_OK */
UINT32 uwRet = LOS_OK;
uwRet = Creat_LED_Task();
if (uwRet != LOS_OK)
{
printf("LED_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
uwRet = Creat_Key_Task();
if (uwRet != LOS_OK)
{
printf("KEY_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
return LOS_OK;
}
static UINT32 Creat_LED_Task()
{
//定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
//定义一个用于创建任务的参数结构体
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 3; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "LED_Task";/* 任务名 */
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)LED_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&LED_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
static UINT32 Creat_Key_Task()
{
// 定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 4; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "KEY_Task"; /* 任务名*/
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Key_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&Key_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
static void LED_Task(void)
{
/* 任务都是一个无限循环,不能返回 */
while(1)
{
LED2_TOGGLE;
printf("LED任务运行中\r\n");
LOS_TaskDelay(1000);
}
}
static void Key_Task(void)
{
UINT32 uwRet = LOS_OK;
/* 任务都是一个无限循环,不能返回 */
while(1)
{
if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==KEY_ON){
printf("挂起LED任务\r\n");
uwRet = LOS_TaskSuspend(LED_Task_Handle);
if(LOS_OK == uwRet){
printf("挂起成功\r\n");
}
}
else if(Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==KEY_ON){
printf("恢复LED任务\r\n");
uwRet = LOS_TaskResume(LED_Task_Handle);
if(LOS_OK == uwRet){
printf("恢复LED任务成功\r\n");
}
}
LOS_TaskDelay(20);
}
}
/*******************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
******************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* KEY初始化 */
Key_GPIO_Config();
}
/******************************END OF FILE*******************/