ARM32开发--FreeRTOS-消息队列

知不足而奋进 望远山而前行

目录

目标

内容

队列

消息队列

功能介绍

创建队列

入队

出队

基本类型数据

复杂类型数据

信号量和消息队列


目标

  1. 理解队列的概念
  2. 掌握消息队列开发流程
  3. 掌握基本数据类型消息队列
  4. 掌握复杂数据类型消息队列

内容

队列

队列(Queue)是一种数据结构,用于存储和管理元素的线性集合。它遵循先进先出(FIFO,First-In-First-Out)的原则,即最先进入队列的元素将首先被移出队列。

队列通常具有两个基本操作:

  1. 入队(Enqueue):将元素添加到队列的末尾。新元素进入队列后成为新的队尾。
  2. 出队(Dequeue):从队列的头部移除并返回元素。被移除的元素为队列中存在时间最长的元素,即最先入队的元素。

队列的特性使其非常适合在任务间进行数据传递和通信。任务可以将数据或消息按顺序放入队列,并按照先入先出的原则进行处理。这种方式可以有效地实现任务间的解耦和异步通信。

队列可以具有固定大小或动态增长的能力,取决于具体的实现和需求。固定大小的队列在创建时需要指定最大容量,而动态队列可以根据需要进行扩展。

消息队列

顾名思义,消息队列就是存储消息的队列。遵循队列的规则。

在 FreeRTOS 中,消息队列(Message Queue)是一种用于任务间通信的机制,允许任务之间传递和共享数据。消息队列提供了一种异步的通信方式,发送任务可以将消息放入队列,接收任务则可以从队列中获取消息。

以下是 FreeRTOS 消息队列的一些关键概念:

  1. 队列大小(Queue Size):消息队列具有固定的容量,即可以容纳的消息数量。在创建队列时,需要指定队列的大小。
  2. 消息类型(Message Type):消息队列可以传递不同类型的消息,消息类型可以是任意数据结构,如整数、结构体、指针等。
  3. 发送任务(Sending Task):发送任务负责将消息发送到队列中。发送任务通过调用 xQueueSend() 或 xQueueSendFromISR() 函数将消息发送到队列。
  4. 接收任务(Receiving Task):接收任务负责从队列中获取消息。接收任务通过调用 xQueueReceive() 或 xQueueReceiveFromISR() 函数从队列中获取消息。
  5. 阻塞与非阻塞操作:发送任务和接收任务可以选择在操作队列时是阻塞还是非阻塞的。阻塞操作会使任务在队列操作无法立即执行时进入阻塞状态,而非阻塞操作则会立即返回,无论队列操作是否成功。

使用消息队列可以实现任务之间的数据传递和同步。发送任务可以将数据封装成消息,并将其发送到队列中,接收任务则可以从队列中获取消息并进行处理。消息队列可以用于解耦任务之间的通信,提高系统的灵活性和可维护性。

功能介绍

基础API如下

功能

描述

xQueueCreate

创建队列

xQueueSend

向队列中添加元素

xQueueReceive

从队列中取出元素

创建队列
QueueHandle_t xQueueCreate(const UBaseType_t uxQueueLength,
                        	const UBaseType_t uxItemSize);

参数说明:

  1. const UBaseType_t uxQueueLength消息队列的长度
  2. const UBaseType_t uxItemSize队列中单个元素的所占字节数。

返回值:

QueueHandle_t为消息队列的句柄。

入队
BaseType_t xQueueSend( QueueHandle_t xQueue,
                    	const void * const pvItemToQueue,
                        TickType_t xTicksToWait);

参数说明:

  1. QueueHandle_t xQueue:队列句柄。
  2. const void * const pvItemToQueue:数据。
  3. TickType_t xTicksToWait:入队列等待时间。

返回值:

BaseType_t:队列是否成功或者失败,pdTrue或者pdFalse

出队
BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void * const pvBuffer,
                          TickType_t xTicksToWait);

参数说明:

  1. QueueHandle_t xQueue:队列句柄。
  2. void * const pvBuffer:数据。
  3. TickType_t xTicksToWait:出队列等待时间。

返回值:

BaseType_t:出队列是否成功或者失败,pdTrue或者pdFalse

基本类型数据

通常基本数据类型用uint8_tuint16_tuint32_t等进行表示。

此处以uint8_t为例。

构建时定义,定义数据的长度。

QueueHandle_t queue = xQueueCreate(128, sizeof(uint8_t));

入队列操作

uint8_t cnt = 0;
xQueueSend(queue, &cnt, portMAX_DELAY);
cnt++;

出队列操作

uint8_t data;
xQueueReceive(queue, &data, portMAX_DELAY);
printf("queue recv: %d\r\n", data);

完整示例逻辑:

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "Usart0.h"

TaskHandle_t            task_handler;
TaskHandle_t            task_key_handler;
TaskHandle_t            task1_handler;
QueueHandle_t 					queue;

