(学习日记)2024.04.01:UCOSIII第二十九节:消息队列实验(待续)

news2024/11/19 14:23:22

写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。


标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。


点击此处进入学习日记的总目录

2024.04.01:UCOSIII第二十八节:消息队列实验

  • 四十三、UCOSIII:消息队列实验
    • 1、消息队列使用注意事项
    • 2、消息队列实验
    • 3、消息队列实验现象
    • 4、常见错误
      • 1. Default Compiler Version 5
      • 2. core_cm3.h(1213): error: unknown type name 'inline'
      • 3. 程序正常运行无报错,烧录到板子上无反应

四十三、UCOSIII:消息队列实验

1、消息队列使用注意事项

在使用μC/OS提供的消息队列函数的时候,需要了解以下几点:

  1. 使用OSQPend()、OSQPost()等这些函数之前应先创建需消息队列, 并根据队列句柄(队列控制块)进行操作。

  2. 队列读取采用的是先进先出(FIFO)模式,会先读取先存储在队列中的数据。 当然也μC/OS也支持后进先出(LIFO)模式,那么读取的时候就会读取到后进队列的数据。

  3. 无论是发送或者是接收消息都是以 数据引用的方式进行。

  4. 队列是具有自己独立权限的内核对象,并不属于任何任务。所有任务都可以向同一队列写入和读出。 一个队列由多任务或中断写入是经常的事,但由多个任务读出倒是用的比较少。

  5. 消息的传递实际上只是传递传送内容的指针和传送内容的字节大小。这在使用消息队列的时候就要注意了, 获取消息之前不能释放存储在消息中的指针内容,比如中断定义了一个局部变量,然后将其地址放在消息中进行传递, 中断退出之前消息并没有被其他任务获取,退出中断的时候 CPU已经释放了中断中的这个局部变量,后面任务获取这个地址的内容就会出错。 所以一定要保证在获取内容地址之前不能释放内容这个内存单元。有三种方式可以避免这种情况:

  • 将变量定义为静态变量,即在其前面加上 static,这样内存单元就不会被释放。
  • 将变量定义为全局变量。
  • 将要传递的内容当做指针传递过去。比如地址 0x12345678存放一个变量的值为 5, 常规是把0x12345678这个地址传递给接收消息的任务, 任务接收到这个消息后,取出这个地址的内容 5。
    但是如果我们把 5 当做“地址”传递给任务, 最后接收消息的任务直接拿着这个“地址”当做内容去处理即可。不过这种方法不能传递结构体等比较复杂的数据结构, 因为消息中存放地址的变量内存大小是有限的(一个指针大小)。

2、消息队列实验

消息队列实验是在μC/OS中创建了两个任务AppTaskPost()和 AppTaskPend()。

  • 任务 AppTaskPost() 用于发送消息。
  • 任务 AppTaskPend()用于接收消息。

两个任务独立运行,并把接收到的消息通过串口调试助手打印出来。

#include <includes.h>

/**************************************************************************
                                    LOCAL DEFINES
**************************************************************************/
OS_Q queue;                             //声明消息队列

/**************************************************************************
                                        TCB
************************************************************************/

static  OS_TCB   AppTaskStartTCB;      //任务控制块
static  OS_TCB   AppTaskPostTCB;
static  OS_TCB   AppTaskPendTCB;

/*************************************************************************
                                         STACKS
************************************************************************/

static  CPU_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];       //任务栈
static  CPU_STK  AppTaskPostStk [ APP_TASK_POST_STK_SIZE ];
static  CPU_STK  AppTaskPendStk [ APP_TASK_PEND_STK_SIZE ];

/*************************************************************************
                                  FUNCTION PROTOTYPES
*************************************************************************/

static  void  AppTaskStart  (void *p_arg);               //任务函数声明
static  void  AppTaskPost   ( void * p_arg );
static  void  AppTaskPend   ( void * p_arg );

/*
***********************************************************************
*                                                main()
*
* Description : This is the standard entry point for C code.  It is assumed that
*    your code will call main() once you have performed all necessary
*   initialization.
* Arguments   : none
*
* Returns     : none
**************************************************************************/

