还在为实时系统发愁?FreeRTOS如何确保任务永不超时,颠覆你对操作系统的认知

作者:佚名 时间:2025-11-14 08:48

字号

1-1.png

科技编辑,长时间留意嵌入式系统演进状况这样的我,察觉到任务时效性严谨确保方面有着实时操作系统的关键价值,物联网设备以及工业控制领域里这一要点特别重要。今日深度剖析开源实时操作系统FreeRTOS的调度机制以及达成原理,期望可为开发者给予实用技术参照。 的。

调度机制解析

1-3.png

FreeRTOS将固定优先级抢占式调度视为核心策略,该系统会持续监控就绪队列中最高优先级任务,要是出现更高优先级任务,立马切换去执行,每个任务创建之时都被赋予0到31的优先级数值,数值越大表明优先级越高,借此确保关键任务能够及时获取CPU资源。

1-4.png

系统针对多个有着相同优先级的任务,借助时间片轮转来达成公平调度,这个公平调度是依靠时间片轮转得以实现的。默认时间长度由configTICK_RATE_HZ参数决定,这种决定通常将默认时间片设置在1毫秒左右。当任务持续运行到满一个时间片后,调度器会触发PendSV异常从而开启上下文切换,通过这个上下文切换把CPU使用权转交给下一个处于就绪状态的任务。

1-5.png

1-6.png

中断管理配置

1-7.png

进行ARM Cortex - M架构移植工作之际,要妥善处理三个核心中断,vPortSVCHandler是用以启动调度器的,xPortPendSVHandler进行上下文切换的处理,xPortSysTickHandler负责维护系统时钟节拍,这些中断服务程序在port.c文件里实现了完整框架。

1-8.png

1-9.png

对于开发者来讲,在启动阶段的时候,要去配置中断控制器,通过这样做来确保那三个异常能获取到正确的优先级。在这个过程中需要格外留意,PendSV必须被设置成最低优先级的异常,不然的话,就有可能在中断处理的进程里,出现任务切换的状况,进而致使系统出现异常,而这一点恰恰是保证实时性的关键设计所要留意之处 , 。

> 需要在FreeRTOSConfig.h中额外添加一些配置:
>
> ```
>  #define xPortPendSVHandler PendSV_Handler
>  #define vPortSVCHandler SVC_Handler
>  #define INCLUDE_xTaskGetSchedulerState 1
> ```

任务状态管理

1-10.png

其中,任务生命周期包含创建该状态,经历运行该状态,遭遇阻塞该状态,碰到挂起该状态,还有删除该状态众多状态。通过vTaskSuspend函数和vTaskResume函数能够手动进行挂起并恢复任务执行此操作,然而这得把FreeRTOSConfig.h里的INCLUDE_vTaskSuspend宏定义设置为1方可启用相关功能。

去执行任务删除这般操作,得去开展调用 vTaskDelete 接口这个行为,系统会自动地去履行释放任务控制块以及堆栈资源的动作。为了避免产生内存泄漏此种状况,建议在着手进行删除之前,要保证任务已经释放掉它所持有的全部动态分配资源,在这些资源里包含信号量、消息队列等系统资源。

 // void SVC_Handler(void)
 // {
 //   /* USER CODE BEGIN SVCall_IRQn 0 */
 ​
 //   /* USER CODE END SVCall_IRQn 0 */
 //   /* USER CODE BEGIN SVCall_IRQn 1 */
 ​
 //   /* USER CODE END SVCall_IRQn 1 */
 // }
 ​
 // void PendSV_Handler(void)
 // {
 //   /* USER CODE BEGIN PendSV_IRQn 0 */
 //   xPortPendSVHandler();
 //   /* USER CODE END PendSV_IRQn 0 */
 //   /* USER CODE BEGIN PendSV_IRQn 1 */
 ​
 //   /* USER CODE END PendSV_IRQn 1 */
 // }

时间管理功能

 void SysTick_Handler(void)
 {
   /* USER CODE BEGIN SysTick_IRQn 0 */
   if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)//判断调度器状态(挂起、不运行、运行)
   {
     xPortSysTickHandler();//调用写好的中断函数
   }
   /* USER CODE END SysTick_IRQn 0 */
 ​
   /* USER CODE BEGIN SysTick_IRQn 1 */
 ​
   /* USER CODE END SysTick_IRQn 1 */
 }

