(学习日记)2024.03.06:UCOSIII第八节:空闲任务+阻塞延时+main函数修改

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


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


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

2024.03.05

  • 十八、UCOSIII:空闲任务
    • 1、阻塞延时与空闲任务介绍
    • 2、定义空闲任务栈
    • 3、定义空闲任务TCB
    • 4、定义空闲任务函数
    • 5、空闲任务初始化
  • 十九、UCOSIII:实现阻塞延时
    • 1、阻塞延时函数
    • 2、记录任务需要延时的时间
    • 3、任务切换的部分修改
    • 4、修改中断服务函数
  • 二十、UCOSIII:修改main()函数
    • 1、程序代码
    • 2、编译调试

十八、UCOSIII:空闲任务

1、阻塞延时与空闲任务介绍

在之前的十七章中,任务体内的延时使用的是软件延时,即让CPU空等来达到延时的效果。
使用RTOS的很大优势就是榨干CPU的性能, 永远不能让它闲着,任务如果需要延时也就不能再让CPU空等来实现延时的效果。

RTOS中的延时叫阻塞延时,即任务需要延时的时候, 任务会放弃CPU的使用权,CPU可以去干其他的事情,当任务延时时间到,重新获取CPU使用权,任务继续运行, 这样就充分地利用了CPU的资源,而不是干等着。

当任务需要延时,进入阻塞状态,那CPU又去干什么事情了?
如果没有其他任务可以运行,RTOS都会为CPU创建一个空闲任务, 这个时候CPU就运行空闲任务。

在μC/OS-III中,空闲任务是系统在初始化的时候创建的优先级最低的任务,空闲任务主体很简单, 只是对一个全局变量进行计数。
鉴于空闲任务的这种特性,在实际应用中,当系统进入空闲任务的时候, 可在空闲任务中让单片机进入休眠或者低功耗等操作。

2、定义空闲任务栈

空闲任务栈在os_cfg_app.c文件中定义
在这里插入图片描述

#include <os_cfg_app.h>
#include <os.h>/*
************************************************************************************************************************
*                                                    DATA STORAGE
************************************************************************************************************************
*/CPU_STK        OSCfg_IdleTaskStk   [OS_CFG_IDLE_TASK_STK_SIZE];/*
************************************************************************************************************************
*                                                      CONSTANTS
************************************************************************************************************************
*//* 空闲任务堆栈起始地址 */
CPU_STK      * const  OSCfg_IdleTaskStkBasePtr   = (CPU_STK    *)&OSCfg_IdleTaskStk[0];
/* 空闲任务堆栈大小 */
CPU_STK_SIZE   const  OSCfg_IdleTaskStkSize      = (CPU_STK_SIZE)OS_CFG_IDLE_TASK_STK_SIZE;
  • CPU_STK OSCfg_IdleTaskStk [OS_CFG_IDLE_TASK_STK_SIZE];
    空闲任务的栈是一个定义好的数组,大小由OS_CFG_IDLE_TASK_STK_SIZE这个宏控制。 OS_CFG_IDLE_TASK_STK_SIZE在os_cfg_app.h这个头文件定义,大小为128
    在这里插入图片描述

  • CPU_STK * const OSCfg_IdleTaskStkBasePtr = (CPU_STK *)&OSCfg_IdleTaskStk[0];
    空闲任务的栈的起始地址和大小均被定义成一个常量,不能被修改。
    变量OSCfg_IdleTaskStkBasePtr和OSCfg_IdleTaskStkSize同时还在os.h中声明,这样就具有全局属性, 可以在其他文件里面被使用
    在这里插入图片描述

3、定义空闲任务TCB

任务控制块TCB是每一个任务必须的,空闲任务的TCB在os.h中定义,是一个全局变量

在这里插入图片描述

4、定义空闲任务函数

空闲任务正如其名,空闲,任务体里面只是对全局变量OSIdleTaskCtr ++ 操作

/* 空闲任务 */
void  OS_IdleTask (void  *p_arg)
{p_arg = p_arg;/* 空闲任务什么都不做,只对全局变量OSIdleTaskCtr ++ 操作 */for(;;){OSIdleTaskCtr++;}
}

全局变量OSIdleTaskCtr在os.h中定义
在这里插入图片描述

