UCOS-III笔记
1.单片机程序分类:轮询程序,前后台程序,多任务系统程序
2.多任务系统伪代码

1 int flag1 = 0;
2 int flag2 = 0;
3 int flag3 = 0;
4
5 int main(void)
6 {
7 /* 硬件相关初始化 */
8 HardWareInit();
9
10 /* OS 初始化 */
11 RTOSInit();
12
13 /* OS 启动,开始多任务调度,不再返回 */
14 RTOSStart();
15 }
16
17 void ISR1(void)
18 {
19 /* 置位标志位 */
20 flag1 = 1;
21 }
23 void ISR2(void)
24 {
25 /* 置位标志位 */
26 flag2 = 2;
27 }
28
29 void ISR3(void)
30 {
31 /* 置位标志位 */
32 flag3 = 1;
33 }
34
35 voidDoSomething1(void)
36 {
37 /* 无限循环,不能返回 */
38 for (;;)
39 {
40 /* 任务实体 */
41 if (flag1)
42 {
43 }
44 }
45 }
46
47 voidDoSomething2(void)
48 {
49 /* 无限循环,不能返回 */
50 for (;;)
51 {
52 /* 任务实体 */
53 if (flag2)
54 {
55 }
56 }
57 }
58
59 voidDoSomething3(void)
60 {
61 /* 无限循环,不能返回 */
62 for (;;)
63 {
64 /* 任务实体 */
65 if (flag3)
66 {
67 }
68 }
69 }
3.三类系统异同

