说明

本文仅作为学习FreeRTOS的记录文档,作为初学者肯定很多理解不对甚至错误的地方,望网友指正。

FreeRTOS是一个RTOS(实时操作系统)系统,支持抢占式、合作式和时间片调度。适用于微处理器或小型微处理器的实时应用。

本文档使用的FreeRTOS版本:FreeRTOS Kernel V10.4.1

参考文档:《FreeRTOS_Reference_Manual_V10.0.0.pdf》《FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf》《STM32F4 FreeRTOS开发手册_V1.1.pdf》

参考视频:正点原子FreeRTOS手把手教学-基于STM32_哔哩哔哩_bilibili

6 内核控制函数

内核控制函数就是FreeRTOS内核所使用的函数,一般情况下应用程序不使用这些函数。

官网API说明:FreeRTOS - Task Control Functions and Macros for the Free Open Source RTOS FreeRTOS

6.1 任务切换

函数原型:

#include “FreeRTOS.h”
#include “task.h”
void taskYIELD( void );

函数描述:任务切换函数。如果没有与当前任务同等优先级或高优先级的任务,则任务调度器会选择当前任务继续运行。必须在调度器初始化之后使用。

函数参数:

返回值:

测试代码:创建两个任务,任务task0优先级为2,任务函数中每次从0计数到2进行一次任务切换;任务task1优先级也为2,任务函数中每次打印之后就挂起自己。

configSTACK_DEPTH_TYPE Task0_STACK_SIZE = 5;
UBaseType_t Task0_Priority = 2;
TaskHandle_t Task0_xhandle; configSTACK_DEPTH_TYPE Task1_STACK_SIZE = 5;
UBaseType_t Task1_Priority = 2;
TaskHandle_t Task1_xhandle; void task0_code(void *para)
{
unsigned int i = 0; for (;;)
{
for (i = 0; i < 4; i++) {
PRINT(" task0 cnt %u...", i);
if (i == 2) {
vTaskResume(Task1_xhandle);
taskYIELD();
}
}
vTaskDelay(2000);
}
} void task1_code(void *para)
{
static unsigned int cnt = 0; for (;;)
{
PRINT(" task1 cnt %u...", cnt);
cnt++;
vTaskSuspend(Task1_xhandle);
}
} void creat_task(void)
{
taskENTER_CRITICAL(); if (xTaskCreate(task0_code, "task0 task",
Task0_STACK_SIZE, NULL, Task0_Priority,
&Task0_xhandle) != pdPASS)
{
PRINT("creat task failed!\n");
} if (xTaskCreate(task1_code, "task1 task",
Task1_STACK_SIZE, NULL, Task1_Priority,
&Task1_xhandle) != pdPASS)
{
PRINT("creat task failed!\n");
} taskEXIT_CRITICAL();
vTaskStartScheduler();
}

编译、运行,结果符合预期,每次调度taskYIELD()之后执行一次任务切换,结果如下:

$ ./build/freertos-simulator
task0 cnt 0...
task0 cnt 1...
task0 cnt 2...
task1 cnt 0...
task0 cnt 3...
task0 cnt 0...
task0 cnt 1...
task0 cnt 2...
task1 cnt 1...
task0 cnt 3...

现在将任务task0的优先级改为3,大于任务task1的优先级:

UBaseType_t  Task0_Priority = 3;

编译、运行,结果符合预期,每次调度taskYIELD()之后不会任务切换,结果如下:

$ ./build/freertos-simulator
task0 cnt 0...
task0 cnt 1...
task0 cnt 2...
task0 cnt 3...
task1 cnt 0...
task0 cnt 0...
task0 cnt 1...
task0 cnt 2...
task0 cnt 3...
task1 cnt 1...
task0 cnt 0...

6.2 进入临界区

函数原型:

#include “FreeRTOS.h”
#include “task.h”
void taskENTER_CRITICAL( void );

函数描述:进入临界区,不能在中断服务函数中调用。中断服务函数中调用taskENTER_CRITICAL_FROM_ISR()进入临界区。

函数参数:

返回值:

6.3 退出临界区

函数原型:

#include “FreeRTOS.h”
#include “task.h”
void taskEXIT_CRITICAL( void );

函数描述:退出临界区,不能在中断服务函数中调用。中断服务函数中调用taskEXIT_CRITICAL_FROM_ISR()退出临界区。

函数参数:

返回值:

taskENTER_CRITICAL()和taskEXIT_CRITICAL()函数用于临界段代码保护(任务级)。

taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()函数用于中断级临界段代码保护。

临界段代码:也叫做临界区,指那些必须完整运行,不能被打断的代码。比如某些外设的初始化。FreeRTOS在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。

6.4 关闭可屏蔽中断

函数原型:

#include “FreeRTOS.h”
#include “task.h”
void taskDISABLE_INTERRUPTS( void );

函数描述:关闭可屏蔽中断。不可嵌套使用。

函数参数:

返回值:

6.4 打开可屏蔽中断

函数原型:

#include “FreeRTOS.h”
#include “task.h”
void taskENABLE_INTERRUPTS( void );

