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

news2024/9/23 7:29:46

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


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


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

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       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 );

/*
*******************************************************************
*                            函数声明
*******************************************************************
*/
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()

OSStartHighRdy
	LDR		R0, = NVIC_SYSPRI14              ; 设置  PendSV 异常优先级为最低
	LDR     R1, = NVIC_PENDSV_PRI
	STRB    R1, [R0]
	
	MOVS    R0, #0                           ; 设置psp的值为0,开始第一次上下文切换
	MSR     PSP, R0
	
	LDR     R0, =NVIC_INT_CTRL               ; 触发PendSV异常
	LDR     R1, =NVIC_PENDSVSET
	STR     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的值加载到R0
	CBZ     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 到 OSTCBCurPtr
	
	LDR     R0, [R2]                          ; 加载 OSTCBHighRdyPtr 到 R0
	LDMIA   R0!, {R4-R11}                     ; 加载需要手动保存的信息到CPU寄存器R4-R11
	
	MSR     PSP, R0                           ; 更新PSP的值,这个时候PSP指向下一个要执行的任务的堆栈的栈底(这个栈底已经加上刚刚手动加载到CPU寄存器R4-R11的偏移)
	ORR     LR, LR, #0x04                     ; 确保异常返回使用的堆栈指针是PSP,即LR寄存器的位2要为1
	CPSIE   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异常,开始上下文切换
;********************************************************************************************************
OSStartHighRdy
	LDR		R0, = NVIC_SYSPRI14              ; 设置  PendSV 异常优先级为最低
	LDR     R1, = NVIC_PENDSV_PRI
	STRB    R1, [R0]
	
	MOVS    R0, #0                           ; 设置psp的值为0,开始第一次上下文切换
	MSR     PSP, R0
	
	LDR     R0, =NVIC_INT_CTRL               ; 触发PendSV异常
	LDR     R1, =NVIC_PENDSVSET
	STR     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中断, 从而实现系统调度的自动切换。

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

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

相关文章

连锁线上线下门店管理为什么需要使用分账系统

连锁线上线下门店管理的复杂性要求采用有效的分账系统,以实现降低税务风险、保障资金安全和合规运营。首先,通过分账系统,可以清晰记录不同门店的收入和支出情况,有利于确保税务合规性。其次,分账系统有助于将不同门店…

Scratch 第十六课-弹珠台游戏

第十六课-弹珠台游戏 大家好,今天我们一起做一款弹珠台scratch游戏,我们也可以叫它弹球游戏!这款游戏在刚出来的时候非常火爆。小朋友们要认真学习下! 这节课的学习目标 物体碰撞如何处理转向问题。复习键盘对角色的控制方式。…

23.基于springboot + vue实现的前后端分离-在线旅游网站系统(项目 + 论文PPT)

项目介绍 本旅游网站系统采用的数据库是MYSQL ,使用 JSP 技术开发,在设计过程中,充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。 技术选型 后端: SpringBoot Mybatis 数据库 : MyS…

Django官网项目 二

官网地址:Writing your first Django app, part 2 | Django documentation | Django 创建模组: 注册model (bug:没有加后面的逗号) 在manage.py 的目录下: python manage.py makemigrations polls pyth…

打印机状态错误的解决方法

在电脑操作中,用户遇到打印机状态错误的问题,但不知道怎么操作才可以解决问题?接下来小编介绍打印机状态错误的解决方法,帮助大家轻松解决打印机出现错误状态的问题,快速恢复打印机的正常工作状态,从而满足…

固定资产管理系统建设方案

固定资产管理系统需求要点: 1. 实现公司内部固定资产管理全生命周期管理,包括资产采购、资产入库、资产领用、资产借用、资产归还、资产报废、资产维修、资产调拨等全过程管理。 2. 可实现集团内部固定资产盘点管理,包括盘点计划、盘点查询等…

多平台拼音输入法软件的开发

拼音输入法从上个世纪发展到现在, 已经发展了几十年了, 技术上已经非常成熟了. 换句话说, 就是实际上没多少技术含量, 随便来个人就能手搓一个. 本文介绍一个简单的多平台拼音输入法软件的设计和实现, 支持 GNU/Linux (ibus) 平台 (PC) 和 Android 平台 (手机). 目录 1 中文输…

【C++庖丁解牛】默认成员函数

📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 前言1. 构造函数1.1 …

低空经济20人|卓翼智能任雪峰:以技术驱动市场,引领无人机细分领域创新

作为国内系留无人机领域的领头羊企业,卓翼智能致力于提供智能无人系统解决方案。本期“低空经济20人”请到卓翼智能CEO任雪峰分享他对系留无人机研发应用的经验以及未来无人机行业生态发展的观点。 如今,无人机的应用场景逐渐广泛,在社会发展…

pycharm社区版+miniconda 环境配置学习

使用电脑为win10 64bit 一、下载: 1.pycharm社区版地址: 其他版本 - PyCharm 2.miniconda下载地址: Index of /anaconda/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 3.python下载地址: https://www.python.or…

百度AI,能否“投”出未来?

图片|freeflo.ai ©自象限原创 作者丨程心、罗辑 2月28日,百度发布了2023年四季度财报及全年未经审计的财务报告,AI大模型带来的收入和利润成为最大的亮点。 财报显示,2023年百度集团总营收达1345.98亿元,同比增…

一文读懂Penpad 以 Fair Launch 方式推出的首个资产 PEN

随着 2 月 28 日比特币重新站上 6 万美元的高峰后,标志着加密市场正在进入新一轮牛市周期。在 ETF 的促进作用下,加密市场不断有新的资金流入,加密货币总市值不断攀升。Layer2 市场率先做出了反应,有数据显示,当前以太…

混合注意力机制全新工作!性能表现SOTA!准确率达98.53%

在神经网络中,为增强模型对数据中重要特征的识别和处理能力,研究者们通常会同时使用不同类型的注意力机制,包括空间注意力、通道注意力、自注意力等。这种结合了多种不同类型注意力机制的技术就是混合注意力机制。 混合注意力机制的研究和应…

Thingsboard本地源码部署教程

本章将介绍ThingsBoard的本地环境搭建,以及源码的编译安装。本机环境:jdk11、maven 3.6.2、node v12.18.2、idea 2023.1、redis 6.2 环境安装 开发环境要求: Jdk 11 版本 ;Postgresql 9 以上;Maven 3.6 以上&#xf…

电脑无法开启虚拟化

vm开启虚拟化失败 如果出现以下错误,需要进入bios界面,不同电脑进入界面的方式不一样。这里以hp为例: 1、关机后,按住开机键和f10,进入如下界面(语言在主菜单里面) 2、选择先进和下面的系统选…

检修弧形导轨需遵守的原则

弧形导轨被广泛应用在各行各业中,特别是工业自动化领域中,是自动化机械设备中重要的传动零部件。在使用弧形导轨时,为防止意外发生或对机械设备造成损坏,在检修过程中必须遵守以下一些原则: ●安全第一:出现…

windows安装pytorch(anaconda安装)

文章目录 前言一、安装anaconda1、进入官网下载(1)点击view all Installers(2)下载需要的版本 2、一顿默认安装就行(到这一步这样填)3、进入开始找到Anaconda Prompt,点击进入到base环境 二、新…

docker部署aria2-pro

前言 我平时有一些下载视频和一些资源文件的需求,有时候需要离线下载,也要速度比较快的方式 之前我是用家里的玩客云绝育之后不再写盘当下载机用的,但是限制很多 我发现了aria2 这个下载器非常适合我,而有个大佬又在原来的基础…

Linux笔记-3

软件安装 概述 在Linux中,软件安装分为3种方式:绿色安装(压缩包解压之后就能直接使用),rpm安装(类似于Windows中的exe或者msi文件),yum安装 RPM(Red Hat Package Manager):红帽提供的软件包的管理工具。可以通过rpm命…

世界的本质是旋转(5)-在复平面上驱动软件无线电SDR交换BPSK波形

在上一篇文章中,我们介绍了复平面、拍照采样的一些思维实验。从本节开始,转入现实应用,通过控制复平面向量的位置,实现一个完整的BPSK全双工通信通道。 发射方:通过控制复平面向量在各个时刻的位置来携带信息的技术&a…