OS_IDLE_CTR是在os_type.h中重新定义的数据类型
在这里插入图片描述

5、空闲任务初始化

空闲任务的初始化在OSInit()在完成,意味着在系统还没有启动之前空闲任务就已经创建好,具体在os_core.c定义

/* RTOS初始化
** 初始化全局变量
*/
void OSInit (OS_ERR *p_err)
{/* 配置OS初始状态为停止态 */OSRunning =  OS_STATE_OS_STOPPED;/* 初始化两个全局TCB,这两个TCB用于任务切换 */OSTCBCurPtr = (OS_TCB *)0;OSTCBHighRdyPtr = (OS_TCB *)0;/* 初始化就绪列表 */OS_RdyListInit();/* 初始化空闲任务 */OS_IdleTaskInit(p_err);if (*p_err != OS_ERR_NONE) {return;}
}

OS_RdyListInit() 就绪列表初始化函数如下

/* 就绪列表初始化 */
void OS_RdyListInit(void)
{OS_PRIO i;OS_RDY_LIST *p_rdy_list;for( i=0u; i<OS_CFG_PRIO_MAX; i++ ){p_rdy_list = &OSRdyList[i];p_rdy_list->HeadPtr = (OS_TCB *)0;p_rdy_list->TailPtr = (OS_TCB *)0;}
}

OS_IdleTaskInit() 空闲任务初始化函数在OSInit()中调用,在系统还没有启动之前就被创建。
OS_IdleTaskInit() 函数如下:

/* 空闲任务初始化 */
void  OS_IdleTaskInit(OS_ERR  *p_err)
{	/* 初始化空闲任务计数器 */OSIdleTaskCtr = (OS_IDLE_CTR)0;/* 创建空闲任务 */OSTaskCreate( (OS_TCB     *)&OSIdleTaskTCB, (OS_TASK_PTR )OS_IdleTask, (void       *)0,(CPU_STK    *)OSCfg_IdleTaskStkBasePtr,(CPU_STK_SIZE)OSCfg_IdleTaskStkSize,(OS_ERR     *)p_err );
}
此时os_core.c中函数的顺序如下:/* 就绪列表初始化 */
void OS_RdyListInit(void)/* RTOS初始化 初始化全局变量 */
void OSInit (OS_ERR *p_err)/* 启动RTOS,将不再返回 */
void OSStart (OS_ERR *p_err)/* 任务切换,实际就是触发PendSV异常,然后在PendSV异常中进行上下文切换 */
void OSSched (void)/* 空闲任务 */
void  OS_IdleTask (void  *p_arg)/* 空闲任务初始化 */
void  OS_IdleTaskInit(OS_ERR  *p_err)

由于编译顺序,我们需要在os.h文件添加 OS_IdleTaskInit() 和 OS_IdleTask 的声明

/* ------------------------------------------------ INTERNAL FUNCTIONS ---------------------------------------------- */void          OS_IdleTask               (void                  *p_arg);void          OS_IdleTaskInit           (OS_ERR                *p_err);			

十九、UCOSIII:实现阻塞延时

阻塞延时的阻塞是指任务调用该延时函数后,任务会被剥离CPU使用权,然后进入阻塞状态,直到延时结束, 任务重新获取CPU使用权才可以继续运行。
在任务阻塞的这段时间,CPU可以去执行其他的任务, 如果其他的任务也在延时状态,那么CPU就将运行空闲任务。

1、阻塞延时函数

阻塞延时函数在os_time.c中定义

/* 阻塞延时 */
void  OSTimeDly(OS_TICK dly)
{/* 设置延时时间 */OSTCBCurPtr->TaskDelayTicks = dly;/* 进行任务调度 */OSSched();	//此处调度和前面不太一样,改动看下文
}

在os.h中声明

/* ================================================================================================================== */
/*                                                 TIME MANAGEMENT                                                    */
/* ================================================================================================================== */				   
void          OSTimeTick                (void);	
void          OSTimeDly                 (OS_TICK dly);

2、记录任务需要延时的时间

TaskDelayTicks是任务控制块的一个成员,用于记录任务需要延时的时间,单位为SysTick的中断周期。
比如我们本章当中SysTick的中断周期为10ms,调用OSTimeDly(2)则完成2*10ms的延时。
TaskDelayTicks被定义在os.h

