(学习日记)2024.03.04:UCOSIII第六节:main函数+前六节总结

2024-03-05 03:52

本文主要是介绍(学习日记)2024.03.04:UCOSIII第六节:main函数+前六节总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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


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


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

2024.03.04

  • 十四、UCOSIII:main()函数
  • 十五、UCOSIII:前六节总结
    • 1、程序的关键
    • 2、PendSV异常的作用
    • 3、前六节代码的运行流程
      • 1. 手动配置任务1为 优先级最高的任务
      • 2.触发PendSV异常
      • 3. 运行异常代码,把当前运行的任务改成优先级最高的任务
      • 4. 运行任务1
      • 5. 手动配置任务2为 优先级最高的任务,然后触发PendSV异常
      • 6. 运行异常代码,把当前运行的任务改成优先级最高的任务
      • 7.运行任务2
      • 8. 手动配置任务1为 优先级最高的任务,然后触发PendSV异常
      • 9. 运行异常代码,把当前运行的任务改成优先级最高的任务
      • 10. 重复流程4 - 9,直至程序结束
    • 4、第六节之后代码的运行流程

十四、UCOSIII:main()函数

main()函数在文件app.c中编写,其中app.c文件如下

/*
*******************************************************************
*                          包含的头文件
*******************************************************************
*/
#include"os.h"
#include"ARMCM3.h"/*
*******************************************************************
*                            宏定义
*******************************************************************
*//*
*******************************************************************
*                          全局变量
*******************************************************************
*/uint32_t flag1;
uint32_t flag2;/*
*******************************************************************
*                        TCB & STACK &任务声明
*******************************************************************
*/
#define  TASK1_STK_SIZE       20
#define  TASK2_STK_SIZE       20static   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 );/*
*******************************************************************
*                            函数声明
*******************************************************************
*/
void delay(uint32_t count);/*
*******************************************************************
*                            main()函数
*******************************************************************
*/
/*
* 注意事项:1、该工程使用软件仿真,debug需选择 Ude Simulator
*         2、在Target选项卡里面把晶振Xtal(Mhz)的值改为25,默认是12,
*              改成25是为了跟system_ARMCM3.c中定义的__SYSTEM_CLOCK相同,
*              确保仿真的时候时钟一致
*/
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);
}/*
*******************************************************************
*                           函数实现
*******************************************************************
*/
/* 软件延时 */
void delay (uint32_t count)
{for (; count!=0; count--);
}/* 任务1 */
void Task1( void *p_arg )
{for ( ;; ) {flag1 = 1;delay( 100 );flag1 = 0;delay( 100 );/* 任务切换,这里是手动切换 */OSSched();}
}/* 任务2 */
void Task2( void *p_arg )
{for ( ;; ) {flag2 = 1;delay( 100 );flag2 = 0;delay( 100 );/* 任务切换,这里是手动切换 */OSSched();}
}

所有代码在本小节之前都有循序渐进的讲解,这里这是融合在一起放在main()函数中。
其实现在Task1和Task2并不会真正的自动切换,而是在各自的函数体里面加入了OSSched()函数来实现手动切换

/* 任务切换,实际就是触发PendSV异常,然后在PendSV异常中进行上下文切换 */
void OSSched (void)
{if( OSTCBCurPtr == OSRdyList[0].HeadPtr ){OSTCBHighRdyPtr = OSRdyList[1].HeadPtr;}else{OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;}OS_TASK_SW();
}

OSSched()函数的调度算法很简单,即如果当前任务是任务1,那么下一个任务就是任务2,如果当前任务是任务2,那么下一个任务就是任务1, 然后再调用OS_TASK_SW()函数触发PendSV异常,最后在PendSV异常里面实现任务的切换。
在往后的章节中,我们将继续完善,加入SysTick中断, 从而实现系统调度的自动切换。

OS_TASK_SW()函数其实是一个宏定义,具体是往中断及状态控制寄存器SCB_ICSR的位28(PendSV异常启用位)写入1, 从而触发PendSV异常。OS_TASK_SW()函数在os_cpu.h文件中实现
在这里插入图片描述

仿真后可得flag1 和 flag2的图像为
在这里插入图片描述

十五、UCOSIII:前六节总结

1、程序的关键

如果从头到尾把前六节做下来的话,可以体会到目前程序的关键
那就是 触发PendSV异常
即以下语句:

NVIC_INT_CTRL = NVIC_PENDSVSET

其中NVIC_INT_CTRL声明如下,即中断控制及状态寄存器 SCB_ICSR

#ifndef  NVIC_INT_CTRL
#define  NVIC_INT_CTRL    *((CPU_REG32 *)0xE000ED04)   /* 中断控制及状态寄存器 SCB_ICSR */
#endif

NVIC_PENDSVSET声明如下,即一个第28位为1的十六进制数

#ifndef  NVIC_PENDSVSET
#define  NVIC_PENDSVSET   0x10000000    /* 触发PendSV异常的值 Bit28:PENDSVSET */
#endif

不管是在每个任务中都会用到的OS_TASK_SW()函数

#define  OS_TASK_SW()    NVIC_INT_CTRL = NVIC_PENDSVSET

还是再启动函数OSStart(&err)中的任务切换函数OSStartHighRdy()

OSStartHighRdyLDR		R0, = NVIC_SYSPRI14              ; 设置  PendSV 异常优先级为最低LDR     R1, = NVIC_PENDSV_PRISTRB    R1, [R0]MOVS    R0, #0                           ; 设置psp的值为0,开始第一次上下文切换MSR     PSP, R0LDR     R0, =NVIC_INT_CTRL               ; 触发PendSV异常LDR     R1, =NVIC_PENDSVSETSTR     R1, [R0]CPSIE   I                                 ; 开中断

全部都是通过给中断控制及状态寄存器 SCB_ICSR赋值0x10000000触发PendSV异常

2、PendSV异常的作用

如果说程序的关键是触发PendSV异常
那么PendSV异常的作用就是 切换任务
在PendSV异常函数中,代码如下

;********************************************************************************************************
;                                          PendSVHandler异常
;********************************************************************************************************
PendSV_Handler
; 任务的保存,即把CPU寄存器的值存储到任务的堆栈中	CPSID   I                                 ; 关中断,NMI和HardFault除外,防止上下文切换被中断	MRS     R0, PSP                           ; 将psp的值加载到R0CBZ     R0, OS_CPU_PendSVHandler_nosave   ; 判断R0,如果值为0则跳转到OS_CPU_PendSVHandler_nosave; 进行第一次任务切换的时候,R0肯定为0;-----------------------一、保存上文-----------------------------
; 任务的切换,即把下一个要运行的任务的栈内容加载到CPU寄存器中
; 在进入PendSV异常的时候,当前CPU的xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0会自动存储到当前任务堆栈,同时递减PSP的值
;--------------------------------------------------------------STMDB   R0!, {R4-R11}                     ; 手动存储CPU寄存器R4-R11的值到当前任务的堆栈LDR     R1, = OSTCBCurPtr                 ; 加载 OSTCBCurPtr 指针的地址到R1,这里LDR属于伪指令LDR     R1, [R1]                          ; 加载 OSTCBCurPtr 指针到R1,这里LDR属于ARM指令STR     R0, [R1]                          ; 存储R0的值到	OSTCBCurPtr->OSTCBStkPtr,这个时候R0存的是任务空闲栈的栈顶;-----------------------二、切换下文-----------------------------
; 实现 OSTCBCurPtr = OSTCBHighRdyPtr
; 把下一个要运行的任务的栈内容加载到CPU寄存器中
; 任务的切换,即把下一个要运行的任务的堆栈内容加载到CPU寄存器中
;--------------------------------------------------------------
OS_CPU_PendSVHandler_nosave  ; OSTCBCurPtr = OSTCBHighRdyPtr;LDR     R0, = OSTCBCurPtr                 ; 加载 OSTCBCurPtr 指针的地址到R0,这里LDR属于伪指令LDR     R1, = OSTCBHighRdyPtr             ; 加载 OSTCBHighRdyPtr 指针的地址到R1,这里LDR属于伪指令LDR     R2, [R1]                          ; 加载 OSTCBHighRdyPtr 指针到R2,这里LDR属于ARM指令STR     R2, [R0]                          ; 存储 OSTCBHighRdyPtr 到 OSTCBCurPtrLDR     R0, [R2]                          ; 加载 OSTCBHighRdyPtr 到 R0LDMIA   R0!, {R4-R11}                     ; 加载需要手动保存的信息到CPU寄存器R4-R11MSR     PSP, R0                           ; 更新PSP的值,这个时候PSP指向下一个要执行的任务的堆栈的栈底(这个栈底已经加上刚刚手动加载到CPU寄存器R4-R11的偏移)ORR     LR, LR, #0x04                     ; 确保异常返回使用的堆栈指针是PSP,即LR寄存器的位2要为1CPSIE   I                                 ; 开中断BX      LR                                ; 异常返回,这个时候任务堆栈中的剩下内容将会自动加载到xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参); 同时PSP的值也将更新,即指向任务堆栈的栈顶。在STM32中,堆栈是由高地址向低地址生长的。NOP                                       ; 为了汇编指令对齐,不然会有警告END                                       ; 汇编文件结束

可以清楚看到系统在这里只完成了一个操作

  • 实现OSTCBCurPtr = OSTCBHighRdyPtr,即把当前运行的任务改成优先级最高的任务,实现任务的切换

然后在每个任务完成后再触发PendSV异常,实现整个系统的流转运行

3、前六节代码的运行流程

省略初始化、宏定义、变量定义等等一系列流程,我们只看任务运行流程

1. 手动配置任务1为 优先级最高的任务

void OSStart (OS_ERR *p_err)
{if ( OSRunning == OS_STATE_OS_STOPPED ) {(1)/* 手动配置任务1先运行 */OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;(2)/* 启动任务切换,不会返回 */OSStartHighRdy();(3)/* 不会运行到这里,运行到这里表示发生了致命的错误 */*p_err = OS_ERR_FATAL_RETURN;}else{*p_err = OS_STATE_OS_RUNNING;}
}

2.触发PendSV异常

;********************************************************************************************************
;                                          开始第一次上下文切换
; 1、配置PendSV异常的优先级为最低
; 2、在开始第一次上下文切换之前,设置psp=0
; 3、触发PendSV异常,开始上下文切换
;********************************************************************************************************
OSStartHighRdyLDR		R0, = NVIC_SYSPRI14              ; 设置  PendSV 异常优先级为最低LDR     R1, = NVIC_PENDSV_PRISTRB    R1, [R0]MOVS    R0, #0                           ; 设置psp的值为0,开始第一次上下文切换MSR     PSP, R0LDR     R0, =NVIC_INT_CTRL               ; 触发PendSV异常LDR     R1, =NVIC_PENDSVSETSTR     R1, [R0]CPSIE   I                                 ; 开中断

在这里插入图片描述

3. 运行异常代码,把当前运行的任务改成优先级最高的任务

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

4. 运行任务1

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

5. 手动配置任务2为 优先级最高的任务,然后触发PendSV异常

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

#define  OS_TASK_SW()   NVIC_INT_CTRL = NVIC_PENDSVSET

6. 运行异常代码,把当前运行的任务改成优先级最高的任务

在这里插入图片描述

7.运行任务2

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

8. 手动配置任务1为 优先级最高的任务,然后触发PendSV异常

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

9. 运行异常代码,把当前运行的任务改成优先级最高的任务

在这里插入图片描述

10. 重复流程4 - 9,直至程序结束

在这里插入图片描述

4、第六节之后代码的运行流程

在第七节及之后,我们将逐渐把任务切换交给SysTick中断, 从而实现系统调度的自动切换。

这篇关于(学习日记)2024.03.04:UCOSIII第六节:main函数+前六节总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/775258

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识