FreeRTOS实时操作系统(七)时间片调度及RTOS的滴答定时器

news2024/10/6 10:38:52

系列文章目录

FreeRTOS实时操作系统(一)RTOS的基本概念

FreeRTOS实时操作系统(二)任务创建与任务删除(HAL库)

FreeRTOS实时操作系统(三)任务挂起与恢复

FreeRTOS实时操作系统(四)中断任务管理

FreeRTOS实时操作系统(五)进入临界区、任务调度器挂起与恢复

FreeRTOS实时操作系统(六)列表与列表项


文章目录

  • 系列文章目录
  • 前言
  • 时间片调度概念
  • 滴答定时器
    • 未带RTOS的HAL库
    • 带RTOS的HAL库
  • 时间片调度实验
    • 改变时间片大小


前言

在学习正点原子的时间片调度的教程中,突然要改变滴答定时器的中断频率,而我之前对这方面没有一点点了解,所以需要详细补充一下这个知识点。


时间片调度概念

在第一片文章里面提到过时间片调度的概念,这里重复一下:
在这里插入图片描述

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

首先每个任务只执行一个时间片,不管任务里面的while循环运行了几次,时间到达之后就要切换到下一个任务。

时间片的大小取决于滴答定时器的中断频率。

像Task3这样被阻塞的任务(系统延时或等待信号等),将直接切换到下一个任务,时间片没执行到也不管,剩下的时间片直接丢掉了。

滴答定时器

像上面的就涉及到了滴答定时器的中断设置,影响了时间片的长度,之前学习HAL库或者蓝桥杯等等,最多是对1ms的滴答定时器利用,从没想过更改,如下图:
在这里插入图片描述
滴答定时器有四个寄存器:

CTRLSysTick控制及状态寄存器
LOADSysTick重装载数值寄存器
VALSysTick当前数值寄存器
CALIBSysTick校准数值寄存器

未带RTOS的HAL库

参考手册中说:RCC通过AHB时钟(HCLK)8分频后作为Cortex系统定时器(SysTick)的外部时钟。通过对SysTick控制与状态寄存器的设置,可选择上述时钟或Cortex(HCLK)时钟作为SysTick时钟。

在这里插入图片描述
也就是是说可以选择AHB时钟8分频或HCLK(内核)时钟。

网上介绍说:HAL库是无法配置这个1分频和8分频,生成的都是一样的,由下图所示,默认选择的是HCLK时钟。
至于解决方案:stm32cubemx配置systick滴答定时器有bug
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我这里虽然灰了,但是我看了看条件,确实定义了__Vendor_SysTickConfig,同时它的值也是0,这里可能是编译器有问题吧。
在这里插入图片描述

带RTOS的HAL库

在生成的RTOS的HAL库代码中,与标准库有一些区别,在这里重新定义了这个函数。

在这里插入图片描述

HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  RCC_ClkInitTypeDef    clkconfig;
  uint32_t              uwTimclock = 0;
  uint32_t              uwPrescalerValue = 0;
  uint32_t              pFLatency;
  /*Configure the TIM1 IRQ priority */
  HAL_NVIC_SetPriority(TIM1_UP_IRQn, TickPriority ,0);

  /* Enable the TIM1 global Interrupt */
  HAL_NVIC_EnableIRQ(TIM1_UP_IRQn);

  /* Enable TIM1 clock */
  __HAL_RCC_TIM1_CLK_ENABLE();

  /* Get clock configuration */
  HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);

  /* Compute TIM1 clock */
  uwTimclock = HAL_RCC_GetPCLK2Freq();
  /* Compute the prescaler value to have TIM1 counter clock equal to 1MHz */
  uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);

  /* Initialize TIM1 */
  htim1.Instance = TIM1;

  /* Initialize TIMx peripheral as follow:
  + Period = [(TIM1CLK/1000) - 1]. to have a (1/1000) s time base.
  + Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
  + ClockDivision = 0
  + Counter direction = Up
  */
  htim1.Init.Period = (1000000U / 1000U) - 1U;
  htim1.Init.Prescaler = uwPrescalerValue;
  htim1.Init.ClockDivision = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;

  if(HAL_TIM_Base_Init(&htim1) == HAL_OK)
  {
    /* Start the TIM time Base generation in interrupt mode */
    return HAL_TIM_Base_Start_IT(&htim1);
  }

  /* Return function status */
  return HAL_ERROR;
}