/*
------------------------------------------------------------------------------------------------------------------------
*                                                   任务控制块
------------------------------------------------------------------------------------------------------------------------
*/
struct os_tcb
{CPU_STK         *StkPtr;CPU_STK_SIZE    StkSize;/* 任务延时周期个数 */OS_TICK         TaskDelayTicks;
};

3、任务切换的部分修改

OSSched(); 此处调度和前面不太一样,改动看下文

/* 任务切换,实际就是触发PendSV异常,然后在PendSV异常中进行上下文切换 */
void OSSched(void)
{
#if 0	/* 非常简单的任务调度:两个任务轮流执行 */if( OSTCBCurPtr == OSRdyList[0].HeadPtr ){OSTCBHighRdyPtr = OSRdyList[1].HeadPtr;}else{OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;}
#endif/* 如果当前任务是空闲任务,那么就去尝试执行任务1或者任务2,看看他们的延时时间是否结束如果任务的延时时间均没有到期,那就返回继续执行空闲任务 */if( OSTCBCurPtr == &OSIdleTaskTCB ){if(OSRdyList[0].HeadPtr->TaskDelayTicks == 0){OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;}else if(OSRdyList[1].HeadPtr->TaskDelayTicks == 0){OSTCBHighRdyPtr = OSRdyList[1].HeadPtr;}else{return;		/* 任务延时均没有到期则返回,继续执行空闲任务 */} }else{/*如果是task1或者task2的话,检查下另外一个任务,如果另外的任务不在延时中,就切换到该任务否则,判断下当前任务是否应该进入延时状态,如果是的话,就切换到空闲任务。否则就不进行任何切换 */if(OSTCBCurPtr == OSRdyList[0].HeadPtr){if(OSRdyList[1].HeadPtr->TaskDelayTicks == 0){OSTCBHighRdyPtr = OSRdyList[1].HeadPtr;}else if(OSTCBCurPtr->TaskDelayTicks != 0){OSTCBHighRdyPtr = &OSIdleTaskTCB;}else {return;		/* 返回,不进行切换,因为两个任务都处于延时中 */}}else if(OSTCBCurPtr == OSRdyList[1].HeadPtr){if(OSRdyList[0].HeadPtr->TaskDelayTicks == 0){OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;}else if(OSTCBCurPtr->TaskDelayTicks != 0){OSTCBHighRdyPtr = &OSIdleTaskTCB;}else {return;		/* 返回,不进行切换,因为两个任务都处于延时中 */}}}/* 任务切换 */OS_TASK_SW();
}

4、修改中断服务函数

在os_cpu_c.c中添加 中断服务函数 SysTick_Handler

/* SysTick 中断服务函数 */
void SysTick_Handler(void)
{OSTimeTick();
}

时钟中断处理函数在每次时钟中断发生后必须调用该函数。uC/OS-III使用这个函数来更新延时时间。
OSTimeTick() 函数如下:

void  OSTimeTick (void)
{unsigned int i;/* 扫描就绪列表中所有任务的TaskDelayTicks,如果不为0,则减1 */for(i=0; i<OS_CFG_PRIO_MAX; i++){if(OSRdyList[i].HeadPtr->TaskDelayTicks > 0){OSRdyList[i].HeadPtr->TaskDelayTicks --;}}/* 任务调度 */OSSched();
}

同时在 os.h中声明一下
在这里插入图片描述

二十、UCOSIII:修改main()函数

1、程序代码

