前言

资源:

任务概念

进程:进程是程序执行的过程,是程序在执行过程中分配和管理资源的基本单位。拥有独立的虚拟地址空间。

线程:线程是CPU调度和分派的基本单位。与其它同一进程的线程共享当前进程资源。

协程:比线程更加轻量级的存在,不是由操作系统内核管理,而是由程序控制的。其实就是在同一线程内时分地执行不同的子程序。(注意:不是函数调用)

还有管程、纤程。

并发:多个任务看起来是同时进行, 这是一种假并行。

并行:并行是指令同一时刻一起运行。

对于目前主流的RTOS的任务,大部分都属于并发的线程。

因为MCU上的资源每个任务都是共享的,可以认为是单进程多线程模型。

任务状态

freertos有四种状态,每种状态都有对应的状态链表管理。

运行态:占用CPU使用权时的状态。

就绪态:能够运行(没有被阻塞和挂起),但是当前没有运行的任务的状态。

阻塞态:由于等待信号量、消息队列、事件标志组、调用延迟函数等而处于的状态被称之为阻塞态。

挂起态:调用函数vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行。

  • 调用函数xTaskResume()可退出挂起状态。
  • 不可以指定超时周期事件(不可以通过设定超时事件而退出挂起状态)

任务状态转换图:

任务优先级

每个任务被分配一个从0到(configMAX_PRIORITIES - 1)的优先级。

configMAX_PRIORITIES 是在 FreeRTOSConfig.h文件中被定义。

优先级数值越高,优先级越高。

idle任务的优先级为0。

多个任务可以共享一个任务优先级。

如果在FreeRTOSConfig.h文件中配置宏定义configUSE_TIME_SLICING为1,或者没有配置此宏定义,时间片调度都是使能的。

使能时间片后,处于就绪态的多个相同优先级任务将会以时间片切换的方式共享处理器。

如果硬件架构支持CLZ指令,可以使用该特性,使能配置如下:

  1. FreeRTOSConfig.hconfigUSE_PORT_OPTIMISED_TASK_SELECTION设置为1;
  2. 最大优先级数目configMAX_PRIORITIES不能大于CPU位数。

空闲任务和空闲任务钩子

空闲任务

空闲任务是启动RTOS调度器时由内核自动创建的任务,其优先级为0,确保系统中至少有一个任务在运行。

空闲任务可用来释放RTOS分配给被删除任务的内存。

空闲任务钩子

空闲任务钩子是一个函数,每一个空闲任务周期被调用一次。

空闲任务钩子应该满足一下条件:

  1. 不可以调用可能引起空闲任务阻塞的API函数;
  2. 不应该陷入死循环,需要留出部分时间用于系统处理系统资源回收。

创建空闲钩子

FreeRTOSConfig.h头文件中设置configUSE_IDLE_HOOK为1;

定义一个函数,名字和参数原型如下所示:

void vApplicationIdleHook( void ); // FreeRTOS 规定了函数的名字和参数

一般设置CPU进入低功耗模式都是使用空闲任务钩子函数实现的。

创建任务

任务的创建有两种:创建静态内存任务和创建动态内存任务。

任务参数相关概念

任务入口函数:即是任务函数,是该任务需要跑的函数。

任务名称:即是任务名,主要用于调试。

任务堆栈大小:即是任务栈大小,单位是word。

任务入口函数参数:传递给任务入口函数的参数。在任务函数里,通过形参获得。

任务控制块:主要用于内核管理任务,记录任务信息。

任务句柄:用于区分不同的任务,用于找到该任务的任务控制块。

创建静态内存任务

xTaskCreateRestrictedStatic(),该函数不讲解,因为需要MPU,想研究的同学可以参考:freertos官网API

配置静态内存

创建静态内存任务需要先实现以下内容:

  1. 需要在FreeRTOSConfig.h打开configSUPPORT_STATIC_ALLOCATION宏,开启静态内存。

  2. 开启静态内存的同时需要实现两个函数:(使用静态内存分配任务堆栈和任务控制块内存)

    1. vApplicationGetIdleTaskMemory():空闲任务堆栈函数。
    2. vApplicationGetTimerTaskMemory():定时器任务堆栈函数。
  3. 注意静态内存对齐。

实现空闲任务堆栈函数

实现该函数是为了给内核提供空闲任务关于空闲任务控制块和空闲任务堆栈的相关信息。

/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;
/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE]; /** @brief vApplicationGetIdleTaskMemory
* @details 获取空闲任务的任务堆栈和任务控制块内存
* @param
* @retval
* @author lizhuming
*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer = &Idle_Task_TCB; /* 任务控制块内存 */
*ppxIdleTaskStackBuffer = Idle_Task_Stack; /* 任务堆栈内存 */
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /* 任务堆栈大小 */
}

