自学内容网 自学内容网

FreeRTOS学习11——时间片任务调度

时间片任务调度

时间片任务调度

概念:时间片调度主要针对优先级相同的任务,当多个任务的优先级相同时,任务调度器会在每一次系统时钟节拍到的时候切换任务,也就是说 CPU 轮流运行优先级相同的任务,每个任务运行的时间就是一个系统时钟节拍。

同等优先级任务轮流地享有相同的 CPU 时间(可设置), 叫时间片,在FreeRTOS中,一个时间片就等于SysTick 中断周期

特点:

  • 同等优先级任务,轮流执行;时间片流转
  • 一个时间片大小,取决为滴答定时器中断频率
  • 注意没有用完的时间片不会再使用,下个任务得到执行还是按照一个时间片的时钟节拍运行

下面我们举个例子详细说

image-20241111200848938

我们创建优先级相同的几个任务,其运行流程如下

  1. 首先Task1运行完一个时间片后,切换至Task2运行
  2. Task2运行完一个时间片后,切换至Task3运行
  3. Task3运行过程中(还不到一个时间片),Task3阻塞了(系统延时或等待信号量等),此时直接切换到下一个任务Task1
  4. Task1运行完一个时间片后,切换至Task2运行

接下来用一个实验来验证我们的理论

​ 实验设计:将设计三个任务:start_task、task1、task2,其中task1和task2优先级相同均为2。为了使现象明显,将滴答定时器的中断频率设置为50ms中断一次,即一个时间片50ms

​ 三个任务的功能如下:

  • start_task:用来创建其他的2个任务
  • task1:通过串口打印task1的运行次数
  • task2:通过串口打印task2的运行次数

注意:使用时间片调度需把宏 configUSE_TIME_SLICING 和configUSE_PREEMPTION 置1

在打印任务运行次数的时候需要使用到串口硬件,为了避免多个任务“同时”使用同一个硬件,因此在使用串口硬件打印任务运行次数之前,进入临界区,在使用串口硬件打印任务运行次数之后,再退出临界区

实验代码freertos_demo.c

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "delay.h"
/*FreeRTOS********************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/*****************************************************************************/
/*FreeRTOS 配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 2/* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */



/*****************************************************************************/

/**
* @brief FreeRTOS 例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
 xTaskCreate((TaskFunction_t )start_task, /* 任务函数 */
 (const char* )"start_task", /* 任务名称 */
 (uint16_t )START_STK_SIZE, /* 任务堆栈大小 */
 (void* )NULL, /* 传入给任务函数的参数 */
 (UBaseType_t )START_TASK_PRIO,/* 任务优先级 */
 (TaskHandle_t* )&StartTask_Handler); /* 任务句柄 */
 vTaskStartScheduler();                           /* 开启任务调度 */
}
/**
* @brief start_task
* @param pvParameters : 无
* @retval 无
*/
void start_task(void *pvParameters)
{
 taskENTER_CRITICAL(); /* 进入临界区 在临界区内不进行任务调度,保证新创建的任务不会抢占start_task,也就是等所有任务创建完后再开始任务调度*/
 /* 创建任务 1 */
 xTaskCreate((TaskFunction_t )task1,
 (const char* )"task1",
 (uint16_t )TASK1_STK_SIZE,
 (void* )NULL,
 (UBaseType_t )TASK1_PRIO,
 (TaskHandle_t* )&Task1Task_Handler);
 /* 创建任务 2 */
 xTaskCreate((TaskFunction_t )task2,
 (const char* )"task1",
 (uint16_t )TASK2_STK_SIZE,
 (void* )NULL,
 (UBaseType_t )TASK2_PRIO,
 (TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(NULL);/* 删除开始任务 */
 taskEXIT_CRITICAL();  /* 退出临界区 */
}
/**
* @brief task1
* @param pvParameters : 无
* @retval 无
*/
/* task1实现LED0每200ms闪烁一次*/
void task1(void *pvParameters)
{

static uint16_t num;
while(1)
 {
 taskENTER_CRITICAL();
 num++;
printf("任务1运行次数:%d\r\n",num);
 taskEXIT_CRITICAL();
 delay_ms(10);         
}
}


/* task2 列表项的插入和删除*/
void task2(void *pvParameters)
{
static uint16_t num;
while(1)
 {
 taskENTER_CRITICAL();
 num++;
printf("任务2运行次数:%d\r\n",num);
 taskEXIT_CRITICAL();
 delay_ms(10);
 }
}

实验现象
在这里插入图片描述
每个任务执行 四到五次
与理论相符

delay_ms(10);延时为 “死延时” 在延时时不会进行任务调度
vTaskDelay(1000);该函数在延时时会进行任务调度


原文地址:https://blog.csdn.net/m0_74219261/article/details/143695333

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!