(学习日记)2024.03.02:UCOSIII第四节:创建任务

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


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


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

2024.03.02

  • 九、UCOSIII:创建任务
    • 1、任务的定义
    • 2、定义任务栈
    • 3、定义任务函数
    • 4、定义任务控制块TCB
    • 5、任务创建函数
    • 6、任务栈初始化函数
    • 7、将任务添加到就绪列表
    • 8、全局变量的声明方法

九、UCOSIII:创建任务

1、任务的定义

在裸机系统中,系统的主体就是main函数里面顺序执行的无限循环,这个无限循环里面CPU按照顺序完成各种事情。
在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务。

void task_entry (void *parg)
{
    /* 任务主体,无限循环且不能返回 */
    for (;;)
    {
        /* 任务主体代码 */
    }
}

2、定义任务栈

在这里插入图片描述

/*
************************************************************************************************************************
*                                                  TCB & STACK & 任务声明
************************************************************************************************************************
*/
#define  TASK1_STK_SIZE       20
#define  TASK2_STK_SIZE       20

static   CPU_STK   Task1Stk[TASK1_STK_SIZE];
static   CPU_STK   Task2Stk[TASK2_STK_SIZE];

static   OS_TCB    Task1TCB;
static   OS_TCB    Task2TCB;

void     Task1( void *p_arg );
void     Task2( void *p_arg );

在多任务系统中,有多少个任务就需要定义多少个任务栈。

任务栈的大小由宏定义控制,在μC/OS-III中, 空闲任务的栈最小应该大于128,那么我们这里的任务的栈也暂且配置为128。
(但是代码里任务栈数量为20)

任务栈其实就是一个预先定义好的全局数据,数据类型为CPU_STK。
在μC/OS-III中,凡是涉及数据类型的地方, μC/OS-II都会将标准的C数据类型用typedef重新取一个类型名,命名方式则采用见名之义的方式命名且统统大写。

3、定义任务函数

任务是一个独立的函数,函数主体无限循环且不能返回。
任务的代码示意如下:

/* flag 必须定义成全局变量才能添加到逻辑分析仪里面观察波形
** 在逻辑分析仪中要设置以 bit 的模式才能看到波形,不能用默认的模拟量
*/
uint32_t flag1;
uint32_t flag2;

/* 任务1 */
void Task1( void *p_arg )(2)
{
for ( ;; ) {
        flag1 = 1;
        delay( 100 );
        flag1 = 0;
        delay( 100 );
    }
    //任务是一个独立的、无限循环且不能返回的函数。
}

/* 任务2 */
void Task2( void *p_arg )(3)
{
for ( ;; ) {
        flag2 = 1;
        delay( 100 );
        flag2 = 0;
        delay( 100 );
    }
}

4、定义任务控制块TCB

在这里插入图片描述

/*
************************************************************************************************************************
************************************************************************************************************************
*                                                  数据类型
************************************************************************************************************************
************************************************************************************************************************
*/

typedef  struct  os_rdy_list         OS_RDY_LIST;
typedef  void                        (*OS_TASK_PTR)(void *p_arg);
typedef  struct  os_tcb              OS_TCB;
/*
------------------------------------------------------------------------------------------------------------------------
*                                                   就绪列表
------------------------------------------------------------------------------------------------------------------------
*/
struct os_rdy_list
{
	OS_TCB        *HeadPtr;
	OS_TCB        *TailPtr;
};

在裸机系统中,程序的主体是CPU按照顺序执行的。而在多任务系统中,任务的执行是由系统调度的。

系统为了顺利的调度任务, 为每个任务都额外定义了一个任务控制块TCB(Task ControlBlock),这个任务控制块就相当于任务的身份证, 里面存有任务的所有信息,比如任务的栈,任务名称,任务的形参等。

有了这个任务控制块之后, 以后系统对任务的全部操作都可以通过这个TCB来实现。
TCB是一个新的数据类型, 在os.h这个头文件中声明,使用它可以为每个任务都定义一个TCB实体。
目前TCB里面的成员还比较少,只有栈指针和栈大小。其中为了以后操作方便,我们把栈指针作为TCB的第一个成员。

