FreeRTOS学习笔记(5)

9、任务调度

9.1 开启任务调度器

vTaskStartSchedure()

作用:用于启动任务调度器,任务调度器启动后,FreeRTOS便会开始进行任务调度。

该函数内部实现,如下:

1、创建空闲任务

2、如果使能软件定时器,则创建定时器任务

3、关闭中断,防止调度器开启之前或过程中,受中断干扰,会在运行第一个任务时打开中断

4、初始化全局变量,并将任务调度器的运行标志设置为已运行

5、初始化任务运行时间统计功能的时基定时器

6、调用函数 xPortStartScheduler()

9.2 启动第一个任务

prvStartFirstTask();                /*开启第一个任务*/

该函数用于初始化第一个任务前的环境,主要是重新设置MSP指针,并使能全局中断。

1、什么是MSP指针:程序在运行过程中需要一定的栈空间来保存局部变量等一些信息。当有信息保存到栈中时, MCU 会自动更新 SP 指针,ARM Cortex-M 内核提供了两个栈空间

主堆栈指针(MSP):由OS内核、异常服务例程以及所有需要特权访问的应用程序代码使用。

进程堆栈指针(PSP):用于常规的应用程序代码。

FreeRTOS中,中断使用MSP(主堆栈),中断以外使用PSP(进程堆栈)

vPortSVCHandler();               /*SVC中断服务函数*/

如何启动第一个任务:

假设我们要启动的第一个任务是任务A,那么就需要将任务A的寄存器值恢复到CPU寄存器

任务A的寄存器值,在一开始创建任务时就保存在任务堆栈里边。

ldmia指令:出栈,将从R4开始到R11寄存器内的数据转移到CPU上。

需要注意的是,CortexM3系列不需要对R14进行出栈处理,而M4、M7系列则需要进行出栈处理,因为M4、M7系列支持FPU。

出栈/压栈汇编指令详解:

1、出栈(恢复现场),方向:从下往上(低地址往高地址):假设r0地址为0x04汇编指令示例:

ldmia r0!, {r4-r6}   /* 任务栈r0地址由低到高,将r0存储地址里面的内容手动加载到 CPU寄存器r4、r5、r6 */

r0地址(0x04)内容加载到r4,此时地址r0 = r0+4  = 0x08

r0地址(0x08)内容加载到r5,此时地址r0 = r0+4  = 0x0C

r0地址(0x0C)内容加载到r6,此时地址r0 = r0+4  = 0x10

2、压栈(保存现场),方向:从上往下(高地址往低地址):假设r0地址为0x10汇编指令示例:

stmdb r0!, {r4-r6} }   /* r0的存储地址由高到低递减,将r4、r5、r6里的内容存储到r0的任务栈里面。 */

地址:r0 = r0-4  = 0x0C,将r6的内容(寄存器值)存放到r0所指向地址(0x0C)

地址:r0 = r0-4  = 0x08,将r5的内容(寄存器值)存放到r0所指向地址(0x08)

地址:r0 = r0-4  = 0x04,将r4的内容(寄存器值)存放到r0所指向地址(0x04) 

9.3 任务切换

任务切换的本质:就是CPU寄存器的切换。

由任务A切换到任务B,主要分为两步:

1、暂停任务A的执行,并将此时任务A的寄存器保存到任务堆栈---这个过程就是保存现场

2、将任务B的各个寄存器的值(存于任务堆栈中)恢复到CPU寄存器中---这个过程就是恢复现场

整体的过程就被称之为:上下文切换 

  

PendSV中断是如何触发的?
1、滴答定时器中断调用

2、执行FreeRTOS提供的相关API函数:PortYIELD()

本质:通过向中断控制和状态寄存器ICSR的bit28写入1挂起PendSV中断

由上图可以看出,CortexM3内核的自动压栈、出栈使用的是PSP 

前导置零指令:

#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities )      uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )

所谓的前导置零指令,可以理解为计算一个32位数头部0的个数,通过前导置零指令,可以获得最高优先级。

获取最高优先级任务的任务控制块:

9.4 FreeRTOS时间片调度

针对同等优先级任务

什么是时间片调度?

同等优先级的任务轮流享有CPU时间(可设置-指滴答计时器的时钟周期),即为时间片,在FreeRTOS中,一个时间片就等于Systick中断周期。

假设创建了三个任务:task1、task2、task3,他们的任务优先级相同,当程序执行时:

1、首先task1执行一个时间片,切换至task2运行

2、task2执行一个时间片,切换至task3运行

3、task3在运行的过程中(不到一个时间片)阻塞,此时直接切换到下一个任务task1

4、task1运行完一个时间片后,切换至task2运行

在使用时间片调度前需要把宏configUSE_TIME_SLICING 和 configUSE_PREEMPTION 置1

10、FreeRTOS任务相关API函数

