当前位置: 首页 > news >正文

Freertos系列(调度机制与创建任务)

如果不想看的可以直接使用git把我的代码下载出来,里面工程挺全的,后期会慢慢的补注释之类的

码云地址:stm32学习笔记: stm32学习笔记源码

如果不会使用git快速下载可以选择直接下载压缩包或者去看看git的使用

Git入门教程-CSDN博客

一  调度机制

1.1 任务调度介绍

在创建任务之前,我们先熟悉下调度机制

在freertos中,我们一般使用的就是抢占式+时间片

在不同优先级的情况下默认为抢占式,就是谁优先级高,优先级高的任务进入就绪态后可以直接执行。

相同优先级下:使用时间片调度,在时间片调度下每个任务会固定执行一个时间片

携程式调度:这个官方自己不更新了,并且不管是什么优先级的任务,如果任务不释放CPU任务就会一直继续运行。所以我感觉没那么方便控制,这边就不写了,有兴趣了的可以自己去试试。

1.1.1 抢占式调度

抢占式调度,需要任务在不同优先级下才能体现出来。

1 高优先级会先执行   2高优先级任务不停止,低优先级任务就不会执行   3被抢占的任务会进入就绪态(什么是就绪态后面会写,可以直接跳目录到任务状态那里去)

这里有个示例:如大家可以看看上面的图。

