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 然后根据安富莱的教程自己做了分析和测试,希望大家共同进步. ...
随机推荐
- GPU 高性能计算
背景 近日忽然想到,在CPU类型的服务器即使给到足够的运算资源,与GPU类型的服务器做运算来讲仍然是相差甚远,而本人有一台闲置的AMD vega8集显的电脑.想要用来做计算,来探究其与CPU运算的差别 ...
- Https:Java代码设置使用证书访问Https
设置证书进行访问或被访问操作 String keyStore = "keyStore的文件路径": String KEY_STORE_PWD = "1234"; ...
- Hadoop0.20.2中MapReduce读取gb2312文件出现乱码问题
单位用的是Linux系统的字符编码是gb2312,所以生成的文件都是按照默认编码生成的.给我的文件也都是gb2312的,在hadoop中运行mapreduce出现乱码,在网上查资料说是因为hadoop ...
- 21 shell Here Document/String
Here Document 的用法 Here Document可以将它理解为"嵌入文档""内嵌文档""立即文档",它将命令需要处理的数据或者 ...
- HGAME2020 reverse maze
1.查壳,发现是64位的linux文件,由于虚拟机没装linux,只能静态调了 2.拖入ida分析,找到主函数 2.1.思路分析 1.先将对应的ASCII的转换成字符,因为迷宫题一般都是用wsad,或 ...
- C语言:数组长度的检测方法
//数组长度的检测方法 #include <stdio.h> int main() { int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5 ...
- 学习总结 NCRE二级和三级
NCRE二级C语言 证书 考试感想 2016年考的认证,5年过去了,"光阴荏苒真容易".趁着心有余力有余的时候,把一些个人的体会分享给大家,希望后来人能平稳前行. Windows ...
- 新建一个DJango项目
好长时间没有用过DJango了,都忘记了怎么使用,哈哈哈,看到一篇讲的很好的博客,直接附链接了. https://blog.csdn.net/woo_home/article/details/9645 ...
- C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍
因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚.因此想看一下驱动开发.查了很多资料,看到有人推荐Windows驱动 ...
- Django关闭html转义
我们在views定义的html语句传递到html文件会按照原样式输出,并把我们定义的html标签页输出了,这是因为django模板默认帮我们开起了html转义功能 {{ lp}} <hr> ...