void task1(void *pvParameters) {
    BaseType_t result;
    uint8_t value;
    while(1) {
        xQueueReceive(queue, &value, portMAX_DELAY);
        printf("queue recv: %d\r\n", value);
    }
}

void task_key(void *pvParameters) {
    FlagStatus pre_state = RESET;
    uint8_t cnt = 0;
    while(1) {
        FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
        if(SET == state && pre_state == RESET) {
            // 当前高电平, 上一次为低电平,按下
            pre_state = state;

            xQueueSend(queue, &cnt, portMAX_DELAY);
            cnt++;
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;


        }
        vTaskDelay(20);
    }
}

void Usart0_recv(uint8_t *data, uint32_t len)
{
    printf("recv: %s\n", data);
}

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

void start_task(void *pvParameters) {
    GPIO_config();
    Usart0_init();

    taskENTER_CRITICAL();

    xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
    xTaskCreate(task1, "task1", 64, NULL, 2, &task1_handler);
    vTaskDelete(task_handler);

    taskEXIT_CRITICAL();
}

int main(void)
{
    
    nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
    
    queue = xQueueCreate(128, sizeof(uint8_t));
    xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_handler);
    vTaskStartScheduler();

    while(1) {}
}

复杂类型数据

通常复杂类型用struct表示。

例如可以定义一个结构体:

typedef struct {
	char buffer[64];
	uint32_t len;
} MyData_t;

构建时定义,定义数据的长度。

QueueHandle_t queue = xQueueCreate(128, sizeof(MyData_t));

入队列操作

MyData_t data;

char strbuffer[100];
sprintf(strbuffer, "struct_%d", queue_num);
data.len = strlen(strbuffer);
strcpy(data.buffer, strbuffer);
//data.buffer[data.len] = '\0';
xQueueSend(queue, &data, portMAX_DELAY);

出队列操作

MyData_t data;
xQueueReceive(queue, &data, portMAX_DELAY);
printf("queue recv: %d %s\r\n", data.len, data.buffer);

完整示例。

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "Usart0.h"
#include <string.h>

TaskHandle_t            task_handler;
TaskHandle_t            task_key_handler;
TaskHandle_t            task1_handler;
QueueHandle_t 					queue;

typedef struct {
    char buffer[64];
    uint32_t len;
} MyData_t;

void task1(void *pvParameters) {
    BaseType_t result;
    MyData_t data;

    while(1) {
        xQueueReceive(queue, &data, portMAX_DELAY);
        printf("queue recv: %d %s\r\n", data.len, data.buffer);
    }
}

void task_key(void *pvParameters) {
    FlagStatus pre_state = RESET;
    MyData_t data;
    uint32_t cnt = 0;
    while(1) {
        FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
        if(SET == state && pre_state == RESET) {
            // 当前高电平, 上一次为低电平,按下
            pre_state = state;

			char strbuffer[100];
			sprintf(strbuffer, "struct_%d", queue_num);
			data.len = strlen(strbuffer);
			strcpy(data.buffer, strbuffer);
            xQueueSend(queue, &data, portMAX_DELAY);
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;


        }
        vTaskDelay(20);
    }
}


void USART0_recv(uint8_t *data, uint32_t len)
{
    printf("recv: %s\n", data);

    MyData_t d;
    d.len = len;
    sprintf(d.buffer, "%s", data);
    d.buffer[d.len] = '\0';
    //xQueueSend(queue, &d, portMAX_DELAY);

    xQueueSendFromISR(queue, &d, NULL);
}

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

void start_task(void *pvParameters) {
    
    GPIO_config();
    USART0_init();

    queue = xQueueCreate(128, sizeof(MyData_t));
    
    taskENTER_CRITICAL();
    xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
    xTaskCreate(task1, "task1", 64, NULL, 2, &task1_handler);
    vTaskDelete(task_handler);

    taskEXIT_CRITICAL();
}
    
int main(void)
{
    nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
    xTaskCreate(start_task, "start_task", 128, NULL, 1, &task_handler);
    vTaskStartScheduler();

    while(1) {}
}

信号量和消息队列

  1. 从数据结构上进行比较。

消息队列是一种数据结构,用于在任务之间传递和共享数据或消息。它可以存储多个消息,并且可以按照先进先出(FIFO)的顺序进行访问。

信号量是一种同步机制,用于控制对共享资源的访问和任务之间的同步。它可以表示可用资源的数量或资源的占用状态。

  1. 从操作方式上进行比较。

消息队列可以存储和获取多个消息。

信号量只有获取和释放两种操作。

  1. 从特性上进行比较。

消息队列可以按照先进先出的顺序处理消息。

信号量只表示资源的可用状态或占用状态。

信号量

队列

初始化

仅有计数值,信号量结构体

可存储队列项目,队列结构体

Input

Give信号量:计数值++,不阻塞

计数值已经最大时,返回Fail

Send队列项,队列满时,可阻塞

Output

Take信号量:计数值--

计数值为0时,可阻塞

Receive队列,队列空时,可阻塞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛慕昭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值