/*
************************************************************************************************************************
*                                                    main函数
************************************************************************************************************************
*/
/*
* 注意事项:1、该工程使用软件仿真,debug需选择 Ude Simulator
*           2、在Target选项卡里面把晶振Xtal(Mhz)的值改为25,默认是12,
*              改成25是为了跟system_ARMCM3.c中定义的__SYSTEM_CLOCK相同,确保仿真的时候时钟一致
*/
int main(void)
{OS_ERR err;/* 关闭中断 */CPU_IntDis();/* 配置SysTick 10ms 中断一次 */OS_CPU_SysTickInit (10);/* 初始化相关的全局变量 */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);
}/*
************************************************************************************************************************
*                                                    函数实现
************************************************************************************************************************
*/
/* 软件延时 
void delay (volatile uint32_t count)
{for(; count!=0; count--);
}
*//* 任务1 */
void Task1( void *p_arg )
{for ( ;; ) {flag1 = 1;//delay( 100 );OSTimeDly(2);				//延时函数均替代为阻塞延时,延时时间均为2个SysTick中断周期,即20ms。flag1 = 0;//delay( 100 );OSTimeDly(2);/* 任务切换,这里是手动切换 *///OSSched();}
}/* 任务2 */
void Task2( void *p_arg )
{for ( ;; ) {flag2 = 1;//delay( 100 );OSTimeDly(2);//延时函数均替代为阻塞延时,延时时间均为2个SysTick中断周期,即20ms。flag2 = 0;//delay( 100 );OSTimeDly(2);/* 任务切换,这里是手动切换 *///OSSched();}
}

主要改动这些位置:
在这里插入图片描述
在这里插入图片描述

2、编译调试

逻辑分析仪中可看到两个任务的波形是完全同步,就好像CPU在同时干两件事情
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【高效开发工具系列】vimdiff简介与使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

LeetCode102.二叉树的层序遍历

题目 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]]输入&#xff1a;root [1] 输出&am…

python基础——基础语法

文章目录 一、基础知识1、字面量2、常用值类型3、注释4、输入输出5、数据类型转换6、其他 二、字符串拓展1、字符串定义2、字符串拼接3、字符串格式化4、格式化精度控制 三、条件/循环语句1、if2、while3、for循环 四、函数1、函数定义2、函数说明文档3、global关键字 五、数据…

在亚马逊云科技上开启您的多机多卡分布式训练之旅

随着机器学习模型规模的扩大和数据量的增加&#xff0c;单个设备的计算能力和内存容量逐渐成为瓶颈。这导致训练过程变得缓慢且耗时长&#xff0c;限制了模型的进一步发展和改进。为了解决这个问题&#xff0c;分布式训练应运而生。它利用多个计算资源并行地执行计算任务&#…

二维码门楼牌管理系统应用场景:紧急服务部门的新助手

文章目录 前言一、二维码门楼牌管理系统的引入二、紧急服务部门的应用场景三、与紧急服务部门的联动机制四、技术挑战与未来发展 前言 在数字化时代&#xff0c;二维码门楼牌管理系统不仅为城市管理带来了便捷&#xff0c;更为紧急服务部门开辟了新的救援通道。本文将探讨二维…

Claude3荣登榜首,亚马逊云科技为您提供先行体验!

Claude3荣登榜首&#xff0c;亚马逊云科技为您提供先行体验&#xff01; 个人简介前言抢先体验关于Amazon BedrockAmazon Bedrock 的功能 Claude3体验教程登录Amazon Bedrock试用体验管理权限详细操作步骤1.提交应用场景详细信息2.请求模型的访问权限3.请求成功&#xff0c;开始…

【Docker】技术架构演变

【Docker】技术架构演变 目录 【Docker】技术架构演变架构中的概念架构演进单机架构相关软件 应用数据分离架构应用服务集群架构相关软件 读写分离/主从分离架构相关软件 引入缓存——冷热分离架构相关软件 垂直分库&#xff08;分布式数据库架构&#xff09;相关软件 业务拆分…

第一节 JDBC是什么?

JDBC代表Java数据库连接(Java Database Connectivity)&#xff0c;它是用于Java编程语言和数据库之间的数据库无关连接的标准Java API&#xff0c;换句话说&#xff1a;JDBC是用于在Java语言编程中与数据库连接的API。 JDBC库包括通常与数据库使用相关&#xff0c;如下面提到的…

自学Java的第二十一天(在学校版)

一&#xff0c;每日收获 类与对象 1.看一个养猫猫问题 2.使用现有技术解决 Object01.java 3.类与对象的关系示意图 4.快速入门 5.类和对象的区别和联系 6.对象在内存中存在形式 7.属性/成员变量/字段 8.如何创建对象 9.如何访问属性 二&#xff0c;新名词与小技巧 三…

学不动系列-eslint

