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笔记的更多相关文章

  1. UCOS 杂项 笔记

    1.  建立任务和建立数据队列  哪个先建立? 经过试验得知,数据队列和任务的建立先后没有顺序,都可以正常运行. 2.关于主函数的面试问题. 主函数写法有:   int  main()    和voi ...

  2. ucos III中任务之间的数据通信和任务划分

    1. 如果将关系密切(比如两个任务之间需要经常收发数据)的若干功能分别用不同的任务来实现,则需要进行大量的任务之间数据通信和同步通信,这系统来说是一个很大的负担.因此应该将关系密切的若干功能组合成一个 ...

  3. UCOS III的时间片轮转调度的一个问题

    1. 如果当前一个任务A在时间片未到来之前,主动放弃剩下的时间片,进入下一个任务B,那么下一个任务的的执行时间是多久? 书上说,是重置时间片,也就是说任务B也运行一个完整的时间片.

  4. Issue 6: 装机系列1,PC下windows系统安装指南

    0.前言 接触电脑将近7年时间,多次说要写下这篇文章,一直未曾提笔,始终怕给人以误导.到如今,来来回回装系统的次数得超过百次了.本着不误导人的想法,本文试着总结一下装系统的基本方法和思路,但不会过多涉 ...

  5. uC/OS-III 时钟节拍,时间管理,时间片调度

    uC/OS-III 时钟节拍,时间管理,时间片调度   时钟节拍 时钟节拍可谓是 uC/OS 操作系统的心脏,它若不跳动,整个系统都将会瘫痪. 时钟节拍就是操作系统的时基,操作系统要实现时间上的管理, ...

  6. [STemWin教程入门篇] 第一期:emWin介绍

    转自:http://bbs.armfly.com/read.php?tid=1544 SEGGER公司介绍 了解emWin之前,先了解一下SEGGER这家公司,了解生产商才能对emWin有更加全面的认 ...

  7. 关于STM32F103系列从大容量向中容量移植的若干问题

    一.把STM32F103大容量移植到STM32F103C8T6上的步骤: 1.换启动文件 startup_stm32f10x_cl.s           ——互联型的器件 包括:STM32F105x ...

  8. ucos实时操作系统学习笔记——任务间通信(消息)

    ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...

  9. ucos实时操作系统学习笔记——内核结构和任务创建

    对于ucos实时操作系统,邵贝贝的那本书已经写得很详细了,我因为之前不深的研究过ucos,所以在这里做一个笔记,写一些个人对该操作系统的理解,仅仅是个人理解,如果有人看到这边随笔有不对的地方,望给我指 ...

  10. ucos实时操作系统学习笔记——操作系统在STM32的移植

    使用ucos实时操作系统是在上学的时候,导师科研项目中.那时候就是网上找到操作系统移植教程以及应用教程依葫芦画瓢,功能实现也就罢了,没有很深入的去研究过这个东西.后来工作了,闲来无聊就研究了一下这个只 ...

随机推荐

  1. idea debug---启动超级慢,提示”Method breakpoints may dramatically slow down debugging“的解决办法

    https://blog.csdn.net/hanqing456/article/details/111878982 1.问题项目正常启动的时候没问题,debug模式就卡住了,很久不动.我推测是哪个断 ...

  2. C#通过OLEDB将DataTable写入Excel文件中

    利用OLEDB将DataTable数据写入Excel文件中,如果数据量过多,执行效率很缓慢,大数据量不推荐使用此方法. /// <summary> /// 创建DataTable /// ...

  3. try-catch易错分析

    /* * @author YAM */public class Test01 { public static void main(String[] args) { try { showExce(); ...

  4. cdq 分治学习笔记

    CDQ 分治是一种很玄学的东西. Part 0 引子 相信大家都会归并排序,又都知道归并排序求逆序对怎么求的 如果连归并求逆序对都不会那你为什么要学 cdq 分治阿喂 void merge_sort( ...

  5. html(Angular) 调用本地安装exe程序

    1.写注册表 新建 .reg文件 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\creoparametric] "URL P ...

  6. 【PyQt5学习-02-】PyQt5 库的结构

    1.pyqt5模块 pyqt5模块(module)文档 https://www.riverbankcomputing.com/static/Docs/PyQt5/module_index.html q ...

  7. SQLServer 编程总结

    case的用途 case语句有四个关键字,缺一不可,分别是:case when then end,另外还有一个else.case 后面跟字段名(当在when后面出现字段名时,case里不能写出来),w ...

  8. vue学习笔记:vue.js基础语法

    一.VUE 概述 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅 ...

  9. Java后台校验手机号和邮箱

    //true是手机号,false不是手机号 public static boolean checkPhone(String phone){ Pattern p = Pattern.compile(&q ...

  10. '大龄‘后端程序员,快速捡起ECMAScript6

    1.Promise 2.迭代器 3.async 4.module 5. this的理解