当前位置: 首页 > news >正文

【BES2500x系列 -- RTX5操作系统】Battery模块 -- 邮箱线程诞生的第一视角 -- osThreadDef --(十三)

请添加图片描述

  • 💌 所属专栏:【BES2500x系列】

  • 😀 作  者:我是夜阑的狗🐶

  • 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询!

  • 💖 欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!🤩 🤩 🤩

请添加图片描述

文章目录

  • 前言
  • 1 系统初始化
      • 1.1 邮箱线程的诞生
      • 1.2 app_os_init()
  • 2 Battery模块
      • 2.1 app_mailbox_init()
      • 2.2 osMailQDef()
      • 2.3 osMailQ()
      • 2.4 osThreadDef
      • 2.5 app_thread()
      • 2.6 osThread
  • 总结


<<【系列文章索引】>>

前言

  大家好,又见面了,我是夜阑的狗🐶,本文是专栏【BES2500x系列】专栏的第13篇文章;
  今天开始学习BES2500x系列的一天💖💖💖,开启新的征程,记录最美好的时刻🎉,每天进步一点点。
  专栏地址:【BES2500x系列】, 此专栏是我是夜阑的狗对BES2500x系列开发过程的总结,希望能够加深自己的印象,以及帮助到其他的小伙伴😉😉。对了,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。
  如果文章有什么需要改进的地方还请大佬不吝赐教👏👏。


1 系统初始化

  前面学习了嵌入式系统启动的基本流程,可以分为 引导程序 和 系统初始化程序 这两部分,并了解系统初始化中各个函数作用,但是看到这里我们估计还是一头雾水,此时就有大佬说“不妨从 Battery 模块入手了解该 SDK 框架”。那接下来就学习 Battery 模块具体是怎么跑的吧。话不多说,让我们原文再续,书接上回吧。

请添加图片描述

1.1 邮箱线程的诞生

  从上一篇文章可以了解到系统的 main 函数是如何跑到的,并且在讲解 main 函数中各个函数的作用,还知道了 main 中的 app_init 应用初始化涉及到了蓝牙、存储、系统状态、电源管理以及音频等方面的初始化和配置。知道这些初始化流程之后,接下来就深入了解一下 Battery 模块是怎么运转的吧,可以在 app.cpp 文件中看到该 app_init 函数。

在这里插入图片描述

  这里由于 app_init 函数代码过多,这里不就放出来了,只讲解一下其中的各个函数作用。app_init 函数中 个别函数 讲解

1.2 app_os_init()

  这段代码是一个应用程序操作系统初始化函数的实现,我们可以看到了线程创建,这是 CMSIS 的风格,相信开发过 STM32 的大佬应该比较熟悉,用来定义线程、定时器和邮箱通讯等。

  • 代码
/*** 初始化操作系统环境* * 该函数负责初始化应用程序的操作系统环境它首先尝试初始化邮箱,然后创建一个应用线程* 这是应用程序启动的入口点,确保操作系统环境正确初始化,以便应用程序能够正常运行* * @return 返回-1表示初始化失败,0表示成功*/
int app_os_init(void)
{// 初始化邮箱系统,如果失败则返回-1if (app_mailbox_init())return -1;// 创建应用线程,如果创建失败则返回0app_thread_tid = osThreadCreate(osThread(app_thread), NULL);if (app_thread_tid == NULL)  {TRACE(0,"Failed to Create app_thread\n");return 0;}return 0;
}
  • 参数/函数讲解
序号参数/函数说明
1app_mailbox_init()初始化应用程序的邮箱,邮箱通常用于线程间的通信和消息传递。如果初始化失败,函数会返回非零值。
2osThreadCreate()创建了一个名为 app_thread 的线程,并将其标识符保存在 app_thread_tid 变量中。osThreadCreate 是操作系统提供的函数,用于创建一个新的线程。osThread 宏用于指定要创建的线程的名称。

