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

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


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


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

四十三、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开发板。
在计算机上打开串口调试助手,然后复位开发板就可以在调试助手中看到串口的打印信息,具体如下
在这里插入图片描述

最近更新

  1. TCP协议是安全的吗?

    2024-04-04 12:56:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-04 12:56:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-04 12:56:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-04 12:56:02       20 阅读

热门阅读

  1. html中的div标签

    2024-04-04 12:56:02       16 阅读
  2. mvn怎么安装jar

    2024-04-04 12:56:02       16 阅读
  3. Python零基础教学(if条件判断·1)

    2024-04-04 12:56:02       18 阅读
  4. MetaGPT部分源码解读--Role

    2024-04-04 12:56:02       17 阅读
  5. Linux 网络: 网卡速度异常案例(1)

    2024-04-04 12:56:02       13 阅读
  6. 服务器硬件基础知识

    2024-04-04 12:56:02       13 阅读
  7. excel怎么快速去掉多个空行

    2024-04-04 12:56:02       14 阅读
  8. 网络安全包括哪些方面?

    2024-04-04 12:56:02       15 阅读
  9. Linux Shell文件描述符和重定向

    2024-04-04 12:56:02       17 阅读
  10. vue2升级到vue3的一些使用注意事项记录(二)

    2024-04-04 12:56:02       13 阅读
  11. 软件设计原则:里氏替换原则

    2024-04-04 12:56:02       13 阅读
  12. dubbo 统一异常处理

    2024-04-04 12:56:02       12 阅读
  13. react diffing算法及函数柯里化

    2024-04-04 12:56:02       18 阅读
  14. 【技巧】Leetcode 136. 只出现一次的数字【中等】

    2024-04-04 12:56:02       19 阅读