1 时间片调度简介
同等优先级任务轮流享有相同的CPU时间(可设置),叫时间片,FreeRTOS中一个时间片就等于SysTick中断周期
运行条件:
1、创建三个任务:Task1、Task2、Task3
2、Task1、Task2、Task3的优先级均为1;即3个任务同等优先级
运行过程:
1)首先,Task1运行完一个时间片后,切换至Task2运行
2)Task2运行完一个时间片后,切换至Task3运行
3)Task3运行过程中(还不到一个时间片),Task3阻塞了(系统延时或等待信号量等),此时直接切换到下一个任务Task1.
4)Task1运行玩一个时间片后,切换至Task2运行
时间片特点:
1)同等优先级任务,轮流执行;时间片流转
2)一个时间片大小,取决于滴答定时器中断频率
3)注意没有用完的时间片不会再使用,下次Task3得到执行,还是按照一个时间片的时钟节拍运行。
2 时间片调度实验演示
实验设计:设计三个任务:start_task、task1、task2,其中task1和task2优先级相同均为2。为了使现象明显,将滴答定时器的中断频率设置为50ms中断一次,即一个时间片50ms。
#define configTICK_RATE_HZ 1000
/* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
/*1000Hz对应1ms,因此 1000/50 Hz对应50ms*/
滴答定时器的中断频率设置为50ms中断一次修改如下:
#define configTICK_RATE_HZ 20
注意:使用时间片调度需把宏configUSE_TIME_SLICING 和configUSE_PREEMPTION 置1
三个任务的功能如下:
start_task: 用来创建其他的2个任务
task1: 通过串口打印task1的运行次数
task2: 通过串口打印task2的运行次数
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.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); /* 任务函数 */
/******************************************************************************************************/
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(); //任务调度器
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务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* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
void task1(void *pvParameters)
{
uint32_t task1_num = 0;
while(1)
{
printf("task1运行次数:%d\r\n",++task1_num);
delay_ms(10);//调用该函数vTaskDelay()会引起任务调度,会将当前运行的任务挂载到阻塞列表
}
//理论上会打印50/10=5次,但printf也是需要耗时的,因此实际上整个运行过程>10ms,所以一个时间片50ms可以运行4~5次
}
void task2(void *pvParameters)
{
uint32_t task2_num = 0;
while(1)
{
printf("task2运行次数:%d\r\n",++task2_num);
delay_ms(10);//调用该函数vTaskDelay()会引起任务调度,会将当前运行的任务挂载到阻塞列表
}
//理论上会打印50/10=5次,但printf也是需要耗时的,因此实际上整个运行过程>10ms,所以一个时间片50ms可以运行4~5次
}
运行结果:
根据 运行结果可以看出,数据打印混乱。
修改如下程序:
void task1(void *pvParameters)
{
uint32_t task1_num = 0;
while(1)
{
taskENTER_CRITICAL(); /* 进入临界区 */
printf("task1运行次数:%d\r\n",++task1_num);
taskEXIT_CRITICAL(); /* 退出临界区 */
delay_ms(10);//调用该函数vTaskDelay()会引起任务调度,会将当前运行的任务挂载到阻塞列表
}
//理论上会打印50/10=5次,但printf也是需要耗时的,因此实际上整个运行过程>10ms,所以一个时间片50ms可以运行4~5次
}
void task2(void *pvParameters)
{
uint32_t task2_num = 0;
while(1)
{
taskENTER_CRITICAL(); /* 进入临界区 */
printf("task2运行次数:%d\r\n",++task2_num);
taskEXIT_CRITICAL(); /* 退出临界区 */
delay_ms(10);//调用该函数vTaskDelay()会引起任务调度,会将当前运行的任务挂载到阻塞列表
}
//理论上会打印50/10=5次,但printf也是需要耗时的,因此实际上整个运行过程>10ms,所以一个时间片50ms可以运行4~5次
}
运行结果:
添加临界区,则不会出现此情况。
注意此时Task1和Task2是同等优先级的,都是2 。
但此时如果将Task1的优先级设置成2,Task2的优先级设置成3,则会导致一直在Task2运行。
这是因为在每一个任务中我们使用了自己编写的delay_ms()函数不会导致任务进入阻塞状态,而ST官方提供的vTaskDelay()延时函数会导致任务进入阻塞态,因此不会进行任务调度,从而一直运行Task2,导致Task1无法运行,但如果Task1和Task2 优先级相同,则不会导致只有Task2运行。
注意:
1)在任务不同的优先级的任务中,使用delay()延时函数,则不会导致任务切换
2)在相同的任务优先级的任务中,使用dealy()延时函数,可以进行任务切换,因为同等优先级的任务都有相同的时间片,进行任务流转。