5、任务创建函数

在这里插入图片描述

#include "os.h"

void OSTaskCreate (OS_TCB        *p_tcb, 
                   OS_TASK_PTR   p_task, 
                   void          *p_arg,
                   CPU_STK       *p_stk_base,
                   CPU_STK_SIZE  stk_size,
                   OS_ERR        *p_err)
{
	CPU_STK       *p_sp;

	p_sp = OSTaskStkInit (p_task,
	p_arg,
	p_stk_base,
	stk_size);
	p_tcb->StkPtr = p_sp;
	p_tcb->StkSize = stk_size;

	*p_err = OS_ERR_NONE;
}

任务的栈,任务的函数实体,任务的TCB最终需要联系起来才能由系统进行统一调度。

那么这个联系的工作就由任务创建函数 OSTaskCreate来实现,该函数在os_task.c中定义,所有跟任务相关的函数都在这个文件定义。

  • OSTaskCreate函数遵循μC/OS-III中的函数命名规则,以大小的OS开头,表示这是一个外部函数,可以由用户调用,以OS_开头的函数表示内部函数,只能由μC/OS-III内部使用。紧接着是文件名,表示该函数放在哪个文件,最后是函数功能名称。
  • p_tcb是任务控制块指针。
  • p_task 是任务函数名,类型为OS_TASK_PTR,原型声明在os.h中

在这里插入图片描述
在这里插入图片描述

  • p_arg是任务形参,用于传递任务参数。 p_stk_base 用于指向任务栈的起始地址。 stk_size 表示任务栈的大小。
  • p_err 用于存错误码,μC/OS-III中为函数的返回值预先定义了很多错误码,
    错误码是枚举类型的数据,在os.h中定义 OSTaskStkInit()是任务栈初始化函数。 当任务第一次运行的时候, 加载到CPU寄存器的参数就放在任务栈里面,在任务创建的时候,预先初始化好栈。
  • OSTaskStkInit()函数在os_cpu_c.c中定义
  • p_tcb->StkPtr = p_sp;
    将剩余栈的栈顶指针p_sp保存到任务控制块TCB的第一个成员StkPtr中。
  • p_tcb->StkSize = stk_size;
    将任务栈的大小保存到任务控制块TCB的成员StkSize中。
  • *p_err = OS_ERR_NONE;
    函数执行到这里表示没有错误,即OS_ERR_NONE。

6、任务栈初始化函数

在这里插入图片描述

#include "os.h"