int  main (void)
{
    OS_ERR  err;
    OSInit(&err);                                     //初始化 μC/OS-III

    /* 创建起始任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,       //任务控制块地址
                (CPU_CHAR   *)"App Task Start",             //任务名称
                (OS_TASK_PTR ) AppTaskStart,                  //任务函数
                (void       *) 0,
                //传递给任务函数(形参p_arg)的实参
                (OS_PRIO     ) APP_TASK_START_PRIO,         //任务的优先级
                (CPU_STK    *)&AppTaskStartStk[0],
                //任务栈的基地址
                (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,
                //任务栈空间剩下1/10时限制其增长
                (CPU_STK_SIZE) APP_TASK_START_STK_SIZE,
                //任务栈空间(单位:sizeof(CPU_STK))
                (OS_MSG_QTY  ) 5u,
                //任务可接收的最大消息数
                (OS_TICK     ) 0u,
                //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)
                (void       *) 0,
                //任务扩展(0表不扩展)
                (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                (OS_ERR     *)&err);  //返回错误类型

    OSStart(&err);
    //启动多任务管理(交由μC/OS-III控制)

}


/*************************************************************************
*                         STARTUP TASK
*
* Description : This is an example of a startup task.  As mentioned in
* the book's text, you MUST initialize the ticker only once mu
*           ltitasking has started.
* Arguments   : p_arg   is the argument passed to 'AppTaskStart()' by
*           'OSTaskCreate()'.
* Returns     : none
*
* Notes       : 1) The first line of code is used to prevent a compiler
        warning because 'p_arg' is not
*                  used.  The compiler should not generate any code for
this statement.
***********************************************************
*/

static  void  AppTaskStart (void *p_arg)
{
    CPU_INT32U  cpu_clk_freq;
    CPU_INT32U  cnts;
    OS_ERR      err;


    (void)p_arg;

    BSP_Init();                                    //板级初始化
    CPU_Init();
    //初始化 CPU组件(时间戳、关中断时间测量和主机名)

    cpu_clk_freq = BSP_CPU_ClkFreq();
    //获取 CPU内核时钟频率(SysTick 工作时钟)
    cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;
    //根据用户设定的时钟节拍频率计算 SysTick 定时器的计数值
    OS_CPU_SysTickInit(cnts);
    //调用 SysTick初始化函数,设置定时器计数值和启动定时器

    Mem_Init();
    //初始化内存管理组件(堆内存池和内存池表)

#if OS_CFG_STAT_TASK_EN > 0u
    //如果启用(默认启用)了统计任务
    OSStatTaskCPUUsageInit(&err);
    //计算没有应用任务(只有空闲任务)运行时 CPU的(最大)

#endif//容量(决定 OS_Stat_IdleCtrMax的值,为后面计算 CPU使用率使用)。
    CPU_IntDisMeasMaxCurReset();
    //复位(清零)当前最大关中断时间


    /* 创建消息队列 queue */
    OSQCreate ((OS_Q         *)&queue,            //指向消息队列的指针
                (CPU_CHAR     *)"Queue For Test",  //队列的名字
                (OS_MSG_QTY    )20,                //最多可存放消息的数目
                (OS_ERR       *)&err);             //返回错误类型


    /* 创建 AppTaskPost 任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskPostTCB,           //任务控制块地址
                (CPU_CHAR   *)"App Task Post",           //任务名称
                (OS_TASK_PTR ) AppTaskPost,             //任务函数
                (void       *) 0,
                //传递给任务函数(形参p_arg)的实参
                (OS_PRIO     ) APP_TASK_POST_PRIO,  //任务的优先级
                (CPU_STK    *)&AppTaskPostStk[0],
                //任务栈的基地址
                (CPU_STK_SIZE) APP_TASK_POST_STK_SIZE / 10,
                //任务栈空间剩下1/10时限制其增长
                (CPU_STK_SIZE) APP_TASK_POST_STK_SIZE,
                //任务栈空间(单位:sizeof(CPU_STK))
                (OS_MSG_QTY  ) 5u,
                //任务可接收的最大消息数
                (OS_TICK     ) 0u,
                //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)
                (void       *) 0,
                //任务扩展(0表不扩展)
                (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                (OS_ERR     *)&err);                   //返回错误类型

    /* 创建 AppTaskPend 任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskPendTCB,           //任务控制块地址
                (CPU_CHAR   *)"App Task Pend",               //任务名称
                (OS_TASK_PTR ) AppTaskPend,                //任务函数
                (void       *) 0,
                //传递给任务函数(形参p_arg)的实参
                (OS_PRIO     ) APP_TASK_PEND_PRIO,//任务的优先级
                (CPU_STK    *)&AppTaskPendStk[0],
                //任务栈的基地址
                (CPU_STK_SIZE) APP_TASK_PEND_STK_SIZE / 10,
                //任务栈空间剩下1/10时限制其增长
                (CPU_STK_SIZE) APP_TASK_PEND_STK_SIZE,
                //任务栈空间(单位:sizeof(CPU_STK))
                (OS_MSG_QTY  ) 5u,
                //任务可接收的最大消息数
                (OS_TICK     ) 0u,
                //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)
                (void       *) 0,
                //任务扩展(0表不扩展)
                (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                (OS_ERR     *)&err);                     //返回错误类型

    OSTaskDel ( & AppTaskStartTCB, & err );
    //删除起始任务本身,该任务不再运行


}


/************************************************************************
                                     POST TASK
***********************************************************************/
static  void  AppTaskPost ( void * p_arg )
{
    OS_ERR      err;
    (void)p_arg;

    while (DEF_TRUE)                            //任务体
    {
        /* 发布消息到消息队列 queue */
        OSQPost ((OS_Q        *)&queue,          //消息变量指针
                (void        *)"Fire μC/OS-III",
                //要发送的数据的指针,将内存块首地址通过队列“发送出去”
                (OS_MSG_SIZE  )sizeof ( "Fire μC/OS-III" ),//数据字节大小
                (OS_OPT       )OS_OPT_POST_FIFO | OS_OPT_POST_ALL,
                //先进先出和发布给全部任务的形式
                (OS_ERR      *)&err);               //返回错误类型

        OSTimeDlyHMSM ( 0, 0, 0, 500, OS_OPT_TIME_DLY, & err );
    }
}


/*************************************************************************
                                     PEND TASK
*************************************************************************/
static  void  AppTaskPend ( void * p_arg )
{
    OS_ERR      err;
    OS_MSG_SIZE msg_size;
    CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和
    //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
    // SR(临界段关中断只需保存SR),开中断时将该值还原。
    char * pMsg;
    (void)p_arg;

    while (DEF_TRUE)                                         //任务体
    {
        /* 请求消息队列 queue 的消息 */
        pMsg = OSQPend ((OS_Q         *)&queue,          //消息变量指针
                        (OS_TICK       )0,               //等待时长为无限
                        (OS_OPT        )OS_OPT_PEND_BLOCKING,
                        //如果没有获取到信号量就等待
                        (OS_MSG_SIZE  *)&msg_size,     //获取消息的字节大小
                        (CPU_TS       *)0,         //获取任务发送时的时间戳
                        (OS_ERR       *)&err);         //返回错误

        if ( err == OS_ERR_NONE )                       //如果接收成功
        {
            OS_CRITICAL_ENTER();                      //进入临界段
            printf ( "\r\n接收消息的长度:%d字节,内容:%s\r\n", msg_size, pMsg );
            OS_CRITICAL_EXIT();
        }
    }
}

3、消息队列实验现象

将程序编译好,用USB线连接计算机和开发板的USB接口(对应丝印为USB转串口), 用DAP仿真器把配套程序下载到野火STM32开发板。
在计算机上打开串口调试助手,然后复位开发板就可以在调试助手中看到串口的打印信息,具体如下
在这里插入图片描述

4、常见错误

1. Default Compiler Version 5

*** Target 'Fire_uCOS' uses ARM-Compiler 'Default Compiler Version 5' which is not available.

此错误是ARM编译器缺失导致的,解决办法是换成已安装的编译器
在这里插入图片描述

2. core_cm3.h(1213): error: unknown type name ‘inline’

错误是找不到 inline变量或函数,这个inline是在core_cm3.h里第1213行用到的
解决办法是把包含 inline的文件加入到工程里

inline可以是别的值,core_cm3.h也可以是别的文件
这些都是可以更改的

3. 程序正常运行无报错,烧录到板子上无反应

可能是库的问题,试试改成Micro Lib
在这里插入图片描述

Micro Lib是一个针对用C编写的基于ARM的嵌入式应用程序的高度优化的库。
与包含在ARM编译器工具链中的标准C库相比,MicroLib提供了许多嵌入式系统所需的代码大小的显著优势。

下图对使用标准库和使用微库代码大小进行了对比
在这里插入图片描述

MicroLib和标准C库之间的主要区别是:

1、MicroLib是专为深度嵌入式应用程序而设计的。

2、MicroLib经过优化,比使用ARM标准库使用更少的代码和数据内存。

3、MicroLib被设计成在没有操作系统的情况下工作,但是这并不妨碍它与任何操作系统或RTOS一起使用,如Keil RTX。

4、MicroLib不包含文件I/O或宽字符支持。

5、由于MicroLib已经优化到最小化代码大小,一些函数将比ARM编译工具中可用的标准C库例程执行得更慢。

6、MicroLib和ARM标准库都包含在Keil MDK-ARM中。

参考链接:keil勾选Use MicroLIB 的作用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LearnOpenGL_part1

创建窗口 - LearnOpenGL CN (learnopengl-cn.github.io) 最原始的黑框框&#xff1a; #include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> int main() {glfwInit();//初始化GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//配置G…

亚马逊AWS永久免费数据库

Amazon DynamoDB 是一项无服务器的 NoSQL 数据库服务&#xff0c;您可以通过它来开发任何规模的现代应用程序。作为无服务器数据库&#xff0c;您只需按使用量为其付费&#xff0c;DynamoDB 可以扩展到零&#xff0c;没有冷启动&#xff0c;没有版本升级&#xff0c;没有维护窗…

如何同时安全高效管理多个谷歌账号?

您的业务活动需要多个 Gmail 帐户吗&#xff1f;出海畅游&#xff0c;Gmail账号是少不了的工具之一&#xff0c;可以关联到Twitter、Facebook、Youtube、Chatgpt等等平台&#xff0c;可以说是海外网络的“万能锁”。但是大家都知道&#xff0c;以上这些平台注册多账号如果产生关…

Codeforces Round 930 (Div. 2) ---- E. Pokémon Arena ---- 题解

E. Pokmon Arena&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 可以想到的是&#xff0c;可以用最短路来解决这个问题&#xff0c;但是如果简单的建图的话&#xff0c;时间复杂度将会达到 O(n*n*m)&#xff0c;我们考虑怎么减少图中边的个数。 我们考虑一个颜色&…

C语言中的字符与字符串:魔法般的函数探险

前言 在C语言的世界里&#xff0c;字符和字符串是两个不可或缺的元素&#xff0c;它们像是魔法般的存在&#xff0c;让文字与代码交织出无限可能。而在这个世界里&#xff0c;有一批特殊的函数&#xff0c;它们如同探险家&#xff0c;引领我们深入字符与字符串的秘境&#xff0…

探索GlassWire:网络安全与流量监控软件

名人说&#xff1a;东边日出西边雨&#xff0c;道是无晴却有晴。——刘禹锡 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、软件介绍1、GlassWire&#xff08;免费版本&#xff09;2、核心特点 二、下载安装①…

想拿高薪?云计算或许是你的跳板!

随着科技的不断进步&#xff0c;云计算作为一项重要的技术趋势&#xff0c;正引领着整个行业的快速发展。越来越多的人开始关注云计算领域&#xff0c;希望通过学习和掌握这一技能来获得更高的薪资。那么&#xff0c;为什么选择云计算作为职业发展方向&#xff1f;学习云计算又…

sharding‐jdbc之分库分表(mysql主从同步的数据库安装和使用)

水平分表 创建基础工程.. 引入sharding‐jdbc的maven依赖包 注意需要数据库连接池等依赖 <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.0.0-RC1&l…

【pycharm】在debug循环时,如何快速debug到指定循环次数

【pycharm】在debug循环时&#xff0c;如何快速debug到指定循环次数 【先赞后看养成习惯】求关注收藏点赞&#x1f600; 在 PyCharm 中&#xff0c;可以使用条件断点来实现在特定循环次数后停止调试。这可以通过在断点处右键单击&#xff0c;然后选择 “Add Breakpoint” -&g…

ES6学习(五)-- Module 语法

文章目录 Module 语法1.1 痛点介绍(1) 异步加载(2) 私密(3) 重名(4) 依赖 1.2 解决方法(1) 解决异步加载问题(2) 解决私密问题(3) 重名解决方法(4) 解决依赖问题 1.3 模块化使用案例 Module 语法 之前js 出现的某些痛点&#xff1a; 在script 中引入的变量名不可以重复&#…

位运算-191. 位1的个数- 136. 只出现一次的数字

位1的个数 已解答 简单 相关标签 相关企业 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中 设置位 的个数&#xff08;也被称为汉明重量&#xff09;。 示例 1&#xff1a; 输入&#xff1a;n 11 输…

gpt4.0获取方法

今天我们想要进行的一项尝试就是—— 如何从一个不知道内容的数据文件中&#xff0c;一键生成一篇像模像样的经济学"论文”。 在开始之前&#xff0c;我们要准备好必要的AI利器&#xff1a; GPT3.5镜像站&#xff08;简单问题极快回答&#xff09;&#xff1a; https:/…

python写文件怎么读出来

python中对文件的操作大概分为三步&#xff1a;打开文件、操作文件&#xff08;读、写、追加写入&#xff09;、关闭文件。 1、无论对文件做哪种操作&#xff0c;操作前首先要保证文件被打开了&#xff0c;即需要一个打开的操作。 例&#xff1a;open(XXX.txt) 打开文件的同…

ARP 攻击神器:Macof 保姆级教程

一、介绍 macof 是一个用于生成伪造数据流的网络工具&#xff0c;常用于进行网络攻击和测试。它的主要作用是生成大量的伪造 MAC 地址的数据包&#xff0c;并将这些数据包发送到网络中&#xff0c;从而混淆网络设备的 MAC 地址表&#xff0c;导致网络拥堵或服务中断。 以下是…

C++数据结构——顺序表

C数据结构——顺序表 以下代码可以作为一个顺序表的模板&#xff0c;从顺序表的初始化创建到增删改查&#xff0c;都有详细的过程&#xff0c;供学习参考。 #include<iostream> #include<stdio.h>using namespace std;#define elemType intstruct SequentialList…

关联规则(理论及实例)

目录 一、啤酒和尿布的故事 二、理论 三、实例 1. 自定义数据集 2. 数据需转换成one-hot编码 3.电影题材关联分析 一、啤酒和尿布的故事 在美国&#xff0c;一些年轻的父亲下班后经常要到超市去购买婴儿尿布&#xff0c;超市因此发现一个规律&#xff0c;在购买婴儿尿布的…

scala实现通过Spark统计人均登录次数最终写入MySQL

谨以此博客作为记录 小编这里用的版本是&#xff1a; <hadoop.version>2.7.7</hadoop.version> <spark.version>2.4.5</spark.version> <scala.version>2.12.10</scala.version> 如果没用到Hadoop可以忽略 步骤 准备数据&#xff0c;知道…

信息系统项目管理师——第18章项目绩效域管理(二)

项目工作绩效域 预期目标 高效且有数的项目绩效 2.适合项目和环境的项目过程 3.干系人适当的沟通和参与 4.对实物资源进行了有效管理 5.对采购进行了有效管理 6.有效处理了变更 7.通过持续学习和过程改进提高了团队能力 绩效要点 1.项目过程 2.项目制约因素 3.专注于工作过…

[调度算法]

目录 一. 先到先服务(FCFS)二. 短作业优先(SJF)三. 高响应比优先(HRRN)四. 时间片轮转调度算法(RR)五. 优先级调度算法六. 多级反馈队列调度算法七. 多级队列调度算法 \quad 一. 先到先服务(FCFS) \quad 比如你要买一杯奶茶, 在你前面的要买20杯奶茶, 那么你就要等很久 \quad …

pwn学习笔记(7)--堆相关源码

相关源码&#xff1a; 1. chunk 相关源码&#xff1a; ​ 对于用户来说&#xff0c;只需要确保malloc()函数返回的内存不会发生溢出&#xff0c;并且在不用的时候使用free() 函数将其释放&#xff0c;以后也不再做任何操作即可。而对于glibc来说’它要在用户第一次调用malloc…