首页 > 其他分享 >FreeRTOS

FreeRTOS

时间:2022-09-01 13:55:32浏览次数:61  
标签:函数 FreeRTOS 队列 创建 列表 信号量 互斥

FreeRTOS

新建模板小书匠

一、任务

1. 任务状态

四种任务状态:

四种任务状态
四种任务状态

挂起态与阻塞态的区别:阻塞态有超时,挂起态没有

2. 中断

2-1. 三个重点寄存器

寄存器名 功能
PRIMASK 禁止除NMI、HardFault外的所有异常和中断
FAULTMASK 禁止包括HardFault的所有中断
BASEPRI 禁止低于BASEPRI寄存器中值的中断

2-2. 临界区代码

功能 函数名
进入临界区代码 taskENTER_CRITICAL()
退出临界区代码 taskEXIT_CRITICAL()
进入临界区代码(中断) taskENTER_CRITICA_FROM_ISR()
退出临界区代码(中断) taskEXIT_CRITICAL_FROM_ISR()

3. 任务创建与使用

3-1. 创建任务

  • xTaskCreate()
  • xTaskC reateStatic()

3-1-1. 常用宏定义

  • 任务优先级
  • 任务堆栈大小
  • 任务句柄
  • 任务函数

3-2. 退出任务

  • vTaskDelete(NULL)
    在创建任务后就需要退出需要使用

3-3. 开启任务调度

vTaskStartScheduler()

3-4. 任务切换

引起任务切换的事件:
+延时函数(常用)

  • 请求信号量
  • 请求队列
  • 任务调度器

3-5. 任务优先级

在FreeRTOS.h中配置宏configMAX_PRIORITIES可以设置最大优先级,其中优先级越高值越大
任务配置最高优先级:configMAX_PRIORITIES -1

3-5-1. 优先级翻转

taskA>taskB>taskC,taskC请求资源S,获得资源S,taskA请求资源S,taskA优先级高,运行taskA,taskA获取不到资源S,挂起,继续运行taskC,taskB就绪,运行taskB,taskC挂起,taskB运行完,运行taskC,taskC结束,taskA才的到资源继续运行。

优先级翻转
优先级翻转

3-6. 任务函数

需要运行的功能,创建任务函数的第一个参数
返回值:void
传入参数:void*

3-7. 任务控制块

一个结构体, 存储任务的基本属性,创建任务时返回值即是一个任务控制块,在文件task.c中定义

3-8. 任务挂起与恢复

  • 挂起任务
  • vTaskSuspend() 挂起任务
  • vTaskResume() 恢复任务
  • vTaskResumeFromISR() 中断服务函数恢复一个任务

4. 信号量

4-1. 二值信号量

  • 机制:二值,判断队列是空或满

  • 阻塞时间:进入阻塞的最大时间

  • 替代:任务通知(占用内存小)

  • 使用场景

    1.任务与任务
    2.中断与任务

4-1-1. 创建信号量

创建信号量
创建信号量

4-1-2. 请求信号量

请求信号量
请求信号量

4-1-3. 释放信号量

释放信号量
释放信号量

4-2. 互斥信号量

  • 与二值信号量区别:拥有优先级继承的二值信号量
  • 使用场景:需要互斥访问的应用中
  • 注意:不能适用于中断服务函数中(没有优先级,中断不能进入阻塞态)

4-2-1. 创建互斥信号量

创建互斥信号量
创建互斥信号量

动态创建互斥信号量参数解释
动态创建互斥信号量参数解释

静态创建互斥信号量参数结束
静态创建互斥信号量参数结束

4-2-2. 释放互斥信号量

释放互斥信号量和二值信号量、计数信号量一致:

xSemaphoreGive()

4-2-3. 获取互斥信号量

获取互斥信号量和计数刑信号量一致:

xSemaphoreTake();

4-2-4. 例子

//创建互斥信号量
MutexSemaphore = xSemaplhoreCreateMutex();