这是因为在HAL库中,我把TIM1定时器作为基础时钟源,TIM1就不能在作为其他用途。在STM32CubeMX中不能再对TIM1做任何设置。在NVIC中,TIM1的中断被自动启用,可以修改TIM1的中断优先级,但是不能关闭TIM1的中断。同时,SysTick定时器的中断也还是被自动启用的,且不能关闭,
在这里插入图片描述

在这里插入图片描述
所以在这里,我用TIM1替代了滴答定时器,完全代替了SysTick的功能。

原因是:在使用FreeRTOS时,必须为HAL设置一个非SysTick定时器作为HAL的基础时钟,SysTick将自动作为FreeRTOS的基础时钟。也是就强制滴答定时器作为FreeRTOS的心跳,关系到任务调度和时间片,但是我们之前了解过HAL_Delay,原来其内部实现靠的是滴答定时器,现在自然变成你设置的其他定时器提供了。为啥HAL和FreeRTOS不都使用一个SysTick,是因为内部会发生冲突
参考:FreeRTOS的基础时钟
参考:FreeRTOS的Systick和HAL时基

SysTick定时器:
文件port.c中的函数xPortStartScheduler()和vPortSetupTimerInterrupt()。函数xPortStartScheduler()中设置SysTick和PendSV中断的中断优先级,函数vPortSetupTimerInterrupt()设置SysTick的定时周期,
在这里插入图片描述
portNVIC_SYSTICK_CTRL_REG、portNVIC_SYSTICK_LOAD_REG等宏就是相关寄存器。更改这些能改变滴答定时器的时间。

综合上面这些程序代码,就可以分析出来,改变上图的宏定义就可以实现对时间片大小的调整。

时间片调度实验

设计目标:将设计三个任务:start_task、task1、task2,其中task1和task2优先级相同均为2。为了使现象明显,将滴答定时器的中断频率设置为50ms中断一次,即一个时间片50ms。

将宏configUSE_TIME_SLICING 和 configUSE_PREEMPTION 置1。
抢占式调度器宏:
在这里插入图片描述
时间片调度宏:
在这里插入图片描述

改变时间片大小

/* Configure SysTick to interrupt at the requested rate. */
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );

现在详细介绍一下这个portNVIC_SYSTICK_LOAD_REG :

configSYSTICK_CLOCK_HZ :
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一步一步追踪:configSYSTICK_CLOCK_HZ=16M;

configTICK_RATE_HZ=1000;
在这里插入图片描述
所以计算可得:portNVIC_SYSTICK_LOAD_REG=16k-1;

在16M的时钟下,每16k次计数就中断一次,所以滴答定时器的中断时间就是1ms。
所以一般我们改变这个configTICK_RATE_HZ值就可以改变滴答定时器的时间。

要改成50ms的滴答定时器,就把这个configTICK_RATE_HZ值改为20即可。

之后就可以进行时间片调度的测试:

TaskHandle_t    task1_handler;

#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 );

void vTaskCode( 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();                /* 退出临界区 */
 }

 // Function that creates a task.
 void vOtherFunction( void )
 {
	xTaskCreate( vTaskCode, "tak1", 128, NULL, 1, &task1_handler );
	vTaskStartScheduler();
 }
 
void task1( void * pvParameters )
{
    uint32_t task1_num = 0;
    while(1)
    {
        taskENTER_CRITICAL();               /* 进入临界区 */
        printf("task1运行次数:%d\r\n",++task1_num);
        taskEXIT_CRITICAL();                /* 退出临界区 */
        HAL_Delay(10);
    }
}

void task2( void * pvParameters )
{
    uint32_t task2_num = 0;
    while(1)
    {
        taskENTER_CRITICAL();               /* 进入临界区 */
        printf("task2运行次数:%d\r\n",++task2_num);
        taskEXIT_CRITICAL();                /* 退出临界区 */
        HAL_Delay(10);
    }
}