实现定时器任务堆栈函数

实现该函数是为了给内核创建定时器任务时提供定时器任务控制块和定时器任务堆栈的相关信息。

/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH]; /** @brief vApplicationGetTimerTaskMemory
* @details 获取定时器任务的任务堆栈和任务控制块内存
* @param
* @retval
* @author lizhuming
*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer = &Timer_Task_TCB;/* 任务控制块内存 */
*ppxTimerTaskStackBuffer = Timer_Task_Stack;/* 任务堆栈内存 */
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */

配置内存对齐

内存对齐的配置在portmacro.h里面的portBYTE_ALIGNMENT宏,按自己需求配置即可。

在任务堆栈初始化时会把栈顶指针纠正为内存对齐。参考下列代码:

pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );

纠正后可以通过以下代码检查是否正确的代码如下:

configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

分配静态内存

静态内存分配是有编译器决定的。

在freertos中,创建任务需要分配的内存主要是任务控制块和任务堆栈。

/* 任务控制快 */
static StaticTask_t lzmStaticTestTaskTCB = {0};
/* 任务堆栈 */
static StackType_t lzmStaticTestTaskStack[256] = {0};

创建任务原型

创建任务函数原型:

TaskHandle_t xTaskCreateStatic( // 返回任务句柄
TaskFunction_t pxTaskCode, // 任务入口函数
const char * const pcName, // 任务名称
const uint32_t ulStackDepth, // 任务堆栈大小
void * const pvParameters, // 传递给任务入口函数的参数
UBaseType_t uxPriority, // 任务优先级
StackType_t * const puxStackBuffer, // 任务堆栈
StaticTask_t * const pxTaskBuffer ) // 任务控制块

创建任务

/* 创建静态内存任务 */
lzmStaticTestTaskHandle = xTaskCreateStatic((TaskFunction_t) lzmStaticTestTask, // 任务入口函数
(const char*) "lzm static test task", // 任务函数名
(uint32_t )256, // 任务堆栈大小
(void* )NULL, // 传递给任务入口函数的参数
(UBaseType_t)5, // 任务优先及
(StackType_t* )lzmStaticTestTaskStack, // 任务堆栈地址
(StaticTask_t* )&lzmStaticTestTaskTCB); // 任务控制块地址

创建动态内存任务

配置动态内存

动态内存配置是在FreeRTOSConfig.h配置的,这些内存主要供给FreeRTOS动态内存分配函数使用。

#define configTOTAL_HEAP_SIZE	( ( size_t ) ( 32 * 1024 ) ) // 系统总堆大小

而freertos的动态内存管理是有文件heap_x.c实现的,具体实现算法,后面讲到内存时会分析。

uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; // 系统总堆

任务句柄

static TaskHandle_t lzmTestTaskHandle = NULL;

创建任务原型

创建任务函数原型:

BaseType_t xTaskCreate( // 返回任务句柄
TaskFunction_t pxTaskCode, // 任务入口函数
const char * const pcName, // 任务名称
const configSTACK_DEPTH_TYPE usStackDepth, // 任务堆栈大小
void * const pvParameters, // 传递给任务入口函数的参数
UBaseType_t uxPriority, // 任务优先级
TaskHandle_t * const pxCreatedTask ) // 任务控制块指针

创建任务

/* 创建动态内存任务 */
xReturn = xTaskCreate((TaskFunction_t) lzmTestTask, // 任务入口函数
(const char*) "lzm test task", // 任务函数名
(uint16_t )256, // 任务堆栈大小
(void* )NULL, // 传递给任务入口函数的参数
(UBaseType_t)5, // 任务优先及
(TaskHandle_t* )&lzmTestTaskHandle); // 任务句柄

删除任务

配置删除任务

在文件FreeRTOSConfig.h中,必须定义宏INCLUDE_vTaskDelete 为 1,删除任务的API才会失效。

调用API删除任务后,将会从就绪、阻塞、暂停和事件列表中移除该任务。

如果是动态内存创建任务,删除任务后,其占用的空间资源有空闲任务释放,所以删除任务后尽量保证空闲任务获取一定的CPU时间。

如果是静态内存创建任务,删除任务后,需要自己处理释放任务占用的空间资源。

删除任务原型

void vTaskDelete( TaskHandle_t xTaskToDelete ); // 参数为任务句柄

注意:传入的参数为任务句柄,当出入的参数为NULL时,表示删除调用者当前的任务。

实战

源码:拉取 freertos_on_linux_task_01 文件夹

