10 FreeRTOS时间片调度

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()延时函数,可以进行任务切换,因为同等优先级的任务都有相同的时间片,进行任务流转。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值