ESLint 介绍在最简单的项目使用eslint,包括eslint的vscode插件的使用&#xff0c;自动化格式代码&#xff0c;自动化修复代码&#xff0c;和webpack&#xff0c;vite的配合使用 单独使用 第一步&#xff1a;构建一个空项目 npm init -y 在根目录新建文件./src/app.js&#…

Spring Cloud Gateway核心之Predicate

路由 Predicate 工厂 Spring Cloud Gateway 将路由作为 Spring WebFluxHandlerMapping基础设施的一部分进行匹配。Spring Cloud Gateway 包含许多内置的路由Predicate 工厂。所有这些谓词都匹配 HTTP 请求的不同属性。多个 Route Predicate Factory 可以组合&#xff0c;并通过…

[项目设计] 从零实现的高并发内存池(四)

&#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[高并发内存池] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 ​ 目录 6.内存回收 6.1 ThreadCache回收内存 6.2 CentralCache回收内存 Rele…

Doris实战——金融壹账通指标中台的应用实践

目录 前言 一、业务痛点 二、早期架构挑战 三、架构升级 四、一体化指标数据平台 4.1 构建指标体系 4.2 构建指标平台功能 五、Doris指标应用实践 六、未来规划 原文大佬的这篇指标中台的应用实践有借鉴意义&#xff0c;这里摘抄下来用作学习和知识沉淀。 前言 在搭建…

20240305-2-海量数据处理常用技术概述

海量数据处理常用技术概述 如今互联网产生的数据量已经达到PB级别&#xff0c;如何在数据量不断增大的情况下&#xff0c;依然保证快速的检索或者更新数据&#xff0c;是我们面临的问题。 所谓海量数据处理&#xff0c;是指基于海量数据的存储、处理和操作等。因为数据量太大无…

【机器人最短路径规划问题(栅格地图)】基于遗传算法求解

代码获取方式&#xff1a;QQ&#xff1a;491052175 或者 私聊博主获取 基于遗传算法求解机器人最短路径规划问题&#xff08;栅格地图&#xff09;的仿真结果 仿真结果&#xff1a; 路径长度的变化曲线&#xff1a; 遗传算法优化后的机器人避障路径&#xff1a;

裸机编程的几种模式、架构、缺陷

目录 裸机编程模式/架构 1&#xff1a;初始化代码的编写 裸机编程模式/架构 2&#xff1a;轮询模式 裸机编程模式/架构 3&#xff1a;轮询加中断执行模式 裸机编程模式/架构 4&#xff1a;中断定时器主循环的前后台架构 裸机编程模式/架构 5&#xff1a;前后台 状态机架构…

初次实战SQL注入

目录 1.判断漏洞是否存在 2.判断注入类型&#xff08;数字型/字符型&#xff09; 3.猜列数 4.联合查询判断回显位 6.获取数据库表明 此实验为本人学习内容&#xff0c;从未攻击任何网站&#xff01;&#xff01;&#xff01;请伙伴们同样遵纪守法&#xff01;&#xff01;…

24计算机考研深大经验分享(计算机专业考研综合安排)

文章目录 背景科目选择高数选课一轮二轮冲刺阶段 线代一轮二轮 概率论计算机学科专业基础408数据结构计算机组成原理操作系统计算机网络总结 英语政治 末言 背景 首先贴一下初试成绩。这篇分享主要是给零基础的同学使用的&#xff0c;基础好的同学可以自行了解补充一下&#xf…

CTP-API开发系列之柜台系统简介

CTP-API开发系列之柜台系统简介 CTP-API开发系列之柜台系统简介中国金融市场结构---交易所柜台系统通用柜台系统极速柜台系统主席与次席 CTP柜台系统CTP组件名称对照表CTP柜台系统程序包CTP柜台系统架构图 CTP-API开发系列之柜台系统简介 中国金融市场结构—交易所 我们知道提…

【Flink入门修炼】2-2 Flink State 状态

什么是状态&#xff1f;状态有什么作用&#xff1f;如果你来设计&#xff0c;对于一个流式服务&#xff0c;如何根据不断输入的数据计算呢&#xff1f;又如何做故障恢复呢&#xff1f; 一、为什么要管理状态 流计算不像批计算&#xff0c;数据是持续流入的&#xff0c;而不是…