您的当前位置:首页正文

基于STM32的嵌入式操作系统程序设计及实现毕业论文之欧阳与创编

2023-03-11 来源:客趣旅游网
本科毕业论文(设计)

时间:创2021.03.08 作:欧阳与 论文题目 姓名 学号 班级 年级 专业 学 院 指导教师 完成时间

基于STM32的嵌入式操作系统

程序设计及实现

: 郝宇 : 093001020144

: 01班 : 2009级 : 电子信息工程 : 信息工程学院 : 丁光哲讲师 :

2013年5月20日

欧阳与创编 2021.03.08

作者声明

本毕业论文(设计)是在导师的指导下由本人独立撰写完成的,没有剽窃、抄袭、造假等违反道德、学术规范和其他侵权行为。对本论文(设计)的研究做出重要贡献的个人和集体,均已在文中以明确方式标明。因本毕业论文(设计)引起的法律结果完全由本人承担。

毕业论文(设计)成果归武昌工学院所有。 特此声明

作者专业 作者学号 作者签名

: : :

年 月 日 电子信息工程 093001020144

基于STM32的嵌入式操作系统

程序设计及实现

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

郝宇

The Design and Implementation of embedded operating system program based on STM32

Hao, Yu 2013年 5月 20日

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

摘要

随着科学技术不断的进步,工业生产越来越先进复杂,操作系统µC/OS-II是高效、稳定、可靠、节能的系统,广泛应用安防,消费电子中。而基于Cortex-M3架构下的STM32是一款性价比优越新型微处理器,将µC/OS-II移植到STM32上能够发挥其高效的性能,从而投入社会生产,制造出很多有用又实惠的电子产品,为我们的生活带来便利。

本文主要的研究内容是µC/OS-II操作系统理论分析、移植方法、应用程序设计及调试仿真实现。首先,对µC/OS-II的理论分析,研究其实际应用及系统结构;其次,分析STM32硬件平台及µC/OS-II的移植需求;最后,在µC/OS-II上开发LCD,LED,按键KEY等应用程序,并对多任务系统调试分析。主要研究结论如下:

(1)µC/OS-II操作系统主要分为任务管理、内存管理和时间管理三大部分,其间通信是通过消息队列和消邮箱。 (2)µC/OS-II移植主要在OS_CPU.H,OS_CPU_C.C,OS_CPU_A.ASM三个文件中,涉及到数据类型、堆栈、中断定义和任务切换等。

(3)应用程序设计优先级分配要合理,硬件平台初始化模块化处理。

关键词:嵌入式系统;µC/OS-II;移植

Abstract

With the progress of science and technology constantly, advanced industrial production to more complex, the operating systemµC/OS-II is efficient, stable,reliable, energy saving system, widely used in the security, and consumer electronics. Andbased on the STM32 architecture Cortex-M3 framework is a superior cost-effective new microprocessor, µC/OS-II transplantation to STM32 can play its efficient performance, thus in social production and create a lot of useful and affordable electronic product, bring convenience to our lives.

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

This article main research content isµC/OS-II operating system theory analysis, method of transplantation, application design and debugging of the simulation implementation. First of all, the theoretical analysis ofµC/OS-II, research the actual application and system structure; Second, analysis of STM32 hardware platform and the demand µC/OS-II transplantation. Finally, on theµC/OS-II development of LCD, LED, button KEY applications, and analysis of multitasking system debugging. Main research conclusion is as follows:

(1)µC/OS-II operating system consists of three major task management, memory management and time management, in which communication is through the message queue and email.

(2)µC/OS-II transplantation mainly inOS_CPU_C.C, OS_CPU_A.ASM file, OS_CPU.H,three involves the data type definition and task switching etc, stack, interrupt.

(3)The application design to the allocation of priorities, initialize the modular processing hardware platform.

Keywords:

embedded system;µC/OS-II; transplant

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

目录

0

0

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

1 概 述

1.1 研究的目的及意义

µC/OS-II是由美国工程师Jean Labrosse编写的嵌入式多任务的实时操作系统,包括实时内核、任务管理、时钟管理、任务间通信同步(信号量、邮箱、消息队列)和内存管理。除了有上面的优点外,µC/OS-II它具有别的操作系统没有的优点,具体如下:

(1)源代码开放:µC/OS-II的源代码可以免费获取,且标有清晰的注释,可读性好。

(2)可移植性好:µC/OS-II的源代码90%以上是用C语言编写的,可以很容易地把它移植到各类8位、16位和32位处理器上。

(3)稳定性高:µC/OS-II已得到FAA的标准认证,且目前已有上百个商业应用实例,其稳定性和可靠性是经过实践验证的。

因此,µC/OS-II广泛的应用于控制系统中,如在衍射仪高压控制系统中使用µC/OS-II操作系统是一种很好的选择。控制系统是一个复杂的系统,它需要多个系统协同工作。传统的系统开发我们往往使用前后台的方式,但是这种开发方式在任务较简单的开发中比较适用,对于任务比较复杂的系统往往力不从心。对于任务较多而且复杂的情况我们就要引入实时操作系统RTOS。

RTOS体现了一种新的应用程序设计思想和开放的框架,用户在编写程序时,可以分别编写各个任务,不必同时将所有任务运行的各种可能情况记在心中,大大减小了程序编写的工作量,而且减小了出错的可能,保证最终程序具有高可靠性,从而降低程序的复杂度和开发周期。由于控制系统功能较复杂,诸多的功能可以划分成许多不同的模块,模块之间既彼此联系又相对独立,可以当作不同的任务来进行处理。所以,使用实时操作系统,将不同的功能划分成不同的任务进行处理使得设计大大简化。

1.2国内外研究状况综述

