目录
注意:使用时间片调度需把宏 configUSE_TIME_SLICING 和 configUSE_PREEMPTION 置1
前言
其实在网上已经有很多相关的博文了,不过我还是决定自己动手来记录一下。按照之前的经验,需要达到的目的为切题,详细。否则回看的时候不清楚。好多,整理的好累。
并且,在函数开始前我们首先需要知道一下在RTOS中地变量和函数命名风格。
https://blog.youkuaiyun.com/xinghuanmeiying/article/details/78180231
上面这个链接地博主写的特别好。理解如下
命名规则
u :代表unsigned。
s :代表short。
c :char。
所以类似uc,us类的变量就是unsigned char,unsigned short,分别对应uint8_t,uint16_t。
x :为用户自定义的数据类型,比如结构体,队列等。
常看到ux开头的函数,就是unsigned且用户自定义的类型。需要注意的是size_t变量前缀也是ux。一般返回值为任务句柄
e :枚举变量
p :指针变量
类似(uint16_t *)变量前缀为pus。
prv :static函数
v: void函数
ux:一般是返回值为无符号整数,变量类为UBaseType_t
动态创建任务函数原型
BaseType_t xTaskCreate
( TaskFunction_t pxTaskCode, /* 指向任务函数的指针 */
const char * const pcName, /* 任务名字,最大长度configMAX_TASK_NAME_LEN */
const configSTACK_DEPTH_TYPE usStackDepth, /* 任务堆栈大小,注意字为单位 */
void * const pvParameters, /* 传递给任务函数的参数 */
UBaseType_t uxPriority, /* 任务优先级,范围:0 ~ configMAX_PRIORITIES - 1 */
TaskHandle_t * const pxCreatedTask /* 任务句柄,就是任务的任务控制块 */
)
返回值 | 描述 |
pdPASS | 任务创建成功 |
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY | 任务创建失败 |
动态创建任务函数使用范例
将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
//开始任务任务函数
void start_task(void *pvParameters)
{
taskEXIT_CRITICAL(); //退出临界区
//代码执行逻辑
taskENTER_CRITICAL(); //进入临界区
}
静态创建函数原型
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 /* 任务控制块指针,由用户分配 */
);
返回值 | 描述 |
NULL | 用户没有提供相应的内存,任务创建失败 |
其他值 | 任务句柄,任务创建成功 |
静态创建任务函数使用范例
不全,有点多,先放着静态的
#define START_TASK_PRIO 1 // 任务优先级
#define START_STK_SIZE 128 // 任务堆栈大小
StackType_t StartTaskStack[START_STK_SIZE]; // 任务堆栈
StaticTask_t StartTaskTCB; // 任务控制块
TaskHandle_t StartTask_Handler; // 任务句柄
void start_task(void *pvParameters); // 任务函数
// 创建开始任务
StartTask_Handler = xTaskCreateStatic((TaskFunction_t)start_task, // 任务函数
(const char *)"start_task", // 任务名称
(uint32_t)START_STK_SIZE, // 任务堆栈大小
(void *)NULL, // 传递给任务函数的参数
(UBaseType_t)START_TASK_PRIO, // 任务优先级
(StackType_t *)StartTaskStack, // 任务堆栈
(StaticTask_t *)&StartTaskTCB); // 任务控制块 // 开启任务调度
// 开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); // 进入临界区
// 创建TASK1任务
taskEXIT_CRITICAL(); // 退出临界区
}
任务删除函数原型
void vTaskDelete(TaskHandle_t xTaskToDelete);
形参 | 描述 |
xTaskToDelete | 待删除任务的任务句柄 |
任务删除函数使用范例
vTaskDelete(Task2Task_Handler);
这里调用这个函数的话,只需要传入需要删除的函数句柄就好了。这里的Task2Task_Handler只是为了进行演示使用的,大家可以根据自己的实际情况调整。以后这种只需要传入单个参数且参数为任务句柄同时无返回值时就不再写使用范例了,大家看到这里应该都是有基础的。
2、任务挂起和恢复函数
任务挂起函数原型
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
形参 | 描述 |
xTaskToSuspend | 待挂起任务的任务句柄 |
此函数用于挂起任务,使用时需将宏 INCLUDE_vTaskSuspend 配置为 1。
无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复 。
注意:当传入的参数为NULL,则代表挂起任务自身(当前正在运行的任务)
任务恢复函数(任务中恢复)原型
void vTaskResume(TaskHandle_t xTaskToResume)
形参 | 描述 |
xTaskToResume | 待恢复任务的任务句柄 |
使用该函数注意宏:INCLUDE_vTaskSuspend必须定义为 1
注意:任务无论被 vTaskSuspend() 挂起多少次,只需在任务中调用 vTakResume() 恢复一次,就可以继续运行。且被恢复的任务会进入就绪态!
任务恢复函数(中断中恢复)原型
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
形参 | 描述 |
xTaskToResume | 待恢复任务的任务句柄 |
函数:xTaskResumeFromISR返回值描述如下:
返回值 | 描述 |
pdTRUE | 任务恢复后需要进行任务切换 |
pdFALSE | 任务恢复后不需要进行任务切换 |
使用该函数注意宏:INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必须定义为 1
该函数专用于中断服务函数中,用于解挂被挂起任务
注意:中断服务程序中要调用freeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级
任务恢复函数(中断中恢复)使用范例
#include <Arduino_FreeRTOS.h>
TaskHandle_t xTaskToResume = NULL;
void MyInterruptHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// 尝试从 ISR 中恢复任务,这里注入的参数为函数句柄就可以了
xHigherPriorityTaskWoken = xTaskResumeFromISR(xTaskToResume);
// 如果需要,请求上下文切换,因为这个时候被恢复的任务优先级可能比正在执行地任务优先级更高。
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void setup()
{
// 初始化任务、中断等
// 将中断处理函数与相应的中断连接
attachInterrupt(digitalPinToInterrupt(2), MyInterruptHandler, RISING);
}
void loop()
{
// Empty. Things are done in tasks.
}
任务恢复和挂起函数总结
3、临界区的代码保护函数
应用场景:FreeRTOS在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断
任务级进入和退出临界段函数原型
函数 | 描述 |
taskENTER_CRITICAL() | 任务级进入临界段 |
taskEXIT_CRITICAL() | 任务级退出临界段 |
任务级进入和退出临界段函数使用范例
taskENTER_CRITICAL() ;
{
… … /* 临界区 */
}
taskEXIT_CRITICAL() ;
中断级进入和退出临界段函数原型
函数 | 描述 |
taskENTER_CRITICAL_FROM_ISR() | 中断级进入临界段 |
taskEXIT_CRITICAL_FROM_ISR() | 中断级退出临界段 |
中断级进入和退出临界段函数使用范例
uint32_t save_status;
save_status = taskENTER_CRITICAL_FROM_ISR();
{
… … /* 临界区 */
}
taskEXIT_CRITICAL_FROM_ISR(save_status );
4、任务调度器的挂起和恢复
挂起任务调度器, 调用此函数仅仅只是防止了任务间的资源争夺,中断照样可以响应。挂起调度器的防止,适用于临界区位于任务与任务之间。这样既不会延时中断,又可以做到临界区安全。
任务调度器的挂起和恢复函数原型
函数 | 描述 |
vTaskSuspendAll() | 挂起任务调度器 |
xTaskResumeAll() | 恢复任务调度器 |
任务调度器的挂起和恢复函数的使用范例
vTaskSuspendAll() ;
{
… … /* 内容 */
}
xTaskResumeAll() ;
5、列表
6、任务调度器的开启
开启任务调度器函数原型
vTaskStartScheduler()
prvStartFirstTask () /* 开启第一个任务 */
vPortSVCHandler () /* SVC中断服务函数 */
7、时间片调度配置
注意:使用时间片调度需把宏 configUSE_TIME_SLICING 和 configUSE_PREEMPTION 置1
8、Freertos任务相关API函数
获取任务优先级的函数原型
UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask )
此函数用于获取指定任务的任务优先级,使用该函数需将宏 INCLUDE_uxTaskPriorityGet 置 1
形参 | 描述 |
xTask | 要查找的任务句柄,NULL代表任务自身 |
返回值 | 描述 |
整数 | 任务优先级数值 |
获取任务优先级函数优先级的函数使用范例
// 创建任务并获取任务句柄
xTaskCreate(Task1, "Task1", 100, NULL, 1, &xTaskHandle);
// 获取任务的优先级并输出
UBaseType_t priority = uxTaskPriorityGet(xTaskHandle);
改变某个任务的任务优先级函数原型
void vTaskPrioritySet( TaskHandle_t xTask , UBaseType_t uxNewPriority )
此函数用于改变某个任务的任务优先级,使用该函数需将宏 INCLUDE_vTaskPrioritySet 为 1
xTask | 任务句柄,NULL代表任务自身 |
uxNewPriority | 需要设置的任务优先级 |
改变某个任务的任务优先级函数原型的使用范例
// 定义任务句柄
TaskHandle_t xTask1Handle, xTask2Handle;
// 创建任务2
xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);
vTaskPrioritySet(xTask2Handle, 3); // 将任务2的优先级设置为3
获取系统中任务数量的API函数原型
UBaseType_t uxTaskGetNumberOfTasks( void )
形参 | 描述 |
xTask | 任务句柄,NULL代表任务自身 |
uxNewPriority | 需要设置的任务优先级 |
获取系统中任务数量的API函数使用范例
// 获取当前活动任务的数量并输出
UBaseType_t numTasks = uxTaskGetNumberOfTasks();
获取所有任务状态信息函数原型
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
const UBaseType_t uxArraySize,
configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime )
此函数用于获取系统中所有任务的任务状态信息,使用该函数需将宏 configUSE_TRACE_FACILITY 置 1
形参 | 描述 |
xTaskStatusArray | 指向TaskStatus_t 结构体数组首地址 |
uxArraySize | 接收信息的数组大小 |
pulTotalRunTime | 系统总运行时间,为NULL 则省略总运行时间值 |
形参 | 描述 |
xTaskStatusArray | 指向TaskStatus_t 结构体数组首地址 |
获取所有任务状态信息函数原型使用范例
#include <FreeRTOS.h>
#include <task.h>
#define TASKS_ARRAY_SIZE 10
TaskHandle_t xTask1Handle, xTask2Handle;
void vTask1(void *pvParameters) {
// 任务1的代码
while(1) {
// 执行任务1的操作
}
}
void vTask2(void *pvParameters) {
// 任务2的代码
while(1) {
// 执行任务2的操作
}
}
void main(void) {
TaskStatus_t xTaskStatusArray[TASKS_ARRAY_SIZE];
UBaseType_t uxArraySize;
// 创建任务1
xTaskCreate(vTask1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);
// 创建任务2
xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);
// 启动任务调度器
vTaskStartScheduler();
// 获取系统中所有任务的状态信息
uxArraySize = uxTaskGetSystemState(xTaskStatusArray, TASKS_ARRAY_SIZE, NULL);
// 打印任务状态信息
for (UBaseType_t i = 0; i < uxArraySize; i++) {
printf("Task Name: %s, Priority: %d, State: %d\n",
xTaskStatusArray[i].pcTaskName,
xTaskStatusArray[i].uxCurrentPriority,
xTaskStatusArray[i].eCurrentState);
}
// 任务调度器不会返回,除非发生错误
for(;;);
}
这里想要提取哪些信息的话需要根据结构体的成员变量改变
typedef struct xTASK_STATUS
{
TaskHandle_t xHandle; //任务句柄
const char * pcTaskName; //任务名字
UBaseType_t xTaskNumber; //任务编号
eTaskState eCurrentState; //当前任务壮态, eTaskState 是一个枚举类型
UBaseType_t uxCurrentPriority; //任务当前的优先级
UBaseType_t uxBasePriority; //任务基础优先级
uint32_t ulRunTimeCounter;//任务运行的总时间
StackType_t * pxStackBase; //堆栈基地址
uint16_t usStackHighWaterMark;//从任务创建以来任务堆栈剩余的最小大小,此
//值如果太小的话说明堆栈有溢出的风险。
} TaskStatus_t;
获取单个任务的状态信息原型
void vTaskGetInfo(
FTaskHandle_t xTask,
TaskStatus_t *pxTaskStatus,
BaseType_t xGetFreeStackSpace,
eTaskState eState)
形参 | 描述 |
xTask | 指定获取信息的任务的句柄 |
pxTaskStatus | 接收任务信息的变量 |
xGetFreeStackSpace | 任务栈历史剩余最小值, 当为“pdFALSE” 则跳过这个步骤, 当为“pdTRUE”则检查历史剩余最小堆栈 |
eState | 任务状态,可直接赋值,如想获取代入“eInvalid” |
获取单个任务的状态信息使用范例
TaskStatus_t xTaskStatus;
// 创建一个任务
TaskHandle_t xTaskHandle;
xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);
// 获取任务的信息
vTaskGetInfo(xTaskHandle, &xTaskStatus, pdFALSE, eRunning);
// 打印任务的状态信息
printf("Task Name: %s\n", xTaskStatus.pcTaskName);
printf("Task Priority: %d\n", xTaskStatus.uxCurrentPriority);
printf("Task State: %d\n", xTaskStatus.eCurrentState);
直接获取当前任务的任务句柄函数原型
TaskHandle_t xTaskGetCurrentTaskHandle( void )
此函数用于获取当前任务的任务句柄, 使用该函数需将INCLUDE_xTaskGetCurrentTaskHandle 置 1
返回值 | 描述 |
TaskHandle_t | 当前任务的任务句柄 |
直接获取当前任务的任务句柄函数使用范例
TaskHandle_t xTaskHandle;
// 获取当前任务的句柄
xTaskHandle = xTaskGetCurrentTaskHandle();
// 打印当前任务的句柄
printf("Current Task Handle: %p\n", xTaskHandle);
通过任务名获取任务句柄函数原型
TaskHandle_t xTaskGetHandle(const char * pcNameToQuery);
通过任务名获取任务句柄函数使用范例
void main(void) {
TaskHandle_t xTaskHandle;
// 创建一个任务
xTaskCreate(vTaskFunction, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);
// 获取任务句柄
TaskHandle_t xTaskHandle2 = xTaskGetHandle("Task1");
// 检查句柄是否有效
if (xTaskHandle2 != NULL) {
printf("Task1 handle: %p\n", xTaskHandle2);
} else {
printf("Task1 not found!\n");
}
for (;;) {
// 主循环
}
}
获取指定任务的任务历史堆栈最小值函数原型
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
使用该函数需将宏 INCLUDE_uxTaskGetStackHighWaterMark 置 1
形参 | 描述 |
xTask | 任务句柄 |
返回值 | 描述 |
UBaseType_t | 任务栈的历史剩余最小值 |
获取指定任务的任务历史堆栈最小值函数使用范例
// 在这里检查任务堆栈的高水位标记
UBaseType_t uxHighWaterMark;
uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL ); // 传入NULL表示获取当前任务的堆栈高水位标记
// 输出高水位标记到串口或者日志
printf( "Task stack high water mark: %u bytes\n", uxHighWaterMark );
此函数用于查询某个任务的运行状态原型
需将宏 INCLUDE_eTaskGetState 置一
eTaskState eTaskGetState(TaskHandle_t xTask)
形参 | 描述 |
xTask | 待获取状态任务的任务句柄 |
返回值 | 描述 |
eTaskState | 任务状态 |
eTaskState可能的参数
typedef enum
{
eRunning = 0, /* 运行态 */
eReady /* 就绪态 */
eBlocked, /* 阻塞态 */
eSuspended, /* 挂起态 */
eDeleted, /* 任务被删除 */
eInvalid /* 无效 */
} eTaskState;
此函数用于查询某个任务的运行状态使用范例
// 获取任务状态
eTaskState taskState = eTaskGetState(xTaskHandle);
// 根据任务状态执行相应的操作
switch(taskState)
{
case eRunning:
// 任务正在运行
break;
case eReady:
// 任务处于就绪状态
break;
case eBlocked:
// 任务被阻塞
break;
case eSuspended:
// 任务被挂起
break;
case eDeleted:
// 任务已被删除
break;
default:
// 未知状态
break;
}
用表格的形式获取系统中任务的信息函数原型
void vTaskList(char * pcWriteBuffer)
宏 configUSE_TRACE_FACILITYconfigUSE_STATS_FORMATTING_FUNCTIONS 置1
形参 | 描述 |
pcWriteBuffer | 接收任务信息的缓存指针 |
表格如下
Name : 创建任务的时候给任务分配的名字。
State : 任务的壮态信息, B 是阻塞态, R 是就绪态, S 是挂起态, D 是删除态
Priority :任务优先级。
Stack : 任务堆栈的“高水位线”,就是堆栈历史最小剩余大小。
Num : 任务编号,这个编号是唯一的,当多个任务使用同一个任务名的时候可以通过此编号来做区分。
用表格的形式获取系统中任务的信息函数使用范例
// 获取任务列表信息
char taskListBuffer[1024]; // 用于保存任务列表信息的缓冲区
vTaskList(taskListBuffer);
// 打印任务列表信息
printf("Task List:\n%s", taskListBuffer);
时间统计API函数原型
Void vTaskGetRunTimeStats( char * pcWriteBuffer )
此函数用于统计任务的运行时间信息,使用此函数需将宏 configGENERATE_RUN_TIME_STAT 、configUSE_STATS_FORMATTING_FUNCTIONS 置1
形参 | 描述 |
pcWriteBuffer | 接收任务运行时间信息的缓存指针 |
时间统计API函数使用流程
1、将宏 configGENERATE_RUN_TIME_STATS 置1
2、将宏 configUSE_STATS_FORMATTING_FUNCTIONS 置1
3、当将此宏 configGENERATE_RUN_TIME_STAT 置1之后,还需要实现2个宏定义:
① portCONFIGURE_TIMER_FOR_RUNTIME_STATE() :用于初始化用于配置任务运行时间统计的时基定时器;
注意:这个时基定时器的计时精度需高于系统时钟节拍精度的10至100倍!
② portGET_RUN_TIME_COUNTER_VALUE():用于获取该功能时基硬件定时器计数的计数值 。
9、延时函数(相对延时和绝对延时)
函数 | 描述 |
vTaskDelay() | 相对延时 |
xTaskDelayUntil() | 绝对延时 |
相对延时:
指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束
绝对延时:
指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务
10、队列
队列结构体
typedef struct QueueDefinition
{
int8_t * pcHead /* 存储区域的起始地址 */
int8_t * pcWriteTo; /* 下一个写入的位置 */
union
{
QueuePointers_t xQueue;
SemaphoreData_t xSemaphore;
} u ;
List_t xTasksWaitingToSend; /* 等待发送列表 */
List_t xTasksWaitingToReceive; /* 等待接收列表 */
volatile UBaseType_t uxMessagesWaiting; /* 非空闲队列项目的数量 */
UBaseType_t uxLength; /* 队列长度 */
UBaseType_t uxItemSize; /* 队列项目的大小 */
volatile int8_t cRxLock; /* 读取上锁计数器 */
volatile int8_t cTxLock; /* 写入上锁计数器 */
/* 其他的一些条件编译 */
} xQUEUE;
结构体中的联合体
当用于队列使用时
typedef struct QueuePointers
{
int8_t * pcTail; /* 存储区的结束地址 */
int8_t * pcReadFrom; /* 最后一个读取队列的地址 */
} QueuePointers_t;
当用于互斥信号量和递归互斥信号量时
typedef struct SemaphoreData
{
TaskHandle_t xMutexHolder; /* 互斥信号量持有者 */
UBaseType_t uxRecuarsiveCallCount; /* 递归互斥信号量的获取计数器 */
} SemaphoreData_t;
动态创建队列的API函数原型
#define xQueueCreate ( uxQueueLength, uxItemSize ) \ a
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), (queueQUEUE_TYPE_BASE ))
形参 | 描述 |
uxQueueLength | 队列长度 |
uxItemSize | 队列项目的大小 |
返回值 | 描述 |
NULL | 队列创建失败 |
其他值 | 队列创建成功,返回队列句柄 |
动态创建队列的API函数使用范例
#include <queue.h> // 假设宏定义的头文件是这个
// 在某个函数或全局范围内调用 xQueueCreate 宏来创建队列
void createQueueExample() {
// 假设要创建一个长度为 10,每个项目大小为 sizeof(int) 的队列
QueueHandle_t myQueue = xQueueCreate(10, sizeof(int));
if (myQueue == NULL) {
// 队列创建失败,进行错误处理
printf("Failed to create queue\n");
} else {
// 队列创建成功,进行其他操作
printf("Queue created successfully\n");
}
}
前面说 FreeRTOS 基于队列实现了多种功能,每一种功能对应一种队列类型,队列类型的 queue.h 文件中有定义:
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) /* 队列 */
#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) /* 队列集 */
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) /* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) /* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) /* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) /* 递归互斥信号量 */
往队列中写入消息的API函数原型
//默认发送到尾部
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )
#define xQueueOverwrite( xQueue, pvItemToQueue ) \
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )
其实本质上都是调用同一个函数xQueueGenericSend( ) ,只是指定了不同的写入位置
三种写入位置
#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) /* 写入队列尾部 */
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) /* 写入队列头部 */
#define queueOVERWRITE ( ( BaseType_t ) 2 ) /* 覆写队列*/
注意:覆写方式写入队列,只有在队列的队列长度为 1 时,才能够使用
往队列中写入消息的API函数入口参数解析
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition );
形参 | 描述 |
xQueue | 待写入的队列 |
pvItemToQueue | 待写入消息的地址(指针变量) |
xTicksToWait | 阻塞超时时间 |
xCopyPosition | 写入的位置 |
返回值 | 描述 |
pdTRUE | 队列写入成功 |
errQUEUE_FULL | 队列写入失败 |
往队列中写入消息的API函数使用范例
#include <stdio.h>
#include <stdint.h>
#include "FreeRTOS.h"
#include "queue.h"
#define QUEUE_LENGTH 5
void sender_task(void *pvParameters) {
QueueHandle_t queue = (QueueHandle_t)pvParameters;
// 要发送的数据
uint32_t data_to_send = 123;
// 发送数据到队列
BaseType_t result = xQueueGenericSend(queue, &data_to_send, portMAX_DELAY, queueSEND_TO_BACK);
if (result == errQUEUE_FULL) {
printf("Data sent to queue: %u\n", data_to_send);
} else {
printf("Failed to send data to queue\n");
}
vTaskDelete(NULL);
}
在队列头部读取消息,并根据函数名字不一做调整
在任务中读取,不删除消息
BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
在中断中读取,不删除消息
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
在任务中读取,并删除消息
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
在中断中读取,并删除消息
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
函数参数解析
形参 | 描述 |
xQueue | 待读取的队列 |
pvBuffer | 信息读取缓冲区 |
xTicksToWait | 阻塞超时时间 |
返回值 | 描述 |
pdTRUE | 读取成功 |
pdFALSE | 读取失败 |
函数使用实例
#include <stdio.h>
#include "FreeRTOS.h"
#include "queue.h"
void peek_task(void *pvParameters) {
QueueHandle_t queue = (QueueHandle_t)pvParameters;
// 缓冲区用于存储查看到的数据
uint32_t peeked_data;
// 查看队列中的下一个可用项
BaseType_t result = xQueuePeek(queue, &peeked_data, portMAX_DELAY);
if (result == pdPASS) {
printf("Next item in queue: %u\n", peeked_data);
} else {
printf("Failed to peek into queue\n");
}
vTaskDelete(NULL);
}
int main() {
// 创建一个队列
QueueHandle_t myQueue = xQueueCreate(5, sizeof(uint32_t));
if (myQueue == NULL) {
printf("Failed to create queue\n");
return 1;
}
// 向队列中发送一些数据
uint32_t data_to_send = 123;
xQueueSend(myQueue, &data_to_send, portMAX_DELAY);
// 创建查看任务
xTaskCreate(peek_task, "Peek Task", configMINIMAL_STACK_SIZE, (void *)myQueue, tskIDLE_PRIORITY + 1, NULL);
// 启动调度器
vTaskStartScheduler();
// 不应该运行到这里
return 0;
}
11、信号量
创建二值信号量函数原型
SemaphoreHandle_t xSemaphoreCreateBinary( void )
相关宏定义及参数
#define xSemaphoreCreateBinary( ) \
xQueueGenericCreate( 1 , semSEMAPHORE_QUEUE_ITEM_LENGTH , queueQUEUE_TYPE_BINARY_SEMAPHORE )
#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) /* 队列 */
#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) /* 队列集 */
#define queueQUEUE_TYPE_MUTEX
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) /* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) /* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) /* 递归互斥信号量 */
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 创建成功返回二值信号量的句柄 |
创建二值信号量函数使用范例
// 定义一个二进制信号量句柄
SemaphoreHandle_t binarySemaphore;
// 创建二进制信号量
binarySemaphore = xSemaphoreCreateBinary();
获取二值信号量函数原型
BaseType_t xSemaphoreTake( xSemaphore, xBlockTime )
形参 | 描述 |
xSemaphore | 要获取的信号量句柄 |
xBlockTime | 阻塞时间 |
返回值 | 描述 |
pdTRUE | 获取信号量成功 |
pdFALSE | 超时,获取信号量失败 |
获取二值信号量函数使用实例
SemaphoreHandle_t binarySemaphore;
void someTask(void *pvParameters) {
// 尝试获取二值信号量,等待时间为100个时钟节拍
BaseType_t result = xSemaphoreTake(binarySemaphore, 100);
if (result == pdPASS) {
// 成功获取到了信号量
} else {
// 未能获取到信号量,超时或其他原因
}
}
释放二值信号量函数原型与参数解析
BaseType_t xSemaphoreGive( xSemaphore )
#define xSemaphoreGive ( xSemaphore ) \
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ) , NULL , semGIVE_BLOCK_TIME , queueSEND_TO_BACK )
#define semGIVE_BLOCK_TIME ( ( TickType_t ) 0U )
形参 | 描述 |
xSemaphore | 要释放的信号量句柄 |
返回值 | 描述 |
pdTRUE | 获取信号量成功 |
pdFALSE | 超时,获取信号量失败 |
释放二值信号量函数使用实例
BaseType_t giveResult = xSemaphoreGive(binarySemaphore);
if (giveResult == pdPASS) {
// 释放成功
} else {
// 释放失败,进行错误处理
}
创建(动态)计数型信号量函数原型
#define xSemaphoreCreateCounting( uxMaxCount , uxInitialCount ) \
xQueueCreateCountingSemaphore( ( uxMaxCount ) , ( uxInitialCount ) )
SemaphoreHandle_t xQueueCreateCountingSemaphore(const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount);
此函数用于创建一个计数型信号量 。
形参 | 描述 |
uxMaxCount | 计数值的最大值限定 |
uxInitialCount | 计数值的初始值 |
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 创建成功返回计数型信号量的句柄 |
创建(动态)计数型信号量函数使用实例
SemaphoreHandle_t countingSemaphore;
void someFunction(void) {
// 创建一个最大计数值为5,初始计数值为0的计数信号量
countingSemaphore = xSemaphoreCreateCounting(5, 0);
if (countingSemaphore != NULL) {
// 计数信号量创建成功
} else {
// 计数信号量创建失败
}
}
获取信号量当前计数值函数原型
UBaseType_t uxSemaphoreGetCount(QueueHandle_t xQueue);
实际上是调用了 uxQueueMessagesWaiting()
函数
形参 | 描述 |
xSemaphore | 信号量句柄 |
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 创建成功返回计数型信号量的句柄 |
获取信号量当前计数值函数使用示例
SemaphoreHandle_t mySemaphore;
void someFunction(void) {
// 创建一个二进制信号量
mySemaphore = xSemaphoreCreateBinary();
// 获取信号量的计数值
if (mySemaphore != NULL) {
UBaseType_t count = uxSemaphoreGetCount(mySemaphore);
// 此时 count 应该为 0(如果是二进制信号量)
}
}