/* 任务堆栈初始化 */
CPU_STK *OSTaskStkInit (OS_TASK_PTR  p_task,
                        void         *p_arg,
                        CPU_STK      *p_stk_base,
                        CPU_STK_SIZE stk_size)
{
	CPU_STK  *p_stk;

	p_stk = &p_stk_base[stk_size];
															/* 异常发生时自动保存的寄存器                              */
	*--p_stk = (CPU_STK)0x01000000u;                        /* xPSR的bit24必须置1                                     */
	*--p_stk = (CPU_STK)p_task;                             /* 任务的入口地址                                         */
	*--p_stk = (CPU_STK)0x14141414u;                        /* R14 (LR)                                               */
	*--p_stk = (CPU_STK)0x12121212u;                        /* R12                                                    */
	*--p_stk = (CPU_STK)0x03030303u;                        /* R3                                                     */
	*--p_stk = (CPU_STK)0x02020202u;                        /* R2                                                     */
	*--p_stk = (CPU_STK)0x01010101u;                        /* R1                                                     */
	*--p_stk = (CPU_STK)p_arg;                              /* R0 : 任务形参                                          */
															/* 异常发生时需手动保存的寄存器                            */
	*--p_stk = (CPU_STK)0x11111111u;                        /* R11                                                    */
	*--p_stk = (CPU_STK)0x10101010u;                        /* R10                                                    */
	*--p_stk = (CPU_STK)0x09090909u;                        /* R9                                                     */
	*--p_stk = (CPU_STK)0x08080808u;                        /* R8                                                     */
	*--p_stk = (CPU_STK)0x07070707u;                        /* R7                                                     */
	*--p_stk = (CPU_STK)0x06060606u;                        /* R6                                                     */
	*--p_stk = (CPU_STK)0x05050505u;                        /* R5                                                     */
	*--p_stk = (CPU_STK)0x04040404u;                        /* R4                                                     */
	return (p_stk);
}
  • p_task是任务名,指示着任务的入口地址,在任务切换的时候,需要加载到R15, 即PC寄存器,这样CPU就可以找到要运行的任务。

  • p_arg 是任务的形参,用于传递参数,在任务切换的时候,需要加载到寄存器R0。R0寄存器通常用来传递参数。

  • p_stk_base 表示任务栈的起始地址。

  • stk_size 表示任务栈的大小, 数据类型为CPU_STK_SIZE,在Cortex-M3内核的处理器中等于4个字节,即一个字。

  • p_stk = &p_stk_base[stk_size];
    获取任务栈的栈顶地址,ARMCM3处理器的栈是由高地址向低地址生长的。所以初始化栈之前, 要获取到栈顶地址,然后栈地址逐一递减即可。

  • /* 异常发生时自动保存的寄存器 */ 下面的八行
    任务第一次运行的时候,加载到CPU寄存器的环境参数我们要预先初始化好。初始化的顺序固定, 首先是异常发生时自动保存的8个寄存器,即xPSR、R15、R14、R12、R3、R2、R1和R0。其中xPSR寄存器的位24必须是1, R15PC指针必须存的是任务的入口地址,R0必须是任务形参,剩下的R14、R12、R3、R2和R1为了调试方便,填入与寄存器号相对应的16进制数。

  • /* 异常发生时需手动保存的寄存器 */ 下面的八行
    剩下的是8个需要手动加载到CPU寄存器的参数,为了调试方便填入与寄存器号相对应的16进制数。

  • 返回栈指针p_stk,这个时候p_stk指向剩余栈的栈顶。

7、将任务添加到就绪列表

任务创建好之后,我们需要把任务添加到一个叫就绪列表的数组里面,表示任务已经就绪,系统随时可以调度。

在这里插入图片描述

在这里插入图片描述

int main(void)
{	
	OS_ERR err;
	
	
	
	/* 初始化相关的全局变量 */
	OSInit(&err);
	
	/* 创建任务 */
	OSTaskCreate ((OS_TCB*)      &Task1TCB, 
	              (OS_TASK_PTR ) Task1, 
	              (void *)       0,
	              (CPU_STK*)     &Task1Stk[0],
	              (CPU_STK_SIZE) TASK1_STK_SIZE,
	              (OS_ERR *)     &err);

	OSTaskCreate ((OS_TCB*)      &Task2TCB, 
	              (OS_TASK_PTR ) Task2, 
	              (void *)       0,
	              (CPU_STK*)     &Task2Stk[0],
	              (CPU_STK_SIZE) TASK2_STK_SIZE,
	              (OS_ERR *)     &err);
				  
	/* 将任务加入到就绪列表 */
	OSRdyList[0].HeadPtr = &Task1TCB;
	OSRdyList[1].HeadPtr = &Task2TCB;
	
	/* 启动OS,将不再返回 */				
	OSStart(&err);
}

把任务TCB指针放到OSRDYList数组里面。
OSRdyList定义如下
在这里插入图片描述
OS_CFG_PRIO_MAX是一个定义,表示这个系统支持多少个优先级(刚开始暂时不支持多个优先级,往后章节会支持), 目前这里仅用 来表示这个就绪列表可以存多少个任务的TCB指针。

具体的宏在os_cfg.h中定义
在这里插入图片描述

OSRdyList是一个类型为OS_RDY_LIST的全局变量, 在os.h中定义

μC/OS-III中中会为每个数据类型重新取一个大写的名字。