函数描述:关闭可屏蔽中断。不可嵌套使用。

函数参数:

返回值:

6.5 启动任务调度器

函数原型:

#include “FreeRTOS.h”
#include “task.h”
void vTaskStartScheduler( void );

函数描述:启动任务调度器。典型应用为:main()函数先于调度器使用,调度器启动之后,执行任务及中断函数。调度器启动之后将选择优先级最高的任务进行执行。调度器启动之后,空闲任务(Idle task)将自动被创建。

函数参数:

返回值:

6.5 关闭任务调度器

函数原型:

#include “FreeRTOS.h”
#include “task.h”
void vTaskEndScheduler( void );

函数描述:关闭任务调度器。仅支持x86架构处理器。关闭任务调度器之后,内核时钟将停止计数,所有创建的任务都会自动删除。

函数参数:

返回值:

7 时间管理

FreeRTOS延时函数分为相对模式和绝对模式。vTaskDelay()是相对延时函数。vTaskDelayUntil()是绝对延时函数。

官网API说明:Links to the FreeRTOS task control API function vTaskDelay, vTaskDelayUntil, uxTaskPriorityGet, vTaskPrioritySet, vTaskSuspend, vTaskResume, xTaskResumeFromISR, vTaskSetApplicationTaskTag, xTaskCallApplicationTaskHook

7.1 相对延时

函数原型:

#include "FreeRTOS.h"
#include "task.h" void vTaskDelay( const TickType_t xTicksToDelay );

函数描述:调用该函数的任务将进入阻塞态,中断一段固定的时钟周期。使用这个函数必须将宏INCLUDE_vTaskDelay置1。

函数参数:xTicksToDelay表示调用函数的任务的阻塞态保持时间,单位为时钟节拍数。真正的延时时间取决于时钟节拍频率。宏 portTICK_PERIOD_MS被用来根据时钟节拍数来计算一个时钟节拍的延时周期。

#define portTICK_PERIOD_MS			( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define configTICK_RATE_HZ ( 1000 )

可以看出,单个时钟节拍计数时间为1ms,比如参数xTicksToDelay设为100,就表示延时100ms。

延时达到之后将进入就绪态。例如:当时钟计数到10000时,函数调用了vTaskDelay(100),然后任务进入阻塞态,并且保持阻塞态直到时钟计数到10100。

宏pdMS_TO_TICKS()可以被使用来延时毫秒。例如:调用vTaskDelay( pdMS_TO_TICKS(100) ),任务将进入阻塞态100毫秒。

如果参数xTicksToDelay为0,则等同于调用了一次taskYIELD()函数进行了一次任务切换。

返回值:

7.2 绝对延时

函数原型:

#include “FreeRTOS.h”
#include “task.h”
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, TickType_t xTimeIncrement );

函数描述:调用该函数的任务将进入阻塞态直到一个绝对的时间到来。周期任务可以调用这个函数来实现一个固定的执行频率。使用这个函数必须将宏INCLUDE_vTaskDelayUntil置1。

函数参数:pxPreviousWakeTime:上一次任务延时结束被唤醒的时间点,任务中第一次调用函数vTaskDelayUntil()需要将pxPreviousWakeTime初始化为进入任务的while()循环体的时间点值。在以后的运行中函数vTaskDelayUntil()会自动更新pxPreviousWakeTime。

xTimeIncrement:任务需要延时的节拍数(相对于pxPreviousWakeTime本次延时的节拍数),也就是任务在pxPreviousWakeTime+xTimeIncrementpd时钟计数时从阻塞态恢复。MS_TO_TICKS()宏可用于延时毫秒。

(1)为任务主体,也就是任务执行的工作;(2)为任务调用vTaskDelayUntil()函数;(3)为其它任务执行。任务延时时间为xTimeIncrement,可看出任务总的执行时间一定小于任务的延时时间,也就是说使用vTaskDelayUntil()函数任务的执行周期永远是xTimeIncrement,而任务一定要在这个时间内完成,这个延时值就是绝对延时时间。

上面图中,xConstTickCount和xTimeToWake可能溢出,这些情况暂不讨论,这里仅说明函数的用法。

测试代码:创建一个任务,使用绝对延时函数延时50ms。

configSTACK_DEPTH_TYPE Task1_STACK_SIZE = 5;
UBaseType_t Task1_Priority = 2;
TaskHandle_t Task1_xhandle; void task1_code(void *para)
{
unsigned int cnt = 0;
TickType_t xLastWakeTime;
const TickType_t xPeriod = pdMS_TO_TICKS(500); xLastWakeTime = xTaskGetTickCount();
for (;;)
{
vTaskDelayUntil(&xLastWakeTime, xPeriod);
PRINT(" task1 cnt %u...", cnt);
cnt++;
} } void creat_task(void)
{ if (xTaskCreate(task1_code, "task1 task",
Task1_STACK_SIZE, NULL, Task1_Priority,
&Task1_xhandle) != pdPASS)
{
PRINT("creat task failed!\n");
} }

7.3 系统时钟节拍

