FREERTOS

1.创建任务

2.vTaskDelay和vTaskDelayUntil

3.任务状态(就绪、运行、阻塞、暂停,删除任务,空闲钩子函数)

4.队列和邮箱

5.队列集

6.信号量

7.事件组

8.任务通知

9.定时器

10.中断

11.临界资源管理

12.系统优化

13.队列保护数据完整性保护

  1. 创建任务原型

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的定时器句柄。
如果定时器创建失败(例如,由于内存不足),函数返回NULL2.
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.系统优化

  1. 调试
在这里插入代码片

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值