OS_RDY_LIST里面目前暂时只有两个TCB类型的指针,一个是头指针,一个是尾指针。

需要使用头尾指针来将TCB串成一个双向链表。
在这里插入图片描述

8、全局变量的声明方法

在μC/OS-III中,需要使用很多全局变量,这些全局变量都在os.h这个头文件中定义,但是os.h会被包含进很多的文件中, 那么编译的时候,os.h里面定义的全局变量就会出现重复定义的情况,而我们要的只是os.h里面定义的全局变量只定义一次, 其他包含os.h头文件的时候只是声明。

通常我们的做法都是在C文件里面定义全局变量,然后在头文件里面加extern声明,哪里需要使用就在哪里加extern声明。
但是μC/OS-III中,文件非常多,这种方法可行,但不现实。所以就有了现在在os.h头文件中定义全局变量, 然后在os.h文件的开头加上 代码清单:任务-17 的宏定义的方法。

但是到了这里还没成功, μC/OS-III再另外新建了一个os_var.c的文件,在里面包含os.h, 且只在这个文件里面定义OS_GLOBALS这个宏

如果没有定义OS_GLOBALS这个宏,那么OS_EXT就为空,否则就为extern。
在这里插入图片描述
经过这样处理之后,在编译整个工程的时候,只有var.c里面的os.h的OS_EXT才会被替换为空,即变量的定义, 其他包含os.h的文件因为没有定义OS_GLOBALS这个宏,则OS_EXT会被替换成extern,即变成了变量的声明。 这样就实现了在头文件中定义变量。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL 系列】在 Windows 上安装 MySQL

在 Windows 平台上安装 MySQL 很简单,并不需要太复杂的步骤。按照本文的步骤操练起来就可以了。 文章目录 1、下载 MySQL 安装程序2、安装 MySQL 数据库2.1、选择安装类型2.2、检查所需组件2.3、安装所选产品组件2.4、产品配置2.5、配置高可用性2.6、配置服务器类型…

UDP协议和TCP协议详解

文章目录 应用层自定义协议 传输层udp协议TCP协议1.确认应答2.超时重传3.连接管理建立连接, 三次握手断开连接, 四次挥手tcp的状态 4.滑动窗口5.流量控制6.拥塞控制7.延时应答8.携带应答9.面向字节流10.异常情况 应用层 自定义协议 客户端和服务器之间往往要进行交互的是“结构…

网络工程师笔记8

华为VRP系统 设备管理方式 web管理方式 命令行管理方式 修改命令:undo 基础配置命令

学习python时一些笔记

1、winr 命令提示符的快捷键 输入cmd进入终端 2、在终端运行桌面上的python文件 cd desktop(桌面) cd是进入该文件夹的意思。 cd .. 回到上一级 运行python时一定要找到文件的所在地 输入python进入,exit()退出%s字符串占位符%d数字占位符%f浮点数占位符input输…

【Python】变量的引用

🚩 WRITE IN FRONT 🚩 🔎 介绍:"謓泽"正在路上朝着"攻城狮"方向"前进四" 🔎🏅 荣誉:2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…

KMP算法和Manacher算法

KMP算法 KMP算法解决的问题 KMP算法用来解决字符串匹配问题: 找到长串中短串出现的位置. KMP算法思路 暴力比较与KMP的区别 暴力匹配: 对长串的每个位,都从头开始匹配短串的所有位. KMP算法: 将短字符串前后相同的部分存储在 n e x t next next数组里,让之前匹配过的信息指…

如何利用pynlpir进行中文分词并保留段落信息

一、引言 nlpir是由张华平博士开发的中文自然处理工具,可以对中文文本进行分词、聚类分析等,它既有在线的中文数据大数据语义智能分析平台,也有相关的python包pynlpir,其github的地址是: Pynlpir在Github上的地址 这…

算法(6)——模拟

一、什么是模拟 模拟是对真实事物或者过程的虚拟。在编程时为了实现某个功能,可以用语言来模拟那个功能,模拟成功也就相应地表示编程成功。 二、模拟算法的思路 模拟算法是一种基本的算法思想,可用于考查程序员的基本编程能力,…