嵌入式系统是继IT网络技术之后,又一个新的技术发展方向。中国单片机二十年论坛总结出,我国嵌入式起步较早,但总体来说发展缓慢,和国外的开发应用具有很大的差距,造成这一局面的原因是多方面的。在国内嵌入式系统开发方面,多是一些低层次的应用,停留在以前老的技术基础之上。例如,经典51系列单片机在上世纪我国的工业信息化改造过程中发挥了重要的作用,渗透到生产

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

生活的各个方面。与此同时在大学电类相关的工科单片机教学中,依然是经典的51,微机原理依然是8086/88,这显然体现不了最新的技术特征,造成了大学教育与实际社会需要的脱节。国外的大部分高校和国内的极少数大学相继开设嵌入式微处理器设计等相关的前沿性的课程,可见基于STM32技术将是未来微控制开发的主流方向。由于µC/OS-II系统具有体积小、性能强、功耗低、可靠性高以及面向行业应用的突出特征,目前已经被广泛的应用于军事国防、消费电子、网络通信、工业控制等各个领域。

今天嵌入式系统带来的工业年产值已超过了1万亿美元,1997年来自美国嵌入式系统大会(Embedded System Conference)的报告指出,未来5年仅基于嵌入式计算机系统的全数字电视产品,就将在美国产生一个每年1500亿美元的新市场。美国汽车大王福特公司的高级经理也曾宣称,“福特出售的‘计算能力’已超过了IBM”,由此可以想见嵌入式计算机工业的规模和广度。1998年11月在美国加州举行的嵌入式系统大会上,基于RTOS的Embedded Internet成为一个技术新热点。在国内,“维纳斯计划”和“女娲计划”一度闹得沸沸扬扬,机顶盒、信息加电这两年更成了IT热点,而实际上这些都是嵌入式系统在特定环境下的一个特定应用。据调查,目前国际上已有两百多种嵌入式操作系统,而各种各样的开发工具、应用于嵌入式开发的仪器设备更是不可胜数。在国内,虽然嵌入式应用、开发很广,但该领域却几乎还是空白,只有三两家公司和极少数人员在从事这方面工作。由此可见,嵌入式系统技术发展的空间真是无比广大。

1.3 研究的主要内容

本文是在基于32位的ARM微处理器STM32和嵌入式实时操作系统µC/OS-II上进行嵌入式操作系统的移植和功能实现。通过将嵌入式实时操作系统µC/OS-II移植到STM32微处理器上,并对其进行软件功能的扩展和硬件扩展,实现了一个基本完整的嵌入式实时操作系统。建立了基于嵌入式ARM处理器的应用软件体系;将µC/OS-II移植到STM32,建立了嵌入式操作系统研究及µC/OS-II下的开发环境体系。包括µC/OS-II系统配置、µC/OS-II下的移植、启动、测试和功能实现等。完成了基于STM32的µC/OS-II的应用设计。

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

本文主要分为4章,章节安排如下:

(1)绪论。主要介绍了开题的背景和研究意义,以及µC/OS-II的国内外研究现状。

(2)µC/OS-II的理论介绍。主要介绍µC/OS-II各模块的基本功能和在STM32上移植方法。

(3)硬件平台介绍及LCD屏程序设计及调试。多任务的建立并实现基本功能。

(4)结语。主要介绍本论文中的优点和不足之处。

2µC/OS-II的理论介绍

2.1 µC/OS-II各模块的基本功能

2.1.1µC/OS-II内核结构

(1)µC/OS-II是以源代码形式提供的实时操作系统内核,其包含的文件结构如图2.1所示:

基于µC/OS-II操作系统进行应用时,设计时的主要任务是将系应用软件 统合理划分成多个任务,并由RTOS进行调度,任务之间使用(用户代码) µC/OS-II提供的系统服务进行通信,以配合实现应用系统的功能。与前后台系统一样,基于µC/OS-II的多任务系统也有一个main主函µC/OS-II内核文件C 启动程序调用。在µC/OS-II 配置文件 数,main函数由编译器所带的main主函数中主要实现µ的初始化OSInit()、任务创建、一些任务通信方法的(C/OS-II与处理器类型无关的代码) (与应用程序有关) 创建、µC/OS-II的多任务启动OSStart()等常规操作。另外,还有一 OS_CORE.C OS_TASK.C 些应用程序相关的初始化操作,例如:硬件初始化、数据结构初始OS_CFG.H OS_FLAG.C OS_TIME.C 化等。 (2)OSInit()初始化µC/OS-II所有的变量和数据结构,并建立INCLUDES.H OS_MBOX.C µCOS-II.C 空闲任务OS_TaskIdle(),这个任务总是处于就绪态。 OS_MEM.C µCOS-II.H 2.1.2µC/OS-II内核体系结构图 OS_MUTEX.C OS_SEM.C µC/OS-II内核主要对用户任务进行调度和管理,并为任务间共享资源提供服务。包含的模块有任务管理、任务调度、任务间通OS_Q.C 信、时间管理、内核初始化等。µC/OS-II内核体系结构如图2.2所示:

移植µC/OS-II (与处理器类型有关的代码) OS_CPU.H OS_CPU_C.C OS_CPU_A.ASM 软

欧阳与创编 CPU 2021.03.08 定时器 硬

欧阳与创编 2021.03.08

图2.2 内核结构图

2.1.3任务状态及其转换关系

在多任务系统中,任务是设计者实现应用系统的基本形式,也是µC/OS-II系统进行调度的基本单元。任务可以是一个无限的循环,也可以在一次执行后被操作系统删除。任务函数和任何C函数一样,具有一个返回类型和一个参数,但是它决不返回。