//获取互斥信号量
xSemaphoreTake(MutexSemaphore,portMAX_DELAY);
// 释放信号量
xSemaphoreGive(MutexSemaphore);

4-3. 递归互斥信号量

要使用递归互斥信号量的宏configUSE_RECURSIVE_MUTEXES 需要为1

4-3-1. 创建递归互斥信号量

创建递归互斥信号量
创建递归互斥信号量

递归互斥信号量参数解释

动态互斥信号量创建参数解释
动态互斥信号量创建参数解释

静态创建递归互斥信号量参数解释
静态创建递归互斥信号量参数解释

4-3-2. 释放递归互斥信号量

释放递归互斥信号量有专用的函数:

xSemaphoreGiveRecursive()

4-3-3. 获取递归互斥信号量

xSemaphoreTakeRecursive();

4-3-4. 例子

  1. //递归互斥信号量句柄 
  2. SemaphoreHandle_t RecursiveMutex; 
  3.  
  4. //创建递归互斥信号量 
  5. RecursiveMutex = xSemaphoreRecursiveMutex(); 
  6.  
  7. //获取递归互斥信号量 
  8. err = xSemaphoreTakeRecursive(RecursiveMutex,(TickType_t)10); 
  9. err = xSemaphoreTakeRecursive(RecursiveMutex,(TickType_t)10);//多次获取递归互斥信号量 
  10.  
  11. //释放递归互斥信号量 
  12. xSemaphoreGiveRecursive(RecursiveMutex); 
  13. xSemaphoreGiveRecursive(RecursiveMutex)//获取几次就需要释放几次 

4-4. 计数信号量

  • 使用场景

    1. 事件计数(队列结构体成员uxMessagesWaiting)
    2. 资源管理(有多少资源可用)
  • 简单原理

    1.uxMaxCount(创建计数信号量的参数) 计数的最大值——队列的长度
    2.uxMessagesWaiting(队列结构体成员)——创建队列的初始值

4-4-1. 创建计数信号量

创建计数信号量
创建计数信号量

参数解释
参数解释

静态创建参数解释
静态创建参数解释

4-4-2. 例子

//创建信号量句柄
SemaphoreHandle_t CountSemahpore;

//创建计数信号量
CountSemaphore = xSemaphoreCreateCounting(255,0);

//释放计数信号量
BaseType_t err = xSemahphoreGive(CountSemaphore);
if(err == pdFALSE)
{
	printf("信号量释放失败\n");
}

//获取信号量的值
int semaphoreValue = uxSemaphoreGetCount(CountSemaphore);

//等待计数信号量(未等到阻塞)
xSemaphoreTake(CountSemaphore,portMax_DELAY);
int semaphoreValue = uxSemaphoreGetCount(CountSemaphore);

4-5. 信号量AP

4-5-1. 发送信号量

  • 中断
    xSemaphoreGiveFromISR()

4-5-2. 请求信号量

xSemaphoreTake()

5. 列表

5-1. 列表结构体

5-1-1. 所在文件

list.h

5-1-2. 各结构体重要成员

  • 列表 List_t

列表结构体成员
列表结构体成员

  • 列表项 ListItem_t

列表项结构体成员
列表项结构体成员

  • MINI列表项 MiniListItem_t

mini列表项结构体成员
mini列表项结构体成员

5-2. 列表和列表项初始化

5-2-1. 列表初始化函数

  • 文件名 list.c
  • 函数名:vListInitialise()
  • 初始化后各结构体指向

列表初始化结构
列表初始化结构

  • 解释

列表中存在一个xListEnd项,uxNumberOfItems不将xListEnd计算在内,xList列表指向xListEnd列表项。列表项索引号指向listEnd,listEnd中的值设置为portMAX_DELAY,listEnd列表项的pxNext和pxPrevious指向listEnd自己

5-2-2. 列表项初始化

  • 文件 list.c
  • 函数名:vListInitialiseItem()
  • 解释

列表初始化
列表初始化

所属列表pvContainer成员初始化为NULL,初始化完整性检查变量。