抖店入驻费用是多少?新手入驻都有哪些要求?2024费用明细!

我是电商珠珠 我做电商做了将近五年,做抖店做了三年多,期间还带着学员一起做店。 今天,就来给大家详细的讲一下在抖音开店,需要多少费用,最低需要投入多少。 1、营业执照200元左右 就拿个体店举例,在入…

二叉搜索树题目:将有序数组转换为二叉搜索树

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法证明代码复杂度分析 题目 标题和出处 标题:将有序数组转换为二叉搜索树 出处:108. 将有序数组转换为二叉搜索树 难度 4 级 题目描述 要求 给定整数数组 nums \texttt{nums}…

大辩论:人工智能时代人类和软件的未来

关于人工智能将在多大程度上接管软件开发、交付和支持任务及其对就业的影响,聆听双方的争论,对于技术来说既令人兴奋,又让人类感到恐惧。争论是这样的: 人工智能倡导者:欢迎来到未来!凭借当今人工智能的能…

猫挑食不吃猫粮怎么办?可以解决猫咪挑食的主食冻干推荐

现在的猫奴们普遍将自家的小猫视为掌上明珠,宠爱有加。然而,这种宠爱有时也会导致猫咪养成一些不良习惯,比如挑食。猫挑食不吃猫粮怎么办?今天为大家分享一个既不让咱宝贝猫咪受罪又可以改善猫咪挑食的方法。 一、猫咪是为什么挑食…

(C语言)qsort函数详解

目录 1. qsort解释 2. qsort实例 2.1 qsort排列整形数组类型: 2.2 qsort排列结构体类型数据(字符串): 2.3 qsort排列结构体类型数据(整形): 1. qsort解释 我们可以进入网站:qso…

第一天 走进Docker的世界

第一天 走进Docker的世界 介绍docker的前世今生,了解docker的实现原理,以Django项目为例,带大家如何编写最佳的Dockerfile构建镜像。通过本章的学习,大家会知道docker的概念及基本操作,并学会构建自己的业务镜像&…

王者荣耀整蛊搭建直播新玩法/obs贴纸配置教程

最近很火的王者荣耀整蛊直播,相信很多玩王者的玩家也想开一个直播,但是看到这种直播娱乐效果很有意思也想搭建一个,这里梦哥给大家出了一期搭建的教程! 进阶版视频教程: 这期的教程是进阶版新玩法升级,具体…

DataGrip 2023:让数据库开发变得更简单、更高效 mac/win

JetBrains DataGrip 2023是一款功能强大的数据库IDE,专为数据库开发和管理而设计。通过DataGrip,您可以连接到各种关系型数据库管理系统(RDBMS),并使用其提供的一组工具来查询、管理、编辑和开发数据库。 DataGrip 2023软件获取 DataGrip 2…

前端面试 跨域理解

2 实现 2-1 JSONP 实现 2-2 nginx 配置 2-2 vue 开发中 webpack自带跨域 2 -3 下载CORS 插件 或 chrome浏览器配置跨域 2-4 通过iframe 如:aaa.com 中读取bbb.com的localStorage 1)在aaa.com的页面中,在页面中嵌入一个src为bbb.com的iframe&#x…

大模型理论基础(so-large-lm)课程笔记!

Datawhale干货 作者:辣条,Datawhale优秀学习者 前 言 在当前信息时代,大型语言模型(Large Language Models,LLMs)的发展速度和影响力日益显著。随着技术进步,我们见证了从基本的Transformer架构…

【三维重建】【SLAM】SplaTAM:基于3D高斯的密集RGB-D SLAM(CVPR 2024)

题目:SplaTAM: Splat, Track & Map 3D Gaussians for Dense RGB-D SLAM 地址:spla-tam.github.io 机构:CMU(卡内基梅隆大学)、MIT(美国麻省理工) 总结:SplaTAM,一个新…