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实时操作系统是在上学的时候,导师科研项目中.那时候就是网上找到操作系统移植教程以及应用教程依葫芦画瓢,功能实现也就罢了,没有很深入的去研究过这个东西.后来工作了,闲来无聊就研究了一下这个只 ...
随机推荐
- spring cloud alibaba sentinel 运行及简单使用
1.官网 英文:https://github.com/alibaba/Sentinel 中文:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7 ...
- Spring(IOC自动装配-基于注解开发)
Spring IoC 自动装载 autowire: 自动装载是Spring提供的一种更加简单的方式,来完成DI,不需要手动配置property ,IoC容器会自动选择Bean玩成注入. 自动装载俩种: ...
- Vue前后端交互、生命周期、组件化开发
目录 Vue前后端交互.生命周期.组件化开发 一.Vue用axios与后端交互 二.Vue的生命周期 三.组件化开发 Vue前后端交互.生命周期.组件化开发 一.Vue用axios与后端交互 如果 ...
- ASP.NET Core知识之RabbitMQ组件使用(二)
近期,业务调整,需要内网读取数据后存入到外网,同时,其他服务器也需要读取数据,于是我又盯上了RabbitMQ.在展开业务代码前,先看下RabbitMQ整体架构,可以看到Exchange和队列是多对 ...
- axSpA患者新发炎症更容易发生在既往发生过炎症的区域
axSpA患者新发炎症更容易发生在既往发生过炎症的区域 EULAR2015; PresentID: SAT0240 NEW INFLAMMATORY LESIONS IN AXIAL SPONDYLO ...
- PostgreSQL 实现快速删除一个用户
一.具体方法 一般情况下直接执行 drop role xxx; 就可以把这个用户删除.但是很多时候会因为用户有依赖而报错. 二.权限依赖 postgres=# create role test wit ...
- 2023 年 CCF 春季测试赛模拟赛 - 2
T1 分治,\(a^b + \dots + 1 = (a^{\lfloor\frac{b}{2}\rfloor} + \dots + 1) \times (a^{\lfloor\frac{b}{2}\ ...
- Python读取保存图像文件
Python处理图像数据时通常需要对图像文件进行读取.保存等操作,因此将现有的方法归纳了一下. 1. PIL 依赖包:Pillow 安装:pip install Pillow 源码: 1 import ...
- DataGridView添加新一行数据可添加到最后一行或第一行
整体代码如下: using System;using System.Collections.Generic;using System.ComponentModel;using System.Data; ...
- linux安装datax +datax-web踩坑总结
一丶安装datax 环境:JDK8+ py2.7+ 下载地址:http://datax-opensource.oss-cn-hangzhou.aliyuncs.com/datax.tar.gz ...