FreeRTOS的任务理论

文章目录

  • 2 FreeRTOS的任务理论
    • 2.1 任务及任务优先级
    • 2.2 任务状态理论
      • 2.2.1 任务状态的转换
      • 2.2.2 任务状态改变相关函数
      • 2.2.3 调度器相关函数
    • 2.3 FreeRTOS延时
      • 2.3.1 vTaskDelay延时
      • 2.3.2 vTaskDelayUntil延时
      • 2.3.3 pdMS_TO_TICKS(x)宏
    • 2.4 TCB任务控制块
    • 2.5 任务调度宏与任务调度算法

2 FreeRTOS的任务理论

2.1 任务及任务优先级

​ 简单来说,任务是指可独立运行的基本执行单元。任务是并发执行的最小单位,每个任务都有自己的代码逻辑和资源。FreeRTOS多任务执行其实是多任务交替执行实现的。实现多任务交替执行的基础是tick中断,滴答中断,周期性的定时器中断。类比Linux,我们可以类比的认为一个任务相当于一个线程,同时,任务也有不同的种类和实现方式,比如说定时器任务等等。

​ 在FreeRTOS中,任务具有不同的优先级,不过FreeRTOS中,优先级是与大多数操作系统相反的,其值越小,优先级越低,**反之越大,优先级越高。**和CubeMX中的配置也是相反的。关于优先级我们需要注意以下几点:

  • 优先级相同时,任务是可以交替进行的,但是需要同优先级正在运行的任务释放自己的资源,例如使用vTaskDelete(NULL)退出,taskYIELD()释放等。或使用vTaskDelay()等函数暂时的释放了CPU资源时,才会轮到另一个任务执行;
  • 优先级不同时,高优先级的任务先执行,执行结束后才轮到低优先级的任务。这里需要注意的是,高优先级的任务中如果也有vTaskDelete(NULL),或vTaskDelay()等暂时的释放CPU资源的函数存在,这时候调度器会自动执行低优先级的的任务。而不是等着高优先级的delay延迟结束。
  • 任务的优先级不可以无限制的高,在FreeRTOSConfig.h中有一个宏configMAX_PRIORITIES规定了能到达的最高的优先级,其优先级的取值范围是( 0 ~ configMAX_PRIORITIES-1 )。 ps:注意是最大值是 configMAX_PRIORITIES-1而不是 configMAX_PRIORITIES-1
  • 这里我要再次强调,任务的优先级不是中断的优先级,任务也不是中断,任务优先级的高低仅仅决定了在就绪态的队伍中的排队次序,若低优先级的任务一直不释放CPU资源,那么再高的优先级也无法执行!!!
/*
  @brief  设置任务的优先级
  @retval None
  @param  pxTask:要修改优先级的任务句柄,通过NULL改变任务自身优先级
          uxNewPriority:要修改的任务优先级 
*/
void vTaskPrioritySet(TaskHandle_t pxTask, UBaseType_t uxNewPriority);
 
/*
  @brief  获取任务优先级
  @retval 任务优先级
  @param  pxTask:要获取任务优先级的句柄,通过NULL获取任务自身优先级
*/
UBaseType_t uxTaskPriorityGet(TaskHandle_t pxTask);

2.2 任务状态理论

2.2.1 任务状态的转换

在通用操作系统中有5态(或3态),而在FreeRTOS中稍有不同,他拥有4个状态。同时要注意,FreeRTOS中的任务函数,是一个永远不会退出的C函数,是无法使用return来退出的,要彻底清除他我们必须使用vTaskDelete(NULL)删除它。

  1. (RUNNING)运行态:正在运行的任务,只能有一个。
  2. (Ready)就绪态:条件Event都满足了,只等待正在运行的任务释放CPU后就轮到他来执行了。
  3. (Block)阻塞态:相当于等待态,在等待IO或者条件。
  4. (Suspended)挂起态:将运行到一半的程序挂起(主动暂停),暂时脱离调度器的调度,但是不可以直接回到运行态,只能回到就绪态。

他们的转换关系如下:

在这里插入图片描述

2.2.2 任务状态改变相关函数

/*
  @brief  查询一个任务当前处于什么状态
  @retval 任务状态的枚举类型
  @param  pxTask:要查询任务状态的任务句柄,NULL查询自己
*/
eTaskState eTaskGetState(TaskHandle_t pxTask);
 
