移植LiteOS到STM32后,开始学习使用LiteOS。
先从创建任务开始,实现LED翻转的功能。
启动LiteOS之前,需要先对系统硬件进行初始化,想当于STM32编写程序时要先对LED初始化才能使用。
一、硬件初始化
移植完后,在main.c文件中会有一个BSP_Init函数,在该函数中进行LED的初始化,和在STM32中进行硬件的初始化完全一样,在main函数中调用BSP_Init时未涉及LiteOS操作系统,可以在初始完后立马进行点灯看看是否成功。
int main(){
BSP_Init();
LED_ON;//点灯
while(1);
}
static void BSP_Init(void){
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_GPIO_Config();//LED初始化
}
二、创建单任务
在LiteOS中,任务使用的栈空间和任务控制块由LiteOS动态分配,每一个任务占用的是单片机的静态物理内存SRAM,任务控制块也是从SRAM分配的,创建的任务越多消耗的物理内存越大。
2.1 动态内存空间的堆
LiteOS分配任务栈和任务控制块,LiteOS中定义了一个OS_SYS_MEM_SIZE宏,用于指定系统可以管理的内存大小。在target_config.h中进行配置。
系统使用LOS_MemAllocAlign函数从内存池中分配需要的内存,在los_memory.c文件中定义。
LITE_OS_SEC_TEXT VOID *LOS_MemAllocAlign(VOID *pPool, UINT32 uwSize, UINT32 uwBoundary)
{
VOID *pRet = NULL;
UINT32 uwUseSize;
UINT32 uwGapSize;
VOID *pAlignedPtr;
do {
if ((NULL == pPool) || (0 == uwSize) || (0 == uwBoundary) || !IS_ALIGNED(uwBoundary, sizeof(VOID *)))
{
break;
}
uwUseSize = uwSize + uwBoundary + 4;
pRet = osHeapAlloc(pPool, uwUseSize);
if (pRet)
{
pAlignedPtr = (VOID *)OS_MEM_ALIGN(pRet, uwBoundary);
if (pRet == pAlignedPtr)
{
break;
}
uwGapSize = (UINT32)pAlignedPtr - (UINT32)pRet;
OS_MEM_SET_ALIGN_FLAG(uwGapSize);
*((UINT32 *)((UINT32)pAlignedPtr - 4)) = uwGapSize;
pRet = pAlignedPtr;
}
} while (0);
return pRet;
}
2.2 LiteOS核心初始化
创建任务前,需要对LiteOS的核心组件进行初始化,初始化函数接口为LOS_KernelInit,函数做了以下事情:
(1)系统内存初始化,将LiteOS在MCU中管理的内存初始化
(2)任务基本的底层初始化,LiteOS在自己可管理的内存中选取一块出来,用来管理所有的任务控制块信息。最大任务数:LOSCFG_BASE_CORE_TSK_LIMIT+1(含空闲任务,用户自定义)
(3)如果需要让LiteOS接管来自STM32的中断,那么LiteOS会把所有的中断入口函数通过一个指针数组存储起来,不接管就不对中断入口函数处理。是否接管由LOSCFG_PLATFORM_HWI配置,最大管理中断数:OS_VECTOR_CNT=OS_SYS_VECTOR_CNT+OS_HWI_MAX_NUM
(4)判断用户使能了什么功能,就初始化相应的功能
(5)如果系统使用了软件定时器,不是单片机的定时器,则会使用消息队列,进行相应的初始化。
(6)LiteOS会创建一个空闲任务,处理器一直在运行,空闲任务虽然啥也不干,但是它用于保证系统能一直在运行,没有任务系统就停止运作了,因此空闲任务的优先级最低。空闲任务默认栈大小:LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE(可自定义)
2.3 定义任务函数
创建的任务函数是一个无限死循环,不能有返回值
static void Test1_Task(void){
while(1){ //死循环
LED2_TOGGLE; //点灯测试,翻转LED灯,现在还没介绍
LOS_TaskDelay(1000);//延时1000个Tick
}
}
2.4 定义任务ID变量
任务ID,任务的唯一标识,任务创建成功后返回一个任务ID给用户,用户通过任务ID即可对任务进行操作(挂起、恢复、查询信息等)。在此之前,用户需要定义一个任务ID变量,用于存储返回的任务ID。
/*定义任务ID变量*/
UINT32 Test1_Task_Handle;
2.5 任务控制块
每个任务都有一个任务控制块(TCB)。
TCB:任务栈指针(Stack Pointer)、任务状态、任务优先级、任务ID、任务名、任务栈大小等信息。
任务栈:局部变量寄存器、函数参数、函数返回地址等,切换任务时会将切出任务的上下文信息保存在任务自身的任务栈中,确保任务恢复时不丢失数据。
任务上下文:任务运行过程中使用到的资源,如寄存器。
/*以下内容在los_tack.ph文件中*/
/*任务控制块清单*/
typedef struct tagTaskCB
{
VOID *pStackPointer; /**< 任务栈指针 */
UINT16 usTaskStatus; /**< 任务状态 */
UINT16 usPriority; /**< 任务优先级 */
UINT32 uwStackSize; /**< 任务栈大小 */
UINT32 uwTopOfStack; /**< 任务栈顶 */
UINT32 uwTaskID; /**< 任务ID */
TSK_ENTRY_FUNC pfnTaskEntry; /**< 任务入口函数 */
VOID *pTaskSem; /**< 任务阻塞在哪个信号量 */
VOID *pTaskMux; /**< 任务阻塞在哪个互斥锁 */
UINT32 uwArg; /**< 参数 */
CHAR *pcTaskName; /**< 任务名称 */
LOS_DL_LIST stPendList; /**< 挂起列表 */
LOS_DL_LIST stTimerList; /**< 时间相关列表 */
UINT32 uwIdxRollNum;
EVENT_CB_S uwEvent; /**< 事件 */
UINT32 uwEventMask; /**< 事件掩码 */
UINT32 uwEventMode; /**< 事件模式 */
VOID *puwMsg; /**< 内存分配给队列 */
} LOS_TASK_CB;
2.6 创建具体任务
可使用LOS_TaskCreate函数创建任务,每个任务的具体参数由用户定义。例如创建一个任务Creat_Test1_Task(),需要在里面使用函数LOS_TaskCreate进行创建,创建格式如下所示。
/*LOS_TaskCreate函数清单*/
static UINT32 Creat_Test1_Task()
{
//定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
//定义一个用于创建任务的参数结构体
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 3; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "Test1_Task";/* 任务名 */
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Test1_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&Test1_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
三、单任务main文件全貌
/* LiteOS 头文件 */
#include "los_sys.h"
#include "los_task.ph"
/* 板级外设头文件 */
#include "bsp_usart.h"
#include "bsp_led.h"
/* 定义任务句柄 */
UINT32 Test1_Task_Handle;
/* 函数声明 */
static UINT32 AppTaskCreate(void);
static UINT32 Creat_Test1_Task(void);
static void Test1_Task(void);
static void BSP_Init(void);
/***************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动LiteOS,开始多任务调度,启动失败则输出错误信息
**************************************************************/
int main(void)
{
UINT32 uwRet = LOS_OK; //定义一个任务创建的返回值,默认为创建成功
/* 板载相关初始化 */
BSP_Init();
printf("这是一个[野火]-STM32全系列开发板-LiteOS-SDRAM动态创建单任务实验!\n\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);
}
/*******************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 任务创建,为了方便管理,所有的任务创建函数都可以放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
*************************************************************/
static UINT32 AppTaskCreate(void)
{
/* 定义一个返回类型变量,初始化为LOS_OK */
UINT32 uwRet = LOS_OK;
uwRet = Creat_Test1_Task();
if (uwRet != LOS_OK)
{
printf("Test1_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
return LOS_OK;
}
/******************************************************************
* @ 函数名 : Creat_Test1_Task
* @ 功能说明: 创建Test1_Task任务
* @ 参数 :
* @ 返回值 : 无
******************************************************************/
static UINT32 Creat_Test1_Task()
{
//定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
//定义一个用于创建任务的参数结构体
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 3; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "Test1_Task";/* 任务名 */
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Test1_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&Test1_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
/******************************************************************
* @ 函数名 : Test1_Task
* @ 功能说明: Test1_Task任务实现
* @ 参数 : NULL
* @ 返回值 : NULL
*****************************************************************/
static void Test1_Task(void)
{
/* 任务都是一个无限循环,不能返回 */
while(1)
{
LED2_TOGGLE;
printf("任务1运行中,每1000ms打印一次信息\r\n");
LOS_TaskDelay(1000);
}
}
/*******************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
******************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
}
/*********************************************END OF FILE**********************/
步骤总结:
定义任务ID—>板载硬件初始化—>调用内核初始化函数LOS_KernelInit()—>自定义一个任务创建函数,里面包含多个任务函数—>创建具体任务(本文2.6)—>实现具体任务(本文2.3)。—>开启LiteOS任务调度
其中,创建完具体任务后,里面有一条语句是任务名,实现具体任务就是将这个任务名的函数写出来。
四、创建多任务
和创建单任务的流程一样,不同的地方是,多个任务时要设置好该任务的优先级
多任务的main文件
/* LiteOS 头文件 */
#include "los_sys.h"
#include "los_task.ph"
/* 板级外设头文件 */
#include "bsp_usart.h"
#include "bsp_led.h"
/* 定义任务句柄 */
UINT32 Test1_Task_Handle;
UINT32 Test2_Task_Handle;
/* 函数声明 */
static UINT32 AppTaskCreate(void);
static UINT32 Creat_Test1_Task(void);
static UINT32 Creat_Test2_Task(void);
static void Test1_Task(void);
static void Test2_Task(void);
static void BSP_Init(void);
/***************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动LiteOS,开始多任务调度,启动失败则输出错误信息
**************************************************************/
int main(void)
{
UINT32 uwRet = LOS_OK; //定义一个任务创建的返回值,默认为创建成功
/* 板载相关初始化 */
BSP_Init();
printf("这是[野火]-STM32全系列开发板-LiteOS-SDRAM动态创建多任务!\n\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);
}
/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 任务创建,为了方便管理,所有的任务创建函数都可以放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
*****************************************************************/
static UINT32 AppTaskCreate(void)
{
/* 定义一个返回类型变量,初始化为LOS_OK */
UINT32 uwRet = LOS_OK;
uwRet = Creat_Test1_Task();
if (uwRet != LOS_OK)
{
printf("Test1_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
uwRet = Creat_Test2_Task();
if (uwRet != LOS_OK)
{
printf("Test2_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
return LOS_OK;
}
/******************************************************************
* @ 函数名 : Creat_Test1_Task
* @ 功能说明: 创建Test1_Task任务
* @ 参数 :
* @ 返回值 : 无
******************************************************************/
static UINT32 Creat_Test1_Task()
{
//定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
//定义一个用于创建任务的参数结构体
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 3; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "Test1_Task";/* 任务名 */
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Test1_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&Test1_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
/*******************************************************************
* @ 函数名 : Creat_Test2_Task
* @ 功能说明: 创建Test2_Task任务
* @ 参数 :
* @ 返回值 : 无
******************************************************************/
static UINT32 Creat_Test2_Task()
{
// 定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 4; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "Test2_Task"; /* 任务名*/
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Test2_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&Test2_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
/******************************************************************
* @ 函数名 : Test1_Task
* @ 功能说明: Test1_Task任务实现
* @ 参数 : NULL
* @ 返回值 : NULL
*****************************************************************/
static void Test1_Task(void)
{
/* 任务都是一个无限循环,不能返回 */
while(1)
{
LED2_TOGGLE;
printf("任务1进行中,每1000ms打印一次信息\r\n");
LOS_TaskDelay(1000);
}
}
/******************************************************************
* @ 函数名 : Test2_Task
* @ 功能说明: Test2_Task任务实现
* @ 参数 : NULL
* @ 返回值 : NULL
*****************************************************************/
static void Test2_Task(void)
{
/* 任务都是一个无限循环,不能返回 */
while(1)
{
LED3_TOGGLE;
printf("任务2运行中,每500ms打印一次信息\n");
LOS_TaskDelay(500);
}
}
/*******************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
******************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
}
五、LiteOS启动流程
实时操作系统(RTOS)主要有两种启动方式
5.1 第一种启动方式
和上面创建单任务的main文件中的结构类似,先在main函数中初始化硬件,初始化RTOS内核,将任务创建完成,启动RTOS调度器。
int main(void){
BSP_Init(); //硬件初始化
LOS_KernelInit(); //内核初始化
Creat_Test1_Task(); //创建任务
LOS_Start(); //启动调度器
}
void Test1_Task(){
while(1){
//任务体
}
}
5.2 第二种启动方式
初始化硬件——>初始化内核——>创建启动任务(App任务)——>启动调度器
在启动任务中创建各种应用任务,所有任务创建完后,启动任务把自己删除掉。
int main(void){
BSP_Init(); //硬件初始化
LOS_KernelInit(); //内核初始化
Creat_App_Task(); //创建启动任务
LOS_Start(); //启动调度器
}
void App_Task_Start(void *arg){
Creat_Test1_Task(); //创建任务1并执行
Creat_Test2_Task(); // 当任务1阻塞时创建任务2并执行
Delete_App_Task(); //删除启动任务
}
void Test1_Task(){
while(1){
//任务体, 但必须要有阻塞的情况发生
}
}
void Test2_Task(){
while(1){
//任务体,但必须要有阻塞的情况发生
}
}