FreeRTOS【7】队列使用

1.开发背景

       操作系统提供了多线程并行的操作,为了方便代码的维护,各个线程都分配了专用的内存并处理对应的内容。但是线程间也是需要协助操作的,例如一个主线程接收信息,会把接收的信息并发到其他线程,即主线程不阻塞,子线程做实际的处理。

        如果是在裸机编程的方式,常用的操作方式是主线程设置 Flag,子线程循环查询 Flag 是否达到预期再执行对应的操作。问题就来了,为了提高子线程的响应实时性,需要频繁判断,导致CPU占用率极高,显然这样的设计是不合理的,所以队列的存在就变得合理了。

        FreeRTOS 的队列传递信息采用的是内存拷贝,队列在接收信息的时候会阻塞线程,释放CPU,降低 CPU 的使用率,相当于线程间信息的交互交给内核去统筹。

        下面是队列的常用接口,不包含中断操作,需要注意的是 xQueueOverwrite只在单成员队列中使用。

        鉴于有些人是不知道队列的特性的,队列就是先进先出,当然 FreeRTOS 提供了队头插队的操作,总体如下图:

2.开发需求

        设计实验实现线程到线程,中断到线程的信息传递。

3.开发环境

        window10 + MDK + STM32F429 + FreeRTOS10.3.1

4.实现步骤

1)创建接收线程,持续等待队列信息

2)创建发送线程,发送队列信息

3)重写按键中断,中断中发送队列信息

        核心代码如下:

#include "appTest.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "mspDwt.h"
#include "mspGpio.h"
#include "mspExti.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#include "appLog.h"

typedef struct
{
    /* 队列相关 */
    QueueHandle_t queue;            // 队列句柄
    
    /* 创建任务 */
    TaskHandle_t taskCtrl;          // 控制线程
    TaskHandle_t taskQueueRx;       // 接收线程
    TaskHandle_t taskQueueTx;       // 发送线程
    
}Ctrl_t;

/* 文件指针 */
static Ctrl_t s_ctrl = {0};
static Ctrl_t *p = &s_ctrl;
static void TaskCtrl(void *pvParameters);
static void TaskQueueRx(void *pvParameters);
static void TaskQueueTx(void *pvParameters);

/* 控制线程 */
static void TaskCtrl(void *pvParameters)
{
    vTaskDelay(1000);
    for ( ; ; )
    {
        vTaskDelay(1000);
    }
}

/* 队列接收线程 */
static void TaskQueueRx(void *pvParameters)
{
    unsigned int item = 0;
    vTaskDelay(100);
    
    for ( ; ; )
    {
        xQueueReceive(p->queue, &item, portMAX_DELAY);
        Log_Debug("%s 线程接收 Rx = 0x%.8X\r\n", __func__, item);
    }
}

/* 队列发送线程 */
static void TaskQueueTx(void *pvParameters)
{
    vTaskDelay(1000);
    
    unsigned int threadData = 0x12345678;
    xQueueSend(p->queue, &threadData, 1000);
    Log_Debug("%s 线程发送 Tx = 0x%.8X\r\n", __func__, threadData);
    
    for ( ; ; )
    {
        vTaskDelay(1000);
    }
}


/* 测试初始化 */
void aTest_Init(void)
{
    /* 创建队列 */
    UBaseType_t uxQueueLength = 10;
    UBaseType_t uxItemSize = sizeof(unsigned int);
    p->queue = xQueueCreate(uxQueueLength, uxItemSize);
    
    /* 创建动态任务 */
    xTaskCreate(TaskCtrl,       "TaskCtrl",     500, NULL, 5, &p->taskCtrl);
    xTaskCreate(TaskQueueRx,    "TaskQueueRx",  500, NULL, 5, &p->taskQueueRx);
    xTaskCreate(TaskQueueTx,    "TaskQueueTx",  500, NULL, 5, &p->taskQueueTx);
}

/* Key2 PC13   Key0 PH3 Key1 PH2 */
void Exti13_TriggerInterrupt(void)
{
    mspExti_Close(13);
    
    if (mspGpio_GetInput("PC13") == 0)
    {
        /* 队列发送数据 */
        unsigned int irqData = 0xABCDEF;
        BaseType_t xHigherPriorityTaskWoken;
        xQueueSendFromISR(p->queue, &irqData, &xHigherPriorityTaskWoken);
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        Log_Debug("%s 中断发送 Tx = 0x%.8X\r\n", __func__, irqData);
    }
}

4)查看运行结果

        线程和中断发送的数据都被接收无误,而且通过时间戳可以看出,线程发送完 1ms 内数据就被接收线程接收并处理,实时性还是很不错的。

FreeRTOS提供了一个非常方便的消息队列机制,可以让不同的任务之间进行通信和数据传输。以下是使用FreeRTOS消息队列的步骤: 1. 创建一个消息队列 使用xQueueCreate()函数可以创建一个消息队列,需要指定消息队列的长度和每个消息的大小。例如: ```c xQueueHandle queue = xQueueCreate(10, sizeof(int)); ``` 上述代码创建了一个长度为10的消息队列,每个消息的大小为int类型。 2. 发送消息到队列 使用xQueueSend()函数可以将消息发送到队列中,需要指定消息队列的句柄、要发送的消息的指针以及阻塞时间。例如: ```c int message = 42; xQueueSend(queue, &message, portMAX_DELAY); ``` 上述代码将整数42发送到消息队列中,并且在消息队列满时会一直等待直到有空间可以发送。 3. 从队列中接收消息 使用xQueueReceive()函数可以从队列中接收消息,需要指定消息队列的句柄、用于接收消息的指针以及阻塞时间。例如: ```c int received_message; xQueueReceive(queue, &received_message, portMAX_DELAY); ``` 上述代码从消息队列中接收一个消息,并将接收到的消息存储在整数变量received_message中。 4. 删除消息队列 使用vQueueDelete()函数可以删除一个消息队列。例如: ```c vQueueDelete(queue); ``` 上述代码删除了名为queue的消息队列。 需要注意的是,在使用消息队列时需要注意消息的类型和大小,以及消息队列的长度和阻塞时间等参数设置。此外,还需要注意在发送和接收消息时对消息队列的访问进行互斥保护,避免多个任务同时访问消息队列导致的竞争条件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值