2 Battery模块

  在系统初始化中,我们可以看到调用邮箱线程初始化函数,接下来看看里面都做了些什么吧

2.1 app_mailbox_init()

  • 代码
/*** 初始化应用程序邮箱* * 本函数用于创建一个邮箱对象,并初始化相关计数器。如果创建失败,则会打印错误信息并返回负值。* * @param 无* @return int 返回0表示成功,返回-1表示失败。*/
static int app_mailbox_init(void)
{// 创建邮箱对象,如果创建失败,则返回-1app_mailbox = osMailCreate(osMailQ(app_mailbox), NULL);if (app_mailbox == NULL)  {TRACE(0,"Failed to Create app_mailbox\n");return -1;}// 初始化邮箱使用计数器app_mailbox_cnt = 0;return 0;
}

  本函数用于创建一个邮箱对象,并初始化相关计数器。如果创建失败,则会打印错误信息并返回负

  • 参数/函数讲解
序号参数/函数说明
1osMailCreate创建邮箱队列
2osMailQ该宏通过指定的邮箱队列名称,生成一个指向该邮箱队列定义的指针。

2.2 osMailQDef()

  一般在文件开头会看到这样的定义:osMailQDef

  • 代码
/*** 定义一个邮箱队列。* * 该函数用于定义一个邮箱队列,邮箱队列名为app_mailbox,最大容量为APP_MAILBOX_MAX,消息块类型为APP_MESSAGE_BLOCK。* 这是一个静态邮箱队列定义,意味着它在程序运行期间一直存在。* * @param app_mailbox 邮箱队列的名称。* @param APP_MAILBOX_MAX 邮箱队列的最大容量。* @param APP_MESSAGE_BLOCK 消息块的类型。*/
osMailQDef (app_mailbox, APP_MAILBOX_MAX, APP_MESSAGE_BLOCK);// 静态邮箱队列的ID初始化为NULL。
static osMailQId app_mailbox = NULL;

  该函数用于定义一个邮箱队列,邮箱队列名为 app_mailbox,最大容量为APP_MAILBOX_MAX,消息块类型为 APP_MESSAGE_BLOCK。这是一个静态邮箱队列定义,意味着它在程序运行期间一直存在。这个邮箱队列可以用于多线程或任务之间的数据通信,确保数据安全地传递。app_mailbox 是一个全局变量,用于存储邮箱队列的标识符,方便后续操作。