/*任务状态枚举类型返回值*/
typedef enum
{
	eRunning = 0,		/* 任务正在查询自身的状态,因此肯定是运行状态 */
	eReady,				  /* 就绪状态 */
	eBlocked,		 		/* 阻塞状态 */
	eSuspended,			/* 挂起状态 */
	eDeleted,				/* 正在查询的任务已被删除,但其 TCB 尚未释放 */
	eInvalid				/* 无效状态 */
} eTaskState;
  1. (RUNNING)运行态:

    没有主动使得任务进入运行态的函数,同时要注意,单核处理器,那么同一时间只可能有一个任务再执行
    
  2. (Ready)就绪态:

  3. (Block)阻塞态:

    //vTaskDelay()和vTaskDelayUntil()这两个函数,都会使得任务进入阻塞状态。
      
    //当一个任务因为延时函数或者其他同步事件进入阻塞状态后,可以通过 xTaskAbortDelay() API 函数终止任务的阻塞状态,即使事件任务等待尚未发生,或者任务进入时指定的超时时间阻塞状态尚未过去,都会使其进入就绪状态,具体函数描述如下所述
    /*
      @brief  终止任务延时,退出阻塞状态
      @retval pdPASS:任务成功从阻塞状态中删除,pdFALSE:任务不属于阻塞状态导致删除失败
      @param  xTask:操作的任务句柄
    */
    BaseType_t xTaskAbortDelay(TaskHandle_t xTask);
    
  4. (Suspended)挂起态:

    /*
      @brief  将某个任务挂起
      @retval None
      @param  pxTaskToSuspend:被挂起的任务的句柄,通过传入NULL来挂起自身
    */
    void vTaskSuspend(TaskHandle_t pxTaskToSuspend);
     
    /*
      @brief  将某个任务从挂起状态恢复
      @retval None
      @param  pxTaskToResume:正在恢复的任务的句柄
    */
    void vTaskResume(TaskHandle_t pxTaskToResume);
     
    /*
      @brief  vTaskResume的中断安全版本
      @retval 返回退出中断之前是否需要进行上下文切换(pdTRUE/pdFALSE)
      @param  pxTaskToResume:正在恢复的任务的句柄
    */
    BaseType_t xTaskResumeFromISR(TaskHandle_t pxTaskToResume);
    

2.2.3 调度器相关函数

/*
  @brief  启动调度器
  @retval None
*/
void vTaskStartScheduler(void);
 
/*
  @brief  停止调度器
  @retval None
*/
void vTaskEndScheduler(void);
 
/*
  @brief  挂起调度器
  @retval None
*/
void vTaskSuspendAll(void);
 
/*
  @brief  恢复调度器
  @retval 返回是否会导致发生挂起的上下文切换(pdTRUE/pdFALSE)
*/
BaseType_t xTaskResumeAll(void);


/*
  @brief  让位于另一项同等优先级的任务
  @retval None
*/
void taskYIELD(void);
 
/*
  @brief  ISR 退出时是否执行上下文切换(汇编)
  @retval None
  @param  xHigherPriorityTaskWoken:pdFASLE不请求上下文切换,反之请求上下文切换
*/
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
 
/**
  @brief  ISR 退出时是否执行上下文切换(C语言)  
  @retval None
  @param  xHigherPriorityTaskWoken:pdFASLE不请求上下文切换,反之请求上下文切换
*/
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

2.3 FreeRTOS延时

​ 在裸机开发中,我们常常使用HAL_Delay()来实现延时功能,但FreeRTOS开发中,此时系统滴答是FreeRTOS主导的,所以我们应使用FreeRTOS中的延时函数来实现延迟。这两个函数都会使得当前的任务进入阻塞状态,使其他的任务有机会运行

2.3.1 vTaskDelay延时

/*			
		@brief:相对延时函数,宏定义INCLUDE_vTaskDelay必须定义为1,此函数才可用。相对延时,指的是从调用函数后开始计时,直到延时					 指定的时间结束。观察源代码发现,原来他只是把任务挂起(挂起态),等延时时间到,再把任务恢复(注意此时不是直接进入到					 运行态,而是就绪态)。
		@retval:None
		@param:const TickType_t xTicksToDelay:表示延时的时钟周期数(tick),configTICK_RATE_HZ配置FreeRTOS的内核时钟周期。																				 例如,如果内核时钟为1kHz,那么1tick对应的时间为1ms。																														  这个函数是仅仅通过tick数来计算延时持续的时间。
*/
void vTaskDelay(const TickType_t xTicksToDelay);

