首页 > 其他分享 >FreeRTOS 简单内核实现8 时间片轮询

FreeRTOS 简单内核实现8 时间片轮询

时间:2024-06-18 15:44:56浏览次数:22  
标签:优先级 FreeRTOS 轮询 链表 任务 内核 任务调度 void

0、思考与回答

0.1、思考一

为什么要增加时间片轮询?

目前的 RTOS 内核已经支持抢占优先级,即高优先级的任务会抢占低优先级的任务得到执行,但是对于同等优先级的任务,如果不支持时间片轮询,则只能有一个任务运行,并且由于优先级相同所以除延时阻塞到期外也不会发生任务调度,因此需要增加时间片轮询保证同等优先级的任务能得到轮流执行

1、内核程序修改

1.1、xTaskIncrementTick( )

在该函数中除了任务延时阻塞时间到期产生任务调度外,增加支持时间片轮询的任务切换,具体如下所示

/*task.c*/
BaseType_t xTaskIncrementTick(void)
{
	// 省略未修改的程序
	......
#if((configUSE_PREEMPTION == 1) && (configUSE_TIME_SLICING == 1))
	// 支持时间片轮询
	if(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[pxCurrentTCB->uxPriority])) > 1)
	{
		xSwitchRequired = pdTRUE;
	}
#endif
	return xSwitchRequired;
}
/* FreeRTOSConfig.h */
// 支持时间片轮询
#define configUSE_TIME_SLICING                  1

1.2、原理

假设当前系统中只存在两个优先级相同的任务,时间片轮询任务切换流程如下

xTaskIncrementTick()
-> vTaskSwitchContext()
-> taskSELECT_HIGHEST_PRIORITY_TASK()
-> listGET_OWNER_OF_NEXT_ENTRY()

当进入滴答定时器中断服务函数时,如果发现就绪链表数组中的某个链表中链表项的数量大于 1 ,则表示该优先级下有不止一个任务,此时就可以产生任务调度

当有任务调度产生的时候,会调用 vTaskSwitchContext()taskSELECT_HIGHEST_PRIORITY_TASK() 两个函数寻找当前的最高优先级任务,但是系统中只有两个优先级相同的任务,因此最高优先级仍然没变,但是在这个优先级下返回的任务却变成了下一个

为什么呢?

关键在于 listGET_OWNER_OF_NEXT_ENTRY() 函数,这个宏函数每次调用会获取链表中下一个链表项的 pvOwner 参数,由于是双向链表,因此会不断的循环链表中的链表项(两个同等优先级的任务),每次发生任务调度就会切换一次任务,所以就实现了时间片轮询

3、实验

3.1、测试

参考 FreeRTOS 简单内核实现6 优先级 "3.1、测试" 小节内容,将两个任务的优先级修改为一样,然后在两个任务中均使用软件延时模拟任务连续运行,具体程序如下所示

/* main.c */
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
/* USER CODE END Includes */

/* USER CODE BEGIN PV */
// 软件延时
void delay(uint32_t count)
{
	for(;count!=0;count--);
}

TaskHandle_t Task1_Handle;
#define TASK1_STACK_SIZE                    128
StackType_t Task1Stack[TASK1_STACK_SIZE];
TCB_t Task1TCB;
UBaseType_t Task1Priority = 2;

TaskHandle_t Task2_Handle;
#define TASK2_STACK_SIZE                    128
StackType_t Task2Stack[TASK2_STACK_SIZE];
TCB_t Task2TCB;
UBaseType_t Task2Priority = 2;

// 任务 1 入口函数
void Task1_Entry(void *parg)
{
	for(;;)
	{
		HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin);
		delay(10000000);
	}
}
// 任务 2 入口函数
void Task2_Entry(void *parg)
{
	for(;;)
	{
		HAL_GPIO_TogglePin(ORANGE_LED_GPIO_Port, ORANGE_LED_Pin);
		delay(10000000);
	}
}
/* USER CODE END PV */

/* USER CODE BEGIN 2 */
// 创建任务 1 和 2
Task1_Handle = xTaskCreateStatic((TaskFunction_t)Task1_Entry,
								 (char *)"Task1",
								 (uint32_t)TASK1_STACK_SIZE,
								 (void *)NULL,
								 (UBaseType_t)Task1Priority,
								 (StackType_t *)Task1Stack,
								 (TCB_t *)&Task1TCB);
														
Task2_Handle = xTaskCreateStatic((TaskFunction_t)Task2_Entry,
								 (char *)"Task2",
								 (uint32_t)TASK2_STACK_SIZE,
								 (void *) NULL,
								 (UBaseType_t)Task2Priority,
								 (StackType_t *)Task2Stack,
								 (TCB_t *)&Task2TCB );
// 启动任务调度器,永不返回
vTaskStartScheduler();
/* USER CODE END 2 */

configUSE_TIME_SLICING 调整为 0 ,然后烧录程序,仍然使用逻辑分析仪捕获两个 LED 的引脚电平,结果如下图所示

可以发现在不启用时间片轮询时,由于两个任务优先级一致,并且两个任务模拟连续运行,因此只有任务 Task2 被运行 (为什么是 Task2 ?)