任务控制块(TCB)是一个数据结构OS_TCB,一旦一个任务创建,就有一个和它关联的TCB被赋值。当任务的CPU使用权被剥夺时,它用来保存该任务的状态。这样,当任务重新获得CPU使用权时,可以从TCB中获取任务切换前的信息,准确的继续运行。 2.1.4任务调度器

µC/OS-II总是运行进入就绪态的优先级最高的任务。任务调度器的功能是:在就绪表中查找最高优先级的任务,然后进行必要的任务切换,运行该任务。µC/OS-II的任务调度有两种情况:任务级的任务调度由OS_Sched()完成;中断级的任务调度由OSIntExt()完成。这两种任务调度情况调用的任务切换函数不同:任务级的任务调度OS_Sched()调用了任务切换函数 OS_TASK_SW(),而中断级的调度OSIntExt()调用了任务切换函数OSIntCtxSw()。

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

任务级的任务调度是由于有更高优先级的任务进入就绪态,当前的任务的CPU使用权被剥夺,发生了任务到任务的切换;中断级的调度是指当前运行的任务被中断打断,由于ISR运行过程中有更高优先级的任务被激活进入就绪态。而中断返回前ISR调用OSIntExt()函数,该函数查找就绪表发现有必要进行任务切换,从而被中断的任务进入等待状态,运行被激活的高优先级的任务。

(1)任务切换

任务切换有两种:OS_TASK_SW()和OSIntCtxSw()。任务级的任务切换OS_TASK_SW()是宏调用,通过软中断指令来实现CPU寄存器内容切换。例如:#define OS_TASK_SW() asm(“int #32”)。

任务级的任务切换过程:①保存当前运行的任务的CPU寄存器值到该任务的堆栈。如:堆栈指针,程序计数器,状态寄存器等。②将要运行的高优先级的任务的寄存器值从堆栈恢复到CPU寄存器。③进行TCB的切换,并运行任务。

中断级的任务切换OSIntCtxSw()是在OSIntExt()中调用的,我们一般在用户ISR中调用OSIntExt()以实现中断返回前的任务调度。由于ISR已经将CPU寄存器的值存入被中断的任务的堆栈中,所以OSIntCtxSw()的实现和OS_TASK_SW()不一样,具体参见移植文档。

(2)就绪表

每个就绪的任务都放在就绪表中,就绪表有两个变量:OSRdyGrp和OSRdyTbl[]。OSRdyGrp中,将任务按优先级分组,八个为一组。OSRdyGrp的每一位代表每组任务是否有进入就绪态的任务。

在就绪表中查找优先级最高的任务不需要扫描整个OSRdyTbl[],只要查优先级判定表OSUnMapTbl[]。OSUnMapTbl[]是常量表,所以查找优先级最高的任务的执行时间为常量,和就绪表的任务数无关。 2.1.5中断服务

在用户的ISR中可以调用OSIntEnter()和OSIntExit()通知µC/OS-II发生了中断,这样可以实现ISR返回前的任务调度。 2.1.6时钟节拍

µC/OS-II要求用户提供一个周期性的时钟源,来实现时间的延

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

迟和超时功能,时钟节拍应该每秒发生10~100次/秒。时钟节拍率越高,系统的额外负荷就越重。

应该在多任务系统启动后,也就是调用OSStart()后再开启时钟节拍器。系统设计者可以在第1个开始运行的任务中调用时钟节拍启动函数。假设用定时器TA0作为时钟中断源,那么,在移植过程中实现了函数init_timer_ta0(),此函数用来初始化定时器TA0,并将其打开。µC/OS-II中的时钟节拍服务是在ISR中调用OSTimeTick()实现的。OSTimeTick()跟踪所有任务的定时器以及超时时限。 2.1.7µC/OS-II的初始化和启动

调用µC/OS-II的服务之前要先调用系统初始化函数OSInit()。OSInit()初始化µC/OS-II所有的变量和数据结构,并建立空闲任务。µC/OS-II初始化任务控制块、事件控制块、消息队列缓冲、标志控制块等数据结构的空缓冲区。

多任务的启动是通过调用OSStart()实现的。启动之前要至少创建一个任务。OSStart()调用就绪任务启动函数OSStartHighRdy(),其功能是将任务栈的值恢复到CPU寄存器,并执行中断返回指令,强制执行该任务代码。 2.1.8内存管理

在ANSI C中是使用malloc和free两个函数来动态分配和释放内存。但在嵌入式实时系统中,多次这样的操作会导致内存碎片,且由于内存管理算法的原因,malloc和free的执行时间也是不确定。µC/OS-II中把连续的大块内存按分区管理。每个分区中包含整数个大小相同的内存块,但不同分区之间的内存块大小可以不同。用户需要动态分配内存时,系统选择一个适当的分区,按块来分配内存。释放内存时将该块放回它以前所属的分区,这样能有效解决碎片问题,同时执行时间也是固定的。 2.1.9任务管理

µC/OS-II中最多可以支持64个任务,分别对应优先级0~63,其中0为最高优先级。63为最低级,系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个。µC/OS-II提供了任务管理的各种函数调用,包括创建任务,删除任务,改变任务的优先级,任务挂起和恢复等。系统初始化时会自动产生两个任务:一个是空闲任务,它的优先级最低,该任务仅给一

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

个整型变量做累加运算;另一个是统计任务,它的优先级为次低,该任务负责统计当前CPU的利用率。 2.1.10µC/OS-II任务间通信方式

(1)信号量

信号量由两部分组成:一部分是16位的无符号整型信号量的计数值;另一部分是由等待该信号量的任务组成的等待任务表。信号量用于对共享资源的访问,用钥匙符号,符号旁数字代表可用资源数,对于二值信号量该值为1。信号量还可用于表示某事件的发生,用旗帜符号表示,符号旁数字代表事件已经发生的次数。互斥型信号量用于处理共享资源。