/*** 定义一个邮箱队列。* * 该宏用于静态定义一个邮箱队列以及相关配置,以便在系统运行时使用。邮箱队列可以用来在任务之间传递消息。* * @param name 邮箱队列的名称。* @param queue_sz 邮箱队列中邮件的最大数量。* @param type 邮件中数据的类型。* * @note 此宏定义了一个静态数组来存储邮件,以及一个osMailQDef_t类型的结构体来描述邮箱队列。*/
#define osMailQDef(name, queue_sz, type) \
static uint32_t os_mailQ_m_##name[3+((sizeof(type)+3)/4)*(queue_sz)]; \
osMailQDef_t os_mailQ_def_##name = \
{ {(queue_sz), sizeof(type), (os_mailQ_m_##name)}, NULL, {NULL} }
  • 参数/函数讲解

  该C语言宏 osMailQDef 用于在 FreeRTOS 操作系统中创建和初始化一个邮箱(Mail Queue)结构体。具体功能如下:
  1、定义内存缓冲区:首先,它创建一个名为 os_mailQ_m_name 的静态 uint32_t 类型的数组。数组的大小基于参数 queue_sz (队列容量)和 type (邮件的数据类型)计算得出。计算方式是 (sizeof(type) + 3) / 4,确保数组大小能完整容纳任何type类型的对象,并且对4字节对齐。
  2、初始化MailQ结构体:然后,它定义了一个 osMailQDef_t 类型的变量os_mailQ_def_name。该结构体包含以下字段:

序号参数/函数说明
1mailQ队列的大小(元素个数),由 queue_sz 提供。
2mailSize单个邮件的大小,由 sizeof(type) 提供,用于确保正确分配和传递邮件数据。
3pool指向上面定义的内存缓冲区的指针,即os_mailQ_m_name
4list和mutex这两个字段通常与 FreeRTOS 的内部管理有关,用于同步和保护邮箱操作,这里它们被初始化为NULL

  通过这个宏,用户可以在编译时创建邮箱队列,为实时操作系统中的任务间安全通信提供支持。

2.3 osMailQ()

  • 代码
/*** 宏定义用于获取指定名称的邮箱队列的地址。* * 该宏通过指定的邮箱队列名称,生成一个指向该邮箱队列定义的指针。* * @param name 邮箱队列的名称。此参数将被直接用于构建邮箱队列指针的名称。* @return 返回一个指向指定名称的邮箱队列定义的指针。*/
#define osMailQ(name) \
&os_mailQ_def_##name
  • 参数/函数讲解
序号参数/函数说明
1osMailQ该宏通过指定的邮箱队列名称,生成一个指向该邮箱队列定义的指针。

2.4 osThreadDef

  接下来就让我们来深入看下 osThreadCreate(osThread(app_thread), NULL) 是怎么跑的吧。一般在文件开头会看到这样的定义:osThreadDef

  • 代码
static void app_thread(void const *argument);
osThreadDef(app_thread, osPriorityHigh, 1, APP_THREAD_STACK_SIZE, "app_thread");

  定义了一个名为 app_thread 的线程,具有高优先级 osPriorityHigh,只有一个实例,栈大小为 APP_THREAD_STACK_SIZE,线程的任务名称为 “app_thread”。

/** 定义一个操作系统的线程定义。* * 参数:* name: 线程名称的前缀。* priority: 线程的优先级。* instances: 线程实例的数量。如果为1,则创建一个静态实例;否则,创建动态实例。* stacksz: 线程栈的大小(字节)。如果为0,则分配最小栈大小。* task_name: FreeRTOS任务的名称。* * 返回值:无。* * 说明:此宏用于预先定义线程的栈、控制块和FreeRTOS线程定义结构体。*/
#define osThreadDef(name, priority, instances, stacksz, task_name) \
static uint64_t os_thread_stack##name[(stacksz)?(((stacksz+7)/8)):1]; \
static StaticTask_t os_thread_cb_##name; \
const osThreadDef_t os_thread_def_##name = \
{ (name), \{ task_name, osThreadDetached, \(instances == 1) ? (&os_thread_cb_##name) : NULL,\(instances == 1) ? sizeof(StaticTask_t) : 0U, \((stacksz) && (instances == 1)) ? (&os_thread_stack##name) : NULL, \8*((stacksz+7)/8), \(priority), 0U, 0U } }
  • 参数/函数讲解

  这是一个宏定义,用于定义线程的属性。它包括了以下几个部分:

序号参数/函数说明
1os_thread_stack##name[(stacksz)?(((stacksz+7)/8)):1]这行代码定义了一个名为 os_thread_stack<name> 的静态数组,用于存储线程的堆栈。堆栈的大小由 stacksz 决定,通过 (stacksz+7)/8 计算出需要的 uint64_t 类型的元素个数,并且使用三元运算符 (stacksz)?(((stacksz+7)/8)):1 确保至少有一个元素。
2static StaticTask_t os_thread_cb_##name这行代码定义了一个名为 os_thread_cb_<name> 的静态变量,用于存储线程控制块。
3const osThreadDef_t os_thread_def_##name这行代码定义了一个名为 os_thread_def_<name> 的常量,类型为 osThreadDef_t,其中包含了线程的名称、任务名称、控制块、堆栈等属性。

  宏定义中的最后一部分是一个结构体的初始化,具体属性如下:

序号参数/函数说明
1(name)线程名称。
2{ task_name, osThreadDetached, … }任务的属性,包括任务名称和任务的状态(这里是 osThreadDetached,表示线程是一个分离线程)。
3(instances == 1) ? (&os_thread_cb_##name) : NULL如果线程只有一个实例,将指向线程控制块的指针赋给 cb_mem,否则为 NULL
4(instances == 1) ? sizeof(StaticTask_t) : 0U如果线程只有一个实例,将线程控制块的大小赋给 cb_size,否则为 0U。
5((stacksz) && (instances == 1)) ? (&os_thread_stack##name) : NULL如果线程有堆栈且只有一个实例,将指向堆栈的指针赋给 stack_mem,否则为 NULL
68*((stacksz+7)/8)堆栈的大小,保证是 uint64_t 类型的整数倍。
7(priority)线程的优先级

  总的来说,这段代码定义了一个线程及其相关的属性,包括线程名称、任务名称、线程控制块、堆栈等。

/// Thread Definition structure contains startup information of a thread.
#if (osCMSIS < 0x20000U)
typedef struct os_thread_def {os_pthread                 pthread;   ///< start address of thread functionosPriority               tpriority;   ///< initial thread priorityuint32_t                 instances;   ///< maximum number of instances of that thread functionuint32_t                 stacksize;   ///< stack size requirements in bytes; 0 is default stack size
} osThreadDef_t;
#else
typedef struct os_thread_def {os_pthread                 pthread;   ///< start address of thread functionosThreadAttr_t                attr;   ///< thread attributes
} osThreadDef_t;
#endif

  从前面线程赋值来看,这里用的第二个结构体, osThreadDef宏就是给该结构体变量初始化赋值。

2.5 app_thread()

  • 代码
static void app_thread(void const *argument)
{while(1){APP_MESSAGE_BLOCK *msg_p = NULL; // 声明了一个指向 APP_MESSAGE_BLOCK 类型的指针 msg_p,并将其初始化为 NULL。if (!app_mailbox_get(&msg_p)) { // 从邮箱中获取消息,并将消息的指针存储在 msg_p 中if (msg_p->mod_id < APP_MODUAL_NUM) {if (mod_handler[msg_p->mod_id]) { //检查模块处理函数是否存在。如果模块处理函数存在,则进入条件语句中执行后续操作。int ret = mod_handler[msg_p->mod_id](&(msg_p->msg_body)); // 调用了模块处理函数,并将消息体作为参数传递给它。if (ret)TRACE(2,"mod_handler[%d] ret=%d", msg_p->mod_id, ret);}}app_mailbox_free(msg_p); // 释放了消息所占用的内存,防止内存泄漏。}}
}
  • 参数/函数讲解
序号参数/函数说明
1app_mailbox_get()从邮箱中获取消息,并将消息的指针存储在 msg_p 中
2app_mailbox_free()释放了消息所占用的内存,防止内存泄漏
3mod_handler[]各模块钩子注册的数组

  总的来说,这段代码表示一个线程不断地从邮箱中获取消息,并根据消息中的模块 ID 找到对应的模块处理函数进行处理,然后释放消息所占用的内存。

2.6 osThread

  • 代码
/// Access a Thread definition.
/// \param         name          name of the thread definition object.
#define osThread(name) \
&os_thread_def_##name
  • 参数/函数讲解

  这个宏定义简单地返回了指向特定线程定义结构体的指针。具体来说,它会返回一个指针,该指针指向了以 os_thread_def_<name> 命名的线程定义结构体。通过上面一系列的说明可以知道,该 os_thread_def_<name> 结构体已经被初始化好了,所以是可以获取的。这个宏可以在代码中使用,以便获取特定线程的定义结构体指针,然后可以在操作系统中使用这个指针来管理和操作该线程。

<<【系列文章索引】>>

请添加图片描述


总结

  前面大费周章讲解了一通无非就是对线程函数赋值给结构体,然后用宏获取该结构来为后续创建线程做准备。到这里应该都很清晰起来了吧,来理一下具体步骤:

  • Step 1osThreadDef 是设置线程名、优先级以及堆栈大小;
  • Step 2、在通过 osThread 获取配置的结构体变量的指针;
  • Step 3、最后作为参数转入 osThreadCreate() 创建线程;

  感谢观看,这里就是 Battery模块 – 邮箱线程 的讲解,如果觉得有帮助,请给文章点个赞吧,让更多的人看到。🌹 🌹 🌹

在这里插入图片描述

  也欢迎你,关注我。👍 👍 👍

  原创不易,还希望各位大佬支持一下,你们的点赞、收藏和留言对我真的很重要!!!💕 💕 💕 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!下期再见。🎉

更多专栏订阅:

  • 😀 【LeetCode题解(持续更新中)】

  • 🥇 【恒玄BES】

  • 🌼 【鸿蒙系统】

  • 💎 【蓝牙协议栈】

  • 🎃 【死机分析】

  • 👑 【Python脚本笔记】

  • 🚝 【Java Web项目构建过程】

  • 💛 【微信小程序开发教程】

  • 【JavaScript随手笔记】

  • 🤩 【大数据学习笔记(华为云)】

  • 🦄 【程序错误解决方法(建议收藏)】

  • 🔐 【Git 学习笔记】

  • 🚀 【软件安装教程】



订阅更多,你们将会看到更多的优质内容!!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 阿里巴巴发布Q1财报:电商份额趋稳,市场不再悲观
  • IPC 摄像头通过什么技术实现远程查看
  • 光储电站联合配置!多种调度模式下的光储电站经济性最优储能容量配置分析程序代码!
  • C++ | Leetcode C++题解之第372题超级次方
  • 支付宝小程序websocket长连接(心跳版本)
  • 文件包含漏洞(1)
  • IOS开发 铃声制作(库乐队)
  • Java笔试面试题AI答之线程(21)
  • Android - 自定义view
  • OpenCV几何图像变换(5)旋转和缩放计算函数getRotationMatrix2D()的使用
  • Linux安装Miniconda3
  • 数学理论在编程中的核心应用与实践(上)
  • EmguCV学习笔记 VB.Net 6.3 轮廓外接多边形
  • C#(asp.net)乡镇中学宿舍管理系统---附源码 97861
  • OSPF路由原理详解与关键点
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【知识碎片】第三方登录弹窗效果
  • avalon2.2的VM生成过程
  • gulp 教程
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • java中具有继承关系的类及其对象初始化顺序
  • JS基础之数据类型、对象、原型、原型链、继承
  • laravel5.5 视图共享数据
  • Map集合、散列表、红黑树介绍
  • MySQL-事务管理(基础)
  • Mysql数据库的条件查询语句
  • Netty+SpringBoot+FastDFS+Html5实现聊天App(六)
  • opencv python Meanshift 和 Camshift
  • Python - 闭包Closure
  • quasar-framework cnodejs社区
  • text-decoration与color属性
  • uni-app项目数字滚动
  • vagrant 添加本地 box 安装 laravel homestead
  • 两列自适应布局方案整理
  • 聊聊redis的数据结构的应用
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 码农张的Bug人生 - 初来乍到
  • 前端技术周刊 2019-01-14:客户端存储
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 消息队列系列二(IOT中消息队列的应用)
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • 【干货分享】dos命令大全
  • gunicorn工作原理
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​【经验分享】微机原理、指令判断、判断指令是否正确判断指令是否正确​
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (11)MSP430F5529 定时器B
  • (13)DroneCAN 适配器节点(一)
  • (2)STM32单片机上位机
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (SERIES12)DM性能优化
  • (搬运以学习)flask 上下文的实现