2.3.2 vTaskDelayUntil延时

/*			
		@brief:绝对延时函数,允许任务在指定的绝对时间再次激活。
		@retval:None
		@param:TickType_t *pxPreviousWakeTime:一个指针,指向一个变量,这个变量存储着上次任务唤醒的时刻。
					 TickType_t xTimeIncrement:每次唤醒后,需要延时的时间。
*/
void vTaskDelayUntil(TickType_t *pxPreviousWakeTime, TickType_t xTimeIncrement);

2.3.3 pdMS_TO_TICKS(x)宏

//是一个宏而不是函数,前面所使用的Delay函数是使用tick来计时的,但是我们人更喜欢用ms,s来作为延时的单位,而这个宏就是用来将毫秒数转化为相应的时钟滴答数(tick次数)的。
#ifndef pdMS_TO_TICKS
    #define pdMS_TO_TICKS( xTimeInMs ) 																																\
( (TickType_t) ( ( (TickType_t) (xTimeInMs) * (TickType_t) configTICK_RATE_HZ) / (TickType_t)1000U ) )
#endif

2.4 TCB任务控制块

​ 对于每一个task任务来讲,都有一个TCB_t结构体。这个结构体就叫做任务控制块。我们创建任务时最后的句柄handle就是指向的这个结构体。这个控制块里面包含的是任务的一些基本信息,例如任务状态,任务的优先级,任务栈的头指针,尾指针,任务的标识符等。(下面我们只看一些最重要的变量,重要的变量我将用中文进行标注)

typedef struct tskTaskControlBlock //TCB_T的旧名字      
{
    volatile StackType_t * pxTopOfStack; //一个指针,指向当前栈的栈顶,必须位于控制块的第一项

    #if ( portUSING_MPU_WRAPPERS == 1 )
        xMPU_SETTINGS xMPUSettings;//MPU设置,必须位于结构体的第二项
    #endif

    ListItem_t xStateListItem;       //该任务的任务状态列表项,该任务是运行态,还是就绪态,阻塞态,挂起态,就存在这里。
    ListItem_t xEventListItem;       //事件列项表,将任务以引用方式挂到事件列表中
    UBaseType_t uxPriority;          //任务的优先级
    StackType_t * pxStack;           //栈的起始位置
    char pcTaskName[ configMAX_TASK_NAME_LEN ]; //任务的名字

    #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
        StackType_t * pxEndOfStack; //指向栈最深的有效地址,也就是栈底。
    #endif

    #if ( portCRITICAL_NESTING_IN_TCB == 1 )
        UBaseType_t uxCriticalNesting; 
    #endif

    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxTCBNumber; //存储一个特定的值来标识这个TCB任务块
        UBaseType_t uxTaskNumber; //对于任务,存储了一个特殊的值来表示任务,方便我们进行追踪,错误的定位等。
    #endif

    #if ( configUSE_MUTEXES == 1 )
        UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */
        UBaseType_t uxMutexesHeld;
    #endif

    #if ( configUSE_APPLICATION_TASK_TAG == 1 )
        TaskHookFunction_t pxTaskTag;
    #endif

    #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
        void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
    #endif

    #if ( configGENERATE_RUN_TIME_STATS == 1 )
        configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */
    #endif

    #if ( ( configUSE_NEWLIB_REENTRANT == 1 ) || ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) )
        configTLS_BLOCK_TYPE xTLSBlock; /*< Memory block used as Thread Local Storage (TLS) Block for the task. */
    #endif

    #if ( configUSE_TASK_NOTIFICATIONS == 1 )
        volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    #endif

    /* See the comments in FreeRTOS.h with the definition of
     * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
    #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */
        uint8_t ucStaticallyAllocated;                     /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
    #endif

    #if ( INCLUDE_xTaskAbortDelay == 1 )
        uint8_t ucDelayAborted;
    #endif

    #if ( configUSE_POSIX_ERRNO == 1 )
        int iTaskErrno;
    #endif
} tskTCB;

typedef tskTCB TCB_t;

2.5 任务调度宏与任务调度算法

FreeRTOS的调度算法一般有三种,是否启用的宏定义在FreeRTOSConfig.h文件夹下可以看到,他们分别是:

#define configUSE_PREEMPTION //是否允许高优先级抢占 1允许 0不允许
#define configUSE_TIME_SLICING //是否允许时间片轮转 1允许 0不允许
#define configIDLE_SHOULD_YIELD	//是否允许空闲任务让步 1允许 0不允许