(2)消息邮箱

一种通信机制,可以使一个任务或者中断服务子程序向另一个任务发送一个指针型的变量,通常该指针指向一个包含了消息的特定数据结构。

(3)消息队列

另一种通信机制,允许一个任务或者中断服务子程序向另一个任务发送以指针方式定义的变量或其它任务,因具体应用不同,每个指针指向的包含了消息的数据结构的变量类型也有所不同。

2.2STM32上移植方法

2.2.1平台需求

µC/OS-II的正常运行需要处理器平台满足以下要求: (1)处理器的C能产生可重入代码。 (2)用C语言就可以打开和关闭中断。

(3)处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)。

(4)处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。

(5)处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令。 2.2.2移植方法

(1)内核头文件(OS_CPU.H)

在OS_CP U.H 中,主要声明了一些与微处理器相关的常量、宏和typedef。

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

定义与处理器无关的数据类型

typede f unsigned char BOOLEAN; typedef unsigned char INT8U; typedef signed char NT8S;

typedef unsigned short INT16U; typedef signed short INT16S; typedef unsigned int INT32U; typedef signed int NT32S; typedef float FP32; typedef double FP64;

typedef unsigned int OS_STK; typedef unsigned int OS_CPU_SR;

在STM32处理器及keil MDK 或者IAR 编译环境中可以通过查手册得知short类型是16位而int类型是32位,这对于Cortex-M3内核是一致的。故这部分代码无需修改。尽管µC/OS-II定义了float 类型和double 类型,但为了方便移植它们在µC/OS-II源代码中并未使用。为了方便使用堆栈,µC/OS-II定义了一个堆栈数据类型。在Cortex-M3 中寄存器为32位,故定义堆栈的长度也为32位。Cortex-M3 状态寄存器为32位,定义OS_CPU_SR主要是为了在进出临界代码段保存状态寄存器。

(2)临界代码段

µC/OS-II为了保证某段代码的完整执行,需要临时的关闭中断,在这

段代码执行完成之后再打开中断。这样的代码段称作临界代码段。µC/OS-II通过定义两个宏OS_ENTER_CRITICAL() 和OS_EXIT_CRITICAL() 来分别实现中断的关闭和打开。一般来说,采用方法3来实现这两个宏。这两个宏分别定义如下:

#define OS_CRITICAL_METHOD 3

#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}

#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}

函数OS_CPU_SR_Save()和OS_CPU_SR_Restore(cpu_sr)在OS_CPU_A.ASM中定义。同时得注意,在使用这两个宏之前,必须定义OS_CPU_SR cpu_sr;否则编译时将出错。

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

(3)栈的增长方向

尽管µC/OS-II支持两种方向生长的栈,但对于以Cortex-M3为内核的STM32 微处理器来说,它支持向下增长的满栈,故需要定义栈增长方向宏为1。即定义成如下形式

#define OS_STK_GROWTH 1 (4)任务级任务切换

任务级任务切换调用宏OS_TASK_SW()来实现。因为这个宏也是与处理器相关的,因此这个宏在OS_CPU_A.ASM中描述。

(5)其他函数声明

在OS_ CPU.H中,还声明了以下几个函数,这几个函数均在OS_CPU_A.ASM中实现。

void OSCtxSw(void); void OSIntCtxSw(void); void OSStartHighRdy(void);

void OS_CPU_PendSVHandler(void);

(6)与处理器相关的汇编代码(OS_CPU_A.ASM)

在OS_CP U_A.ASM中实现的是下面五个与处理器相关的函数。

OS_CPU_SR_Save(); OS_CPU_SR_Restore(); OSStartHighRdy(); OSCtxSw(); OSIntCtxSw(); 2.3.3函数实现

(1)关中断函数(OS_CPU_SR_Save())

即先保存当前的状态寄存器然后关中断。故关中断实现代码如下

OS_CPU_SR_Save MRS R0, PRIMASK; CPSID I BX LR

这也是宏OS_ENTER_CRITICAL() 的最终实现。

(2)恢复中断函数(OS_CPU_SR_Restore())

这是宏OS_EXIT_CRITICAL()的最终实现。也就是将状态寄存器的内容从R0中恢复,然后跳转回去。此函数完成的将中断状态恢

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

复到关中断前的状态。其代码如下:

OS_CPU_SR_Restore MSR PRIMASK, R0 BX LR

Cortex-M3处理器有单独的指令来打开或者关闭中断,所以这两个函数实现起来很简单。

(3)启动最高优先级任务运行(OSStartHighRdy()) OSStart()调用OSStartHighRdy()来启动最高优先级任务的运行,从而启动整个系统。OSStartHighRdy()主要完成以下几项工作:

① 为任务切换设置PendSV的优先级; ② 为第一次任务切换设置栈指针为0;

③ 设置OSRunning = TRUE,以表明系统正在运行; ④触发一次PendSV,打开中断等待第一次任务的切换。 (4)任务级和中断级任务切换

因为Cortex-M3进入异常自动保存寄存器R3-R0,R12,LR,PC和xPSR这种的特殊机制,这两个函数都是触发一次PendSV来实现任务的切换。首先是微处理器自动保存上面提到的寄存器,然后把当前的堆栈指针保存到任务的栈中,将要切换的任务的优先级和任务控制块的指针赋值给运行时的最高优先级指针和运行时的任务控制块指针,最后再把要运行的任务的堆栈指针赋值给微处理器的堆栈指针,这样就可以退出中断服务程序了。中断服务程序退出的时候将自动出栈R3-R0,R12,LR,PC和xPSR。具体的PendSV服务程序的伪代码如下:

OS_CPU_PendSVHandler :

// 进入异常,处理器自动保存R3-R0,R12,LR,PC和xPSR if (PSP != NULL) //判断不是开始第一次任务 {

保存R4-R11到任务的堆栈;

OSTCBCur->OSTCBStkPtr = SP; //保存堆栈的指针到任务控

制块

}

OSTaskSwHook(); //实现用户扩展功能而定义的钩子 OSPrioCur = OSPrioHighRdy; //设置运行任务为最高优先级就绪

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

任务

OSTCBCur = OSTCBHighRdy; // 设置运行的任务控制块为最高 //就绪任控制块务

PSP = OSTCBHighRdy->OSTCBStkPtr;//将要切换的任务堆栈指

// 针赋给微处理器的堆栈指 // 针从而实现切换

从堆栈中恢复 R4-R11; 从异常中返回;

// 退出异常,处理器自动恢复R3-R0,R12,LR,PC和xPSR 这样很容易写出PendSV中断服务程序的代码了。

(5)与CPU 相关的C 函数和钩子函数(OS_CPU_C.C ) 这个文件中包含10个函数,具体如下: OSInitHookBegin (); OSInitHookEnd (); OSTaskCreateHook (); OSTaskDelHook (); OSTaskIdleHook (); OSTaskStatHook (); OSTaskStkInit (); OSTaskSwHook (); OSTCBInitHook (); OSTimeTickHook ();

这10个函数有9个是为了扩展用户功能而定义的钩子函数,这些钩子函数可以都为空函数,也可以加上一些用户需要的扩展功能。另外一个不是钩子函数,它是OSTaskStkInit()。这个函数的功能是当一个任务被创建时,它完成这个任务堆栈的初始化。这个函数首先将用户为任务分配的堆栈顶地址赋值给一个栈指针变量,然后再通过这个栈指针向任务的栈空间写入初值。这个初值无关紧要,为0就可以了。这个函数的代码时下如下:

OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt) {

OS_STK *stk; (void)opt; //防止编译器报错

stk = ptos; // 将栈顶地址赋值给栈指针变量 // 以进入异常的顺序来给栈赋初值

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

*(stk) = (INT32U)0x00000000L; //xPSR *(--stk) = (INT32U)task; //Entry Point

*(--stk) = (INT32U)0x00000000L; // R14 (LR) *(--stk) = (INT32U)0x00000000L; //R12 *(--stk) = (INT32U)0x00000000L; //R3 *(--stk) = (INT32U)0x00000000L; // R2 *(--stk) = (INT32U)0x00000000L; // R1 *(--stk) = (INT32U)p_arg; //R0 : 传递的参数 // 剩下的寄存器初始化

*(--stk) = (INT32U)0x00000000L; // R11 *(--stk) = (INT32U)0x00000000L; //R10 *(--stk) = (INT32U)0x00000000L; // R9 *(--stk) = (INT32U)0x00000000L; //R8 *(--stk) = (INT32U)0x00000000L; //R7 *(--stk) = (INT32U)0x00000000L; // R6 *(--stk) = (INT32U)0x00000000L; // R5 *(--stk) = (INT32U)0x00000000L; // R4 return (stk); }

其他的钩子函数都为空函数。这样,整个移植的代码就介绍完了。整个移植的过程非常容易。剩下的工作就是编写用户任务,并在开发板上验证,以此来验证该移植方案是可行的和成功的。

3LCD屏程序设计及调试

3.1 工具概述

RVMDK源自德国的KEIL公司,是RealView MDK的简称。RealView MDK集成了业内最领先的技术,支持ARM7、ARM9和Cortex-M3核处理器,自动配置启动代码,集成Flash烧写模块,强大的Simulation设备模块,性能分析等功能。 3.1.1keil4工程建立以及仿真方法

(1)新建工程。

打开MDK软件,选择Project→New uVision Project菜单项,新建一个文件夹名为“毕业设计”,保存,则弹出器件选择对话框,这里选择STM32F103RB。单击“OK”按钮,则弹出一个对话框加载启动文件到工程中。

打开“毕业设计”文件夹,在里面添加子文件夹

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

(2)添加系统文件与工程管理。

回到“毕业设计”文件夹中,把系统SYSTEM文件夹(delay,sys,usart文件夹)复制过来,再建立main和hardware文件夹用于主函数和各外设资源函数。回到工程中,点击manage components,添加工程中的文件,进行分类管理工程。

(3)最后新建 main文件,在编辑区写代码。

3.1.2硬件平台绍

ALIENTEK MiniSTM32选择的是STM32F103RBT6 作为MCU,STM32F103的型号众多,作为一款低端开发板,选择 STM32F103RBT6是最佳的选择。128K FLASH、20K SRAM、2个SPI、3个串口、1个USB 、1个CAN、2个12位的ADC、RTC、51个可用IO脚···,这样的配置无论放到那里都是很不错的了,更重要的是其价格,18元左右的零售价,相对其他芯片配置及价格,所以我们选择了它作为我们的主芯片。

BOOT1用于设置STM32的启动方式,其对应启动模式如表3.1所示:

表3.1BOOT0、BOOT1启动模式表

BOOT0 BOOT1 0 1 1 X 0 1 启动模式 用户闪存存储器 系统存储器 SRAM启动 说明 用户闪存存储器,也就是FLASH启动 系统存储器启动,用于串口下载 SRAM启动,用于在SRAM中调试代码 按照表3.1,一般情况下如果我们想用用串口下载代码,则必须配置BOOT0为1,BOOT1为0,而如果想让STM32一按复位键就开始跑代码,则需要配置BOOT0为0,BOOT1随便设置都可以。ALIENTEK这款开发板专门设计了一键下载电路,通过串口的 DTR和RTS信号,来自动配置BOOT0和BOOT1,因此不需要用户来手动切换他们的状态,直接串口下载和软件自动控制,可以非常方便的下载代码。

P3和P1分别用于 PORTA和PORTB的IO口引出,其中P2还有部分用于 PORTC口的引出。PORTA和PORTB都是按顺序排列的,这样设计的目的是为了让大家更方便地与外部设备连接。P2连

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

接了DS18B20的数据口以及红外传感器的数据线,它们分别对应着 PA0和PA1,只需要19通过跳线帽将P2和P3连接起来就可以使用了。这里不直接连在一起的原因有二:1,防止红外传感器和DS18B20 对这两个IO口作为其他功能使用的时候的影响;2,DS18B20 和红外传感器还可以用来给其他板子提供输入,等于我们的板子为别的板子提供了红外接口和温度传感器,在调试的时候,还是蛮有用的。P4口连接了PL2303的串口输出,对应着 STM32的串口1(PA9/PA10),在使用的时候,也是通过跳线帽将这两处连接起来。这样设计有2个好处:1,使得 PA9和PA10用作其他用途。

使用的时候,不受到PL2303的影响。2,USB 转串口可以用作他用,并不仅限在这个板上的STM32使用,也可以连接到其他板子上,这样ALIENEK MiniSTM32 就相当于一个USB 串口。P5口是另外一个IO引出排阵,将PORTC和PORTD等的剩余IO口从这里引出。

在此部分原理图中,我们还可以看到STM32F103RBT6 的各个IO口与外设的连接关系,这些将在后面给大家介绍。 这里STM32的VBAT 采用CR1220 纽扣电池和VCC3.3混合供电的方式,在有外部电源(VCC3.3)的时候,CR1220不给VBAT供电,而在外部电源断开的时候,则由CR1220给VBAT供电。这样,VBAT总是有电的,以保证RTC的走时以及后备寄存器的内容不丢失。

3.2硬件结构

3.2.1STM32最小系统

STM32F103最小系统包括电源电路,复位电路,时钟电路,主芯片和下载接口。STM32F103使用3.3V供电,且引脚接有滤波电容,保证芯片工作稳定;复位电路使用的低电平复位,该电路上电可以复位,按键按下时也可以复位;时钟电路使用8MHZ晶振,和22pF电容助振。

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

图3.1 最小系统

3.2.2LCD接口

显示电路使用的是2.4寸的TFT液晶显示器,该显示器有16位的数据口和5位的控制端口。

图3.2液晶显示

3.2.3LED接口

输入单元和指示部分,直接引到STM32F103的IO口。

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

图3.3按键和LED灯

3.2.4红处接收电路

HS0038用于显度的采集,其通信是单总线式,直接与STM32F103相连。

图3.4红处接收单元

3.3C程序设计

在µC/OS-II里面创建6个任务:开始任务、LED0、LED1、触摸屏,KEY按键任务和LCD显示任务,开始任务用于创建其他(LED0、LED1、红处接收、按键和LCD显示)任务,之后挂起;LED0任务用于控制DS0的亮灭,DS0每秒钟亮800ms;LED1任务用于控制DS1的亮灭,DS1亮300ms,灭300ms,依次循环;触摸屏任务用于手写输入,并立即显示;LCD显示任务用于显示图形,和接收红外信号信息显示出来;KEY按键任务分别使屏进入校准功能,以及显示欢迎界面。

首先,建立基本的工程后,在该工程源码下面加入µC/OS-II文件夹,存放µC/OS-II源码(我们已经将µC/OS-II源码分为三个文件夹:CORE、PORT和CONFIG)。打开工程,新建µC/OS-II-CORE、µC/OS-II-PORT和µC/OS-II-CONFIG三个分组,分别添加µC/OS-II三个文件夹下的源码,并将这三个文件夹加入头文件包含路径。

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

µC/OS-II-CORE分组下面是µC/OS-II的核心源码,我们不需要做任何变动。

µC/OS-II-PORT分组下面是我们移植µC/OS-II要修改的3个代码,这个在移植的时候完成。

µC/OS-II-CONFIG分组下面是µC/OS-II的配置部分,主要由用户根据自己的需要对µC/OS-II进行裁剪或其他设置。工程框架如下图3.5:

其次,对os_cfg.h里面定义OS_TICKS_PER_SEC的值为200,也就是设置µC/OS-II的时钟节拍为5ms,同时设置

OS_MAX_TASKS为10,也就是最多10个任务(包括空闲任务和统计任务在内)。

再次,在main.c文件中设置任务堆栈大小、设置任务优先级、任务堆栈,创建5个任务。如图3.5所示:

图3.5 任务结构

3.4调试

3.4.1设置编译环境

(1)打开project菜单,选择Options for Target“毕业设计”对话框,如图3.6在target中,将晶振选为8MHz;在output中,将

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

Creat HEX File复选框选中。为调试方便,将产生调试中间信息的复选框也选中。

图3.6 编译环境窗口

(2)在C/C++中,将各分文件的路径加入如图3.7

图3.7 路径添加

(3)在Debug中,将Use选中并设置为Cortex-M/R J-LINK/J-Trace,设为JINK连接实物仿真了。

(4)在Utilities中,设置如图3.8,并打开Settings,设置FLASH为256KB。

图3.8Utilities

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

3.4.2 调试结果

编译程序,排出error和warning,如图3.9得到正确的程序,下载程序到开发板上,得到了预期结果

图3.9 编译结果

将程序下载到开发板上,并观察现象,LED小灯按程序要求正常运行,红外接收器正确显示数值,按键KEY0可实现进入校准功能,KEY1可以显示欢迎界面,KEY2可以清除,说明程序正确。现象如图3.10

图3.10 实物现象

结语

本文完成基于32位ARMv7微处理器Cortex-M3和嵌入式实时操作系统µC/OS-II的嵌入式操作系统的移植和功能的实现。经过几个月的深入学习,了解了嵌入式操作系统µC/OS-II的性能、特点、结构和基本运行原理,以及通过实验调试的方法完成对µC/OS-II时间管理函数,任务管理函数的灵活应用。认真掌握Cortex-M3的硬件结构和指令系统,并在ALIENTEK MiniSTM32开发板上调试串口,TFT彩屏以及ADC实验。本文将嵌入式实时操作系统µC/OS-II移植到Cortex微处理器上,并对其进行系统测试和系统功能实现,实现了一个基本完整的嵌入式实时操作系统。该系统一方面实现了单个IO口和多人IO口的组合输入输出控制,采用模块化设计思想,具有代表性。另一方面,它又是一个相对完整的系统,可以

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

应用于工业控制的各个显示终端,完成指示功能。由于实时操作系统内核µC/OS-II是为嵌入式应用编写的通用软件,所以不得不强调处理器的通用性和功能的全面性。在实际应用中可以根据需要裁减和优化。其次,本文所调度的任务较简单,主要是为了说明方法。

在程序调试过程中,由于对STM32RTB6内核学习还不是很透彻,出现了警告,一直困扰。任务中加入更多任务时老出错,程序不能正常运行,但通不断学习,查阅资料得以解决。

本人认真进行了课题的研究并完成了本论文,由于作者水平有限,论文中可能仍有错误和不足之处,敬请大家批评指正。

最后,感谢指导老师的关怀和细心的指导,感谢同学们的帮助。谢谢你们的支持!

主要参考文献

[1] 意法半导体.STM32中文参考手册.第10版.意法半导体(中国)投资公司,2010.

[2] 刘军.例说STM32.北京:北京航空航天大学出版社,2011.

[3] 宋岩(译). Joseph Yiu.Cortex—M3权威指南.北京:北京航空航天大学出版社,2009.

[4] 刘荣.圈圈教你玩USB.北京:北京航空航天大学出版社,2009. [5] 杜春雷.ARM体系结构与编程.北京:清华大学出版社,2003. [6] 李宁.ARM开发工具RealView MDK 使用入门.北京:北京航空航天大学出

版社,2008.

[7] 俞建新.嵌入式系统基础教程.北京:机械工业出版社,2008. [8] 李宁.基于MDK的STM32处理器开发应用.北京:北京航空航天大

学出版社,2008.

[9] 王永虹.STM32系列ARM Cortex-M3微控制器原理与实践.北京:北

京航空航天大学出版社,2008.

[10] 邵贝贝(译). 嵌入式实时操作系统µC/OS-II. 第2版.北京:北京航空

航天大学出版社,2003.

[11] 谭浩强(著). C程序设计(第三版).北京:清华大学出版社,2009. [12] 周立功等.ARM嵌入式系统基础教程.北京:北京航空航天大学出版社,2005.

[13] Malinowski Mariusz.Jasin'ski Marek,Kazmierkowski Marian P.Simple

Direct PowerControl of Three-Phase PWM Rectifier Using Space-

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

Vector Modulation.IEEE Transactions on Industrial, Electronics,2004,51(2):447-454.

[14] K.S.Rajashekara.Microprocessoy Based Sinusoidal PWM Imverter by

DMA transfer Transfer.IEEE.Trans.Ind Electrol,Vol.IE-29,No1,1982

P46~51.

[15] http://www.stmcu.org [16] http://www.openedv.com

附 录

附录1

主程序代码#include \"led.h\" #include \"delay.h\" #include \"key.h\" #include \"sys.h\" #include \"usart.h\" #include \"lcd.h\" #include \"spi.h\" #include \"flash.h\" #include \"stdio.h\" #include \"string.h\" #include \"touch.h\" #include \"includes.h\" #include \"remote.h\" //设置任务堆栈大小

#define LED_STK_SIZE 64 #define LED1_STK_SIZE 64 #define LCD_STK_SIZE 64 #define KEY_STK_SIZE 64 #define TOUCH_STK_SIZE 64 #define START_STK_SIZE 512 //设置任务优先级

#define LED_TASK_Prio 6 #define LED1_TASK_Prio 5 #define LCD_TASK_Prio 4 #define KEY_TASK_Prio 2 #define TOUCH_TASK_Prio 7

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

#define START_TASK_Prio 10 //任务堆栈

OS_STK TASK_LED1_STK[LED_STK_SIZE]; OS_STK TASK_LED_STK[LED1_STK_SIZE]; OS_STK TASK_LCD_STK[LCD_STK_SIZE];

OS_STK TASK_START_STK[START_STK_SIZE]; OS_STK TASK_KEY_STK[KEY_STK_SIZE];

OS_STK TASK_TOUCH_STK[TOUCH_STK_SIZE]; //任务申明

void TaskStart(void *pdata); void TaskLed(void *pdata); void TaskLed1(void *pdata); void TaskLCD(void *pdata); void TaskKey(void *pdata); void TaskTouch(void *pdata); //加载触摸画板界面

void Load_Drow_Dialog(void) { LCD_Fill(0, 180,240,320,0x23); }

//进入触摸屏校准界面 void Load_Adjust_Panel() { }

LCD_Clear(WHITE);//清屏 Touch_Adjust(); //屏幕校准

Save_Adjdata(); Load_Drow_Dialog();

LCD_ShowString(10,160,\"Touch Panel:\"); LCD_ShowString(10,4,\"LCD Display\"); POINT_COLOR=BLUE;//设置字体为蓝色 LCD_ShowString(216,304,\"RST\");//显示清屏区域 LCD_DrawLine(0, 158, 240, 158);//分界线 LCD_DrawLine(120, 0, 120, 158); LCD_DrawLine(120, 75, 240, 75); POINT_COLOR=RED;//设置画笔红色

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

int main(void) {

SystemInit(); //系统初始化 72M delay_init(72); //延时初始化 NVIC_Configuration();

uart_init(9600);//串口初始化,波特率9600 LED_Init(); //LED0,LED1端口初始化 KEY_Init(); LCD_Init(); SPI_Flash_Init(); AT24CXX_Init();

//IIC初始化

Touch_Init(); //触摸屏初始化 delay_ms(500);

Load_Drow_Dialog();

Remote_Init(); //初始化红外接收

//KEY端口初始化 //LCD显示初始化

POINT_COLOR=RED; OSInit(); OSTaskCreate( TaskStart, //task pointer (void *)0, //parameter (OS_STK

*)&TASK_START_STK[START_STK_SIZE-1], //task stack top pointer START_TASK_Prio ); //task priority OSStart(); return 0; } //开始任务

void TaskStart(void * pdata) { pdata = pdata; // OS_ENTER_CRITICAL(); OSTaskCreate(TaskLed, (void * )0, (OS_STK *)&TASK_LED_STK[LED_STK_SIZE-1], LED_TASK_Prio); OSTaskCreate(TaskLed1, (void * )0, (OS_STK *)&TASK_LED1_STK[LED1_STK_SIZE-1], LED1_TASK_Prio);

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

OSTaskCreate(TaskLCD, (void * )0, (OS_STK *)&TASK_LCD_STK[LCD_STK_SIZE-1], LCD_TASK_Prio); OSTaskCreate(TaskKey, (void * )0, (OS_STK *)&TASK_KEY_STK[KEY_STK_SIZE-1], KEY_TASK_Prio); OSTaskCreate(TaskTouch, (void * )0, (OS_STK *)&TASK_TOUCH_STK[TOUCH_STK_SIZE-1], TOUCH_TASK_Prio); OSTaskSuspend(START_TASK_Prio); delete OS_EXIT_CRITICAL(); } //任务1

//控制DS0的亮灭.

void TaskLed(void *pdata) { while(1) { LED0=!LED0; OSTimeDlyHMSM(0,0,0,800); } } //任务2

//控制DS1的亮灭.

void TaskLed1(void *pdata) { while(1) { LED1=!LED1; OSTimeDlyHMSM(0,0,0,300); } } //任务3 //按键检测

void TaskKey(void *pdata) { u8 key=0; while(1){ key=KEY_Scan();

欧阳与创编 2021.03.08

//suspend but

not 欧阳与创编 2021.03.08

}

if(key==1) { Load_Adjust_Panel(); }

else if(key==2) {

LCD_ShowString(125,80, \"Welcom !!! \"); LCD_ShowString(125,100, \"The Design of\"); LCD_ShowString(125,120, \" Graduation !\"); }

else if(key==3) {

LCD_ShowString(125,80, \" ^_^ ^_^ \"); LCD_ShowString(125,100, \" \"); LCD_ShowString(125,120, \" ^_^ \"); }

OSTimeDlyHMSM(0,0,0,20); }

//任务4 //液晶显示

void TaskLCD(void *pdata){ u8 colorIndex=0; u8 key; u16 colorTable[]={BLACK,YELLOW,RED,GREEN}; LCD_ShowString(10,4,\"LCD Display Panel\"); while(1) { if(colorIndex==4) colorIndex=0; LCD_Fill(10,30,50,120,colorTable[colorIndex]); colorIndex++; LCD_Fill(60,30,90,120,colorTable[colorIndex]); LCD_ShowString(122,10, \"KEYVAL:\"); LCD_ShowString(122,30, \"KEYCNT:\"); LCD_ShowString(122,50, \"SYMBOL:\");

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

if(Remote_Rdy) {

key=Remote_Process();

LCD_ShowNum(190,10,key,3,16);//显示键值

LCD_ShowNum(190,30,Remote_Cnt,3,16);//显示按键次数

switch(key) { LCD_ShowString(190,50, \"ERROR \"); LCD_ShowString(190,50, \"CH- \"); LCD_ShowString(190,50, \"CH \"); LCD_ShowString(190,50, \"NEXT \"); LCD_ShowString(190,50, \"CH+ \"); LCD_ShowString(190,50, \"OP/PA \"); LCD_ShowString(190,50, \"PREV \");

case 0:

break; case 162:

break; case 98:

break; case 2:

break; case 226:

break; case 194:

break; case 34:

欧阳与创编 2021.03.08

欧阳与创编 2021.03.08

LCD_ShowString(190,50, \"5 \"); LCD_ShowString(190,50, \"VOL- \"); LCD_ShowString(190,50, \"VOL+ \"); LCD_ShowString(190,50, \"EQ \"); LCD_ShowString(190,50, \"0 \"); LCD_ShowString(190,50, \"100+ \"); LCD_ShowString(190,50, \"200+ \"); LCD_ShowString(190,50, \"1 \"); LCD_ShowString(190,50, \"2 \");

欧阳与创编 2021.03.08

break;

case 56:

break; case 224:

break; case 168:

break; case 144:

break; case 104:

break; case 152:

break; case 176:

break; case 48:

break; case 24:

break; case 122:

欧阳与创编 2021.03.08

LCD_ShowString(190,50, \"3 \"); LCD_ShowString(190,50, \"4 \"); 时间:2021.03.08

break; case 16:

break;

创作:欧阳与 欧阳与创编 2021.03.08

因篇幅问题不能全部显示,请点此查看更多更全内容