FreeRTOS-05-队列
说明
本文仅作为学习FreeRTOS的记录文档,作为初学者肯定很多理解不对甚至错误的地方,望网友指正。
FreeRTOS是一个RTOS(实时操作系统)系统,支持抢占式、合作式和时间片调度。适用于微处理器或小型微处理器的实时应用。
本文档使用的FreeRTOS版本:FreeRTOS Kernel V10.4.1
参考文档:《FreeRTOS_Reference_Manual_V10.0.0.pdf》《FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf》《STM32F4 FreeRTOS开发手册_V1.1.pdf》
参考视频:正点原子FreeRTOS手把手教学-基于STM32_哔哩哔哩_bilibili
8 队列
8.1 简介
队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项目。队列所能保存的最大数据项目数量叫做队列的长度,创建队列的时候会指定数据项目的大小和队列的长度。队列是用来传递消息的,所以也称为消息队列。信号量也是依据队列实现的。
8.1.1 数据存储
队列采用先进先出(FIFO)的存储缓冲机制,往队列中放数据叫做入队(放队尾),从队列中取数据叫做出队(从队头)。也可以使用LIFO的存储缓冲,也就是后进先出,FreeRTOS也提供了LIFO的存储缓冲机制。
数据存放到队列中会导致值拷贝,也就是放数据到队列中,而不是数据的指针,这叫值传递。采用值传递,当消息放到队列中后原始的数据缓冲区就可以删除,缓冲区就可以重复使用。FreeRTOS使用的是数据拷贝,但是也可以采用引用(指针)来传递消息,直接往队列中放入发送消息缓冲区的地址的指针。
8.1.2 多任务访问
队列不属于某个特定的任务,任何任务都可以向队列中发送消息,或者从队列中提取消息。
8.1.3 出队阻塞
当任务从一个队列中提取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任务从队列中提取消息无效的时候任务阻塞的时间。出队就是从队列中提取消息,出队阻塞是针对从队列中提取消息的任务而言的。阻塞时间的单位是时钟节拍数,阻塞时间为0就是不阻塞。如果阻塞时间是0~portMAX_DELAY,当任务没有从队列中获取到消息的话就会进入阻塞态,阻塞时间指定了任务进入阻塞态的时间,当阻塞时间到了以后还没有接收到消息就立即退出阻塞态;如果在阻塞时间收到了数据就立即返回。当阻塞时间设置为portMAX_DELAY,任务就会一直进入阻塞态等待,直到接收到数据为止。
8.1.4 入队阻塞
入队就是往队列中发送消息,将消息加入到队列中。入队也可以设置阻塞时间。比如入队时,队列已经满了。
8.1.5 队列操作过程
创建队列:创建一个队列,用于任务A和任务B之间通信,队列数据项目的个数为5,创建时队列为空。

往队列中发送第一个消息:任务A往队列中发送了一个消息,值为10。

往队列中再发送一个消息:任务A往队列中再发送了一个消息,值为20,这时队列剩余空间大小为3。

从队列中取一个消息:任务B从队列中取一个消息,从队列头开始取(值为10),队列中剩下一个消息,剩余空间大小为4。

下图演示了一次完整的任务A和任务B通过队列传递消息的过程:

8.2 队列结构体
队列结构体为Queue_t,在queue.c中定义:
typedef struct QueuePointers
{
int8_t * pcTail; //指向队列存储区最后一个字节
int8_t * pcReadFrom; //作为队列使用时指向最后一个出队的队列项首地址
} QueuePointers_t;
typedef struct SemaphoreData
{
TaskHandle_t xMutexHolder;
UBaseType_t uxRecursiveCallCount; //作为递归互斥量的时候用来记录递归互斥量被调用的次数
} SemaphoreData_t;
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; //当队列上锁后用来统计发送到队列的队列项个数,也就是入队的队列项个数
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; //如果使用静态存储,则设置字段值为pdTURE
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition * pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
typedef xQUEUE Queue_t;
8.3 队列创建
8.3.1 动态队列创建
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );
函数描述:创建一个新的队列,并返回指向队列的句柄。使用这个函数需要将宏configSUPPORT_DYNAMIC_ALLOCATION置为1。每一个队列需要存储空间来存放队列的状态和队列项,如果使用xQueueCreate()创建队列,则存储空间由系统自动分配。如果使用xQueueCreateStatic()创建,存储空间由用户指定。
函数参数:uxQueueLength:要创建的队列项个数。uxItemSize:单个队列项的大小,单位为字节。
返回值:NULL:表示分配存储空间失败。其它值:队列创建成功,返回值是指向创建队列的句柄。
8.3.2 静态队列创建
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
QueueHandle_t xQueueCreateStatic( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize,
uint8_t *pucQueueStorageBuffer,
StaticQueue_t *pxQueueBuffer );
函数描述:创建一个新的队列,并返回指向队列的句柄。存储空间由用户指定。
函数参数:uxQueueLength:要创建的队列项个数。
uxItemSize:单个队列项的大小,单位为字节。
pucQueueStorageBuffer:指向队列项目的存储区,也就是消息存储区,存储区要大于等于uxQueueLength * uxItemSizede 字节。
pxQueueBuffer:指向用来存放队列结构体的空间。
返回值:NULL:表示分配存储空间失败。其它值:队列创建成功,返回值是指向创建队列的句柄。
队列创建的具体实现过程可以参考源码,这里给出创建一个有4个队列项,每个队列项长度为32字节的队列成功的示意图。