10.1 FreeRTOS任务相关API函数介绍

uxTaskPriorityGet(const TaskHandle_t xTask):该函数能够获取任务的优先级,使用前需将宏INCLUDE_uxTaskPriorityGet 置 1。该函数只有一个形参,即要查找的任务句柄。

vTaskPrioritySet(TaskHandle_t xTask , UBaseType_t uxNewPriority):该函数能够重设任务的优先级,使用前需将宏INCLUDE_vTaskPrioritySet置1。该函数有两个形参,分别为任务句柄以及需要重设的优先级。

uxTaskGetNumberOfTasks(void):该函数能够获取系统中任务的数量,无形参,该函数会自动返回任务数量。

uxTaskGetSystemState(TaskStatus_t * const pxTaskStatusArray,                                                       const UBaseType_t uxArraySize,                                                       configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime);该函数能够获取所有任务状态信息,使用该函数需将宏 configUSE_TRACE_FACILITY 置 1。

vTaskGetInfo(TaskHandle_t xTask,
                       TaskStatus_t * pxTaskStatus,
                       BaseType_t xGetFreeStackSpace,
                       eTaskState eState);
该函数能够获取指定任务的信息

xTaskGetCurrentTaskHandle( void );该函数能够获取当前任务的任务句柄

xTaskGetHandle(const char * pcNameToQuery);该函数能够通过任务名获取任务句柄

uxTaskGetStackHighWaterMark( TaskHandle_t  xTask );该函数能够获取任务堆栈历史剩余最小值

eTaskGetState(TaskHandle_t xTask);该函数能够获取任务的当前状态

vTaskList(char * pcWriteBuffer);该函数能够以表格的形式获取系统中任务的信息

vTaskGetRunTimeStats( char * pcWriteBuffer );该函数能够统计任务的运行时间信息。使用此函数需将宏 configGENERATE_RUN_TIME_STAT 、configUSE_STATS_FORMATTING_FUNCTIONS 置1

  代码:

freertos_demo.c:

/**
 ****************************************************************************************************
 * @file        freertos.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.4
 * @date        2022-01-04
 * @brief       FreeRTOS 移植实验
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 精英F103开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 ****************************************************************************************************
 */

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define START_TASK_STACK_SIZE 128
 #define START_TASK_PRIORITY   1
 TaskHandle_t  Start_Task_Handler;
 void start_task( void * pvParameters );
 
 /* Task1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK1_STACK_SIZE 128
 #define TASK1_PRIORITY   2
 TaskHandle_t  Task1_Handler;
 void task1( void * pvParameters );
 
 /* Task2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK2_STACK_SIZE 128
 #define TASK2_PRIORITY   3
 TaskHandle_t  Task2_Handler;
 void task2( void * pvParameters );
 
/******************************************************************************************************/