设置两个串口发送任务的优先级一样,之后就可以测试结果:
在这里插入图片描述
可以看到最后在50ms的时间片内,每个任务的while循环大概执行5次后进行了任务的切换。

在上面代码中,我们在串口发送函数上下加入了临界区代码保护,主要是防止数据发送了一半,切换了任务,数据没发完。

如果上面代码的两个任务优先级不同,就会出现一直执行优先级高的任务的现象,因为它不会出现阻塞了,就一直是就绪态。

这里能使用HAL_Delay()函数,但不能使用vTaskDelay(),都相当于阻塞任务,会出现时间片还没用完就进行了任务切换,而且因为滴答定时器的时间变化了,vTaskDelay的单位就不再是1ms了,是50ms单位了
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/683824.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】硬件性能测试工具

1、CPU 1.1 stress 1)安装 sudo apt install stress 2)用法 stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --timeout 60s3)结果 使用htop查看: 1.2 nbench 1)安装 需要源码编译,源码下载地址 官方(下载很慢):https://www.math.utah.edu/~mayer/linux/bmark.ht…

数据产品-完善DMP系统应该是什么样子的

一、价值定位 首先需要明确DMP系统的价值定位和核心功能,以此才能评估需要涵括哪些内容 ✧用户刻画更清晰:能够通过数据标签化的形式,全面、精准刻画用户全貌 ✧用户触达更精准:能够沉淀不同的策略规则,精准触达不同…

转行做TA(技术美术),需要掌握哪些基础知识?

TA的岗位职责 负责美术与程序之间的沟通,配合实现美术效果,提高画面整体表现力制定美术资源规范,提高导出资源的可用性和规范性快速跟进研发最新的游戏引擎与渲染技术;在性能优化的同时,提高游戏画面表现;…

2023年6月墨天轮中国图数据库排行榜:TGS 开新局,创邻和字节多点突破露锋芒

鸿鹄不坠青云志,鲲鹏展翅九万里。 2023年 墨天轮中国图数据库流行度排行 已经火热出炉,本月中国图数据库排行榜共有31个数据库参与排名,相比今年1月新增3个数据库。本月图数据库榜单前十变动较大:TuGraph 立足创新跃榜首&#xff…

深蓝学院C++基础与深度解析笔记 第 6 章 函数

1. 函数基础 ● 函数:封装了一段代码,可以在一次执行过程中被反复调用。 A、函数头: ● 函数名称 —— 标识符,用于后续的调用 ● 形式参数 —— 代表函数的输入参数 ● 返回类型 —— 函数执行完成后所返回的结果类型 B、函数…

Matplotlib---雷达图

1. 雷达图 fig plt.figure(figsize(6, 6))x np. linspace(0, 2*np.pi, 6, endpointFalse) y [83, 61, 95, 67, 76, 88]# 保证首位相连 x np.concatenate((x, [x[0]])) y np.concatenate((y, [y[0]]))# 雷达图 axes plt.subplot(111, polarTrue) axes.plot(x, y, o-, l…

记录正式环境测试环境【RedHat7编译升级redis7.0.9】--有关报错及解决

记录正式环境&测试环境【RedHat7 编译升级redis7.0.9】--有关报错及解决 🔻 一、报错详情1.1 ⛳ 写在前面1.2 ⛳ 报错11.3 ⛳ 报错21.4 ⛳ 安装redis1.5 ⛳ 版本检查 🔻 二、⛳ 总结 🔻 一、报错详情 1.1 ⛳ 写在前面 🍁 升级…

甘特图神器大比拼——国内外7款经典工具评测

甘特图是一种重要的项目管理工具,它能够帮助我们规划、安排和跟踪项目的进度和任务。然而,市面上的甘特图工具众多,选择恰当的工具并不容易。在本文中,我们将为您介绍国内外7款经典的甘特图神器,并进行详细评测和比较。…

常见分布式事务