接下来我们来介绍这三种调度方式:

  1. 优先级抢占:即高优先级的任务是否可以优先抢占,其实对应的就是在就绪态任务中任务排队的次序,优先级越高,排队越靠前,越优先抢占。
  2. 时间片轮转:即同优先级的任务是否可以轮流运转,当宏定义为1时,可以轮流运行。
  3. 空闲任务让步:如果配置了让步的宏,空闲函数可以执行时,空闲函数只触发一次调度,调度后,又主动让出CPU资源让用户task执行。如果未配置的话,将会在空闲函数的while循环里多次循环,也就是说一直处于空闲任务执行的状态。

PS:我们一般使用的是111,也就是全使能的模式,允许高优先级抢占,允许时间片轮转,允许空闲任务让步。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

kafka 实现精确一次性语义实践总结

文章目录 前言幂等生产者幂等生产者的工作原理幂等生产者的局限性如何使用幂等生产者 事务事务的应用场景事务可以解决哪些问题事务是如何保证精确一次性的使用事物 API事物的工作原理 事务的性能 前言 Kafka的精确一次性语义与国际象棋正好相反&#xff1a;要理解它不容易&am…

怎么分区,新买的电脑只有一个C盘

C盘在大多数默认情况下是一台电脑的系统盘&#xff0c;也是创建硬盘分区时&#xff0c;最先被创建的盘。不过在一些特殊情况下&#xff0c;电脑上只有一个C盘。 为什么会出现只有一个C盘这种情况&#xff1f;当我们发现电脑只有一个C盘时&#xff0c;能通过哪些方法去创建其他硬…

Redis 7.x 系列【8】数据类型之哈希(Hash)

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 常用命令2.1 HSET2.2 HGET2.3 HDEL2.4 HEXISTS2.5 HGETALL2.6 HKEYS2.7 HLE…

SMS多表面同步透镜设计

SMS多表面同步透镜设计 一、设计原理 1、Snell定律的矢量形式 折射定律又称Snell定律&#xff0c;主要包括两个方面&#xff1a;一是入射光线、法线和折射光线共面&#xff0c;二是入射角和折射角满足以下关系&#xff1a; n 1 s i n θ 1 n 2 s i n θ 2 n_1 sin{\theta_1…

arm-linux-gnueabihf-gcc:Command not found 解决办法

问题描述 使用虚拟机交叉编译程序&#xff0c;当使用了sudo去编译, 出现arm-linux-gnueabihf-gcc&#xff1a;Command not found的问题。明明已经安装并配置好环境了&#xff0c;发现还是提示找不到编译器。 原因分析&#xff1a; 特意去查了一下sudo方法&#xff0c;我们在sud…

linux中的进程以及进程管理

程序和进程的区别和联系 程序&#xff08;Program&#xff09;&#xff1a; 程序是一组指令的集合&#xff0c;通常存储在磁盘或其他存储设备上&#xff0c;是一种静态的概念。程序本身并没有运行&#xff0c;它只是一个可执行的文件或脚本&#xff0c;包含了一系列的指令和数…

Listary:文件搜索,一键即达

名人说&#xff1a;莫道谗言如浪深&#xff0c;莫言迁客似沙沉。 ——刘禹锡《浪淘沙》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、软件介绍1、Listary2、核心功能 二、下载安装1、下载2、安装 三、使用方…

mysql8.0.19安装zip版本

下载地址https://downloads.mysql.com/archives/community/ 下载版本 下载后解压&#xff0c;不包括data 和my.ini文件。其中data 文件是自动生成的【mysqld --initialize --console】&#xff0c;my.ini需要自己编写设置。 新建my.ini文件 需要自己设置 basedirG:\soft\mysql…

Hubstudio指纹浏览器:海外代理IP新选择,IPXProxy为何备受推崇?

许多人都会把Hubstudio指纹浏览器和代理IP进行搭配使用&#xff0c;为了保证网络操作的顺利进行&#xff0c;例如亚马逊的多账号管理。那有没有好用的海外代理IP呢&#xff0c;如何在Hubstudio指纹浏览器中使用代理IP呢&#xff1f; 下面就给大家推荐好用的一家海外IP代理&…

【linux】网络基础(2)——udp协议

