当前位置: 首页 > news >正文

µCOS-III从入门到精通 第八章(时间片调度)

参考教程:【正点原子】手把手教你学UCOS-III实时操作系统_哔哩哔哩_bilibili

一、时间片调度简介

1、时间片调度回顾

        时间片调度主要针对优先级相同的任务,当多个任务的优先级相同时,同等优先级任务轮流地享有相应的CPU时间(可分别设置),叫时间片,在µC/OS-III中,一个时间片就等于SysTick(滴答定时器)中断周期。

        创建三个任务——Task1、Task2、Task3,Task1、Task2、Task3的优先级均设置为1,时间片默认值(或者说时间片的数量)均设置为100

        运行过程如下:

                首先Task1运行完100个时间片后,切换至Task2运行

                Task2运行完100个时间片后,切换至Task3运行

                Task3运行过程中被挂起了(如果还不到一个时间片,没有用完的时间片不会再使用),此时直接切换到下一个任务Task1运行

                Task1运行完一个时间片后,切换至Task2运行

2、OSSchedRoundRobinCfg函数

(1)该函数用于开启时间片调度,并设置每个任务的时间片的默认值(或者说每个任务的默认时间片数量)。

(2)函数接口定义:

void OSSchedRoundRobinCfg ( CPU_BOOLEAN   	en, 
				 			OS_TICK       	dflt_time_quanta,                            
				 			OS_ERR      	*p_err )

形参

描述

en

是否使能时间片调度

OS_TRUE——使能,OS_FALSE——失能

dflt_time_quanta

传入0则设置每个任务的默认时间片数量为µC/OS-III默认的时间片数量,否则采用传入的时间片数量为每个任务的默认时间片数量(创建任务时如果设置任务的时间片数量为0,则该任务的时间片数量为OSSchedRoundRobinCfg函数设置的默认值)

p_err

指向接收错误代码变量的指针

(3)使用时间片调度需把宏OS_CFG_SCHED_ROUND_ROBIN_EN置1(在os_cfg.h文件中)。

二、时间片调度实验

1、原理图与实验目标

(1)原理图(未画出OLED屏,接法与stm32教程中的一致):

(2)实验目标:

①设计3个任务——start_task、task1、task2(同优先级):

[1]start_task:用于开启时间片调度,设置每个任务的时间片的默认数量为10个(即10ms),并创建其它的2个任务。

[2]task1:设置时间片数量为10个(即10ms),并维护一个计数值,不断自增(不考虑溢出),OLED屏显示计数值。

[3]task2:设置时间片数量为5个(即5ms),并维护一个计数值,不断自增(不考虑溢出),OLED屏显示计数值。

②预期实验现象:

[1]程序下载到板子上后,OLED屏上显示两个计数值。

[2]task1维护的计数值和task2维护的计数值交替自增,task1计数值自增速度约等于task2计数值自增速度的两倍。

2、实验步骤

(1)将“任务创建和删除实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)Delay.c文件中的延时函数需修改为如下所示的实现(纯占用CPU资源,这种延时函数其实是不好的,但就该实验而言,仅仅是为了观察实验现象,不需要考虑太多)。

void Delay_ms(u16 time)
{    
   u16 i=0;  
   while(time--)
   {
      i=12000;     //可微调,使延时更加精准
      while(i--) ;    
   }
}

(3)修改UCOS_experiment.c文件:

①删除task3任务的相关内容。

②在start_task中调用OSSchedRoundRobinCfg函数开启时间片调度,并设置每个任务的时间片的默认值为10个,同时创建task1时设置其时间片数量为默认值,创建task2时设置其时间片数量为5个。

③将task1和task2的任务优先级均设置为2,并修改task1和task2的实现,各自维护的计数值在死循环内每20ms自增一次。

#include "stm32f10x.h"                  // Device header
#include "os.h"
#include "cpu.h"
#include "OLED.h"
#include "Delay.h"
#include <stdio.h>

/* START_TASK 任务 配置
 * 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define     START_TASK_PRIO         2
#define     START_TASK_STACK_SIZE   256
CPU_STK     start_task_stack[START_TASK_STACK_SIZE];
OS_TCB      start_task_tcb;
void start_task(void);

/* TASK1 任务 配置
 * 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define     TASK1_PRIO              2
#define     TASK1_STACK_SIZE        256
CPU_STK     task1_stack[TASK1_STACK_SIZE];
OS_TCB      task1_tcb;
void task1(void);

/* TASK2 任务 配置
 * 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define     TASK2_PRIO              2
#define     TASK2_STACK_SIZE        256
CPU_STK     task2_stack[TASK2_STACK_SIZE];
OS_TCB      task2_tcb;
void task2(void);

void UCOS_Test(void)
{
	OS_ERR err;
	
	OSInit(&err);    //初始化μC/OS-III

	//创建Start Task
    OSTaskCreate (&start_task_tcb,
                  "start_task",
                  (OS_TASK_PTR)start_task,
                  NULL,
                  START_TASK_PRIO,
                  start_task_stack,
                  START_TASK_STACK_SIZE / 10,
                  START_TASK_STACK_SIZE,
                  0,
                  0,
                  0,
                  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                  &err);
    
    /* 开始任务调度 */
    OSStart(&err);
}