4.任务是什么:
在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按
照顺序完成各种事情。在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的
且无法返回的函数,这个函数我们称为任务。
5.多任务系统中,要为每一个任务分配独立的栈空间
#define TASK1_STK_SIZE 128 (1)
#define TASK2_STK_SIZE 128
static CPU_STK Task1Stk[TASK1_STK_SIZE];(2)
static CPU_STK Task2Stk[TASK2_STK_SIZE];
6.重要宏定义
1 #ifndef CPU_H
2 #define CPU_H
3
4 typedef unsigned short CPU_INT16U;
5 typedef unsigned int CPU_INT32U;
6 typedef unsigned char CPU_INT08U;
7
8 typedef CPU_INT32U CPU_ADDR;
9
10 /* 栈数据类型重定义 */
11 typedef CPU_INT32U CPU_STK;
12 typedef CPU_ADDR CPU_STK_SIZE;
13
14 typedef volatile CPU_INT32U CPU_REG32;
15
16 #endif/* CPU_H */
7.系统为了顺利的调度任务,为每个任务都额外定义了一个任务控制块 TCB(Task Con-
trolBlock),这个任务控制块就相当于任务的身份证,里面存有任务的所有信息,比如任务的栈,
任务名称,任务的形参等。有了这个任务控制块之后,以后系统对任务的全部操作都可以通过这个 TCB 来实现。TCB 是一个新的数据类型
1 /* 任务控制块重定义 */
2 typedefstruct os_tcb OS_TCB;(1)
3
4 /* 任务控制块数据类型声明 */
5 struct os_tcb {(2)
6 CPU_STK *StkPtr;
7 CPU_STK_SIZE StkSize;
8 };
1 static OS_TCB Task1TCB;
2 static OS_TCB Task2TCB;
8.任务的栈,任务的函数实体,任务的 TCB 最终需要联系起来才能由系统进行统一调度。那么这
个联系的工作就由任务创建函数 OSTaskCreate 来实现,该函数在 os_task.c
1 void OSTaskCreate (OS_TCB *p_tcb,(1)//任务控制块指针
2 OS_TASK_PTR p_task,(2)//p_task 是任务函数名,类型为 OS_TASK_PTR
3 void *p_arg,(3)//p_arg 是任务形参,用于传递任务参数
4 CPU_STK *p_stk_base, (4)//p_stk_base 用于指向任务栈的起始地址
5 CPU_STK_SIZE stk_size, (5)//stk_size 表示任务栈的大小
6 OS_ERR *p_err) (6)//p_err 用于存错误码
7 {
8 CPU_STK *p_sp;
9
10 p_sp = OSTaskStkInit (p_task,(7)
11 p_arg,
12 p_stk_base,
13 stk_size);
14 p_tcb->StkPtr = p_sp;(8)
15 p_tcb->StkSize = stk_size;(9)
16
17 *p_err = OS_ERR_NONE;(10)
18 }
9.OSTaskStkInit() 是任务栈初始化函数。当任务第一次运行的时候,
加载到 CPU 寄存器的参数就放在任务栈里面,在任务创建的时候,预先初始化好栈。
1 CPU_STK *OSTaskStkInit (OS_TASK_PTR p_task,(1)//p_task 是任务名,指示着任务的入口地址,在任务切换的时候,需要加载到 R15,即 PC 寄存器,这样 CPU 就可以找到要运行的任务
2 void *p_arg,(2)//p_arg 是任务的形参,用于传递参数,在任务切换的时候,需要加载到寄存器 R0。R0 寄存器通常用来传递参数。
3 CPU_STK *p_stk_base,(3)//p_stk_base 表示任务栈的起始地址
4 CPU_STK_SIZE stk_size)(4)
5 {
6 CPU_STK *p_stk;
7
8 p_stk = &p_stk_base[stk_size];(5)//获取任务栈的栈顶地址,ARMCM3 处理器的栈是由高地址向低地址生长的。所以初始化栈之前,要获取到栈顶地址,然后栈地址逐一递减即可。
9 /* 异常发生时自动保存的寄存器 */(6)
10 *--p_stk = (CPU_STK)0x01000000u; /* xPSR 的 bit24 必须置 1 */
11 *--p_stk = (CPU_STK)p_task; /* R15(PC) 任务的入口地址 */
12 *--p_stk = (CPU_STK)0x14141414u; /* R14 (LR) */
13 *--p_stk = (CPU_STK)0x12121212u; /* R12 */
14 *--p_stk = (CPU_STK)0x03030303u; /* R3 */
15 *--p_stk = (CPU_STK)0x02020202u; /* R2 */
16 *--p_stk = (CPU_STK)0x01010101u; /* R1 */
17 *--p_stk = (CPU_STK)p_arg; /* R0 : 任务形参 */
18 /* 异常发生时需手动保存的寄存器 */(7)
19 *--p_stk = (CPU_STK)0x11111111u; /* R11 */
20 *--p_stk = (CPU_STK)0x10101010u; /* R10 */
21 *--p_stk = (CPU_STK)0x09090909u; /* R9 */
22 *--p_stk = (CPU_STK)0x08080808u; /* R8 */
23 *--p_stk = (CPU_STK)0x07070707u; /* R7 */
24 *--p_stk = (CPU_STK)0x06060606u; /* R6 */
25 *--p_stk = (CPU_STK)0x05050505u; /* R5 */
26 *--p_stk = (CPU_STK)0x04040404u; /* R4 */
27
28 return (p_stk);(8) //返回栈指针 p_stk,这个时候 p_stk 指向剩余栈的栈顶
29 }
10.任务创建好之后,我们需要把任务添加到一个叫就绪列表的数组里面,表示任务已经就绪,系统随时可以调度。
1 typedef struct os_rdy_list OS_RDY_LIST;(1)
2
3 struct os_rdy_list {(2)
4 OS_TCB *HeadPtr;
5 OS_TCB *TailPtr;
6 }; 1 #ifdef OS_GLOBALS
2 #define OS_EXT
3 #else
4 #define OS_EXT extern
5 #endif #define OS_CFG_PRIO_MAX 32u
OS_EXT OS_RDY_LIST OSRdyList[OS_CFG_PRIO_MAX]; 1 /* 将任务加入到就绪列表 */
2 OSRdyList[0].HeadPtr = &Task1TCB;(1)
3 OSRdyList[1].HeadPtr = &Task2TCB;(2)
11.OS 系统初始化一般是在硬件初始化完成之后来做的,主要做的工作就是初始化 μC/OS-III 中定义的全局变量。
1 void OSInit (OS_ERR *p_err)
2 {
3 OSRunning = OS_STATE_OS_STOPPED;(1)//系统用一个全局变量 OSRunning 来指示系统的运行状态,刚开始系统初始化的时候,默认为停止状态,即 OS_STATE_OS_STOPPED
4
5 OSTCBCurPtr = (OS_TCB *)0;(2)//全局变量 OSTCBCurPtr 是系统用于指向当前正在运行的任务的TCB 指针,在任务切换的时候用得到
6 OSTCBHighRdyPtr = (OS_TCB *)0;(3)//全局变量 OSTCBHighRdyPtr 用于指向就绪任务中优先级最高的任务的 TCB,在任务切换的时候用得到
7
8 OS_RdyListInit();(4)
9
10 *p_err = OS_ERR_NONE;(5)
11 }
OS_RdyListInit() 用于初始化全局变量 OSRdyList[],即初始化就绪
列表
1 OS_EXT OS_TCB *OSTCBCurPtr;
2 OS_EXT OS_TCB *OSTCBHighRdyPtr;
3 OS_EXT OS_RDY_LIST OSRdyList[OS_CFG_PRIO_MAX];
4 OS_EXT OS_STATE OSRunning;
1 #define OS_STATE_OS_STOPPED (OS_STATE)(0u)
2 #define OS_STATE_OS_RUNNING (OS_STATE)(1u)
1 void OS_RdyListInit(void)
2 {
3 OS_PRIO i;
4 OS_RDY_LIST *p_rdy_list;
5
6 for ( i=0u; i<OS_CFG_PRIO_MAX; i++ )
7 {
8 p_rdy_list = &OSRdyList[i];
9 p_rdy_list->HeadPtr = (OS_TCB *)0;
10 p_rdy_list->TailPtr = (OS_TCB *)0;
11 }
12 }
12.任务创建好,系统初始化完毕之后,就可以开始启动系统了
1 void OSStart (OS_ERR *p_err)
2 {
3 if ( OSRunning == OS_STATE_OS_STOPPED ) {(1)
4 /* 手动配置任务 1 先运行 */
5 OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;(2) //OSTCBHIghRdyPtr 指向第一个要运行的任务的 TCB
6
7 /* 启动任务切换,不会返回 */
8 OSStartHighRdy();(3)
9
10 /* 不会运行到这里,运行到这里表示发生了致命的错误 */
11 *p_err = OS_ERR_FATAL_RETURN;
12 }
13 else
14 {
15 *p_err = OS_STATE_OS_RUNNING;
16 }
17 }
OSStartHighRdy() 用于启动任务切换,即配置 PendSV 的优先级
为最低,然后触发 PendSV 异常,在 PendSV 异常服务函数中进行任务切换
1 ;*******************************************************************
2 ; 开始第一次上下文切换
3 ; 1、配置 PendSV 异常的优先级为最低
4 ; 2、在开始第一次上下文切换之前,设置 psp=0
5 ; 3、触发 PendSV 异常,开始上下文切换
6 ;*******************************************************************
7 OSStartHighRdy
8 LDR R0, = NVIC_SYSPRI14 ; 设置 PendSV 异常优先级为最低(1)
9 LDR R1, = NVIC_PENDSV_PRI
10 STRB R1, [R0]
11
12 MOVS R0, #0 ; 设置 psp 的值为 0 ,开始第一次上下文切换 (2)
13 MSR PSP, R0
14
15 LDR R0, =NVIC_INT_CTRL ; 触发 PendSV 异常(3)
16 LDR R1, =NVIC_PENDSVSET
17 STR R1, [R0]
18
19 CPSIE I ; 启用总中断,NMI 和 HardFault 除外(4)
20
21 OSStartHang
22 B OSStartHang ; 程序应永远不会运行到这里 5 ; 有关内核外设寄存器定义可参考官方文档:STM32F10xxx Cortex-M3 programming manual
6 ; 系统控制块外设 SCB 地址范围:0xE000ED00-0xE000ED3F
7 ;--------------------------------------------------------------------
8 NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制及状态寄存器 SCB_ICSR。
9 NVIC_SYSPRI14 EQU 0xE000ED22 ; 系统优先级寄存器 SCB_SHPR3:
10 ; bit16~23
11 NVIC_PENDSV_PRI EQU 0xFF ; PendSV 优先级的值(最低)。
12 NVIC_PENDSVSET EQU 0x10000000 ; 触发 PendSV 异常的值 Bit28:PENDSVSET
UCOS-III笔记的更多相关文章
- UCOS 杂项 笔记
1. 建立任务和建立数据队列 哪个先建立? 经过试验得知,数据队列和任务的建立先后没有顺序,都可以正常运行. 2.关于主函数的面试问题. 主函数写法有: int main() 和voi ...
- ucos III中任务之间的数据通信和任务划分
1. 如果将关系密切(比如两个任务之间需要经常收发数据)的若干功能分别用不同的任务来实现,则需要进行大量的任务之间数据通信和同步通信,这系统来说是一个很大的负担.因此应该将关系密切的若干功能组合成一个 ...
- UCOS III的时间片轮转调度的一个问题
1. 如果当前一个任务A在时间片未到来之前,主动放弃剩下的时间片,进入下一个任务B,那么下一个任务的的执行时间是多久? 书上说,是重置时间片,也就是说任务B也运行一个完整的时间片.
- Issue 6: 装机系列1,PC下windows系统安装指南
0.前言 接触电脑将近7年时间,多次说要写下这篇文章,一直未曾提笔,始终怕给人以误导.到如今,来来回回装系统的次数得超过百次了.本着不误导人的想法,本文试着总结一下装系统的基本方法和思路,但不会过多涉 ...
- uC/OS-III 时钟节拍,时间管理,时间片调度
uC/OS-III 时钟节拍,时间管理,时间片调度 时钟节拍 时钟节拍可谓是 uC/OS 操作系统的心脏,它若不跳动,整个系统都将会瘫痪. 时钟节拍就是操作系统的时基,操作系统要实现时间上的管理, ...
- [STemWin教程入门篇] 第一期:emWin介绍
转自:http://bbs.armfly.com/read.php?tid=1544 SEGGER公司介绍 了解emWin之前,先了解一下SEGGER这家公司,了解生产商才能对emWin有更加全面的认 ...
- 关于STM32F103系列从大容量向中容量移植的若干问题
一.把STM32F103大容量移植到STM32F103C8T6上的步骤: 1.换启动文件 startup_stm32f10x_cl.s ——互联型的器件 包括:STM32F105x ...
- ucos实时操作系统学习笔记——任务间通信(消息)
ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...
- ucos实时操作系统学习笔记——内核结构和任务创建
对于ucos实时操作系统,邵贝贝的那本书已经写得很详细了,我因为之前不深的研究过ucos,所以在这里做一个笔记,写一些个人对该操作系统的理解,仅仅是个人理解,如果有人看到这边随笔有不对的地方,望给我指 ...
- ucos实时操作系统学习笔记——操作系统在STM32的移植
使用ucos实时操作系统是在上学的时候,导师科研项目中.那时候就是网上找到操作系统移植教程以及应用教程依葫芦画瓢,功能实现也就罢了,没有很深入的去研究过这个东西.后来工作了,闲来无聊就研究了一下这个只 ...
随机推荐
- 国内怎么玩 ChatGPT
ChatGPT去年已经在互联网技术圈里已经火了一把,现在似乎已经出圈,各行各业都在讨论,可以预见,ChatGPT是继互联网后的又一大技术革命. 如何才能体验ChatGPT呢?很多人卡在账号注册这一步, ...
- Seal 0.4 发布:软件供应链安全洞察更上一层楼!
今天,我们很高兴宣布 Seal 0.4 已正式发布!在上一个版本中,Seal 完成了从单一产品到全链路平台的转变,通过全局视图帮助用户掌握软件开发生命周期各个环节的安全状况. 在 Seal 0.4 中 ...
- 10分钟搞定简易MVVM
实现一个简易的 MVVM 分为这么几步来 1.类 Vue:这个类接收的是一个 options. el属性:根元素的id data属性:双向绑定的数据. 2.Dep 类: subNode数组:存放所依赖 ...
- ChatGPT国内镜像模板,国内使用ChatGPT中文版本
@ 目录 一.什么是ChatGPT国内镜像 二.ChatGPT国内镜像使用教程 免费ChatGPT镜像的功能: 三.ChatGPT中文版作用 四.怎么使用ChatGPT国内镜像 五.中文ChatGPT ...
- CF750H New Year and Snowy Grid
\(\text{Solution}\) 这个问题是不好判断的 考虑简单点的,\((1,1)\) 到 \((h,w)\) 是否连通 那么只要在最外围一圈 #(显然一些位置不能加),判断 \((h+1,n ...
- JZOJ 5347. 【NOIP2017提高A组模拟9.5】遥远的金字塔
题目 分析 毫无疑问 \(dp\) 设 \(f_{i,j}\) 表示选到第 \(i\) 层,已选 \(j\) 个矩阵最大覆盖面积 那么 \(f_{i,j}=\max{f_{l,j}+w_i*(i-l) ...
- .Net依赖注入、控制反转
依赖项是指另一个对象所依赖的对象. 使用其他类所依赖的 WriteMessage 方法检查以下 MyDependency 类: public class MyDependency { public ...
- 好消息!微信小程序开发环境自带vConsole
背景介绍 事情是这样子的,我们在开发小程序的时候,需要在真机上把相关的日志打出来以便进行问题定位和回溯,于是在编程界就有个今天这个新闻.------ 好消息!广东某男子发现微信小程序开发环境自带vCo ...
- new Date(time).getTime()在ios返回NaN
解决: IOS识别(年月日时分秒) new Date(2010,0,1,0,0,0).getTime() 1.问题出在: 日期转成时间戳getTime(): var time = "2017 ...
- ChatGPT强势爆红,背后的技术原理是?一文轻松搞懂!
目录 什么是ChatGPT? OpenAI 背后的原理和发展历程 带来的争议和挑战 尾语 作者:小牛呼噜噜 | https://xiaoniuhululu.com 计算机内功.源码解析.科技故事.项目 ...