STM32移植LiteOS创建任务翻转点亮LED灯

文章详细介绍了如何在STM32微控制器上移植并使用LiteOS操作系统,包括硬件初始化、LiteOS内核初始化、任务创建、任务控制块以及内存管理。通过创建单任务和多任务来展示任务的生命周期,强调了任务优先级和内存分配策略。
摘要由CSDN通过智能技术生成

移植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){
//任务体,但必须要有阻塞的情况发生
}
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值