configUSE_TIME_SLICING 调整为 1,然后烧录程序,仍然使用逻辑分析仪捕获两个 LED 的引脚电平,结果如下图所示

可以发现,对于优先级相同且连续运行的任务几乎是在同时运行,其实是因为每个时间片(滴答定时器间隔)都发生了一次任务调度

3.2、待改进

当前 RTOS 简单内核已实现的功能有

  1. 静态方式创建任务
  2. 手动切换任务
  3. 临界段保护
  4. 任务阻塞延时
  5. 支持任务优先级
  6. 阻塞链表
  7. 时间片轮询

后续 RTOS 简单内核可以增加

  1. 任务间通信机制
    1. 信号量
    2. 互斥锁
    3. 消息队列
    4. ......
  2. 其他功能
    1. 软件定时器
    2. ......

标签:优先级,FreeRTOS,轮询,链表,任务,内核,任务调度,void
From: https://www.cnblogs.com/lc-guo/p/18250554

相关文章

  • FreeRTOS 体验教程:3.如何用互斥量实现FreeRTOS多线程访问共享资源?
    FreeRTOS互斥量使用教程互斥量(Mutex)是一种特殊的信号量,用于管理对共享资源的访问。在FreeRTOS中,互斥量的句柄类型依然是xSemaphoreHandle。本文将详细介绍如何在FreeRTOS中创建和使用互斥量,并通过实例展示其运行效果。1.创建互斥量在FreeRTOS中,创建互斥量非常简......
  • FreeRTOS简单内核实现6 优先级
    0、思考与回答0.1、思考一如何实现RTOS内核支持多优先级?因为不支持优先级,所以所有的任务都插入了一个名为pxReadyTasksLists的就绪链表中,相当于所有任务的优先级都是一致的,那如果我们创建一个就绪链表数组,数组下标代表优先级,优先级为x的任务就插入到pxReadyTasksLists[x......
  • 从Linux内核设计者的角度看 - 设备驱动的架构设计
    Linux中的设备驱动概念中的设备和驱动指的是啥?  直接说设备驱动其实是比较抽象的,举个例子就特别明了了,比如我们要控制1个led的亮灭,那么led就是设备,控制led运行的软件就是该设备的驱动。也就是说,这里的设备就是现实中的一个电子设备,设备驱动就是控制这个电子设备运行的软件程序......
  • FreeRTOS简单内核实现5 阻塞延时
    0、思考与回答0.1、思考一为什么FreeRTOS简单内核实现3任务管理文章中实现的RTOS内核不能看起来并行运行呢?Task1延时100ms之后执行taskYIELD()切换到Task2,Task2延时500ms之后执行taskYIELD()再次切换Task1,在延时期间两个任务均占用MCU,所以只能一个任务执行......
  • Linux 内核定时器实验
    Linux内核定时器实验内核时间管理简介Linux内核中有大量的函数需要时间管理,比如周期性的调度程序、延时程序、对于我们驱动编写者来说最常用的定时器。硬件定时器提供时钟源,时钟源的频率可以设置,设置好以后就周期性的产生定时中断,系统使用定时中断来计时。中断周期性产生的频......
  • FreeRTOS简单内核实现4 临界段
    @目录0、思考与回答0.1、思考一0.2、思考二0.3、思考三1、关中断1.1、带返回值1.2、不带返回值2、开中断3、临界段4、应用0、思考与回答0.1、思考一为什么需要临界段?有时候我们需要部分代码一旦这开始执行,则不允许任何中断打断,这段代码称为临界段0.2、思考二如何实现临界段......
  • 内核参数kernel.shmall和kernel.shmmax
    在Linux系统中,内核参数kernel.shmall和kernel.shmmax与共享内存(SystemV共享内存)有关,它们分别定义了系统可以分配的共享内存段的最大页数和单个共享内存段的最大字节数。以下是一些关于这些参数的推荐设置:kernel.shmall:这个参数控制可以使用的共享内存的总页数。Linux共享内存......
  • 使用Modbus转Profinet网关无需编写Modbus轮询程序,实现PLC和电表通信
    一、无需编写Modbus轮询程序实现PLC与电表通信的方法在工业自动化领域,PLC(可编程逻辑控制器)与电表之间的通信是非常常见的需求。传统上,为了让PLC与电表进行通信,通常需要编写Modbus轮询程序来实现数据的读取和控制。然而,近年来出现了一种新的方法,即通过使用Modbus转Profinet网关,可......
  • 泰山派学习10--内核驱动模块
    1、编写内核驱动模块hello.c2、编写makefile3、编译makemodule4、拷贝到开发板adbpush./hello.ko/home/zbl/drv5、修改文件执行权限chmod777hello.ko6、加载内核驱动sudoinsmodhello.ko7、查看下加载是否成功lsmod8、卸载内核驱动sudormmodhello.ko /**......
  • FreeRTOS简单内核实现3 任务管理
    0、思考与回答0.1、思考一对于Cotex-M4内核的MCU在发生异常/中断时,哪些寄存器会自动入栈,哪些需要手动入栈?会自动入栈的寄存器如下R0-R3:通用寄存器R12:通用寄存器LR(LinkRegister):链接寄存器,保存返回地址PC(ProgramCounter):程序计数器,保存当前执行指令的地址xPSR(Pro......