结果:

【freertos】003-任务基础知识的更多相关文章

  1. FreeRTOS学习笔记——FreeRTOS 任务基础知识

    RTOS 系统的核心就是任务管理,FreeRTOS 也不例外,而且大多数学习RTOS 系统的工程师或者学生主要就是为了使用RTOS 的多任务处理功能,初步上手RTOS 系统首先必须掌握的也是任务的创建 ...

  2. FreeRTOS基础知识

    前面一篇文章介绍了一些命名规范之类的基础知识,但是我觉得还缺少一定前言知识,就是裸机和操作系统有什么区别,为什么我们需要学freertos,因为招聘要求?那么为什么招聘网又会有这个要求呢?所以我们为什 ...

  3. JAVA学习基础知识总结(原创)

    (未经博主允许,禁止转载!) 一.基础知识:1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java语言是跨平 ...

  4. Spring笔记01(基础知识)

    1.基础知识 01.Spring:轻量级Java EE开源框架,它是由Rod Johnson为了解决企业应用程序开发的复杂性而创建. 02.目标:实现一个全方位的整合框架,实现“一站式”的企业应用开发 ...

  5. Vue学习之--------消息订阅和发布、基础知识和实战应用(2022/8/24)

    文章目录 1.基础知识 2.代码实例 2.1 main.js 2.2 School.vue 2.3 Student.vue 2.4 App.vue 3.全局事件总线通信改为消息的订阅和发布 3.1 核 ...

  6. .NET面试题系列[1] - .NET框架基础知识(1)

    很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...

  7. RabbitMQ基础知识

    RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...

  8. Java基础知识(壹)

    写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...

  9. selenium自动化基础知识

    什么是自动化测试? 自动化测试分为:功能自动化和性能自动化 功能自动化即使用计算机通过编码的方式来替代手工测试,完成一些重复性比较高的测试,解放测试人员的测试压力.同时,如果系统有不份模块更改后,只要 ...

  10. [SQL] SQL 基础知识梳理(一)- 数据库与 SQL

    SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 目录 What's 数据库 ...

随机推荐

  1. [LeetCode]2.Add Two Numbers 两数相加(Java)

    原题地址: add-two-numbers 题目描述: 给你两个非空的链表,表示两个非负的整数.它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字. 请你将两个数相加,并以相同形式返回 ...

  2. MySQL数据库的导入方法

    问题 如何导入MySQL数据库 解决方案 1. 概述MySQL数据库的导入,有两种方法:1) 先导出数据库SQL脚本,再导入:2) 直接拷贝数据库目录和文件. 在不同操作系统或MySQL版本情况下,直 ...

  3. Cesium 加载地形数据

    1.注册Cesium Ion账号,注册地址:Sign In | Cesium ion 否则,加载数据会报错{code: "InvalidCredentials", message: ...

  4. Devops 开发运维高级篇之微服务代码上传和代码检查

    Devops 开发运维高级篇之微服务代码上传和代码检查 微服务持续集成(1)-项目代码上传到Gitlab 微服务持续集成(2)-从Gitlab拉取项目源码 微服务持续集成(3)-提交到SonarQub ...

  5. WebGL 与 WebGPU比对[5] - 渲染计算的过程

    目录 1. WebGL 1.1. 使用 WebGLProgram 表示一个计算过程 1.2. WebGL 没有通道 API 2. WebGPU 2.1. 使用 Pipeline 组装管线中各个阶段 2 ...

  6. 【C# 线程】优先级反转与优先级继承

    什么是优先级反转(翻转)优先级反转,是指在使用信号量时,可能会出现的这样一种不合理的现象,即:    高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度.但其他中等优先级的任务却能抢到CP ...

  7. linux shell脚本批量修改密码,无需交互输入

    转至:https://blog.csdn.net/weixin_34409357/article/details/89833777?utm_medium=distribute.pc_relevant. ...

  8. Qt:QJsonArray

    0.说明 QJsonArray中存储了一系列的QJsonValue.可以向其中插入.删除QJsonValue. 一个QJsonArray可以与QVariantList互相转换.可以通过size()访问 ...

  9. k8s全方位监控-prometheus部署

    1.k8s 监控资源对象 2. prometheus简单介绍. https://github.com/prometheus •多维数据模型:由度量名称和键值对标识的时间序列数据•PromSQL:一种灵 ...

  10. (第二章第三部分)TensorFlow框架之读取二进制数据

    系列博客链接: (第二章第一部分)TensorFlow框架之文件读取流程:https://www.cnblogs.com/kongweisi/p/11050302.html (第二章第二部分)Tens ...