一、2PC 将事务的提交过程分为 资源准备 和 资源提交 两个阶段,准备阶段所有事务参与者都预留资源的成功与否,决定了第二阶段提交或回滚。 2PC 第一阶段:准备阶段 1.协调者向所有参与者发送事务内容,询问是否可以提交事务&#…

libevent(7)libevent中的事件event

一、事件状态 libevent有4种事件状态,分别是: initialized、pending、active、persistent。这4种状态的转换关系如下: 1、已初始化(initialized):对应图中的non-pending状态,表示事件已经新建完成,但是还未…

细说设计模式

23种设计模式 (一)、创建型模式 对象实例化的模式,创建型模式用于解耦对象的实例化过程。 单例模式:某个类只能有一个实例,提供一个全局的访问点。工厂方法模式:一个工厂类根据传入的参量决定创建出哪一种…

AutoCMS全自动网站系统,供大家学习研究参考,商用请联系开发者!

AutoCMS全自动建站系统,只要一个关键词即可生成一个站,全自动建站、无需维护、无需数据库、无需安装,下载即用。支持在二级目录运行,丰富您的网站内容。 AutoCMS全自动网站使用说明 1.程序无需安装,解压该软件包之后&…

CSS的自定义属性var和JS的classList.toggle()方法,使用详细(css中var变量怎么应用)

简介:CSS中的var(变量)是CSS3中的新特性,用于定义可重用的值,类似于编程语言中的变量;它允许您在整个CSS文件中定义一个值,并在需要时使用该值。这样可以使CSS更加灵活和易于维护;cl…

VUE L 数据代理 ④

目录 文章有误请指正,如果觉得对你有用,请点三连一波,蟹蟹支持✨ V u e j s Vuejs Vuejs数据代理 什么是数据代理❓ 代理底层~ D e f i n e P r o p e r y DefinePropery DefinePropery V u e Vue Vue数据代理原理理解总结 文章有误…

第三方库介绍——libpng

文章目录 概述库版本的选择与下载安装zlib和libpng交叉编译交叉编译zlib库交叉编译libpng库 libpng的使用 概述 libpng是一款C语言编写的比较底层的读写PNG文件的跨平台的库。由于依赖于zlib库,所以在安装libpng时往往还需要安装zlib库。 库版本的选择与下载 zli…

积分等式与积分不等式

参考资料: B站 - 考研数学-积分不等式(所有方法全归纳)张宇基础和强化及习题册 积分等式 中值定理夹逼准则积分法 在这个专题中,有如下经验: 遇到 f ( x ) f(x) f(x)连续,应当想到变限积分 ∫ a x f ( t…

【软考网络管理员】2023年软考网管初级常见知识考点(6)- 虚拟局域网技术

涉及知识点 什么是虚拟局域网VLAN?VLAN的分类有哪些,IEEE802.1Q,VLAN的端口类型,VLAN信息的传递,软考网络管理员常考知识点,软考网络管理员网络安全,网络管理员考点汇总。 文章目录 涉及知识点…

单臂路由(RIP协议原理)

一、前言 1.1 本文为单臂路由(RIP协议原理)笔记 (供新手参考) 通过RIP协议,交换机、单臂路由的工作原理和配置方法。 1.2 思科路由器设置ip设置 在将设备摆放完毕后,需要配置每台设备的IP,有如下三种基…

深入理解Java中的Lock和AQS

文章目录 前言正文一、Lock接口的定义二、ReentrantLock 的实现三、AbstractQueuedSynchronizer的实现3.1 AQS 中的加锁底层3.2 ReentrantLock中的 Sync 同步器3.3 NonfairSync 的实现3.4 FairSync 的实现3.5 公平锁和非公平锁的总结3.5.1 公平锁3.5.2 非公平锁 3.6 释放锁 前言…

突破边界:高性能计算引领LLM驶向通用人工智能AGI的创新纪元

AGI | AIGC | 大模型训练 | GH200 LLM | LLMs | 大语言模型 | MI300 ChatGPT的成功带动整个AIGC产业的发展,尤其是LLM(大型语言模型,大语言模型)、NLP、高性能计算和深度学习等领域。LLM的发展将为全球和中国AI芯片、AI服务器市场…