文章目录
- 系列教程总目录
- 概述
- 3.1 基本概念
- 3.2 任务创建与删除
- 3.2.1 什么是任务
- 3.2.2 创建任务
- 3.2.3 示例1: 创建任务
- 3.2.4 示例2: 使用任务参数
- 3.2.5 任务的删除
- 3.2.6 示例3: 删除任务
- 3.3 任务优先级和Tick
- 3.3.1 任务优先级
- 3.3.2 Tick
- 3.3.3 示例4: 优先级实验
- 3.3.4 示例5: 修改优先级
- 3.4 任务状态
- 3.4.1 阻塞状态(Blocked)
- 3.4.2 暂停状态(Suspended)
- 3.4.3 就绪状态(Ready)
- 3.4.4 完整的状态转换图
- 3.5 Delay函数
- 3.5.1 两个Delay函数
- 3.5.2 示例6: Delay
- 3.6 空闲任务及其钩子函数
- 3.6.1 介绍
- 3.6.2 使用钩子函数的前提
- 3.7 调度算法
- 3.7.1 重要概念
- 3.7.2 配置调度算法
- 3.7.3 示例7: 调度
- 3.7.4 对比效果: 抢占与否
- 3.7.5 对比效果: 时间片轮转与否
- 3.7.6 对比效果: 空闲任务让步
需要获取更好阅读体验的同学,请访问我专门设立的站点查看,地址:http://rtos.100ask.net/
系列教程总目录本教程连载中,篇章会比较多,为方便同学们阅读,点击这里可以查看文章的 目录列表,目录列表页面地址:https://blog.csdn.net/thisway_diy/article/details/121399484
概述在本章中,会涉及如下内容:
- FreeRTOS如何给每个任务分配CPU时间
- 如何选择某个任务来运行
- 任务优先级如何起作用
- 任务有哪些状态
- 如何实现任务
- 如何使用任务参数
- 怎么修改任务优先级
- 怎么删除任务
- 怎么实现周期性的任务
- 如何使用空闲任务
对于整个单片机程序,我们称之为application,应用程序。
使用FreeRTOS时,我们可以在application中创建多个任务(task),有些文档把任务也称为线程(thread)。
以日常生活为例,比如这个母亲要同时做两件事:
- 喂饭:这是一个任务
- 回信息:这是另一个任务
这可以引入很多概念:
- 任务状态(State):
- 当前正在喂饭,它是running状态;另一个"回信息"的任务就是"not running"状态
- "not running"状态还可以细分:
- ready:就绪,随时可以运行
- blocked:阻塞,卡住了,母亲在等待同事回信息
- suspended:挂起,同事废话太多,不管他了
- 优先级(Priority)
- 我工作生活兼顾:喂饭、回信息优先级一样,轮流做
- 我忙里偷闲:还有空闲任务,休息一下
- 厨房着火了,什么都别说了,先灭火:优先级更高
- 栈(Stack)
- 喂小孩时,我要记得上一口喂了米饭,这口要喂青菜了
- 回信息时,我要记得刚才聊的是啥
- 做不同的任务,这些细节不一样
- 对于人来说,当然是记在脑子里
- 对于程序,是记在栈里
- 每个任务有自己的栈
- 事件驱动
- 孩子吃饭太慢:先休息一会,等他咽下去了、等他提醒我了,再喂下一口
- 协助式调度(Co-operative Scheduling)
- 你在给同事回信息
- 同事说:好了,你先去给小孩喂一口饭吧,你才能离开
- 同事不放你走,即使孩子哭了你也不能走
- 你好不容易可以给孩子喂饭了
- 孩子说:好了,妈妈你去处理一下工作吧,你才能离开
- 孩子不放你走,即使同事连发信息你也不能走
- 你在给同事回信息
这涉及很多概念,后续章节详细分析。
3.2 任务创建与删除 3.2.1 什么是任务在FreeRTOS中,任务就是一个函数,原型如下:
void ATaskFunction( void *pvParameters );
要注意的是:
- 这个函数不能返回
- 同一个函数,可以用来创建多个任务;换句话说,多个任务可以运行同一个函数
- 函数内部,尽量使用局部变量:
- 每个任务都有自己的栈
- 每个任务运行这个函数时
- 任务A的局部变量放在任务A的栈里、任务B的局部变量放在任务B的栈里
- 不同任务的局部变量,有自己的副本
- 函数使用全局变量、静态变量的话
- 只有一个副本:多个任务使用的是同一个副本
- 要防止冲突(后续会讲)
下面是一个示例:
void ATaskFunction( void *pvParameters )
{
/* 对于不同的任务,局部变量放在任务的栈里,有各自的副本 */
int32_t lVariableExample = 0;
/* 任务函数通常实现为一个无限循环 */
for( ;; )
{
/* 任务的代码 */
}
/* 如果程序从循环中退出,一定要使用vTaskDelete删除自己
* NULL表示删除的是自己
*/
vTaskDelete( NULL );
/* 程序不会执行到这里, 如果执行到这里就出错了 */
}
3.2.2 创建任务
创建任务时使用的函数如下:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
const char * const pcName, // 任务的名字
const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
void * const pvParameters, // 调用任务函数时传入的参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务
参数说明:
参数描述pvTaskCode函数指针,可以简单地认为任务就是一个C函数。它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)"pcName任务的名字,FreeRTOS内部不使用它,仅仅起调试作用。长度为:configMAX_TASK_NAME_LENusStackDepth每个任务都有自己的栈,这里指定栈大小。单位是word,比如传入100,表示栈大小为100 word,也就是400字节。最大值为uint16_t的最大值。怎么确定栈的大小,并不容易,很多时候是估计。精确的办法是看反汇编码。pvParameters调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters)uxPriority优先级范围:0~(configMAX_PRIORITIES – 1)数值越小优先级越低,如果传入过大的值,xTaskCreate会把它调整为(configMAX_PRIORITIES – 1)pxCreatedTask用来保存xTaskCreate的输出结果:task handle。以后如果想操作这个任务,比如修改它的优先级,就需要这个handle。如果不想使用该handle,可以传入NULL。返回值成功:pdPASS;失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(失败原因只有内存不足)注意:文档里都说失败时返回值是pdFAIL,这不对。pdFAIL是0,errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY是-1。 3.2.3 示例1: 创建任务代码为:FreeRTOS_01_create_task
使用2个函数分别创建2个任务。
任务1的代码:
void vTask1( void *pvParameters )
{
const char *pcTaskName = "T1 run\r\n";
volatile uint32_t ul; /* volatile用来避免被优化掉 */
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务1的信息 */
printf( pcTaskName );
/* 延迟一会(比较简单粗暴) */
for( ul = 0; ul
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?