文章目录 引言udp协议的特点udp的头部结构UDP的工作原理简单的UDP网络程序套接字的认识udp服务端代码udp客户端代码服务端运行 引言 用户数据报协议&#xff08;User Datagram Protocol, UDP&#xff09;是一种无连接的传输层协议。它是因特网协议家族的一部分&#xff0c;定义…

windows USB设备驱动开发-开发USB 设备端驱动

USB 设备是通过单个端口连接到计算机的外设&#xff0c;例如鼠标设备和键盘。 USB 客户端驱动程序是计算机上安装的软件&#xff0c;该软件与硬件通信以使设备正常运行。 如果设备属于 Microsoft 支持的设备类&#xff0c;Windows 会为该设备加载 Microsoft 提供的 USB 驱动程序…

2024上半年全国各地电子签章政策汇总,契约锁助力政企数字化转型

当前&#xff0c;全社会的数字化建设已经进入关键时期&#xff0c;各行各业都在加速推进业务数字化转型&#xff0c;电子签章作为业务全程数字化网办的关键一公里在政务服务、组织管理等各项工作中的应用价值逐渐凸显。今年上半年&#xff0c;电子签章在各地政府机关的全力推动…

AI新功能发布:AI生成数据库和AI规划任务,CoCodeAI再添新成员!

Hi&#xff0c;大家好&#xff0c;好久不见&#xff01; 我是CoCodeAI智能助手CoCo。 CoCodeAI智能助手CoCo 我无比荣幸地为大家揭晓 CoCode开发云的璀璨新星&#xff1a; AI生成数据库AI规划任务。 近日&#xff0c;CoCode开发云旗下Co-Project V3.8智能项目管理平台重磅发…

红酒与高尔夫:球场上的优雅之选

在绿茵茵的高尔夫球场上&#xff0c;每一个挥杆的瞬间都充满了优雅与力量。而当这种运动与红酒相遇&#xff0c;便是一场关于品味与格调的很好邂逅。今天&#xff0c;就让我们一起探讨红酒与高尔夫这对球场上的优雅之选&#xff0c;感受它们交织出的不同魅力。 一、高尔夫&…

Flink 容错机制

一致性检查点&#xff08;checkpoint&#xff09; 什么是 Checkpoint &#xff1f; Flink 故障恢复机制的核心&#xff0c;就就是应用状态的一致性检查点&#xff1b;有状态流应用的一直检查点&#xff0c;其实就是所有任务的状态&#xff0c;在某一时间点的一份拷贝&#xff…

视创云展3D虚拟艺术展:重塑艺术观赏的未来体验

在数字化浪潮汹涌的今天&#xff0c;3D虚拟艺术展览正迅速崛起&#xff0c;成为艺术爱好者的新宠儿。这种前沿的艺术呈现方式&#xff0c;不仅极大地提升了观赏的便捷性&#xff0c;还凭借其创新功能&#xff0c;为艺术探索与理解开启了全新篇章。 1、前所未有的便利性&#xf…

如何借助物联网实现农情监测与预警

如何借助物联网实现农情监测与预警&#xff1f; 物联网技术&#xff0c;作为信息技术与传统行业的深度融合产物&#xff0c;正逐步变革着农业生产的管理模式&#xff0c;特别是在农情监测与预警领域展现出巨大潜力。其核心在于通过感知层的各类传感器、通信层的数据传输技术以…

策略模式(Strategy Pattern)

策略模式 &#xff08;Strategy Pattern&#xff09; 定义 它是将定义的算法家族、分别封装起来&#xff0c;让它们之间可以相互替换&#xff0c;从而让算法的变化不会影响到使用算法的用户。 可以避免多重分支的 if-else、switch语句。 属于行为型模式。 适用场景 如果系…

Go - 7.const 使用指南

目录 一.引言 二.定义 三.实践 1. 常量的分组定义 2.枚举常量 3.常量类型 四.总结 一.引言 在编程中&#xff0c;常量&#xff08;constant&#xff09;是指在程序运行期间其值不会改变的变量。常量在代码中有助于提高可读性和维护性&#xff0c;因为它们提供了一个明确…

探索视觉世界:深入了解目标检测算法的奥秘

目标检测算法 一、介绍目标检测算法的背景和意义1.1 目标检测的定义和应用场景1.2 目标检测算法的发展历程 二、目标检测算法分类2.1 传统目标检测算法2.1.1 基于分类器的目标检测算法2.1.2 基于模板匹配的目标检测算法 2.2 深度学习目标检测算法2.2.1 两阶段目标检测算法2.2.2…