FreeRTOS
新建模板小书匠一、任务
1. 任务状态
四种任务状态:
![四种任务状态 四种任务状态](./images/1660023744181.png)
四种任务状态
挂起态与阻塞态的区别:阻塞态有超时,挂起态没有
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才的到资源继续运行。
![优先级翻转 优先级翻转](./images/1661752724153.png)
优先级翻转
3-6. 任务函数
需要运行的功能,创建任务函数的第一个参数
返回值:void
传入参数:void*
3-7. 任务控制块
一个结构体, 存储任务的基本属性,创建任务时返回值即是一个任务控制块,在文件task.c
中定义
3-8. 任务挂起与恢复
- 挂起任务
- vTaskSuspend() 挂起任务
- vTaskResume() 恢复任务
- vTaskResumeFromISR() 中断服务函数恢复一个任务
4. 信号量
4-1. 二值信号量
-
机制:二值,判断队列是空或满
-
阻塞时间:进入阻塞的最大时间
-
替代:任务通知(占用内存小)
-
使用场景
1.任务与任务
2.中断与任务
4-1-1. 创建信号量
![创建信号量 创建信号量](./images/1660029179835.png)
创建信号量
4-1-2. 请求信号量
![请求信号量 请求信号量](./images/1660029480148.png)
请求信号量
4-1-3. 释放信号量
![释放信号量 释放信号量](./images/1660029373742.png)
释放信号量
4-2. 互斥信号量
- 与二值信号量区别:拥有优先级继承的二值信号量
- 使用场景:需要互斥访问的应用中
- 注意:不能适用于中断服务函数中(没有优先级,中断不能进入阻塞态)
4-2-1. 创建互斥信号量
![创建互斥信号量 创建互斥信号量](./images/1661923870328.png)
创建互斥信号量
![动态创建互斥信号量参数解释 动态创建互斥信号量参数解释](./images/1661923923046.png)
动态创建互斥信号量参数解释
![静态创建互斥信号量参数结束 静态创建互斥信号量参数结束](./images/1661923970751.png)
静态创建互斥信号量参数结束
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. 创建递归互斥信号量
![创建递归互斥信号量 创建递归互斥信号量](./images/1662003676422.png)
创建递归互斥信号量
递归互斥信号量参数解释
![动态互斥信号量创建参数解释 动态互斥信号量创建参数解释](./images/1662003797599.png)
动态互斥信号量创建参数解释
![静态创建递归互斥信号量参数解释 静态创建递归互斥信号量参数解释](./images/1662003881527.png)
静态创建递归互斥信号量参数解释
4-3-2. 释放递归互斥信号量
释放递归互斥信号量有专用的函数:
xSemaphoreGiveRecursive()
4-3-3. 获取递归互斥信号量
xSemaphoreTakeRecursive();
4-3-4. 例子
- //递归互斥信号量句柄
- SemaphoreHandle_t RecursiveMutex;
-
- //创建递归互斥信号量
- RecursiveMutex = xSemaphoreRecursiveMutex();
-
- //获取递归互斥信号量
- err = xSemaphoreTakeRecursive(RecursiveMutex,(TickType_t)10);
- err = xSemaphoreTakeRecursive(RecursiveMutex,(TickType_t)10);//多次获取递归互斥信号量
-
- //释放递归互斥信号量
- xSemaphoreGiveRecursive(RecursiveMutex);
- xSemaphoreGiveRecursive(RecursiveMutex)//获取几次就需要释放几次
4-4. 计数信号量
-
使用场景
- 事件计数(队列结构体成员uxMessagesWaiting)
- 资源管理(有多少资源可用)
-
简单原理
1.uxMaxCount(创建计数信号量的参数) 计数的最大值——队列的长度
2.uxMessagesWaiting(队列结构体成员)——创建队列的初始值
4-4-1. 创建计数信号量
![创建计数信号量 创建计数信号量](./images/1661908374323.png)
创建计数信号量
![参数解释 参数解释](./images/1661908464709.png)
参数解释
![静态创建参数解释 静态创建参数解释](./images/1661908493460.png)
静态创建参数解释
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
![列表结构体成员 列表结构体成员](./images/1661319409099.png)
列表结构体成员
- 列表项 ListItem_t
![列表项结构体成员 列表项结构体成员](./images/1661319496564.png)
列表项结构体成员
- MINI列表项 MiniListItem_t
![mini列表项结构体成员 mini列表项结构体成员](./images/1661319612775.png)
mini列表项结构体成员
5-2. 列表和列表项初始化
5-2-1. 列表初始化函数
- 文件名 list.c
- 函数名:vListInitialise()
- 初始化后各结构体指向
![列表初始化结构 列表初始化结构](./images/1661320708946.png)
列表初始化结构
- 解释
列表中存在一个xListEnd项,uxNumberOfItems不将xListEnd计算在内,xList列表指向xListEnd列表项。列表项索引号指向listEnd,listEnd中的值设置为portMAX_DELAY,listEnd列表项的pxNext和pxPrevious指向listEnd自己
5-2-2. 列表项初始化
- 文件 list.c
- 函数名:vListInitialiseItem()
- 解释
![列表初始化 列表初始化](./images/1661330799633.png)
列表初始化
所属列表pvContainer成员初始化为NULL,初始化完整性检查变量。
5-3. 列表项的插入
依次插入xItemValue为40、60、50的列表项
- 插入40的列表项
![插入40的列表项 插入40的列表项](./images/1661331392029.png)
插入40的列表项
- 插入60的列表项
![插入60的列表项 插入60的列表项](./images/1661331431331.png)
插入60的列表项
- 插入50的列表项
![插入50的列表项 插入50的列表项](./images/1661331559083.png)
插入50的列表项
5-4. 列表末尾插入
-
插入前的列表
-
插入后的列表
![末尾插入列表项 末尾插入列表项](./images/1661332414505.png)
末尾插入列表项
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
);
![说明 说明](./images/1660807298279.png)
说明
- 实例
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
);
![说明 说明](./images/1660809011353.png)
说明
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