FreeRTOS时间片调度

1. 时间片调度简介

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

  • 像我们的源码,滴答定时器是 1ms 中断一次,那么一个时间片的时间就是 1ms。
  • 可设置:设置的就是滴答定时器的中断周期(中断频率)。

在这里插入图片描述

运行条件:

  1. 创建三个任务:Task1、Task2、Task3
  2. Task1、Task2、Task3的优先级均为1;即3个任务同等优先级

运行过程如下:

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

Task3 只执行不到一个时间片,那下一次再回到 Task3 的时候,怎么样?它剩下的时间片不会要了,同样的也只给它一个时间片的时间去执行。

总结:

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

2. 时间片调度实验

实验目的:学会对FreeRTOS 时间片调度使用
实验设计:将设计三个任务:start_task、task1、task2,其中task1和task2优先级相同均为2。

为了使现象明显,将滴答定时器的中断频率设置为 50ms 中断一次,即一个时间片 50ms

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

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

2.1 宏设置

把宏 configUSE_TIME_SLICING(时间片调度) 和 configUSE_PREEMPTION(抢占式调度) 置1

2.2 滴答定时器中断频率设置

#define configTICK_RATE_HZ                              20                      /* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */

1000Hz 对应 1ms,20Hz 对应 50 ms(倒数关系)。也就是说现在一个时间片的时间就是 50 ms。

2.3 任务函数实现

freertos_demo.c

#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"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

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

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );


/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         2
#define TASK2_STACK_SIZE   128
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_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,通过串口打印task1的运行次数 */
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);
    }
}


/* 任务二,通过串口打印task2的运行次数 */
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);
    }
}
  • 调用系统延时函数是会引起任务调度的,因为会把当前正在运行的任务挂载到阻塞列表,那这样的话,我们就体现不到我们一个时间片 50ms 这样的一个作用了,我们现在知道它的运行次数,那任务一阻塞立马就跳到下一个任务,那这样的话现象就不明显了,所以呢我们这里要用死等(死延时)函数,它就不会实现任务调度这些了。

死等(死延时)函数是没有进入阻塞的,是一直在运行的,我们调用这个延时函数是不会进行任务调度的,所以任务一直在就绪列表里面,这时候它不会进入阻塞状态,理论上的话任务会一直在运行。

  • delay_ms() + printf() > 10ms,所以一个时间片正常能运行 4~5 次,那这个就是正常范围了。

注意:在 printf 函数执行到一半时进行任务切换将会导致数据就乱了,所以避免这种情况建议大家最好是加上临界区保护,保证 printf 函数执行完再执行任务切换。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值