队列
队列(Queue)是一种数据结构,用于存储和管理元素的线性集合。它遵循先进先出(FIFO,First-In-First-Out)的原则,即最先进入队列的元素将首先被移出队列。
队列通常具有两个基本操作:
- 入队(Enqueue):将元素添加到队列的末尾。新元素进入队列后成为新的队尾。
- 出队(Dequeue):从队列的头部移除并返回元素。被移除的元素为队列中存在时间最长的元素,即最先入队的元素。
队列的特性使其非常适合在任务间进行数据传递和通信。任务可以将数据或消息按顺序放入队列,并按照先入先出的原则进行处理。这种方式可以有效地实现任务间的解耦和异步通信。
队列可以具有固定大小或动态增长的能力,取决于具体的实现和需求。固定大小的队列在创建时需要指定最大容量,而动态队列可以根据需要进行扩展。
消息队列
顾名思义,消息队列就是存储消息的队列。遵循队列的规则。
在 FreeRTOS 中,消息队列(Message Queue)是一种用于任务间通信的机制,允许任务之间传递和共享数据。消息队列提供了一种异步的通信方式,发送任务可以将消息放入队列,接收任务则可以从队列中获取消息。
以下是 FreeRTOS 消息队列的一些关键概念:
- 队列大小(Queue Size):消息队列具有固定的容量,即可以容纳的消息数量。在创建队列时,需要指定队列的大小。
- 消息类型(Message Type):消息队列可以传递不同类型的消息,消息类型可以是任意数据结构,如整数、结构体、指针等。
- 发送任务(Sending Task):发送任务负责将消息发送到队列中。发送任务通过调用 xQueueSend() 或 xQueueSendFromISR() 函数将消息发送到队列。
- 接收任务(Receiving Task):接收任务负责从队列中获取消息。接收任务通过调用 xQueueReceive() 或 xQueueReceiveFromISR() 函数从队列中获取消息。
- 阻塞与非阻塞操作:发送任务和接收任务可以选择在操作队列时是阻塞还是非阻塞的。阻塞操作会使任务在队列操作无法立即执行时进入阻塞状态,而非阻塞操作则会立即返回,无论队列操作是否成功。
使用消息队列可以实现任务之间的数据传递和同步。发送任务可以将数据封装成消息,并将其发送到队列中,接收任务则可以从队列中获取消息并进行处理。消息队列可以用于解耦任务之间的通信,提高系统的灵活性和可维护性。
功能介绍
基础API如下
功能 | 描述 |
xQueueCreate | 创建队列 |
xQueueSend | 向队列中添加元素 |
xQueueReceive | 从队列中取出元素 |
创建队列
xQueueCreate( uxQueueLength, uxItemSize )
向队列中放数据
参数一:队列名称
参数二:数据地址
参数三:等待时间
xQueueSend( xQueue, pvItemToQueue, xTicksToWait )
从队列中取数据
参数一:队列名称
参数二:给队列一个指针,把数据拷贝到这个指针里面
参数三:等待时间
void task1() {
uint8_t cnt;
while(1) {
//从队列里面取数据
printf("wait recv.. \r\n");
xQueueReceive(queue,&cnt,portMAX_DELAY);
printf("recv: cnt %x\r\n",cnt);
}
vTaskDelete(0);
}
全部代码
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "Usart0.h"
#include "semphr.h"
TaskHandle_t StartTask_Handler;
TaskHandle_t Task1_Handler;
TaskHandle_t Task2_Handler;
TaskHandle_t Task_key_Handler;
SemaphoreHandle_t semap_handler;
QueueHandle_t queue;
void Usart0_on_recv(uint8_t* data, uint32_t len) {
}
void task1() {
uint8_t cnt;
while(1) {
//从队列里面取数据
printf("wait recv.. \r\n");
xQueueReceive(queue,&cnt,portMAX_DELAY);
printf("recv: cnt %x\r\n",cnt);
}
vTaskDelete(0);
}
void task2() {
while(1) {
}
vTaskDelete(0);
}
void task_key(void *pvParameters) {
//PA0
FlagStatus pre = RESET;
uint8_t cnt = 0;
while(1) {
FlagStatus current = gpio_input_bit_get(GPIOA,GPIO_PIN_0);
if(RESET == current && pre == SET) {
//抬起
pre = current;
} else if(SET == current && pre == RESET) {
//按下
pre = current;
//向队列中放数据
printf("send : cnt %x\r\n",cnt);
xQueueSend(queue,&cnt,portMAX_DELAY);
cnt++;
}
vTaskDelay(20);
}
vTaskDelay(0);
}
void start_task(void *pvParameters) {
taskENTER_CRITICAL();
//创建队列
queue = xQueueCreate(10,sizeof(uint8_t));
xTaskCreate((TaskFunction_t)task1,
(const char* )"task1",
50,
NULL,
2,
(TaskHandle_t* )&Task1_Handler);
// xTaskCreate((TaskFunction_t)task2,
// (const char* )"task2",
// 50,
// NULL,
// 3,
// (TaskHandle_t* )&Task2_Handler);
xTaskCreate((TaskFunction_t)task_key,
(const char* )"task_key",
50,
NULL,
5,
(TaskHandle_t* )&Task_key_Handler);
vTaskDelete(StartTask_Handler);
taskEXIT_CRITICAL();
}
static void GPIO_config() {
//唤醒按键
// 时钟初始化
rcu_periph_clock_enable(RCU_GPIOA);
// 配置GPIO模式
gpio_mode_set(GPIOA, GPIO_MODE_INPUT,GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
systick_config();
GPIO_config();
Usart0_init();
printf("mian ======= ...start\r\n");
//1.创建任务
//参数一:哪个任务
//参数二:任务名字
//参数三:栈的深度(宽度).
//参数四:优先级
//参数五:句柄,任务的引用
xTaskCreate(start_task,"start_task",64,NULL,1,&StartTask_Handler);
//开启调度器
vTaskStartScheduler();
while(1) {
}
}
执行效果如下图