LiteOS-任务篇


前言

移植好内核后,开始实战内核。

链接

参考

  • 野火
  • 上面链接

笔录草稿

基本概念

  • 任务是竞争系统资源的最小运行单元。任务可以使用或等待CPU、使用内存空间等系统资源,并独立于其它任务运行
  • 任务与线程
    • 在 LiteOS 中,一个任务可以表示一条线程。
  • Huawei LiteOS的任务一共有 32 个优先级(0-31),最高优先级为0,最低优先级为31。

任务相关概念

  • 任务状态

    • 就绪 (Ready)
    • 运行(Running)
    • 运行(Running)
    • 退出态(Dead)
  • 任务ID

    • 可通过 任务ID 获取 任务句柄
      • ***任务句柄*** = (((LOS_TASK_CB *)g_pstTaskCBArray) + (TaskID))
  • 任务控制块TCB

    • TCB 包含了任务上下文栈指针(stack pointer)、任务状态、任务优先级、任务ID、任务名、任务栈大小等信息。TCB 可以反映出每个任务运行情况。(TCB 其实就是一个袜子
  • 任务栈

    • 每一个任务都拥有一个独立的栈空间,称为任务栈。
  • 任务上下文切换个人把上下文分开理解

    • ** Huawei LiteOS** 在任务由运行态转为其它状态时会将本任务的上文信息,保存在自己的任务栈里面,也称压栈或入栈;
    • 当任务切换到运行态时,把保存到任务栈中的上文信息加载到CPU寄存器中,即可恢复该任务的运行,称为出栈。新的任务信息也就是下文。
    • 以上流程就是 上文压栈 –> 下文出栈

LiteOS 任务运作机制

  • Huawei LiteOS 任务管理模块提供
    • 任务创建
    • 任务删除
    • 任务延时
    • 任务挂起
    • 任务恢复
    • 更改任务优先级
    • 锁定任务调度
    • 解锁任务调度
    • 根据任务控制块查询任务ID
    • 根据ID查询任务查询任务控制块信息功能。
  • 任务创建时,如果 OS 的系统可用空间少于任务,则创建失败,反之亦然。
  • 用户创建任务时,系统会将任务栈进行初始化,预置上下文。
    • 任务入口函数 也放到了相应的位置,在任务第一次执行时便可执行 任务入口函数

内核初始化

  • 一般的 RTOS 启动流程:MCU进入 main 函数 –> 创建任务 –> 启动 RTOS

    • 当然,在启动的过程中可以插入一些操作,如板级初始化 bspInit(); 等等
  • Huawei LiteOS 的启动流程则要多一步:MCU进入 main 函数 –> LiteOS内核初始化 –> 创建任务 –> 启动 RTOS

    • 在操作 LiteOS 必须先初始化其内核。
    • 函数 LOS_KernelInit();带返回值)。
  • 内核初始化主要工作

    • 配置任务数量上限
    • 内存起始地址
    • 初始化动态内存池(如果内存溢出,则内核初始化失败
    • 接管中断处理(非接管中断跳过
    • 任务初始化
      • 先创建一个空闲任务
    • 任务监视初始化
    • CPU利用率初始化
    • IPC通信初始化:信号量、互斥量、消息队列等等。
    • 软件定时器初始化
      • 该函数内会创建一个队列 和 一个定时任务
        • 后续会在软件定时器相关篇章分析源码

创建任务

创建任务有两种方案

  • 方案一:
    • 先创建所有任务
    • 再启动调度
  • 方案二:
    • 先创建一个创建任务
    • 然后启动调度
    • 创建任务里面创建所有任务
    • 然后删除创建任务

本章实操的是方案二。

任务相关函数

接口名 描述
LOS_TaskCreateOnly 创建任务,并使该任务进入suspend状态,并不调度
LOS_TaskCreate 创建任务,并使该任务进入ready状态,并调度
LOS_TaskDelete 删除指定的任务
LOS_TaskResume 恢复挂起的任务
LOS_TaskSuspend 挂起指定的任务
LOS_TaskDelay 任务延时等待
LOS_TaskYield 显式放权,调整指定优先级的任务调度顺序
LOS_TaskLock 锁任务调度
LOS_TaskUnlock 解锁任务调度
LOS_CurTaskPriSet 设置当前任务的优先级
LOS_TaskPriSet 设置指定任务的优先级
LOS_TaskPriGet 获取指定任务的优先级
LOS_CurTaskIDGet 获取当前任务的ID
LOS_TaskInfoGet 获取指定任务的信息
LOS_TaskStatusGet 获取指定任务的状态
LOS_TaskNameGet 获取指定任务的名称
LOS_TaskInfoMonitor 监控所有任务,获取所有任务的信息
LOS_NextTaskIDGet 获取即将被调度的任务的ID

各函数使用可以看源码或者例程

任务开发流程

  • 配置任务块
  • 锁任务(防止高优先级任务调度
  • 创建任务
  • 解锁任务

创建创建任务

部分源码

  • 使用LOS_TaskCreate函数
    • 需要一个 任务初始化参数结构体 TSK_INIT_PARAM_S 和 一个任务句柄。
  • TSK_INIT_PARAM_S 源码
/**
 * @ingroup los_task
 * Define the structure of the parameters used for task creation.
 *
 * Information of specified parameters passed in during task creation.
 */
typedef struct tagTskInitParam
{
   TSK_ENTRY_FUNC       pfnTaskEntry;               /**< Task entrance function    */
   UINT16               usTaskPrio;                 /**< Task priority             */
   UINT32               uwArg;                      /**< Task parameters           */
   UINT32               uwStackSize;                /**< Task stack size           */
   CHAR                 *pcName;                    /**< Task name                 */
   UINT32               uwResved;                   /**< Reserved                  */
} TSK_INIT_PARAM_S;

例子

  • 把创建创建任务的工作放到一个函数里
    • 这里使用宏,是因为方便管理,框架是基于本人编写的框架。需要移植的可以直接填写数值。
      • lssConfigvStartTaskPRIO 一个数值,表示优先级。
      • lssConfigvStartTaskSIZE 一个数值,表示堆空间字节数
        • 注意:这里表示的是字节数,其它 RTOS 可能会表示 字数
          • 如:FreeRTOS 中任务堆空间赋值就是以 字数 为单位。
          • 一个 字数 表示多个 字节,看CPU架构是多少位的
            • 如:32位CPU,一个字 = 四个字节 / word = 4byte
/**
* @brief  创建一个LED任务
* @param 
* @retval 
* @author lzm
*/
static UINT32 Creat_vStartTask_Task()
{
	UINT32 uwRet = LOS_OK;// 定义一个返回值变量
	TSK_INIT_PARAM_S task_init_param;// 定义一个任务参数结构体

	task_init_param.usTaskPrio = lssConfigvStartTaskPRIO;	     /* 任务优先级,值越少,优先级越高 */
	task_init_param.pcName = "Start_Task";                       /* 任务名 */
	task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)vStartTask;   /* 回调函数 */
	task_init_param.uwStackSize = lssConfigvStartTaskSIZE;		 /* 任务堆空间 */

	uwRet = LOS_TaskCreate(&StartTask_Handle, &task_init_param); /* 创建任务 */
	return uwRet;
}

创建任务的任务回调函数 vStartTask

/**
* @brief  创建任务
* @param 
* @retval 
* @author lzm
*/
void vStartTask (void )
{
    UINT32 uwRet = LOS_OK;
    UINTPTR uvIntSave;
    
    /* 进入临界 */
    uvIntSave = LOS_IntLock();
    
    uwRet = Creat_vLedTask_Task();
	if (uwRet != LOS_OK)
    {
        ; // 创建失败
    }
    
    /* 删除创建任务 */
    LOS_TaskDelete(StartTask_Handle); 
    
    /* 退出临界 */
    (VOID)LOS_IntRestore(uvIntSave);
}

Led任务创建函数 Creat_vLedTask_Task

/**
* @brief  Led任务
* @param 
* @retval 
* @author lzm
*/
static UINT32 Creat_vLedTask_Task()
{
	UINT32 uwRet = LOS_OK;			
	TSK_INIT_PARAM_S task_init_param;	

	task_init_param.usTaskPrio = lssConfigvLedTaskPRIO;
	task_init_param.pcName = "Led Task";
	task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)vLedTask;
	task_init_param.uwStackSize = lssConfigvLedTaskSIZE;

	uwRet = LOS_TaskCreate(&LedTask_Handle, &task_init_param);
	return uwRet;
}

Led任务回调函数 vLedTask

  • 该函数在 LedTask.c 文件中
/**
  ******************************************************************************
  * @file    LedTask.c
  * @author  lzm
  * @version V1.0
  * @date    2020-xx-xx
  * @brief
  * @attention
  * 实验平台:LZM
  ******************************************************************************
  */
#include "LedTask.h"
void vLedTask( void )
{ 
	/* 设备初始化 */
	LSS_LED_Init();    
    /* 进入死循环 */
	while(1)
	{   
        LSS_LED_Flash(&LedA, LedA.cycle); // 设备业务
        LOS_TaskDelay( 10 ); // 进入阻塞态
    }
}

开启调度

  /* 开启调度 */
  LOS_Start();

LOS_Start 函数源码

  • 具体的源码分析可看源码篇
  • 主要内容
    • 配置RTOS的节拍定时器
    • 启动调度
/*****************************************************************************
 Function    : LOS_Start
 Description : Task start function
 Input       : None
 Output      : None
 Return      : LOS_OK on success or error code on failure
 *****************************************************************************/
LITE_OS_SEC_TEXT_INIT UINT32 LOS_Start(VOID)
{
    UINT32 uwRet;
/* 判断是否使用专用定时器 */
#if (LOSCFG_BASE_CORE_TICK_HW_TIME == NO) // 不使用专门的定时器
    uwRet = osTickStart(); // 开启调度

    if (uwRet != LOS_OK)
    {
        PRINT_ERR("osTickStart error\n");
        return uwRet;
    }
#else // 使用专门的定时器
    extern int os_timer_init(void); 
    uwRet = os_timer_init(); // RTOS 配置的专用定时器
    if (uwRet != LOS_OK)
    {
        PRINT_ERR("os_timer_init error\n");
        return uwRet;
    }
#endif
    LOS_StartToRun(); // 启动调度,汇编
    return uwRet;
}

附件

任务错误码列表

序号 定义 实际数值 描述 参考解决方案
1 LOS_ERRNO_TSK_NO_MEMORY 0x03000200 内存空间不足 分配更大的内存分区
2 LOS_ERRNO_TSK_PTR_NULL 0x02000201 任务参数为空 检查任务参数
3 LOS_ERRNO_TSK_STKSZ_NOT_ALIGN 0x02000202 任务栈大小未对齐 对齐任务栈
4 LOS_ERRNO_TSK_PRIOR_ERROR 0x02000203 不正确的任务优先级 检查任务优先级
5 LOS_ERRNO_TSK_ENTRY_NULL 0x02000204 务入口函数为空 定义任务入口函数
6 LOS_ERRNO_TSK_NAME_EMPTY 0x02000205 任务名为空 设置任务名
7 LOS_ERRNO_TSK_STKSZ_TOO_SMALL 0x02000206 任务栈太小 扩大任务栈
8 LOS_ERRNO_TSK_ID_INVALID 0x02000207 无效的任务ID 检查任务ID
9 LOS_ERRNO_TSK_ALREADY_SUSPENDED 0x02000208 任务已经被挂起 等待这个任务被恢复后,再去尝试挂起这个任务
10 LOS_ERRNO_TSK_NOT_SUSPENDED 0x02000209 任务未被挂起 挂起这个任务
11 LOS_ERRNO_TSK_NOT_CREATED 0x0200020a 任务未被创建 创建这个任务
12 LOS_ERRNO_TSK_OPERATE_SWTMR 0x02000222 不允许操作软件定时器任务 用户不要试图去操作软件定时器任务的设置
13 LOS_ERRNO_TSK_MSG_NONZERO 0x0200020c 任务信息非零 暂不使用该错误码
14 LOS_ERRNO_TSK_DELAY_IN_INT 0x0300020d 中断期间,进行任务延时 等待退出中断后再进行延时操作
15 LOS_ERRNO_TSK_DELAY_IN_LOCK 0x0200020e 任务被锁的状态下,进行延时 等待解锁任务之后再进行延时操作
16 LOS_ERRNO_TSK_YIELD_INVALID_TASK 0x0200020f 将被排入行程的任务是无效的 检查这个任务
17 LOS_ERRNO_TSK_YIELD_NOT_ENOUGH_TASK 0x02000210 没有或者仅有一个可用任务能进行行程安排 增加任务数
18 LOS_ERRNO_TSK_TCB_UNAVAILABLE 0x02000211 没有空闲的任务控制块可用 增加任务控制块数量
19 LOS_ERRNO_TSK_HOOK_NOT_MATCH 0x02000212 任务的钩子函数不匹配 暂不使用该错误码
20 LOS_ERRNO_TSK_HOOK_IS_FULL 0x02000213 任务的钩子函数数量超过界限 暂不使用该错误码
21 LOS_ERRNO_TSK_OPERATE_IDLE 0x02000214 这是个IDLE任务 检查任务ID,不要试图操作IDLE任务
22 LOS_ERRNO_TSK_SUSPEND_LOCKED 0x03000215 将被挂起的任务处于被锁状态 等待任务解锁后再尝试挂起任务
23 LOS_ERRNO_TSK_FREE_STACK_FAILED 0x02000217 任务栈free失败 该错误码暂不使用
24 LOS_ERRNO_TSK_STKAREA_TOO_SMALL 0x02000218 任务栈区域太小 该错误码暂不使用
25 LOS_ERRNO_TSK_ACTIVE_FAILED 0x03000219 任务触发失败 创建一个IDLE任务后执行任务转换
26 LOS_ERRNO_TSK_CONFIG_TOO_MANY 0x0200021a 过多的任务配置项 该错误码暂不使用
27 LOS_ERRNO_TSK_CP_SAVE_AREA_NOT_ALIGN 0x0200021b 暂无 该错误码暂不使用
28 LOS_ERRNO_TSK_MSG_Q_TOO_MANY 0x0200021d 暂无 该错误码暂不使用
29 LOS_ERRNO_TSK_CP_SAVE_AREA_NULL 0x0200021e 暂无 该错误码暂不使用
30 LOS_ERRNO_TSK_SELF_DELETE_ERR 0x0200021f 暂无 该错误码暂不使用
31 LOS_ERRNO_TSK_STKSZ_TOO_LARGE 0x02000220 任务栈大小设置过大 减小任务栈大小
32 LOS_ERRNO_TSK_SUSPEND_SWTMR_NOT_ALLOWED 0x02000221 不允许挂起软件定时器任务 检查任务ID, 不要试图挂起软件定时器任务