/*
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
    xTaskCreate(( TaskFunction_t      ) start_task,
               (char *                ) "start_task", 
               (configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
               (void *                ) NULL,
               (UBaseType_t           ) START_TASK_PRIORITY,
               (TaskHandle_t *        ) &Start_Task_Handler );
    vTaskStartScheduler();//开启任务调度器
}

 void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();           /* 进入临界区 */
    /*创建任务1*/
    xTaskCreate(( TaskFunction_t       ) task1,
               (char *                ) "task1", 
               (configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
               (void *                ) NULL,
               ( UBaseType_t          ) TASK1_PRIORITY,
               (TaskHandle_t *        ) &Task1_Handler );
    
    /*创建任务2*/
    xTaskCreate(( TaskFunction_t       ) task2,
               (char *                ) "task2", 
               (configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
               (void *                ) NULL,
               ( UBaseType_t          ) TASK2_PRIORITY,
               (TaskHandle_t *        ) &Task2_Handler );
    vTaskDelete(NULL);//删除开始任务
    taskEXIT_CRITICAL();            /* 退出临界区 */
}


/*任务一,实现LED每500ms翻转一次*/
void task1( void * pvParameters )
{
    while(1)
    {
        LED0_TOGGLE();
        vTaskDelay(500);
    }
}

char Task_Buffer[300];
/*任务二,实现任务状态查询API函数使用*/
void task2( void * pvParameters )
{ 
    UBaseType_t Tasks_Num = 0;
    UBaseType_t Priority_Num = 0;
    UBaseType_t Tasks_Num2 = 0;
    TaskStatus_t * Status_Array = 0;
    TaskStatus_t * Status_Array2 = 0;
    TaskHandle_t Task_Handler = 0;
    UBaseType_t Task_Stack_Min = 0;
    eTaskState TaskState = 0;
    
    uint8_t i = 0;
    
    vTaskPrioritySet(Task2_Handler,4);
    Priority_Num = uxTaskPriorityGet( NULL );
    printf("Task2任务优先级为:%d\r\n",Priority_Num);
    
    Tasks_Num = uxTaskGetNumberOfTasks();
    printf("任务数量为:%d\r\n",Tasks_Num);
    
    Status_Array = mymalloc(SRAMIN,(sizeof(TaskStatus_t) * Tasks_Num));
    Tasks_Num2 = uxTaskGetSystemState(Status_Array,Tasks_Num,NULL);
    printf("任务名\t\t任务优先级\t任务编号\r\n");
    for(i = 0;i<Tasks_Num2;i++)
    {
        printf("%s\t\t%ld\t%ld\r\n",
        Status_Array[i].pcTaskName,
        Status_Array[i].uxCurrentPriority,
        Status_Array[i].xTaskNumber);
    }
    
    Status_Array2 = mymalloc(SRAMIN,sizeof(TaskStatus_t));
    vTaskGetInfo( Task2_Handler,Status_Array2 ,pdTRUE,eInvalid );
    printf("任务名:%s\r\n",Status_Array2->pcTaskName);
    printf("任务优先级:%ld\r\n",Status_Array2->uxCurrentPriority);
    printf("任务编号:%ld\r\n",Status_Array2->xTaskNumber);
    printf("任务状态:%d\r\n",Status_Array2->eCurrentState);
    
    Task_Handler = xTaskGetHandle("task1");
    printf("任务句柄:%#x\r\n",(int)Task_Handler);
    printf("Task1任务句柄:%#x\r\n",(int)Task1_Handler);
    
    TaskState = eTaskGetState(Task1_Handler);
    printf("Task1的任务状态为%d\r\n",TaskState);
    
    vTaskList(Task_Buffer);
    printf("%s\r\n",Task_Buffer);
    while(1)
    {
//        Task_Stack_Min = uxTaskGetStackHighWaterMark(Task2_Handler);
//        printf("Task2历史剩余最小堆栈为:%ld\r\n",Task_Stack_Min);
        vTaskDelay(1000);
    }
}

 
 

10.2 时间管理-延时函数

  FreeRTOS提供了两种延时函数:

1、vTaskDelay();-相对延时,指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束

2、vTaskDelayUntil();-绝对延时,指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务

所谓的相对延时与绝对延时,可以理解为当一个500ms的任务执行时,相对延时是从调用函数vTaskDelay()后才开始计时500ms,而绝对延时则是这一整个任务加起来共执行500ms。

代码:

freertos_demo.c:

/**
 ****************************************************************************************************
 * @file        freertos.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.4
 * @date        2022-01-04
 * @brief       FreeRTOS 移植实验
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 精英F103开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 ****************************************************************************************************
 */

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define START_TASK_STACK_SIZE 128
 #define START_TASK_PRIORITY   1
 TaskHandle_t  Start_Task_Handler;
 void start_task( void * pvParameters );
 
 /* Task1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK1_STACK_SIZE 128
 #define TASK1_PRIORITY   2
 TaskHandle_t  Task1_Handler;
 void task1( void * pvParameters );
 
 /* Task2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
 #define TASK2_STACK_SIZE 128
 #define TASK2_PRIORITY   3
 TaskHandle_t  Task2_Handler;
 void task2( void * pvParameters );
 
/******************************************************************************************************/

/*
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
    xTaskCreate(( TaskFunction_t      ) start_task,
               (char *                ) "start_task", 
               (configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
               (void *                ) NULL,
               (UBaseType_t           ) START_TASK_PRIORITY,
               (TaskHandle_t *        ) &Start_Task_Handler );
    vTaskStartScheduler();//开启任务调度器
}

 void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();           /* 进入临界区 */
    /*创建任务1*/
    xTaskCreate(( TaskFunction_t       ) task1,
               (char *                ) "task1", 
               (configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
               (void *                ) NULL,
               ( UBaseType_t          ) TASK1_PRIORITY,
               (TaskHandle_t *        ) &Task1_Handler );
    
    /*创建任务2*/
    xTaskCreate(( TaskFunction_t       ) task2,
               (char *                ) "task2", 
               (configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
               (void *                ) NULL,
               ( UBaseType_t          ) TASK2_PRIORITY,
               (TaskHandle_t *         ) &Task2_Handler );
    vTaskDelete(NULL);//删除开始任务
    taskEXIT_CRITICAL();            /* 退出临界区 */
}


/*任务一,演示相对延时函数*/
void task1( void * pvParameters )
{
    while(1)
    {
        LED0_TOGGLE();
        delay_ms(10);
        vTaskDelay(500);
    }
}

/*任务二,演示绝对延时函数*/
void task2( void * pvParameters )
{ 
    TickType_t xLastWakeTime;
    xLastWakeTime = xTaskGetTickCount();
    while(1)
    {    
        LED1_TOGGLE();
        delay_ms(10);
        vTaskDelayUntil(&xLastWakeTime,500);
    }
}

 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值