void start_task(void)              
{
    OS_ERR err;    //接收错误代码使用,对错误代码进行分情况处理可增强程序鲁棒性
    CPU_Init();     //初始化CPU库
	
	OSSchedRoundRobinCfg(OS_TRUE, 10, &err);   //使能时间片调度并设置默认时间片数量为10(即10ms)
	
    CPU_SR_ALLOC();
    
	//滴答定时器重装载值 = 系统主频 / 滴答定时器中断频率(滴答定时器是递减计数)
    CPU_INT32U cnts = SystemCoreClock / OS_CFG_TICK_RATE_HZ;
    OS_CPU_SysTickInit(cnts);   //配置Systick中断及优先级
    
    CPU_CRITICAL_ENTER();           //进入临界区(关中断)
	
    //创建task1
    OSTaskCreate (&task1_tcb,
                  "task1",
                  (OS_TASK_PTR)task1,
                  0,
                  TASK1_PRIO,
                  task1_stack,
                  TASK1_STACK_SIZE / 10,
                  TASK1_STACK_SIZE,
                  0,
                  0,          //task1时间片数量设置为默认时间片数量
                  0,
                  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                  &err);

    //创建task2
    OSTaskCreate (&task2_tcb,
                  "task2",
                  (OS_TASK_PTR)task2,
                  0,
                  TASK2_PRIO,
                  task2_stack,
                  TASK2_STACK_SIZE / 10,
                  TASK2_STACK_SIZE,
                  0,
                  5,          //task2时间片数量设置为5个
                  0,
                  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                  &err);      
    
    CPU_CRITICAL_EXIT();            //退出临界区(开中断)
    OSTaskDel(NULL, &err);            //删除任务自身
}

u8 task1_num = 0;         //task1维护的计数值
void task1(void)
{
	CPU_SR_ALLOC();
	while(1)     //task1永远不停歇(除非被其它任务删除)
	{
		CPU_CRITICAL_ENTER(); //进入临界区(防止在下两条语句产生任务切换)
		task1_num++;
		OLED_ShowNum(1,12,task1_num,3);    //显示计数值
		CPU_CRITICAL_EXIT();  //退出临界区(开中断)
		Delay_ms(20);          //20ms死延时
	}
}

u8 task2_num = 0;         //task2维护的计数值
void task2(void)
{
	CPU_SR_ALLOC();
	while(1)     //task2永远不停歇(除非被其它任务删除)
	{
		CPU_CRITICAL_ENTER(); //进入临界区(防止在下两条语句产生任务切换)
		task2_num++;
		OLED_ShowNum(2,12,task2_num,3);    //显示计数值
		CPU_CRITICAL_EXIT();  //退出临界区(开中断)
		Delay_ms(20);          //20ms死延时
	}
}

(4)在main.c文件中添加如下代码。

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "UCOS_experiment.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();    //OLED初始化
	
	OLED_ShowString(1,1,"task1_num:");
	OLED_ShowString(2,1,"task2_num:");
	UCOS_Test();
	
	while (1)
	{
		
	}
}

(5)程序完善好后点击“编译”,然后将程序下载到开发板上。

3、程序执行流程

(1)main函数全流程(省略OLED屏显示字符串部分):

①初始化OLED模块。

②调用UCOS_Test函数。

(2)测试函数全流程:

①创建任务start_task。

②开启任务调度器。

(3)多任务调度执行阶段(发生在开启任务调度器以后):

①start_task任务函数首先进入临界区,在临界区中start_task任务不会被其它任务打断,接着start_task任务依次创建任务task1、task2,然后删除自身,接着退出临界区,让出CPU资源。

②任务task1、task2同优先级,由于task1先创建,它在就绪队列的首端,于是先执行task1,task1的执行逻辑如下图所示(透明蓝色块代表任务不在运行态),方便起见,认为语句集合1中的语句的运行时间为5ms,则语句集合1的执行周期约为35ms。

③task1和task2同优先级,task1享用的时间片结束以后,轮到task2享用下一个时间片,task2的执行逻辑如下图所示(透明蓝色块代表任务不在运行态),方便起见,认为语句集合2中的语句的运行时间为5ms,则语句集合2的执行周期约为75ms。

相关文章:

  • 点云 基于法线的双边滤波原理和过程
  • LeetCode hot 100—二叉树的最大深度
  • 能量石[算法题]
  • YOLOv12 项目部署指南! 含报错解决
  • Flutter底层实现
  • Go学习笔记:基础语法3
  • 【由技及道】镜像星门开启:Harbor镜像推送的量子跃迁艺术【人工智障AI2077的开发日志010】
  • CSS+Html面试题(二)
  • python网络爬虫开发实战之爬虫基础
  • Unity自定义渲染管线(Scriptable Render Pipeline)架构设计与实现指南
  • netty中Future和ChannelHandler
  • Best practice-生产环境中加锁的最佳实践
  • Anaconda 部署 DeepSeek
  • Java 大视界 -- Java 大数据在智能政务公共服务资源优化配置中的应用(118)
  • Linux | Vim 鼠标不能右键粘贴、跨系统复制粘贴
  • 深入解析“Elaborate”——从详细阐述到精心制作的多重含义
  • 绝美焦糖暖色调复古风景画面Lr调色教程,手机滤镜PS+Lightroom预设下载!
  • LLM-初识AI
  • 自律linux 第 35 天
  • 【C++】数据结构 双链表的实现(企业存储用户数据的实现)
  • 中纪报:强化监督推动过紧日子要求落到实处
  • 意德首脑会谈,梅洛尼警告欧盟绿色政策面临“工业荒漠化”
  • 中国旅马大熊猫“福娃”和“凤仪”启程回国
  • 视频丨歼-10CE首次实战大放异彩
  • 湖南4个县市区被确定为野生蘑菇中毒高风险区:中毒尚无特效解毒药
  • 最高人民法院、中国证监会联合发布《关于严格公正执法司法 服务保障资本市场高质量发展的指导意见》