5-3. 列表项的插入

依次插入xItemValue为40、60、50的列表项

  • 插入40的列表项

插入40的列表项
插入40的列表项

  • 插入60的列表项

插入60的列表项
插入60的列表项

  • 插入50的列表项

插入50的列表项
插入50的列表项

5-4. 列表末尾插入

  • 插入前的列表

    插入前的列表

  • 插入后的列表

末尾插入列表项
末尾插入列表项

6. 消息队列

6-1. 创建消息队列

6-1-1. 创建动态消息队列

  • 函数名 xQueueCreate()
  • 注意
    想使用该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 来使能,这 是个用于使能动态内存分配的宏,通常情况下,在 FreeRTOS 中,凡是创建任务,队列, 信号量和互斥量等内核对象都需要使用动态内存分配,所以这个宏默认在 FreeRTOS.h 头文 件中已经使能(即定义为 1)
  • 实例
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  
  /* 创建Test_Queue */
  Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */
                            (UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */
  if(NULL != Test_Queue)
    printf("创建Test_Queue消息队列成功!\r\n");
 taskEXIT_CRITICAL();     //退出临界区

6-1-2. 创建静态消息队列

  • 函数名
    xQueueCreateStatic()
    说明

  • 注意
    要想使用该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_STATIC_ALLOCATION 定义为 1 来使能

  • 实例

 /* 创建一个可以最多可以存储 10 个 64 位变量的队列 */
 #define QUEUE_LENGTH 10
 
 #define ITEM_SIZE sizeof( uint64_t )
 
 /* 该变量用于存储队列的数据结构 */
 
 static StaticQueue_t xStaticQueue;
 
 /* 该数组作为队列的存储区域,大小至少有 uxQueueLength * uxItemSize 个字节 */
 
 uint8_t ucQueueStorageArea[ QUEUE_LENGTH * ITEM_SIZE ]; 
 
  void vATask( void *pvParameters )
 {
 QueueHandle_t xQueue;
 
 /* 创建一个队列 */ 
 xQueue = xQueueCreateStatic( QUEUE_LENGTH, /* 队列深度 */ 
 ITEM_SIZE, /* 队列数据单元的单位 */ 
 ucQueueStorageArea,/* 队列的存储区域 */ 
 &xStaticQueue ); /* 队列的数据结构 */ 
 /* 剩下的其他代码 */
 }

6-2. 读取消息队列

  • 函数名
    • 普通
                          void * const pvBuffer,
                          TickType_t xTicksToWait );
+ 中断
BaseType_t xQueueReceiveFromISR(
                                    QueueHandle_t    xQueue,
                                    void             *pvBuffer,
                                    BaseType_t       *pxTaskWoken
                                );

说明
说明

  • 实例
static void Receive_Task(void* parameter)
{	
  BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdTRUE */
  uint32_t r_queue;	/* 定义一个接收消息的变量 */
  while (1)
  {
    xReturn = xQueueReceive( Test_Queue,    /* 消息队列的句柄 */
                             &r_queue,      /* 发送的消息内容 */
                             portMAX_DELAY); /* 等待时间 一直等 */
    if(pdTRUE == xReturn)
      printf("本次接收到的数据是%d\n\n",r_queue);
    else
      printf("数据接收出错,错误代码0x%lx\n",xReturn);
  }
}

6-3. 写消息队列

可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR中使用。函数原型如下

6-3-1. 函数名

  • 通用版
/* 等同于xQueueSendToBack
 * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
 */
BaseType_t xQueueSend(
                                QueueHandle_t    xQueue,
                                const void       *pvItemToQueue,
                                TickType_t       xTicksToWait
                            );
  • 队列尾部写数据
/* 
 * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
 */
BaseType_t xQueueSendToBack(
                                QueueHandle_t    xQueue,
                                const void       *pvItemToQueue,
                                TickType_t       xTicksToWait
                            );
 
  • 中断队列尾写数据