8.4 向队列发送消息
向队列中发送消息分为任务级入队函数和中断级入队函数。
8.4.1 任务级入队函数
任务级入队函数有3个,分别为xQueueSend()、xQueueSendToFront()、xQueueSendToBack() 、xQueueOverwrite()。
先介绍xQueueSend()、xQueueSendToFront()、xQueueSendToBack() 。
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
BaseType_t xQueueSendToBack( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
函数描述:发送一个队列项到队列头或者队列尾。xQueueSend()和xQueueSendToBack()是同样的操作,都是发送数据到队列的尾部,xQueueSend()是原始版本,当前使用xQueueSendToBack()替换它。xQueueSendToFront()是发送数据到队列项的首部。
函数参数:xQueue:队列句柄,由创建队列的函数返回。
pvItemToQueue:指向待发送的队列项的指针,队列项是拷贝到队列中的。队列项的大小由创建队列的时候指定。
xTicksToWait:阻塞时间,此参数表示队列满的时候任务进入阻塞态等待队列空闲的最大时间。如果为0,任务立即返回;如果为portMAX_DELAY,任务会一直死等,宏portMAX_DELAY需要置为1;其余值为等到的时钟计数值,可使用宏pdMS_TO_TICKS()转换时钟计数值为毫秒。
返回值:pdPASS:数据成功发送到队列;errQUEUE_FULL:队列满,消息发送失败。
另外还有xQueueOverwrite()函数为消息覆盖写函数。
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
BaseType_t xQueueOverwrite( QueueHandle_t xQueue, const void *pvItemToQueue );
函数描述:向队列中发送数据,当队列满了以后会覆盖掉旧的数据,不管这个旧的数据有没有被其它任务或中断取走。这个函数常用于向那些长度为1的队列发送消息。
函数参数:xQueue:队列句柄,由创建队列的函数返回。
pvItemToQueue:指向待发送的队列项的指针,队列项是拷贝到队列中的。队列项的大小由创建队列的时候指定。
返回值:pdPASS:数据成功发送到队列,因为队列满了之后会覆盖写,所以不存在失败的情况。
8.4.2 中断级入队函数
任务级入队函数有3个,分别为xQueueSendFromISR()、xQueueSendToBackFromISR()、xQueueSendToFrontFromISR() 、xQueueOverwriteFromISR ()。
先介绍xQueueSendFromISR()、xQueueSendToBackFromISR()、xQueueSendToFrontFromISR()。
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
BaseType_t xQueueSendFromISR( QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken );
BaseType_t xQueueSendToBackFromISR( QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken );
BaseType_t xQueueSendToFrontFromISR( QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken );
函数描述:发送一个队列项到队列头或者队列尾,用于中断服务函数中。xQueueSendFromISR()和xQueueSendToBackFromISR()是同样的操作,都是发送数据到队列的尾部。xQueueSendToFrontFromISR()是发送数据到队列项的首部。和任务级入队函数不同的是中断级入队函数不允许指定任务的阻塞时间。
函数参数:xQueue:队列句柄,由创建队列的函数返回。
pvItemToQueue:指向待发送的队列项的指针,队列项是拷贝到队列中的。队列项的大小由创建队列的时候指定。
pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量的值由函数指定,用户不进行设置,用户仅需要提供一个变量来保存值。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:pdPASS:数据成功发送到队列;errQUEUE_FULL:队列满,消息发送失败。
另外xQueueOverwriteFromISR()函数为消息覆盖写函数。
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
BaseType_t xQueueOverwriteFromISR( QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken );
函数描述:向队列中发送数据,当队列满了以后会覆盖掉旧的数据,从队尾写入。
函数参数:xQueue:队列句柄,由创建队列的函数返回。
pvItemToQueue:指向待发送的队列项的指针,队列项是拷贝到队列中的。队列项的大小由创建队列的时候指定。
pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量的值由函数指定,用户不进行设置,用户仅需要提供一个变量来保存值。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:pdPASS:数据成功发送到队列,因为队列满了之后会覆盖写,所以不存在失败的情况。
8.5 从队列中读取消息
出队就是从队列中获取队列项。也分为任务级出队函数和中断级出队函数。
8.4.1 任务级出队函数
任务级出队函数有2个,分别为xQueueReceive()、xQueuePeek()。
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
BaseType_t xQueueReceive( QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait );
函数描述:从队列中读取一个队列项,读取成功以后就会将队列中的这条消息删除。读取消息时是采用的拷贝方式,所以用户需要提供一个数组或缓冲区来保存读取到的数据,所读取的数据长度是队列创建的时候设定的每个队列项的长度。
函数参数:xQueue:队列句柄,由创建队列的函数返回。
pvBuffer:保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
xTicksToWait:阻塞时间,此参数表示队列空的时候任务进入阻塞态等待队列空闲的最大时间。如果为0,任务立即返回;如果为portMAX_DELAY,任务会一直死等,宏portMAX_DELAY需要置为1;其余值为等到的时钟计数值,可使用宏pdMS_TO_TICKS()转换时钟计数值为毫秒。
返回值:pdPASS:成功从队列中读取到数据;errQUEUE_EMPTY:队列空,消息读取失败。
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
BaseType_t xQueuePeek( QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait );
函数描述:从队列中读取一个队列项,读取成功以后不会将队列中的这条消息删除。下一次读取的时候仍然可以从这个队列中读取这个消息。读取消息时是采用的拷贝方式,所以用户需要提供一个数组或缓冲区来保存读取到的数据,所读取的数据长度是队列创建的时候设定的每个队列项的长度。
函数参数:xQueue:队列句柄,由创建队列的函数返回。
pvBuffer:保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
xTicksToWait:阻塞时间,此参数表示队列空的时候任务进入阻塞态等待队列空闲的最大时间。如果为0,任务立即返回;如果为portMAX_DELAY,任务会一直死等,宏portMAX_DELAY需要置为1;其余值为等到的时钟计数值,可使用宏pdMS_TO_TICKS()转换时钟计数值为毫秒。
返回值:pdPASS:成功从队列中读取到数据;errQUEUE_EMPTY:队列空,消息读取失败。
8.4.2 中断级出队函数
中断级出队函数xQueueReceiveFromISR()、xQueuePeekFromISR ()。
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
void *pvBuffer,
BaseType_t *pxHigherPriorityTaskWoken );
函数描述:用于在中断服务函数中从队列中读取一条消息,读取成功后就会将队列中的这条数据删除。读取消息的方式采用值拷贝方式,需要用户提供一个数组或者缓冲区来保存读取到的数据,所读取的数据长度是创建队列的时候指定的每个队列项的长度。
函数参数:xQueue:队列句柄,由创建队列的函数返回。
pvBuffer:保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量的值由函数指定,用户不进行设置,用户仅需要提供一个变量来保存值。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:pdPASS:成功从队列中读取到数据;pdFAIL:队列空,消息读取失败。
函数原型:
#include “FreeRTOS.h”
#include “queue.h”
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void *pvBuffer );
函数描述:xQueuePeek()的中断版本,函数读取成功以后不会将消息删除。
函数参数:xQueue:队列句柄,由创建队列的函数返回。
pvBuffer:保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
返回值:pdPASS:成功从队列中读取到数据;pdFAIL:队列空,消息读取失败。
8.6 队列操作实验
目的:熟悉队列的使用
设计:创建两个任务,任务task0每隔2秒发送1个数据到队列中,并每隔4秒检查队列余量;task1读取队列中的消息,阻塞时间为5秒。队列的容量为4,每个队列项的大小为4字节。
测试代码:
/* task00 info */
configSTACK_DEPTH_TYPE Task00_STACK_SIZE = 5;
UBaseType_t Task00_Priority = 1;
TaskHandle_t Task00_xHandle;
/* task01 info */
configSTACK_DEPTH_TYPE Task01_STACK_SIZE = 5;
UBaseType_t Task01_Priority = 1;
TaskHandle_t Task01_xHandle;
#define MSG_QUEUE_NUM 4
#define MSG_QUEUE_ITEM_SIZE 4
QueueHandle_t Message_Queue;
void check_msg_queue(void)
{
unsigned char *p = NULL;
unsigned char msgq_remain_size = 0;
unsigned char msgq_total_size = 0;
taskENTER_CRITICAL();
msgq_remain_size = uxQueueSpacesAvailable(Message_Queue);
msgq_total_size = uxQueueMessagesWaiting(Message_Queue) + msgq_remain_size;
PRINT(" total size:%d remain size:%d", msgq_total_size, msgq_remain_size);
taskEXIT_CRITICAL();
}
void vTask00_Code(void *para)
{
static unsigned int cnt = 0;
BaseType_t err;
unsigned int item = 0;
for (;;)
{
PRINT(" task00 cnt %u...", cnt);
if (cnt%2 == 0) {
item = cnt*100 + 1;
err = xQueueSend(Message_Queue, &item, pdMS_TO_TICKS(5000));
if (err == errQUEUE_FULL)
PRINT("queue full!");
}
if (cnt%4 == 0)
check_msg_queue();
cnt++;
vTaskDelay(1000);
}
}
void vTask01_Code(void *para)
{
static unsigned int cnt = 0;
unsigned int item = 0;
BaseType_t err;
for (;;)
{
PRINT(" task01 cnt %u...", cnt);
err = xQueueReceive(Message_Queue, &item, pdMS_TO_TICKS(5000));
if (err == errQUEUE_EMPTY)
PRINT("queue empty!");
else
PRINT(" item: %d", item);
cnt++;
vTaskDelay(1000);
}
}
void test_queue()
{
Message_Queue = xQueueCreate(MSG_QUEUE_NUM, MSG_QUEUE_ITEM_SIZE);
if (xTaskCreate(vTask00_Code, "task00 task",
Task00_STACK_SIZE, NULL, Task00_Priority,
&Task00_xHandle) != pdPASS)
{
PRINT("creat task00 failed!\n");
}
if (xTaskCreate(vTask01_Code, "task01 task",
Task01_STACK_SIZE, NULL, Task01_Priority,
&Task01_xHandle) != pdPASS)
{
PRINT("creat task01 failed!\n");
}
}
void creat_task(void)
{
test_queue();
}
编译、运行,结果如下:
$ ./build/freertos-simulator
task00 cnt 0...
total size:4 remain size:3
task01 cnt 0...
item: 1
task00 cnt 1...
task01 cnt 1...
task00 cnt 2...
item: 201
task00 cnt 3...
task01 cnt 2...
task00 cnt 4...
total size:4 remain size:3
item: 401
task00 cnt 5...
task01 cnt 3...
task00 cnt 6...
item: 601
task00 cnt 7...
task01 cnt 4...
可以看出,task01会阻塞性的等待队列中的值(见item:201)。
下面测试队列空的情况,task00不往队列中放入数据:
void vTask00_Code(void *para)
{
static unsigned int cnt = 0;
BaseType_t err;
unsigned int item = 0;
for (;;)
{
PRINT(" task00 cnt %u...", cnt);
if (cnt%4 == 0)
check_msg_queue();
cnt++;
vTaskDelay(1000);
}
}
void vTask01_Code(void *para)
{
static unsigned int cnt = 0;
unsigned int item = 0;
BaseType_t err;
for (;;)
{
PRINT(" task01 cnt %u...", cnt);
err = xQueueReceive(Message_Queue, &item, pdMS_TO_TICKS(5000));
if (err == errQUEUE_EMPTY)
PRINT("queue empty!");
else
PRINT(" item: %d", item);
cnt++;
vTaskDelay(1000);
}
}
编译、运行,结果如下,task01阻塞了5秒:
$ ./build/freertos-simulator
task00 cnt 0...
total size:4 remain size:4
task01 cnt 0...
task00 cnt 1...
task00 cnt 2...
task00 cnt 3...
task00 cnt 4...
total size:4 remain size:4
queue empty!
task00 cnt 5...
task01 cnt 1...
task00 cnt 6...
下面测试队列空的情况,task01不从队列中取数据:
void vTask00_Code(void *para)
{
static unsigned int cnt = 0;
BaseType_t err;
unsigned int item = 0;
for (;;)
{
PRINT(" task00 cnt %u...", cnt);
item = cnt*100 + 1;
err = xQueueSend(Message_Queue, &item, pdMS_TO_TICKS(5000));
if (err == errQUEUE_FULL)
PRINT("queue full!");
if (cnt%4 == 0)
check_msg_queue();
cnt++;
vTaskDelay(1000);
}
}
void vTask01_Code(void *para)
{
static unsigned int cnt = 0;
unsigned int item = 0;
BaseType_t err;
for (;;)
{
PRINT(" task01 cnt %u...", cnt);
cnt++;
vTaskDelay(1000);
}
}
编译、运行,结果如下,task00往队列中放了4个数据项之后,就阻塞了,每次阻塞时间为5秒:
$ ./build/freertos-simulator
task00 cnt 0...
total size:4 remain size:3
task01 cnt 0...
task00 cnt 1...
task01 cnt 1...
task00 cnt 2...
task01 cnt 2...
task00 cnt 3...
task01 cnt 3...
task00 cnt 4...
task01 cnt 4...
task01 cnt 5...
task01 cnt 6...
task01 cnt 7...
task01 cnt 8...
queue full!
total size:4 remain size:0
task01 cnt 9...
task00 cnt 5...
task01 cnt 10...
task01 cnt 11...
task01 cnt 12...
task01 cnt 13...
task01 cnt 14...
queue full!
task01 cnt 15...
task00 cnt 6...
FreeRTOS-05-队列的更多相关文章
- FreeRTOS 消息队列
以下基础内容转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 的一个重要的通信机制----消息队列,初学者要熟练掌握,因为消息 ...
- 继续学习freertos消息队列
写在前面:杰杰这个月很忙~所以并没有时间更新,现在健身房闭馆装修,晚上有空就更新一下!其实在公众号没更新的这段日子,每天都有兄弟在来关注我的公众号,这让我受宠若惊,在这里谢谢大家的支持啦!!谢谢^ 在 ...
- FreeRTOS消息队列
FreeRTOS 的一个重要的通信机制----消息队列,消息队列在实际项目中应用较多. 一.消息队列的作用及概念: 消息队列就是通过 RTOS 内核提供的服务,任务或中断服务子程序可以将一个消息(注意 ...
- freertos之队列
任务间信息的传递是通过队列来实现的(单个值.结构体.共享数据指针.),队列是个独立的内核对象,即不属于任何一个任务,每个任务都可以向队列中发送数据和从队列中读数据.对于数据量小的场合通常队列是通过字节 ...
- FreeRTOS学习笔记——任务间使用队列同步数据
1.前言 在嵌入式操作系统中队列是任务间数据交换的常用手段,队列是生产者消费者模型的重要组成部分.FreeRTOS的队列简单易用,下面结合一个具体例子说明FreeRTOS中的队列如何使用. 2.参考代 ...
- FreeRTOS系列第17篇---FreeRTOS队列
本文介绍队列的基本知识,具体源代码分析见<FreeRTOS高级篇5---FreeRTOS队列分析> 1.FreeRTOS队列 队列是基本的任务间通讯方式.能够在任务与任务间.中断和任务间传 ...
- 【FreeRTOS学习04】小白都能懂的 Queue Management 消息队列使用详解
消息队列作为任务间同步扮演着必不可少的角色: 相关文章 [FreeRTOS实战汇总]小白博主的RTOS学习实战快速进阶之路(持续更新) 文章目录 相关文章 1 前言 2 xQUEUE 3 相关概念 3 ...
- FreeRTOS相关转载-(朱工的专栏)
FreeRTOS系列第1篇---为什么选择FreeRTOS? 1.为什么学习RTOS? 作为基于ARM7.Cortex-M3硬件开发的嵌入式工程师,我一直反对使用RTOS.不仅因为不恰当的使用RTOS ...
- FreeRTOS随记
任务函数原型: void ATaskFunction(void * pvParameters); 任务不允许从实现函数中返回.如果一个任务不再需要,可以用vTaskDelete()删除; 一个任务函数 ...
- FreeRTOS基础篇教程目录汇总
以下教程(大部分章节)(尤其理论介绍部分)转载自安富莱电子,官网链接: http://forum.armfly.com/forum.php 然后根据安富莱的教程自己做了分析和测试,希望大家共同进步. ...
随机推荐
- 让你发布的nuget包支持源代码调试
前情概要 在不久的从前(也还是要以年为单位哈), 我们如果需要调试第三方代码, 或者框架代码很麻烦. 需要配置symbols, 匹配原始代码路径等. 为此, MS推出了 Source Link 功能, ...
- CRM软件从哪些方面帮助企业更上一层楼
CRM顾客智能管理系统可以将"以顾客为管理中心"的管理模式与高新科技方式紧密结合,协助公司搭建优良的客户关系管理,改进顾客的消費感受,进而提升顾客的满意率,为公司产生大量的盈利.据 ...
- JavaScript基础以及进阶知识
JavaScript基础知识回顾: 目录: 比较运算符 多行字符串,模板字符串 严格模式 函数定义和调用 变量作用域与解构赋值.NameSpace.块级作用域let.const 方法 高阶函数 闭包 ...
- Docker:docker部署redis
docker镜像库拉取镜像 # 下载镜像 docker pull redis:4.0 查看镜像 # 查看下载镜像 docker images 启动镜像 # 启动镜像 docker run --na ...
- Mybatis学习(3)实现数据的增删改查
前面已经讲到用接口的方式编程.这种方式,要注意的一个地方就是.在User.xml 的配置文件中,mapper namespace="com.yihaomen.mybatis.inter.I ...
- Vue.js 作用域、slot用法(单个slot、具名slot)
作用域 在介绍slot前,需要先知道一个概念:编译的作用域.比如父组件中有如下模板: <child-component> {{message}} <child-component&g ...
- Leetcode No.53 Maximum Subarray(c++实现)
1. 题目 1.1 英文题目 Given an integer array nums, find the contiguous subarray (containing at least one nu ...
- GO系列-ioutil包
ioutil包提供给外部使用的一共有1个变量,7个方法. // Discard 是一个 io.Writer 接口,调用它的 Write 方法将不做任何事情 // 并且始终成功返回. var Disca ...
- Codeforces Round#687 Div2 题解
打这场的时候迷迷糊糊的,然后掉分了( A Prison Break: 题面很复杂,但是题意很简单,仅需求出从这个点到四个角的最大的曼哈顿距离即可 #include <bits/stdc++.h& ...
- 接入 SDK 结果翻车了?了解 SDK 的那些事
前段时间,二狗子的朋友圈被工信部发布的<关于下架侵害用户权益 App 名单的通报>给刷屏了.公告中指出有 90 款 App 未按照要求完成整改将会下架.而这 90 款 App 涉及全国各地 ...