news 2026/4/29 3:58:21

小智打印机第三节:消息队列

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小智打印机第三节:消息队列

我看小智的文档里面使用了操作系统Freertos,但他这个地方没有使用消息队列来接受手机端的数据,他是通过自己写了一个数组缓冲区来接收的,这两个一样的,使用消息队列可能会方便一点。

第一种:环形缓冲区

数据结构:采用了两个结构体套接的形式,第一个结构体用于存放接受的数据,是一个字节数最大为MAX_ONELINE_BYTE的数组,别名是ble_rx_buffer_t,第二个结构体是用于多少个数据,写指针,读指针,还有剩余指针一个四个,别名是ble_rx_t。

// 1. 单个数据包的缓冲结构 // 用于存放一行具体的数据内容 typedef struct{ uint8_t buffer[MAX_ONELINE_BYTE]; }ble_rx_buffer_t; typedef struct{ // 数据池:静态分配的环形数组,存放所有接收到的数据包 ble_rx_buffer_t printer_buffer[MAX_LINE]; uint32_t r_index; uint32_t w_index; uint32_t left_line; }ble_rx_t; ble_rx_t g_ble_rx; xHandler=NULL;//申请的互斥量的句柄

成员变量

类型

作用

形象比喻

printer_buffer

数组

仓库货架。真正存放数据的地方,大小为

MAX_LINE

行。

一排固定的储物柜。

r_index

索引

取货口。告诉读取任务,下一次该从数组的哪个位置拿数据。

仓库管理员记录“下一个该取哪个柜子”。

w_index

索引

存货口。告诉写入中断,下一次该把数据放到数组的哪个位置。

仓库管理员记录“下一个空柜子在哪里”。

left_line

计数

空位统计。记录当前还剩多少个空位。

仓库门口的“剩余空位计数器”。

xHandler

句柄

钥匙(锁)。确保同一时间只有一个人(中断或任务)能操作仓库。

仓库大门的钥匙,防止两人同时进出撞到一起。

🔄 工作原理图解

这个结构体通过 r_index 和 w_index 的移动,将线性的数组变成了环形的逻辑结构:

写入时:数据存入 printer_buffer[w_index],然后 w_index 加 1。如果 w_index 超过 MAX_LINE,则归零(回到数组开头)。

读取时:数据从 printer_buffer[r_index] 取出,然后 r_index 加 1。如果 r_index 超过 MAX_LINE,则归零。

判满:通过 left_line 判断。如果 left_line 为 0,说明 w_index 追上了 r_index,队列满了。

逻辑:

  1. 数据结构设计(环形缓冲区)
    • 核心变量:g_ble_rx 结构体包含 printer_buffer(数据池)、r_index(读指针)、w_index(写指针)和 left_line(剩余空间计数)。
    • 原理:利用数组模拟队列。写指针 w_index 负责在尾部写入,读指针 r_index 负责在头部读取。当指针到达数组末尾时,会自动回绕到开头(if(w_index >= MAX_LINE) w_index = 0),形成闭环,无需移动内存数据,效率极高。
  1. 写入操作 (write_to_printbuffer - 中断上下文):
    • 中断保护:使用 xSemaphoreTakeFromISR 获取互斥锁。这是为了防止在写入过程中被读任务打断,或者被其他中断打断,保证数据完整性。
    • 边界检查:检查 left_line 防止缓冲区溢出(满了就不写了),检查 length 防止单次写入过长。
    • 数据拷贝:使用 memcpy 将数据存入 w_index 指向的位置。
    • 指针推进:写入完成后,w_index 加 1,left_line 减 1。
    • 上下文切换:如果锁操作导致高优先级任务被阻塞,通过 portYIELD_FROM_ISR 触发上下文切换,确保系统实时响应。
    • 就是在中断里面的申请的xHigherPriorityTaskWoken的这个,假如蓝牙在接受数据的时候来了更高的优先级任务,这个xHigherPriorityTaskWoken会变为pdTRUE,在中断结尾的时候,用if条件检查这个数据,可以跳转更高优先级任务