/* 
* 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToBackFromISR(
                                     QueueHandle_t xQueue,
                                     const void *pvItemToQueue,
                                     BaseType_t *pxHigherPriorityTaskWoken
                                  );
  • 队列头部写数据
/* 
 * 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait
 */
BaseType_t xQueueSendToFront(
                                QueueHandle_t    xQueue,
                                const void       *pvItemToQueue,
                                TickType_t       xTicksToWait
                            );
  • 中断队列头部写数据
/* 
* 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToFrontFromISR(
                                     QueueHandle_t xQueue,
                                     const void *pvItemToQueue,
                                     BaseType_t *pxHigherPriorityTaskWoken
                                  );

说明
说明

6-3-2. 实例


static void Send_Task(void* parameter)
{	 
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  uint32_t send_data1 = 1;
  uint32_t send_data2 = 2;
  while (1)
  {
    if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
    {/* K1 被按下 */
      printf("发送消息send_data1!\n");
      xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */
                            &send_data1,/* 发送的消息内容 */
                            0 );        /* 等待时间 0 */
      if(pdPASS == xReturn)
        printf("消息send_data1发送成功!\n\n");
    } 
    if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
    {/* K2 被按下 */
      printf("发送消息send_data2!\n");
      xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */
                            &send_data2,/* 发送的消息内容 */
                            0 );        /* 等待时间 0 */
      if(pdPASS == xReturn)
        printf("消息send_data2发送成功!\n\n");
    }
    vTaskDelay(20);/* 延时20个tick */
  }
}

6-4. 删除消息队列

6-4-1. 函数名

vQueueDelete()

队列删除函数是根据消息队列句柄直接删除的,删除之后这个消息队列的所有信息都 会被系统回收清空,而且不能再次使用这个消息队列了,但是需要注意的是,如果某个消 息队列没有被创建,那也是无法被删除的,动脑子想想都知道,没创建的东西就不存在, 怎么可能被删除。xQueue 是 vQueueDelete()函数的形参,是消息队列句柄,表示的是要删 除哪个想队列,消息队列删除函数 vQueueDelete()的使用也是很简单的,只需传入要删除的消息队列 的句柄即可,

调用函数时,系统将删除这个消息队列。需要注意的是调用删除消息队列函 数前,系统应存在 xQueueCreate()或 xQueueCreateStatic()函数创建的消息队列。此外 vQueueDelete()也可用于删除信号量。如果删除消息队列时,有任务正在等待消息,则不应 该进行删除操作。

6-4-2. 实例

#define QUEUE_LENGTH 5
#define QUEUE_ITEM_SIZE 4
 
int main( void )
{
  QueueHandle_t xQueue;
 /* 创建消息队列 */
 xQueue = xQueueCreate( QUEUE_LENGTH, QUEUE_ITEM_SIZE );
 
 if ( xQueue == NULL ) {
    /* 消息队列创建失败 */
   } else {
    /* 删除已创建的消息队列 */ 
    vQueueDelete( xQueue ); 
   }
}

6-5. 复位

6-5-1. 函数名

BaseType_t xQueueReset( QueueHandle_t pxQueue);
队列刚被创建时,里面没有数据;使用过程中可以调用xQueueReset()把队列恢复为初始状态,此函数原型为

6-6. 查询

可以查询队列中有多少个数据、有多少空余空间。函数原型如下:

/*
 * 返回队列中可用数据的个数
 */
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
 
/*
 * 返回队列中可用空间的个数
 */
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

标签:函数,FreeRTOS,队列,创建,列表,信号量,互斥
From: https://www.cnblogs.com/lukelgl/p/16646243.html

相关文章

  • Windows下ESP32 环境搭建(基于esp-idf FreeRTOS)
    1.之前的尝试(失败的尝试)咸鱼买了3块ESP32开发板。背面写了NODEMCUv1.1,好像这玩意可以直接写lua,也可以刷Micropython写python,还可以用ArduinoIDE写c。我想直接用官方库......