1.创建任务
2.vTaskDelay和vTaskDelayUntil
3.任务状态(就绪、运行、阻塞、暂停,删除任务,空闲钩子函数)
4.队列和邮箱
5.队列集
6.信号量
7.事件组
8.任务通知
9.定时器
10.中断
11.临界资源管理
12.系统优化
13.队列保护数据完整性保护
- 创建任务原型
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,//这是任务函数的指针,即任务要执行的代码。
const char * const pcName,//任务名
const configSTACK_DEPTH_TYPE usStackDepth,//分配栈的空间
void * const pvParameters,//任务的参数
UBaseType_t uxPriority,//优先级
TaskHandle_t * const pxCreatedTask )//任务句柄
//如果任务成功创建,函数返回 pdPASS
//示例
#include "FreeRTOS.h"
#include "task.h"
// 定义任务函数
void vATask( void * pvParameters )
{
for( ;; )
{
// 任务代码
// ...
// 延迟一段时间
vTaskDelay( 1000 );
}
}
int main( void )
{
// 初始化 FreeRTOS
// ...
// 创建任务
TaskHandle_t xHandle = NULL;
const uint16_t usStackDepth = 1024; // 任务堆栈大小
const UBaseType_t uxPriority = 5; // 任务优先级
if( xTaskCreate( vATask, // 任务函数
"ATask", // 任务名称
usStackDepth, // 任务堆栈大小
NULL, // 任务参数
uxPriority, // 任务优先级
&xHandle ) != pdPASS ) // 任务句柄
{
// 任务创建失败
// ...
}
// 开始任务调度
vTaskStartScheduler();
// 如果调度器启动成功,以下代码不会被执行
for( ;; );
return 0;
}
2.vTaskDelay和vTaskDelayUntil.
1.vTaskDelay
//原型
void vTaskDelay( const TickType_t xTicksToDelay );
//示例
void vATask( void * pvParameters )
{
for( ;; )
{
// 执行任务逻辑
// 延迟 1000 毫秒(假设 configTICK_RATE_HZ 为 1000)
vTaskDelay(1000);
}
}
2.TaskDelayUntil
//原型
void vTaskDelayUntil(
const TickType_t * const pxPreviousWakeTime,//xTaskGetTickCount() 函数获取
const TickType_t xTimeIncrement );//pxPreviousWakeTime 指定的时间点开始要延迟的额外时间
//示例 xTaskGetTickCount()为起点,延时100ms
void vATask( void * pvParameters )
{
TickType_t xLastWakeTime;
const TickType_t xDelay = 1000; // 延迟 1000 毫秒
for( ;; )
{
// 获取当前时间
xLastWakeTime = xTaskGetTickCount();
// 执行任务逻辑
// 延迟到指定的时间点
vTaskDelayUntil( &xLastWakeTime, 100);
}
}
3.任务状态
1.就绪
原型
2.运行
原型
xTaskResume
3.阻塞
原型
vTaskDelay(1000);
4.暂停
原型
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
BaseType_t xTaskResumeFromISR( TaskHandle_t pxTaskToResume );
返回:pdTRUE
示例
vTaskSuspend(任务句柄);
xTaskResumeFromISR(任务句柄 );
5.删除
原型
void vTaskDelete( TaskHandle_t xTaskToDelete );
xTaskToDelete = NULL//删除自己
xTaskToDelete = 某个任务句柄 //删除某个任务
6.空闲任务
//空闲钩子函数可以执行 后台任务、系统监控或低优先级的操作
#define configUSE_IDLE_HOOK 1
void vApplicationIdleHook(void)
{
}
4.队列
1.xQueueCreate
原型
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
其中:
uxQueueLength 是队列的深度,即队列可以容纳的最大项数。
uxItemSize 是单个队列项的大小,以字节为单位
示例
QueueHandle_t xMyQueue = NULL;
void vATask(void *pvParameters)
{
// 队列项的结构体
typedef struct
{
int itemValue;
} ItemValue;
// 创建队列
// 队列长度为10,单个队列项大小为sizeof(ItemValue)
xMyQueue = xQueueCreate(10, sizeof(ItemValue));
if (xMyQueue == NULL)
{
// 队列创建失败
// 这里可以添加错误处理代码
}
2.xQueueSend
原型
BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );
参数说明:
xQueue:队列的句柄,这是通过 xQueueCreate 创建队列时返回的。
pvItemToQueue:指向要发送到队列的数据项的指针。数据的具体类型取决于队列创建时指定的 uxItemSize。
xTicksToWait:如果队列已满,此参数指定了任务应该等待队列变得可用的最长时间(以时钟节拍为单位)。如果设置为 0,函数将立即返回而不等待。如果设置为 portMAX_DELAY,则任务将无限期地等待,直到队列变得可用。
返回值:
如果数据项成功发送到队列,则返回 pdPASS
示例
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
// 队列句柄
QueueHandle_t xMyQueue;
void vSenderTask(void *pvParameters)
{
int itemValue = 0;
// 假设 xMyQueue 已经在其他地方被创建
for (;;)
{
// 向队列发送数据项
if (xQueueSend(xMyQueue, &itemValue, 0) == pdPASS)
{
// 数据项成功发送
itemValue++;
}
else
{
// 发送失败,队列可能已满
}
// 等待一段时间或执行其他任务
vTaskDelay(100);
}
}
3.xQueueReceive
原型
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * pvBuffer, TickType_t xTicksToWait );
参数说明:
xQueue:队列的句柄,这是通过 xQueueCreate 创建队列时返回的。
pvBuffer:指向用于存储从队列中接收到的数据项的缓冲区的指针。缓冲区的类型和数据大小应与队列创建时指定的相匹配。
xTicksToWait:如果队列为空,此参数指定了任务应该等待队列变得非空的最长时间(以时钟节拍为单位)。如果设置为 0,函数将立即返回而不等待。如果设置为 portMAX_DELAY,则任务将无限期地等待,直到队列变得非空。
返回值:
如果成功从队列中接收到数据项,则返回 pdTRUE
示例
QueueHandle_t xMyQueue;
void vReceiverTask(void *pvParameters)
{
int receivedValue;
// 假设 xMyQueue 已经在其他地方被创建
for (;;)
{
// 从队列接收数据项
if (xQueueReceive(xMyQueue, &receivedValue, portMAX_DELAY) == pdTRUE)
{
// 成功接收到数据项
// 在这里处理接收到的数据
// ...
}
else
{
// 接收失败,队列可能为空
}
}
}
4.邮箱
邮箱的队列长度是1,只覆盖,Peek查看数据,
QueueHandle_t xMyQueue = NULL;
// 队列项的结构体
typedef struct
{
int itemValue;
} ItemValue;
// 创建队列
// 队列长度为1是邮箱
1.xMyQueue = xQueueCreate(1, sizeof(ItemValue));
BaseType_t xResult;
// 尝试向队列发送数据,如果队列满则覆盖旧数据
2.xResult = xQueueOverwrite(xMyQueue, &xDataToSend);
3.typedef struct {
int value;
} QueueData_t;
// 尝试从队列中查看数据,但不接收它
xResult = xQueuePeek(xMyQueue, &xDataReceived, 0);
if (xResult == pdTRUE) {
// 成功查看到队列中的数据
printf("Data in queue: %d\n", xDataReceived.value);
5.队列集
1.队列集
在这里插入代码片
6.信号量
1.计数信号量(累加)
原型
SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount
);
参数说明:
uxMaxCount:信号量的最大值。
uxInitialCount:信号量的初始值。这表示信号量在创建时的初始计数值。
返回值:
如果信号量成功创建,函数将返回一个非空(NULL 之外)的信号量句柄。
如果信号量创建失败,函数将返回 NULL。
示例:
SemaphoreHandle_t xCountingSemaphore = NULL;
// 创建计数信号量
xCountingSemaphore = xSemaphoreCreateCounting( 5, 0);//一共5个信号量,从0开始累加
xSemaphoreGive( xCountingSemaphore ); //给出信号量 累加
// 尝试获取信号量
if( xSemaphoreTake( xCountingSemaphore, portMAX_DELAY ) == pdPASS ) //信号量自动-1
{
// 成功获取到信号量,现在我们可以安全地访问资源
// ... 访问资源的代码 ...
}
2.二进制信号量
// 创建二进制信号量
SemaphoreHandle_t xBinarySemaphore = NULL;
// 创建二进制信号量
xBinarySemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(xBinarySemaphore);
if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdPASS)
{
// 成功获取信号量,执行任务1的代码
}
3.互斥信号量
// 创建互斥信号量
SemaphoreHandle_t xMutex = NULL;
xMutex = xSemaphoreCreateMutex();//创建时候已经give了,不需要提前手动give
void vTask1(void *pvParameters)
{
// 循环执行
for (;;)
{
// 尝试获取互斥信号量
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdPASS)
{
// 成功获取互斥信号量,执行临界区代码
// ... 访问共享资源 ...
// 释放互斥信号量
xSemaphoreGive(xMutex);
}
}
}
void vTask2(void *pvParameters)
{
// ... 执行其他任务代码 ...
// 在需要访问共享资源时获取互斥信号量
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdPASS)
{
// 执行临界区代码
// ... 访问共享资源 ...
xSemaphoreGive(xMutex);
}
}
7.事件组
1.事件组
原型
1.EventGroupHandle_t xEventGroupCreate(void);
2.BaseType_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
uxBits = xEventGroupSetBits(xEventPurchase, 1<<0); // 将bit 0 设置为1
xEventGroup:事件组的句柄,由 xEventGroupCreate() 函数返回。
uxBitsToSet:一个位掩码,指定要设置的事件。每一位对应事件组中的一个事件,设置为 1 的位表示对应的事件将被设置。
返回值:
如果函数成功执行,返回 pdPASS。
如果事件组句柄无效或事件组被删除,返回 pdFAIL。
3.uxBits = xEventGroupWaitBits (xEventPurchase, //Event Group Handler
ADDTOCART_0 | PAYMENT_1 | INVENTORY_2, //等待Event Group中的那个Bit(s)
pdFALSE, //pdFALSE 不清除对应位 pdTRUE 清楚对应位为0
pdTRUE, // pdTRUE 所有条件都要满足,pdFALSE一个条件满足就好
xTimeOut);
示例
// 全局变量,保存事件组句柄
EventGroupHandle_t xEventGroup;
`// 创建事件组
xEventGroup = xEventGroupCreate();
// 设置事件组中的位0
xEventGroupSetBits( xEventGroup, EVENT_BIT_0 );
// 等待事件组中的事件
EventBits_t uxBits; ``
// 等待事件组中的位0被设置
uxBits = xEventGroupWaitBits( xEventGroup, EVENT_BIT_0, pdTRUE, pdTRUE, xWaitTime );
if( ( uxBits & EVENT_BIT_0 ) != 0 )
{
// 事件被设置,执行相应的操作
// ...
}
2.同步事件组
原型
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet, uxBitsToSet中的某个事件完成要等待uxBitsToWaitFor中所有事件完成
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait );//则在xEventGroupSync()返回之前,将uxBitsToWaitFor指定的事件位清零。
示例
// 等待事件位0和事件位1被设置
EventBits_t uxBits = xEventGroupSync(xEventGroup, 0x00000003, 0x00000003, portMAX_DELAY);
if (uxBits&0x00000003 == 0x00000003)
{
// 执行任务逻辑...
}
8.任务通知
0.任务通知
原型
示例
1.
BaseType_t xResult;//xTaskWait需要通知的任务句柄
xResult=xTaskNotify( xTaskWait, 0, eIncrement ); //对方的notification value +1
//xResult=xTaskNotify( xTaskWait, 0, eNoAction ); //不设置对方的notification value 永远是0
//xResult=xTaskNotify( xTaskWait, ( 1UL << 4UL ), eSetBits ); //第4个bits 设置为 1 和 原有的值是OR运算
//xResult=xTaskNotify( xTaskWait, 0, eIncrement ); //对方的notification value +1
//xResult=xTaskNotify( xTaskWait, LOWTHREEBITS, eSetValueWithOverwrite ); //覆盖原有的值
//xResult=xTaskNotify( xTaskWait, LOWTHREEBITS, eSetValueWithoutOverwrite); //如果没有待处理的就覆盖,如果有待处理的就pending
Serial.println(xResult == pdPASS ? "成功\n":"失败\n");
2.
xResult = xTaskNotifyWait(0x00, //在运行前这个命令之前,先清除这几位
0x00, //运行后,重置所有的bits 0x00 or ULONG_MAX or 0xFFFFFFFF
&ulNotificationValue, //重置前的notification value
portMAX_DELAY ); //一直等待
if (xResult == pdTRUE) {
Serial.println(ulNotificationValue); //将自己的notification value以二进制方式打出来
1.任务通知使用_轻量级信号量.(二值信号量)
原型
示例1
uint32_t ulNotificationValue;
//命令含义,相当于精简化的 xTaskNotify() + eIncrement
xTaskNotifyGive(xflashLED);//传输的任务句柄
ulNotificationValue = ulTaskNotifyTake(pdTRUE, //pdTRUE 运行完后,清零
portMAX_DELAY);
if ( ulNotificationValue > 0 )
{
//接收到二值信号
}
示例2
uint32_t ulNotificationValue;
// 在任务中等待通知
ulNotificationValue = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000));
if (ulNotificationValue > 0)
{
// 接收到了通知,并且通知值被清除了
printf("Received notification: %u\n", ulNotificationValue);
}
else
{
// 超时或者没有接收到通知
printf("Notification timeout or not received\n");
}
2.任务通知使用_轻量级队列
原型
示例
3.任务通知使用_轻量级事件组
原型
示例
9.定时器
1.定时器
原型
1.
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
参数说明:
pcTimerName:定时器的名称,用于调试和日志记录。
xTimerPeriodInTicks:定时器的周期,以系统时钟节拍为单位。
uxAutoReload:定时器是否自动重载。如果设置为 pdTRUE,则定时器到期后会自动重新加载并重新启动;如果设置为 pdFALSE,则定时器到期后会停止。
pvTimerID:定时器的ID,可以是一个指针,用于在回调函数中区分不同的定时器。
pxCallbackFunction:定时器到期时调用的回调函数。
返回值:
如果定时器创建成功,函数返回一个非NULL的定时器句柄。
如果定时器创建失败(例如,由于内存不足),函数返回NULL。
2.
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xBlocksToWait );
参数说明:
xTimer:要启动的定时器的句柄,这个句柄是通过 xTimerCreate 函数获得的。
xBlocksToWait:指定函数等待定时器实际开始运行的最大时间。如果设置为 0,则函数不会阻塞,会立即返回。如果设置为 portMAX_DELAY,则函数将无限期地等待,直到定时器成功启动。对于其他值,它表示等待的时钟节拍数。
3.
xTimerStop(Swtmr1_Handle,portMAX_DELAY); //停止定时器
xTimerDelete(Swtmr1_Handle,portMAX_DELAY); //删除软件定时器
xTimerReset(Swtmr1_Handle,0);//重启定时器 时间到达之后会调用回调函数(按键去抖动)
xTimerChangePeriod( xMyTimer, pdMS_TO_TICKS( 500 ), 0 )//改变定时时间
示例
TimerHandle_t xMyTimer;
void vATimerExpiryHandler( TimerHandle_t xTimer )
{
// 定时器到期时的处理逻辑
// ...
}
void vCreateATimer( void )
{
// 创建一个定时器,周期为1000ms,自动重载,定时器ID为NULL,回调函数为vATimerExpiryHandler
xMyTimer = xTimerCreate( "MyTimer",
pdMS_TO_TICKS( 1000 ),
pdTRUE,
NULL,
vATimerExpiryHandler );
if( xMyTimer == NULL )
{
// 定时器创建失败
}
else
{
// 定时器创建成功,可以启动定时器
xTimerStart( xMyTimer, 0 ); //不阻塞启动定时器
}
}
10.中断
1.中断
原型
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue );
参数说明:
xTaskToNotify:要发送通知的任务的句柄。如果设置为 NULL,则通知将广播给所有任务。
ulValue:要发送给任务的通知值。这可以是一个事件标识符、消息或其他信息。
eAction:指定当任务接收到通知时应采取的动作。可能的值包括 eNoAction(不执行任何动作,仅设置通知值)、eSetBits(将通知值设置为任务通知值的按位或)、eIncrement(增加任务通知值)等。
pulPreviousNotificationValue:如果非 NULL,则此参数将用于返回任务在接收通知之前的通知值。这允许中断服务程序了解任务之前的通知状态。
返回值:
如果通知成功发送,则返回 pdPASS。
如果发送失败(例如,因为传递给函数的任务句柄无效),则返回一个错误代码。
使用 xTaskNotifyFromISR 时,需要注意以下几点:
示例1
uint32_t ulBitsToClear = 0; // 不清除任何位
BaseType_t xResult;
xResult = xTaskNotifyFromISR( xTaskToNotify, ulNotifyValue, eNoAction, &ulBitsToClear );
if( xResult == pdPASS )
{
portYIELD_FROM_ISR( pdTRUE ); // 允许调度器检查上下文切换
}
示例2
void set_event_info(uint32_t val)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t ret;
ret = xTaskNotifyFromISR((TaskHandle_t )EventGroupTask_Handler,//接收任务通知的任务句柄
(uint32_t )val, //要更新的bit
(eNotifyAction )eSetBits,
&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); //启动一次任务切换,确保任务通知被获取
}
注意:#define portYIELD_FROM_ISR if( xSwitchRequired != pdFALSE ) portYIELD();
11.临界资源管理
1.中断中屏蔽
1.在任务中屏蔽中断
taskENTER_CRITICAL();
/* 访问临界资源 */
a += val;
/* 重新使能中断 */
taskEXIT_CRITICAL();
2.在中断中屏蔽中断
UBaseType_t uxSavedInterruptStatus;
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
/* 访问临界资源 */
a++;
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
2.任务中屏蔽任务
vTaskSuspendAll();
/* 访问临界资源 */
a = val;
xTaskResumeAll();
12.系统优化
- 调试
在这里插入代码片
2.优化
2.任务运行时间统计
vTaskList :获得任务的统计信息,形式为可读的字符串。注意,pcWriteBuffer必须足够大
任务名 任务状态 优先级 空闲栈 任务号
void vTaskList( signed char *pcWriteBuffer )
vTaskGetRunTimeStats:获得任务的运行信息,形式为可读的字符串。注意,pcWriteBuffer必须足够大。
任务名 运行时间 任务百分比
void vTaskGetRunTimeStats( signed char *pcWriteBuffer );
13.队列数据完整性保护代码
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
// 定义队列的大小
#define QUEUE_SIZE 10
// 定义信号量
SemaphoreHandle_t queueSem;
// 定义队列
QueueHandle_t queue;
void Task1(void *pvParameters) {
while(1) {
// 向队列中添加数据
xSemaphoreTake(queueSem, portMAX_DELAY);
xQueueSendFromISR(queue, &data, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(100));
// 释放信号量
xSemaphoreGive(queueSem);
}
}
void Task2(void *pvParameters) {
while(1) {
// 向队列中添加数据
xSemaphoreTake(queueSem, portMAX_DELAY);
xQueueSendFromISR(queue, &data, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(500));
// 释放信号量
xSemaphoreGive(queueSem);
}
}
int main(void) {
// 初始化信号量
queueSem = xSemaphoreCreateBinary();
xSemaphoreGive(queueSem);
// 初始化队列
queue = xQueueCreate(QUEUE_SIZE, sizeof(data_t));
// 创建任务
xTaskCreate(Task1, "Task1", 1000, NULL, 2, NULL);
xTaskCreate(Task2, "Task2", 1000, NULL, 2, NULL);
// 开始调度任务
vTaskStartScheduler();
// 任务调度结束后,不会执行到这里
return 0;
}