BaseType_t xHigherPriorityTaskWoken = pdFALSE;// portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

  1. 读取操作 (read_to_printer - 任务上下文):
    • 阻塞/超时机制:使用 xSemaphoreTake(..., 10)。任务会尝试获取锁,最多等待 10 个系统节拍。如果超时(说明总线忙),则返回 NULL,防止任务死锁。
    • 判空与读取:获取锁后,检查 left_line(这里逻辑稍微有点绕,实际是看是否有数据可读)。如果有数据,取出 r_index 指向的数据指针,然后 r_index 加 1。
    • 资源释放:读取完毕后,必须调用 xSemaphoreGive 释放锁,并更新 left_line(释放空间)。
  1. 并发控制(Mutex):
    • 核心作用:xHandler 互斥锁是保护神。它确保了 r_index 和 w_index 这两个共享资源在同一时间只能被一个执行流(中断或任务)修改,彻底杜绝了“读写竞争”导致的数据错乱。

portYIELD_FROM_ISR函数说明:

1. 在 FreeRTOS 中,中断的优先级高于任何任务。当一个中断发生时,CPU 会暂停当前任务,转而去执行中断服务程序(ISR)。

中断中的操作:ISR 通常会通过 xSemaphoreGiveFromISR 或 xQueueSendFromISR 等函数来通知某个任务“有事情发生了”。

2. 唤醒高优先级任务:这些 ...FromISR 函数有一个关键参数 pxHigherPriorityTaskWoken。如果一个优先级比当前被中断任务更高的任务正在等待这个信号,那么该高优先级任务就会被唤醒,并且 pxHigherPriorityTaskWoken 会被设置为 pdTRUE。

中断返回的抉择:ISR 执行完毕后,CPU 需要决定返回到哪里。

3. 不使用 portYIELD_FROM_ISR:CPU 会无条件返回到被中断的那个低优先级任务,继续执行它,直到下一个调度点(如系统节拍)才检查并切换到高优先级任务。这造成了响应延迟。

4. 使用 portYIELD_FROM_ISR(xHigherPriorityTaskWoken):这个宏会检查 xHigherPriorityTaskWoken 的值。如果为 pdTRUE,它会强制进行一次上下文切换,让 CPU 直接去执行那个刚刚被唤醒的高优先级任务,而不是返回原来的低优先级任务。

队列的话比较方便,就是创建一个消息队列来接受,Write和Read队列的函数文档都有

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 3:44:56

统信UOS服务器1060e安装保姆级教程:从镜像下载到LVM分区配置全流程

统信UOS服务器1060e安装全流程精解:从镜像选择到LVM实战配置 在国产操作系统生态快速发展的今天,统信UOS服务器版凭借其出色的安全特性和本土化适配能力,正成为越来越多企业基础设施的新选择。不同于桌面版的轻量体验,服务器操作…

作者头像 李华
网站建设 2026/4/29 3:44:20

具身智能行业应用-农业

随着全球农业面临劳动力短缺、资源有限和气候变化等多重挑战,农业机械化和智能化成为提升农业生产效率和可持续发展的关键路径。农机装备、农业机器人的具身智能技术,作为农业现代化的重要组成部分,正在迅速演进。在“感知-决策-控制&#xf…

作者头像 李华
网站建设 2026/4/29 3:40:22

ARM开发板硬件接口与寄存器配置实战指南

1. ARM开发板硬件接口详解Integrator/IM-PD1开发板作为经典的ARM评估平台,其接口布局体现了嵌入式系统的典型设计思路。板载的PrimeCell系列外设控制器采用AMBA总线架构,通过标准化的寄存器接口与ARM内核交互。我们先从物理连接层开始剖析:1.…

作者头像 李华
网站建设 2026/4/29 3:39:21

OpCore-Simplify:15分钟搞定黑苹果OpenCore配置的终极指南

OpCore-Simplify:15分钟搞定黑苹果OpenCore配置的终极指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为黑苹果复杂的OpenCore配置…

作者头像 李华