xTickCount是FreeRTOS系统时钟节拍计数器,每个滴答定时器中断中xTickCount就会加1。xTickCount具体操作过程在xTaskIncrementTick()函数中进行,这个函数在时钟计数器中断函数中调用。

FreeRTOS-04-内核控制函数+时间管理函数的更多相关文章

  1. μC/OS-Ⅲ系统的时间管理函数和定时器

    一.时间管理函数 μC/OS-Ⅲ系统提供一些列时间管理服务函数: 1.OSTimeDly():任务延时n个时钟节拍. 2.OSTimeDlyHMSM():任务延时指定的时间,采用“时:分:秒:毫秒”方 ...

  2. RTX——第12章 系统时钟节拍和时间管理

    以下内容转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 RTX 操作系统的时钟节拍和时间管理函数,其中时间管理函数是 RTX 的基本函数,初学 ...

  3. 【uTenux实验】时间管理(系统时间/周期性处理/警报处理)

    1.系统时间管理 系统时间管理函数用来对系统时间进行操作,是OS的一个基础性的东西.个人认为,设置系统时间和获取系统时间对OS来说基本是可有可无的. uTenux提供了三个系统时间相关API.分别用于 ...

  4. (笔记)Linux内核学习(八)之定时器和时间管理

    一 内核中的时间观念 内核在硬件的帮助下计算和管理时间.硬件为内核提供一个系统定时器用以计算流逝的时间.系 统定时器以某种频率自行触发,产生时钟中断,进入内核时钟中断处理程序中进行处理. 墙上时间和系 ...

  5. linux设备驱动归纳总结(七):1.时间管理与内核延时【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxx ...

  6. 《Linux内核设计与实现》读书笔记(十一)- 定时器和时间管理【转】

    转自:http://www.cnblogs.com/wang_yb/archive/2013/05/10/3070373.html 系统中有很多与时间相关的程序(比如定期执行的任务,某一时间执行的任务 ...

  7. Linux内核——定时器和时间管理

    定时器和时间管理 系统定时器是一种可编程硬件芯片.它能以固定频率产生中断.该中断就是所谓的定时器中断.它所相应的中断处理程序负责更新系统时间,还负责执行须要周期性执行的任务. 系统定时器和时钟中断处理 ...

  8. Linux内核入门到放弃-时间管理-《深入Linux内核架构》笔记

    低分辨率定时器的实现 定时器激活与进程统计 IA-32将timer_interrupt注册为中断处理程序,而AMD64使用的是timer_event_interrupt.这两个函数都通过调用所谓的全局 ...

  9. 解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】

    转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的 ...

随机推荐

  1. Golang中的各种时间操作

    Golang中的各种时间操作 需求 时间格式的转换比较麻烦,自己写了个工具,可以通过工具中的这些方法相互调用转成自己想要的格式,代码如下,后续有新的函数再添加 实现代码 package utils i ...

  2. 在C++中,你真的会用new吗?

    摘要:"new"是C++的一个关键字,同时也是操作符.关于new的话题非常多,因为它确实比较复杂,也非常神秘. 本文分享自华为云社区<如何编写高效.优雅.可信代码系列(2)- ...

  3. Redis 雪崩、穿透、击穿、并发、缓存讲解以及解决方案

    1.缓存雪崩 数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机. 比如一个雪崩的简单过程 1.redis集群大面积故障 2.缓存 ...

  4. 关于Word转Markdown的工具Typora安装及使用

    简介 Typora是一款轻便简洁的Markdown编辑器,支持即时渲染技术,这也是与其他Markdown编辑器最显著的区别.即时渲染使得你写Markdown就想是写Word文档一样流畅自如,不像其他编 ...

  5. 区分DDD中的Domain, Subdomain, Bounded Context, Problem/Solution Space

    区分DDD中的Domain, Subdomain, Bounded Context, Problem/Solution Space 译自: Domain, Subdomain, Bounded Con ...

  6. SpringMVC(9)实现注解式权限验证

    对大部分系统来说都需要权限管理来决定不同用户可以看到哪些内容,那么如何在Spring MVC中实现权限验证呢?当然我们可以继续使用servlet中的过滤器Filter来实现.但借助于Spring MV ...

  7. 在Xshell中文件内容显示乱码

    1.修改系统语言 支持中文 echo $LANG    查看系统语言  默认 en_US.UFT_8 vim /etc/locale.conf    修改配置文件 将LANG的值改为 zh_CN.UT ...

  8. 26 bash shell中的信号

    当没有任何捕获时,一个交互式 Bash Shell 会忽略 SIGTERM(发送到进程的 TERM 信号用于要求进程终止) 和 SIGQUIT(当用户要求进程执行 core dump 时,QUIT 信 ...

  9. redis广播/订阅模式演示

    参考博客 http://www.pianshen.com/article/7183315879/ 1.首先在本地启动redis服务 2.启动4个客户端 redis-cli 3.将其中三个客户端设置监听 ...

  10. 《PHP设计模式大全》系列分享专栏

    <PHP设计模式大全>已整理成PDF文档,点击可直接下载至本地查阅https://www.webfalse.com/read/201739.html 文章 php设计模式介绍之编程惯用法第 ...