这边我们写个代码测试一下:(先看看效果,具体实现后续代码会细讲


/* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );void freertos_demo(void)
{    xTaskCreate((TaskFunction_t         )   start_task,(char *                 )   "start_task",(configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   START_TASK_PRIO,(TaskHandle_t *         )   &start_task_handler );vTaskStartScheduler();
}void start_task( void * pvParameters )
{taskENTER_CRITICAL();               /* 进入临界区 */xTaskCreate((TaskFunction_t         )   task1,(char *                 )   "task1",(configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK1_PRIO,(TaskHandle_t *         )   &task1_handler );xTaskCreate((TaskFunction_t         )   task2,(char *                 )   "task2",(configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK2_PRIO,(TaskHandle_t *         )   &task2_handler );								vTaskDelete(NULL);taskEXIT_CRITICAL();                /* 退出临界区 */
}void task1( void * pvParameters )
{while (1){printf("task 1\r\n");vTaskDelay(pdMS_TO_TICKS(500));}
}void task2( void * pvParameters )
{while (1){printf("task 2\r\n");for(int i=0;i<10000000;i++){}}
}

这里我们使用xTaskCreate() 创建了3个任务,参数看不懂没关系,后面会细讲,先了解下调度机制和任务状态

分别是一个启动任务:这个启动任务在去创建了两个任务,创建完成之后删除自身,防止任务被重复创建

两个任务:

task1:优先级为2  打印一个task 1 ,之后释放CPU500ms

task1:优先级为2  打印一个task 1 ,之后释放CPU500ms

 taskENTER_CRITICAL();               /* 进入临界区 */

taskEXIT_CRITICAL();                /* 退出临界区 */

这两个函数是防止创建任务之后任务直接被启动,比如task1创建之后,不管后面的task2优先级有多高,task都会先运行一下,直到task2创建完成,抢占他的cpu,我们等会也会对其关闭测试。

1 高优先级任务不释放CPU(开启临界区了的)

这个就是我们上面的代码跑出来的结果,抢占式调度:高优先级的任务如果不释放CPU那么低优先级的任务永远也执行不了

2 高优先级的任务不释放CPU(关闭临界区了的)
void start_task( void * pvParameters )
{//taskENTER_CRITICAL();               /* 进入临界区 */xTaskCreate((TaskFunction_t         )   task1,(char *                 )   "task1",(configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK1_PRIO,(TaskHandle_t *         )   &task1_handler );xTaskCreate((TaskFunction_t         )   task2,(char *                 )   "task2",(configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK2_PRIO,(TaskHandle_t *         )   &task2_handler );								vTaskDelete(NULL);//taskEXIT_CRITICAL();                /* 退出临界区 */
}

我们上面屏蔽临界区代码之后产生的效果,如上所说,不管优先级怎么样,在task2创建出来之前task1会直接执行。

3 高优先级任务加入CPU释放(开启临界区了的)
void task2( void * pvParameters )
{while (1){printf("task 2\r\n");vTaskDelay(pdMS_TO_TICKS(500));}
}

可以明显的看见,我们这里task2运行之后释放掉了CPU,之后task运行。

4 高优先级任务不释放cpu(开启临界区了的)

这里大家根据1.1.1的抢占式调度的任务图,大家觉得会怎么样?是task1一直运行,还是说task进入就绪态后直接抢占task1?

void task1( void * pvParameters )
{while (1){printf("task 1\r\n");for(int i=0;i<10000000;i++){}}
}void task2( void * pvParameters )
{while (1){printf("task 2\r\n");vTaskDelay(pdMS_TO_TICKS(500));}
}

这里很明显,在task2执行完成后,释放掉cpu,这时候task1运行结论就是低优先级的就算不释放cpu高优先级的一样占用。在经过500ms之后,task2又进入了就绪态,直接抢占了task1,这时候task1的for循环都还没有执行完,等task1的for循环执行完成后,又能执行打印task1了。

这个就是抢占式调度的测试了。

1.1.2 时间片调度

同等优先级的任务会轮流使用CPU(一般是1个时间片)

task1 task2 task3只使用一个时间片,如果task1第一次只使用了0.1个时间片,第二次并不会给他补0.9(这样做会让cpu等待的时间累积的越来越多,很不合理)

这里我们可以再次测试

首先将优先级改为一致

/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO         2
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

之后测试:

1.不释放CPU
void task1( void * pvParameters )
{while (1){printf("task 1\r\n");//for(int i=0;i<10000000;i++){}//vTaskDelay(pdMS_TO_TICKS(500));}
}void task2( void * pvParameters )
{while (1){printf("task 2\r\n");//for(int i=0;i<10000000;i++){}//vTaskDelay(pdMS_TO_TICKS(500));}
}

这里会怎么样呢?我的一个时间片是1ms,先运行task1之后,这里会一直循环打印task1,在运行1ms之后,运行task2

这里可以看见,时间戳都给我干卡死了,并且已经全乱码了,在一个时间片来回切换串口资源导致串口已经乱掉了。

2 释放CPU

这里释放了1ms cpu但是还是肉眼可见的乱码了

之后直接释放了10个时间片打印才正常(这里就引出了一个问题,过快的访问外设资源导致的乱码问题,这种时候我们需要添加一个互斥锁这个后面互斥锁章节说)

这里两种调度模式就说完了,时间片调度就是这个效果了,如果不释放cpu就是1ms内循环执行(这里上面的任务图说的是1ms内没执行完就丢掉,但是我们这里是在一个while循环中,并不会说执行完就完了,所以就会循环执行)

1.2 任务状态

1.2.1 任务状态转换图介绍

在freertos中任务有四种状态:挂起、就绪、运行、阻塞

所有状态想进入运行态都需要先进入就绪态

挂起态:运行态的时候或者就绪态的时候调用函数vtasksuspend

就绪态:cpu释放的时间到了(阻塞态的时间结束了),就会进入就绪态。挂起态直接使用函数也能进入就绪态

阻塞态:就是上面的vTaskDelay(pdMS_TO_TICKS(500));这种类型的函数

运行态:就绪态列表的第一个任务就会进入运行态(会按优先级排序)

二 创建任务的函数与整体框架

2.1 整体框架

2.1.1main函数

main函数:单片机最先进入的c语言部分

int main(void)
{	//LCD 初始化ILI9341_Init ();         /* USART config */USART_Config();  //其中0、3、5、6 模式适合从左至右显示文字,//不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果			//其中 6 模式为大部分液晶例程的默认显示方向  ILI9341_GramScan ( 6 );freertos_demo();
}

这里有个freertos_demo函数,就是创建任务的函数,上面的这些都是外设初始化,其实这些外设初始化也可以使用一个单独的函数来初始化,但是需要注意各个外设的开启·时间,防止硬件不执行。

2.1.2 freertos_demo函数

freertos_demo函数内容一个创建任务函数和启动任务调度器

启动任务调度器必须在所有初始化工作(如硬件初始化、任务创建、信号量创建等)完成后调用,且整个系统中只能调用一次。这个start_task就是一个典型的启动任务了。里面包含所有任务,在创建完任务后,就执行任务调度器即可。

void freertos_demo(void)
{    xTaskCreate((TaskFunction_t         )   start_task,(char *                 )   "start_task",(configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   START_TASK_PRIO,(TaskHandle_t *         )   &start_task_handler );vTaskStartScheduler();
}

2.1.3 start_task函数

start_task:创建所有的任务,方便统一管理

start_task 是典型的 “启动任务”(或叫初始化任务),它的唯一职责是:

  1. 集中创建其他业务任务(如 task1task2)。
  2. 完成初始化后立即删除自己,释放系统资源(栈空间、任务控制块等)。
void start_task( void * pvParameters )
{taskENTER_CRITICAL();               /* 进入临界区 */xTaskCreate((TaskFunction_t         )   task1,(char *                 )   "task1",(configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK1_PRIO,(TaskHandle_t *         )   &task1_handler );xTaskCreate((TaskFunction_t         )   task2,(char *                 )   "task2",(configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK2_PRIO,(TaskHandle_t *         )   &task2_handler );								vTaskDelete(NULL);taskEXIT_CRITICAL();                /* 退出临界区 */
}

2.1.4 task函数

task:具体任务的处理,你需要处理数据在其内部处理即可

void task1( void * pvParameters )
{while (1){printf("task 1\r\n");//for(int i=0;i<10000000;i++){}vTaskDelay(pdMS_TO_TICKS(10));}
}void task2( void * pvParameters )
{while (1){printf("task 2\r\n");//for(int i=0;i<10000000;i++){}vTaskDelay(pdMS_TO_TICKS(10));}
}

这里就是整个代码的大概框架了

2.2 本章使用到的函数讲解

2.2.1 动态创建任务函数

xTaskCreate:函数原型如下

动态与静态的区别:

动态:任务的任务控制块以及任务的栈空间所需的内存,均由 FreeRTOS 从 FreeRTOS 管理的堆中分配

静态:任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供

这里来说一下函数原型需要传入的参数:PS文章为使用讲解哈,不涉及函数的实现,这个可以在后面的系统设计讲解

1 pxTaskCode 指向任务函数的指针

就是把你函数的名字传进去即可

2 pcName

给任务起一个名称,主要用于调试和可视化分析。这个名称可以帮助开发者在调试过程中,通过调试工具(如 FreeRTOS 的可视化调试插件)快速识别不同的任务

3 uxStackDepth

指定任务栈的大小:单位是字,不是字节写128的话就是128 * 4 字节

4 pvParameters

用于向任务函数传递参数。当任务函数需要接收外部数据时,可以通过这个参数传入:我们这里没有传入东西,还有后面用到的时候理解

uxPriority

优先级:越大越高理论上软件可以开任意优先级数量,但是由于硬件等限制,在配置文件中一般都写的32

6 pxCreatedTask

是一个指向任务句柄的指针。用于获取新创建任务的句柄。任务句柄是任务的唯一标识,通过任务句柄可以在运行时对任务进行管理,比如挂起、恢复、删除任务等操作。

类型定义为TaskHandle_t即可

2.2.2 vTaskDelete()函数

就是删除任务:传入句柄即可,本文使用:创建完task1和taks2后删除start任务,如果是删除自身,函数内田NULL即可删除自己,如果是在自己函数内需要删除其他任务,就需要填写对应的句柄,句柄保存了任务的内存地址。

2.2.3 vTaskStartScheduler()函数

启动任务调度器函数

启动任务调度器必须在所有初始化工作(如硬件初始化、任务创建、信号量创建等)完成后调用,且整个系统中只能调用一次。这个start_task就是一个典型的启动任务了。里面包含所有任务,在创建完任务后,就执行任务调度器即可。

2.2.4 临界区函数

taskENTER_CRITICAL();               /* 进入临界区 */

taskEXIT_CRITICAL();                /* 退出临界区 */

进入临界区后会停止调度,这时候低优先级的任务就算先创建了也不会先运行了。

附上本文代码(同步上传gitee了)

不想下载的可以直接复制

#include "FreeDome.h"/* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO         2
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );// 全局共享常量(所有任务共用)
#define CHAR_WIDTH     WIDTH_CH_CHAR  // 中文字符宽度
#define SCREEN_WIDTH   LCD_X_LENGTH   // 屏幕宽度
#define FONT_HEIGHT    LCD_GetFont()->Height  // 字体高度void freertos_demo(void)
{    xTaskCreate((TaskFunction_t         )   start_task,(char *                 )   "start_task",(configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   START_TASK_PRIO,(TaskHandle_t *         )   &start_task_handler );vTaskStartScheduler();
}void start_task( void * pvParameters )
{taskENTER_CRITICAL();               /* 进入临界区 */xTaskCreate((TaskFunction_t         )   task1,(char *                 )   "task1",(configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK1_PRIO,(TaskHandle_t *         )   &task1_handler );xTaskCreate((TaskFunction_t         )   task2,(char *                 )   "task2",(configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,(void *                 )   NULL,(UBaseType_t            )   TASK2_PRIO,(TaskHandle_t *         )   &task2_handler );								vTaskDelete(NULL);taskEXIT_CRITICAL();                /* 退出临界区 */
}void task1( void * pvParameters )
{while (1){printf("task 1\r\n");//for(int i=0;i<10000000;i++){}vTaskDelay(pdMS_TO_TICKS(10));}
}void task2( void * pvParameters )
{while (1){printf("task 2\r\n");//for(int i=0;i<10000000;i++){}vTaskDelay(pdMS_TO_TICKS(10));}
}
#include "stm32f10x.h"
#include "./usart/bsp_usart.h"	
#include "./lcd/bsp_ili9341_lcd.h"
#include "./flash/bsp_spi_flash.h"
#include "FreeDome.h"int main(void)
{	//LCD 初始化ILI9341_Init ();         /* USART config */USART_Config();  //其中0、3、5、6 模式适合从左至右显示文字,//不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果			//其中 6 模式为大部分液晶例程的默认显示方向  ILI9341_GramScan ( 6 );freertos_demo();
}


    文章转载自:

    http://38prJTHh.ztmnr.cn
    http://m2BKf2UM.ztmnr.cn
    http://fKKj4XAU.ztmnr.cn
    http://2FTg2neA.ztmnr.cn
    http://2e4ney5P.ztmnr.cn
    http://pzwJjAY2.ztmnr.cn
    http://pQs2wleE.ztmnr.cn
    http://pmkppI32.ztmnr.cn
    http://1f12B5ZD.ztmnr.cn
    http://SAv6LK5h.ztmnr.cn
    http://XhUBE83t.ztmnr.cn
    http://qNC3tt4r.ztmnr.cn
    http://gVHy1UpU.ztmnr.cn
    http://T3EbHS4n.ztmnr.cn
    http://HIawQsBV.ztmnr.cn
    http://5f2Pj7EO.ztmnr.cn
    http://cKhrhmtl.ztmnr.cn
    http://w1y6zV8J.ztmnr.cn
    http://0WO0sJDS.ztmnr.cn
    http://0yaAG4kH.ztmnr.cn
    http://PPiK9won.ztmnr.cn
    http://eI5sXdg5.ztmnr.cn
    http://MUlzFhND.ztmnr.cn
    http://TN0JBWpw.ztmnr.cn
    http://eVWngPU6.ztmnr.cn
    http://8kBT22wz.ztmnr.cn
    http://rFGYpghD.ztmnr.cn
    http://an3hSwOm.ztmnr.cn
    http://5q3MzxEc.ztmnr.cn
    http://UTBfbYi4.ztmnr.cn
    http://www.dtcms.com/a/388108.html

    相关文章:

  1. 深度学习(二)
  2. 搭建node脚手架(六) ESLint 功能模块
  3. mysql面试(2)
  4. Linux系统DNS服务
  5. 如何通过跳板机访问内网 Mysql 服务器
  6. SSH 远程连接内网 Linux 服务器
  7. Spring Cloud - 微服务监控
  8. Flutter-[1]入门指导
  9. Linux服务器运维自动化巡检工具
  10. Java 大视界 -- Java 大数据在智能家居设备联动与场景化节能中的应用拓展(413)
  11. Node.js 部署:PM2 的 Fork 与集群模式
  12. 【C++上岸】C++常见面试题目--网络篇(第二十五期)
  13. LangChain使用方法以OpenAI 的聊天模型GPT-4o为例
  14. CephFS存储文件系统介绍
  15. Java Swagger2 能显示页面但看不到一个接口
  16. SSL证书有效期缩短:自动化解决方案
  17. C# 多线程编程 (.NET Framework 4.0)
  18. 一个手艺活 - 跨语言编程
  19. docker安装ollama、下载模型详细步骤
  20. 微服务和分布式的基础学识
  21. 自动化测试框架pytest---Json Schema
  22. 阿里云PolarDB MySQL版与MCP集成方案:数据处理分析全流程的效能革命
  23. Python实现霸王龙优化算法(Tyrannosaurus Optimization Algorithm, TROA)(附完整代码)
  24. 弥合安全分析与故障仿真之间差距的方法
  25. JavaEE---9.网络原理TCP/IP
  26. @Value
  27. 安装es、kibana、logstash
  28. Leetcode-148.排序链表
  29. 基于ETF底仓的网格交易系统实现动态参数优化与动量因子融合
  30. C++底层刨析章节三: 函数对象与适配器:STL中的智能操作单元