前言

资源:

任务概念

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

线程:线程是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. Solution -「ARC 058C」「AT 1975」Iroha and Haiku

    \(\mathcal{Description}\)   Link.   称一个正整数序列为"俳(pái)句",当且仅当序列中存在连续一段和为 \(x\),紧接着连续一段和为 \(y ...

  2. STM32 HAL 库实现乒乓缓存加空闲中断的串口 DMA 收发机制,轻松跑上 2M 波特率

    前言 直接储存器访问(Direct Memory Access,DMA),允许一些设备独立地访问数据,而不需要经过 CPU 介入处理.因此在访问大量数据时,使用 DMA 可以节约可观的 CPU 处理时 ...

  3. php spl_autoload_register 实现自动加载

    spl_autoload_register (PHP 5 >= 5.1.2, PHP 7) spl_autoload_register - 注册给定的函数作为 __autoload 的实现 语法 ...

  4. (翻译) CAP 理论 FAQ

    CAP 理论 FAQ 0. 关于这个文档 没有其它比CAP理论更引人注意的话题了, 这个FAQ的目的, 是说明对于CAP, 当前哪些是已知的, 并帮助那些刚接触这个理论的人快速了解, 并解决一些错误的 ...

  5. IO_FILE——leak 任意读

    在堆题没有show函数时,我们可以用 IO_FILE 进行leak,本文就记录一下如何实现这一手法. 拿一个输出函数 puts 来说,它在源码里的表现形式为 _IO_puts . _IO_puts ( ...

  6. 微信公众平台网页授权登陆access_token误区

    公众平台里显示 每日获取access_token上线2000次,此access_token并非网页授权登陆的access_token大家不要混淆 1,网页授权登陆的access_token是没有上线的 ...

  7. for循环例子

    代码 点击查看[ForTest.java]代码 //package com.d; import java.util.Scanner; /** * For循环例子 * @date: 2022.2.24 ...

  8. windows消息机制框架原理【简单版本】

    windows消息机制框架原理 结合两张图理解 窗口和窗口类 Windows UI 应用程序 (e) 具有一个主线程 (g).一个或多个窗口 (a) 和一个或多个子线程 (k) [工作线程或 UI 线 ...

  9. 图解volatile

    volatile是什么 出去面试的时候,很多面试官都会问你:说说你对volatile的理解. 下面我将用图的方式告诉大家,volatile是什么? 如上图所示:每个线程都有自己的工作内存,同时还能访问 ...

  10. 关于Cookie的一些小饼干

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOEx ...