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

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


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


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

2024.03.04

  • 十四、UCOSIII:main()函数
    • 1、编写函数并仿真
    • 2、常见错误:
      • 1. 只有一个波峰
      • 2. 只有一条直线
  • 十五、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()函数

1、编写函数并仿真

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的图像为
在这里插入图片描述

2、常见错误:

1. 只有一个波峰

在这里插入图片描述
这个问题主要是编译器优化等级太低,导致堆栈冲突,OSTCBHighRdyPtr指针OSTCBCurPtr指针STMDB指令运行时被错误覆盖
在这里插入图片描述
此时 任务的切换 被破坏,系统陷入死循环,示波器 中只有一个波峰

解决方法是修改编译器优化等级
在这里插入图片描述
修改为O1或其他,可以挨个试
改完一定要 Build 才会生效

2. 只有一条直线

在这里插入图片描述
这个问题主要是编译器优化等级太高,导致时延函数被“优化”了
在这里插入图片描述
前面有绿块才代表被编译,delay直接被跳过去了
NND,偷我代码是吧!

解决办法为使用volatile关键字
volatile英文意思为易变的、易挥发的,在声明变量时加入这个关键字,意思就是告诉编译器这个变量随时能被外部修改,不要对此变量进行优化,代码中引用此变量必须访问内存中实际变量。

************************************************************************************************************************
*                                                    函数实现
************************************************************************************************************************
*/
/* 软件延时 */
void delay (volatile uint32_t count)
{for(; count!=0; count--);
}

给delay函数的 count变量加一个volatile关键字。
编译!仿真!
在这里插入图片描述

搞定!

参考资料:
C语言volatile用法/Keil编译器优化/delay被编译器优化

题外话:
这几个错误足足耗费了我三天时间,一点一点跟着代码看寄存器和变量的变化,不懂就搜,就查,提出各种猜测又全部否决
在这里插入图片描述
深夜公司里只有一个人,倒一杯热水看代码,喝第一口时水已经凉了
一个人的学习是漫漫长征,荆棘密布,坎坷不断。
但还好我们还有时间,与诸位共勉

十五、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中断, 从而实现系统调度的自动切换。

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

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

相关文章

符号函数Sign(博途PLC SCL代码)

符号函数在ADRC自抗扰算法里会有涉及,同时在滑膜控制里也会用到,这篇博客我们介绍符号函数在博途PLC里的实现。 1、ADRC自抗扰算法: https://rxxw-control.blog.csdn.net/article/details/126547180https://rxxw-control.blog.csdn.net/article/details/1265471802、模拟量…

视频生成模型Sora的全面解析:从AI绘画、ViT到ViViT、DiT、VDT、NaViT、VideoPoet

视频生成模型Sora的全面解析:从AI绘画、ViT到ViViT、DiT、VDT、NaViT、VideoPoet 真没想到,举例视频生成上一轮的集中爆发才过去三个月,没想OpenAI一出手,该领域又直接变天了自打2.16日OpenAI发布sora以来,不但把同时…

软考58-上午题-【数据库】-分布式数据库

一、四个透明 二、四种性质 三、真题 真题1: 真题2: 真题3: 真题4: 真题5:

5G智能制造纺织工厂数字孪生可视化平台,推进纺织行业数字化转型

5G智能制造纺织工厂数字孪生可视化平台,推进纺织行业数字化转型。纺织工业作为传统制造业的重要组成部分,面临着转型升级的紧迫需求。随着5G技术的快速发展,智能制造成为纺织工业转型升级的重要方向。数字孪生可视化平台作为智能制造的核心技…

力扣76. 最小覆盖子串(滑动窗口)

Problem: 76. 最小覆盖子串 文章目录 题目描述思路复杂度Code 题目描述 思路 1.定义两个map集合need和window(以字符作为键,对应字符出现的个数作为值),将子串t存入need中; 2.定义左右指针left、right均指向0&#xff…

【AI视野·今日Sound 声学论文速览 第五十期】Fri, 1 Mar 2024

AI视野今日CS.Sound 声学论文速览 Fri, 1 Mar 2024 Totally 9 papers 👉上期速览✈更多精彩请移步主页 Daily Sound Papers Probing the Information Encoded in Neural-based Acoustic Models of Automatic Speech Recognition Systems Authors Quentin Raymondau…

Git分布式管理-头歌实验本地版本库

一、本地版本库创建 任务描述 本地Git操作三部曲是“修改-添加-提交”,即先要在本地仓库进行添加、删除或编辑等修改,然后将本地所做的修改添加至暂存区。添加至暂存区的这些本地修改,并未提交到本地仓库,需要执行提交命令才能将暂…

CAN通信篇 - CanTrcv模块配置(二)

文章目录 CanTrcvConfigSetCanTrcvChannelCanTrcvDioChannelAccess 总结 CanTrcv模块配置也比较简单,主要是配置CanTrcv的行为。有一些工程甚至没有包含CanTrcv配置模块(CanTrcv配置代码已通过手写代码方式嵌入工程)。下图所示为CanTrcv模块配置容器。 CanTrcvConfi…

实名制交友-智能匹配-仿二狗交友系统-TP6+uni-APP小程序H5公众号-源码交付-支持二开!

一、代码风格 通常不同的开发者具备不同的代码风格,但为了保证语音交友系统开发质量,在编码前需要进行代码风格的统一,通过制定一定的规则,约束开发者的行为。具有统一风格的代码才能更清晰、更完整、更容易理解、更方便后期维护…

09 Qt扩展LineEdit组件:Input输入框

系列文章目录 01 Qt自定义风格控件的基本原则-CSDN博客 02 从QLabel聊起:自定义控件扩展-图片控件-CSDN博客 03 从QLabel聊起:自定义控件扩展-文本控件-CSDN博客 04 自定义Button组件:令人抓狂的QToolButton文本图标居中问题-CSDN博客 0…

Mysql80服务无法启动请输入Net helpMsg3534以获得更多的帮助

起因&情景: 朋友正在操作数据库,然后电脑突然死机,再重启电脑后启动数据库服务报: 然后朋友尝试各种操作都没有办法正常启动, 一、网上解决方案:(先别操作) 1 删掉&#xff1a…

爬取博客的图片并且将它存储到响应的目录

目录 前言 思想 注意 不多说解释了,贴代码吧 config.json Get_blog_img.py 把之前的写的代码也贴上 Get_blog_id.py 主函数 main.py 运行结果 前言 在上一篇博客中我们介绍了如何爬取博客链接 利用python爬取本站的所有博客链接-CSDN博客文章浏览阅读74…

MatteFormer:Transformer-based image matting via prior-tokens

【CVPR2022】MatteFormer: Transformer-Based Image Matting via Prior-Tokens-CSDN博客文章浏览阅读1.2k次。【CVPR2022】 MatteFormer: Transformer-Based Image Matting via Prior-Tokens_matteformerhttps://blog.csdn.net/Thinkobj/article/details/128209388本文核心2点&…

Vmware Fusion 13 安装CentOS、Ubuntu、Windows11虚拟机

Vmware Fusion 13 安装CentOS、Ubuntu、Windows11虚拟机 背景:每次安装都要到处找资源,现在一篇文章足以 文章目录 Vmware Fusion 13 安装CentOS、Ubuntu、Windows11虚拟机一、Mac中安装CentOS虚拟机1️⃣:准备镜像2️⃣:创建虚拟…

GitHub热榜第二的sora同款工具——DUSt3R

目录 Sora - 探索AI视频模型的无限可能 传送门: Sora - 探索AI视频模型的无限可能 随着人工智能技术的飞速发展,AI视频模型已成为科技领域的新热点。而在这个浪潮中,OpenAI推出的首个AI视频模型Sora,以其卓越的性能和前瞻性的技…

《低代码平台开发实践:基于React》读书心得与实战体验

低代码平台开发实践标题 🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 📘 一、引…

现代信号处理学习笔记(三)信号检测

通过观测数据判断信号是否存在,这一问题称为信号检测。 目录 前言 一、统计假设检验 1、信号检测的基本概念 2、信号检测理论测度 比率测度 概率测度 3、决策理论空间 二、概率密度函数与误差函数 1、概率密度函数 2、误差函数与补余误差函数 三、检测概…

工业镜头的重要参数之视场、放大倍率、芯片尺寸--51camera

今天来简单介绍下工业镜头中常用的参数中的三个: 1、视场 视场(FOV)也称视野,是指能被视觉系统观察到的物方可视范围。 对于镜头而言,可观察到的视场跟镜头放大倍率及相机芯片选择有关。因此需要根据被观察物体的尺寸&#xff…

史上最全的大数据开发八股文【自己的吐血总结】

自我介绍 我本硕都是双非计算机专业,从研一下开始学习大数据开发的相关知识,从找实习到秋招,我投递过100公司,拿到过10的offer,包括滴滴、字节、蚂蚁、携程、蔚来、去哪儿等大厂(岗位都是大数据开发&#…

快速上手:在 Android 设备上运行 Pipy

Pipy 作为一个高性能、低资源消耗的可编程代理,通过支持多种计算架构和操作系统,Pipy 确保了它的通用性和灵活性,能够适应不同的部署环境,包括但不限于云环境、边缘计算以及物联网场景。它能够在 X86、ARM64、海光、龙芯、RISC-V …