FreeRTOS(二)
一.FreeRTOS的目录结构

主要涉及2个目录:
Core: Inc目录下的FreeRTOSConfig.h是配置文件 ; Src目录下的freertos.c是STM32CubeMX创建的默认任务。


二.数据类型和编程规范


三.FreeRTOS任务管理
<1>任务的创建:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "usart.h"
#include "cmsis_os.h"TaskHandle_t mytask1handle;//任务一(mytask running....)
int fputc(int ch,FILE *f){HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);return EOF;
}//任务执行函数
void task1_fun(void *argument){for(int i=0;i<5;i++){printf("mytask running....\n");vTaskDelay(1000);}	vTaskDelete(NULL);//删除当前任务
}//创建任务
void task1_init(){BaseType_t ret = xTaskCreate(task1_fun,"mytask1", 128, NULL,osPriorityNormal, &mytask1handle);if(ret != pdPASS){printf("创建任务失败");}else{printf("创建任务成功");}
}
2.xTaskCreateStatic

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "usart.h"
#include "cmsis_os.h"TaskHandle_t mytask2handle;//任务二(mytask static running....)
#define STACK_DEPTH 128
StackType_t  uxStackBuffer[STACK_DEPTH];
StaticTask_t xTaskTcb;//任务执行函数
void task2_fun(void *argument){for(;;){printf("mytask static running....\n");vTaskDelay(1000);}	}//创建任务
void task2_init(){mytask2handle = xTaskCreateStatic(task2_fun,"mytask2", STACK_DEPTH, NULL,osPriorityNormal,uxStackBuffer,&xTaskTcb);}StackType_t uxStackBuffer[STACK_DEPTH];StackType_t 用于声明任务栈数组
<2>vTaskList是FreeRTOS中用于获取系统中所有任务状态信息的调试函数。
在task.c文件中定义。它可以将任务的名称、状态、优先级、堆栈剩余空间等信息输出到指定的缓冲区。

示例:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "usart.h"
#include "cmsis_os.h"TaskHandle_t mytask3handle;//任务三(打印任务状态)//任务执行函数
void task3_fun(void *argument){char buffer[256];for(;;){//打印所有的任务状态vTaskList(buffer);printf("Name\t\tState\tPri\tStack\tNum\n");printf("%s\r\n",buffer);vTaskDelay(1000);}	}//创建任务
void task3_init(){BaseType_t ret = xTaskCreate(task3_fun,"mytask3", 128, NULL,osPriorityNormal, &mytask3handle);if(ret != pdPASS){printf("创建任务失败");}else{printf("创建任务成功");}
}结果如下:
<3>vTaskDelete:从RTOS内核管理中移除任务。
要删除的任务将从所有就绪、 阻塞、挂起和事件列表中移除。空闲任务负责释放由RTOS内核分配给已删除任务的内存。但是,任务代码分配的内存不会自动释放, 应在任务删除之前手动释放。
//任务执行函数
void taskX_fun(void *argument){for(;;){......}	vTaskDelete(NULL);//删除当前任务
}要删除的任务的句柄。如果传递NULL,会删除调用任务。
删除任务的过程总结:vTaskDelete :负责将任务从运行状态中移除、标记为待删除,并加入等待清理链表。prvDeleteTCB :负责最终的资源释放(是内核内部的清理函数,由空闲任务调用)。两者共同构成了FreeRTOS的任务删除机制:前者处理 "逻辑删除",后者处理"物理销毁"。
<4>任务的挂起和恢复
1.vTaskSuspend
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
void vTaskResume( TaskHandle_t xTaskToResume );
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "usart.h"
#include "cmsis_os.h"TaskHandle_t mytask1handle;
TaskHandle_t mytask2handle;//任务一(mytask running....)
int fputc(int ch,FILE *f){HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);return EOF;
}//任务执行函数
void task1_fun(void *argument){for(int i=0;i<5;i++){printf("mytask running....\n");vTaskDelay(1000);}	//唤醒任务二vTaskResume(mytask2handle);vTaskDelete(NULL);//删除当前任务
}//创建任务
void task1_init(){BaseType_t ret = xTaskCreate(task1_fun,"mytask1", 128, NULL,osPriorityNormal, &mytask1handle);if(ret != pdPASS){printf("创建任务失败");}else{printf("创建任务成功");}
}//任务二(mytask static running....)
#define STACK_DEPTH 128StackType_t  uxStackBuffer[STACK_DEPTH];
StaticTask_t xTaskTcb;//任务执行函数
void task2_fun(void *argument){//挂起调用任务vTaskSuspend(NULL);for(;;){printf("mytask static running....\n");vTaskDelay(1000);}	}//创建任务
void task2_init(){mytask2handle = xTaskCreateStatic(task2_fun,"mytask2", STACK_DEPTH, NULL,osPriorityNormal,uxStackBuffer,&xTaskTcb);}结果:开始的时候由于任务二中调用了//挂起调用任务vTaskSuspend(NULL);所以只有任务一执行
执行五次之后(5秒之后),在任务一中任务二被唤醒,任务一被删除。结果如下:

四.任务状态转换
创建任务时,任务状态由"未开始"变为"就绪"。调度器根据任务优先级选取该任务并将其状态变为"运行中"。任务等待某个事件(如信号量)时,任务状态变为"阻塞"。任务等待其他任务释放资源时,任务状态可能会转变为"挂起"。任务自己调用删除函数删除自己时,任务状态变为"已删除"。
A.运行态
正在执行的任务,该任务就处于运行态,如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处于运行态。

五.任务调度
例如:创建4个同优先级任务Task1,Task2,Task3和Task4。每个任务分配的时间片大小是1个系统时钟节拍(默认设置为1ms)。
先运行任务Task1,运行够1个系统时钟节拍后,通过时间片调度切换到任务Task2。任务Task2运行够1个系统时钟节拍后,通过时间片调度切换到任务Task3。
任务Task3在运行期间调用了阻塞式API函数,调用函数时,虽然1个系统时钟节拍的时间片大小还没有用完,此时依然会通过时间片调度切换到下一个任务Task4。(注意,没有用完的时间片不会再使用,下次任务Task3得到执行还是按照1个系统时钟节拍运行)。
任务Task4运行够1个系统时钟节拍后,通过时间片调度切换到任务Task1。
ps:抢占式调度和时间片轮转可以同时存在,当有高优先级任务就绪时,运行高优先级任务;当最高优先级的任务有好几个时,这几个任务可以以时间片轮转方式调度。当任务执行到阻塞操作(如等待事件、信号量、队列等)时,它会停止执行并进入阻塞状态。一旦任务阻塞,它将不再消耗CPU时间。此时,调度器会回收该任务剩余的时间片。当阻塞的任务等待的条件得到满足时(例如,它等待的事件或信号量被触发),它将变为就绪状态,并被放入相应的就绪列表。调度器在下一个合适的时机(例如,当前执行的任务完成或时间片用完)会重新调度该任务,它将从上次停止执行的地方继续执行。

六.任务延时
1.vTaskDelay