有种系统给出了vTaskDelay与vTaskDelayUntil这两种延时接口,这两种接口全是以系统节拍数当作单位的。鉴于configTICK_RATE_HZ一般被设置成1000,每一个节拍对应着1毫秒的实际时间。vTaskDelay达成相对延时,然而vTaskDelayUntil却能够达成精确的周期执行。

 #ifndef __FREERTOS_DEMO__
 #define __FREERTOS_DEMO__
 ​
 #include "FreeRTOS.h"
 #include "task.h"
 ​
 void FreeRTOS_Start(void);
 ​
 ​
 #endif // __FREERTOS_DEMO__
 ​

在碰到要临时把任务调度予以禁用的情形之时,能够借由调用vTaskSuspendAll来实现对调度器的挂起行为。在这样的时候,系统仍旧会对中断作出回应,然而却禁止任务去进行切换,一直延续到xTaskResumeAll被调用的时候为止。这样的一种机制适用于有需要原子操作的场景状况,不过却得对挂起的时间予以严格的掌控 。

 #include "FreeRTOS_demo.h"
 ​
 void FreeRTOS_Start(void)
 {
     vTaskStartScheduler();
 }

资源保护机制

 #include "main.h"
 #include "FreeRTOS_demo.h"
 ​
 int main(void)
 {
   .......
   FreeRTOS_Start();
   ......
   while (1)
   {
   }
 ​
 }

于Cortex - M3/M4内核内,临界区的保护是借着taskENTER_CRITICAL以及taskEXIT_CRITICAL这两个宏得以达成,这两个宏会临时性地把可屏蔽中断给关闭掉,通过这样做来确保关键代码段在执行的时候不会受到打断,至于此功能是经过对BASEPRI寄存器进行操作来达成中断屏蔽的。

 #include "FreeRTOS_demo.h"
 ​
 /* 任务1 */
 void task1(void *pvParameters);
 #define task1_STACK_SIZE 128 //128 *4 =512字节
 #define task1_PRIORITY 4
 TaskHandle_t task1_handler;
 ​
 void FreeRTOS_Start(void)
 {
     xTaskCreate(task1, "task1", task1_STACK_SIZE, NULL, task1_PRIORITY, &task1_handler);
 ​
     vTaskStartScheduler();
 }
 ​
 void task1(void *pvParameters)
 {
     while (1)
     {
         HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
         printf("1-toggle\n");
         vTaskDelay(1000);
     }
 }

任务通知系统归属轻量级通信机制范畴,各个任务之中存有通知数组,数组长度借由configTASK_NOTIFICATION_ARRAY_ENTRIES予以配置,默认情形下每个条目包含32位数值以及8位状态标志,与信号量相比而言,任务通知可降低内存占用,也能够提高响应速度。

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

定时器实现原理

系统节拍是软件定时器开展工作所依赖的根基,它对单次触发以及周期触发这两种模式予以支持。定时器回调的函数是于独立的有着高优先级的守护任务内去执行的,因而务必得防范去运用任何会造成阻塞的API函数,这里面包含了像vTaskDelay、队列等待这类操作。

 void vTaskResume( TaskHandle_t xTaskToResume );

定时器服务任务的优先级,是通过configTIMER_TASK_PRIORITY参数来设置的,在默认状态下,它比普通应用任务的优先级要高,开发者一定要确保,定时器回调函数的执行时间尽量短,原因是,处理时间过长的话,会影响其他定时器准时触发。

实际开发之时,如何平衡任务优先级设置与怎样平衡系统实时性要求?这进而化作成为关键课题。各位从事开发业者,于嵌入式项目当中碰到的最为棘手的实时任务调度方面的问题是啥呢?欢迎在评论区分享自身实战经验,若觉本文有助于您,请点赞支持后分享给更多开发者。

 void vTaskDelayUntil( TickType_t *pxPreviousWakeTime,
                       const TickType_t xTimeIncrement );

责任编辑:CQITer新闻报料:400-888-8888   本站原创,未经授权不得转载
继续阅读
热新闻
推荐
关于我们联系我们免责声明隐私政策 友情链接