μC/OS-Ⅱ在ATmega128上的移植Step by Step(1)
- UID
- 1023166
- 性别
- 男
- 来自
- 燕山大学
|
μC/OS-Ⅱ在ATmega128上的移植Step by Step(1)
本文详细介绍了把μC/OS-Ⅱ移植到ATMEL公司的8位微控制器ATmega128上的全过程。所谓移植,就是使一个实时内核能在
某个微处理器或微控制器上运行。在移植之前,希望读者能熟悉所用微处理器和C编译器的特点。
1 ATmega128的内核特点
之所以要先介绍ATmega128 MCU内核特点,是因为在μC/OS-Ⅱ的移植过程中,仍需要用户用C语言和汇编语言编写一些与微
处理器相关的代码。这里主要介绍ATmega128与μC/OS-Ⅱ移植相关的内核特点。如果读者已经对ATmega128 比较了解了,
那就不必阅读这一部分了。
1.1微控制器 (MCU)
ATmega128的MCU包括一个算术逻辑单元(ALU),一个状态寄存器(SREG),一个通用工作寄存器组和一个堆栈指针。状态
寄存器(SREG)的最高位I是全局中断允许位。如果全局中断允许位为零,则所有中断都被禁止。当系统响应一个中断后,I位将由硬件自动清“0”;当执行中断返回(RETI)指令时,I位由硬件自动置“1” ,从而允许系统再次响应下一个中断请求。
通用工作寄存器组是由32个8位的通用工作寄存器组成。其中R26~R31这6个寄存器还可以两两合并为3个16位的间接地址寄存器。这些寄存器可以用来对数据存储空间进行间接寻址。这3个间接地址寄存器的名称为:X寄存器、Y寄存器、Z寄存器。其中Z寄存器还能用作对程序存储空间进行间接寻址的寄存器。有些AVR C语言编译器还把Y寄存器作为软件堆栈的堆栈指针,比如ICC- AVR,CodevisionAVR。
堆栈指针(SP)是一个指示堆栈顶部地址的16位寄存器。在ICCAVR中,它被用作指向硬件堆栈的堆栈指针。AVR单片
机上电复位后,SP指针的初始值为0x0000,由于AVR单片机的堆栈是向下生长的(从高地址向低地址生长),所以系统程序
一开始必须对堆栈指针SP进行初始化,即将SP的值设为数据存储空间的最高地址。ICCAVR编译器在链接C程序文件的时 候,会自动在程序头链入startup文件。startup文件里面的程序将会去做初始化SP指针的工作。链入startup文件是ICCAVR 这个编译器的特点,在用其它编译器的时候,希望读者确认所使用的编译器是否带有自动初始化SP的功能,若没有,应在 用户程序中初始化SP。
1.2 数据存储空间(仅内部)
AVR单片机的数据存储器是线形的,从低地址到高地址依次是CPU寄存器区(32个通用寄存器),I/O寄存器区,数据存储区ICCAVR编译器又将数据存储区划分为全局变量和字符串区,软件堆栈区和硬件堆栈区三个空间。
高地址
硬件堆栈区
软件堆栈区
全局变量和字符串区
I/O寄存器区
CPU寄存器区
低地址
ICCAVR编译器将堆栈分成了两个功能不同的堆栈来处理(这一点与8051系列的单片机编译器处理方式不同)。硬件堆栈用于储存子程序和中断服务子程序调用时的函数返回地址。这块数据区域由堆栈指针SP进行寻址,数据的进栈和出栈有专门的汇编指令(pop,push等)支持,所以叫做硬件堆栈区。软件堆栈用于传递参数,储存临时变量和局部变量。这块数据区域是用软件模拟堆栈储存数据的方式进行数据存储,对该区域寻址的指针由用户自己定义,所以叫做软件堆栈区。AVR单片机的硬件堆栈的生长方向是向下的(从高地址向低地址生长),所以软件堆栈在定义的时候,也采取相同的 生长方向。
这里没有用ATmega128而采用AVR单片机的提法是因为ATmega128属于AVR系列单片机中的一种,而所有的AVR单片机的数据存储器组织方式都是一致的。在创建μC/OS-Ⅱ的任务栈时,需要了解所用微处理器数据存储空间尤其是堆栈空间的组 形式及相关的操作。读者应参阅所用微处理器的资料和编译器的帮助文档,了解该部分知 识。
1.3 ATmega128的中断响应机制
ATmega128有34个不同的中断源,每个中断源和系统复位在程序存储空间都有一个独立的中断向量(中断入口地址)。每个中断源都有各自独立的中断允许控制位,当某个中断源的中断允许控制位为“1”且全局中断允许位I也为“1”时,系统才响应该中断。
当系统响应一个中断请求后,会自动将全局中断允许位I清零,此时,后续中断响应被屏蔽。当系统执行中断返回指令RETI时,会将全局中断允许位I置“1”,以允许响应下一个中断。若用户想实现中断嵌套,必须在中断服务子程序中将全局中断允许位I置“1”。(这一点与8051系列的单片机不同)中断向量表中,处于低地址的中断具有高的优先级。优先级高只是表明在多个中断同时发生的时候,系统先响应优 先级高的中断,并不含有高优先级的中断能打断低优先级的中断处理工程的意思。这与8051系列单片机的中断优先级概念
不同。
由于μC/OS-Ⅱ的任务切换实际上是模拟一次中断,因此需要知道CPU的中断响应机制。中断发生时,ATmega128按以下
步骤顺序执行:
A. 全局中断允许位I清零。
B. 将指向下一条指令的PC值压入堆栈,同时堆栈指针SP减2。
C. 选择最高优先级的中断向量装入PC,程序从此地址继续执行中断处理。
D. 当执行中断处理时,中断源的中断允许控制位清零。
中断结束后,执行RETI指令,此时
A. 全局中断允许位I置“1”。
B. PC从堆栈推出,程序从被中断的地方继续执行。
特别要注意的是:AVR单片机在响应中断及从中断返回时,并不会对状态寄存器SREG和通用寄存器自动进行保存和恢复操作,因此,对状态寄存器SREG和通用寄存器的中断保护工作必须由用户来完成。
1.4 ATmega128的定时器中断
ATmega128有三个定时器:T0,T1,T2;它们三者都有计数溢出中断功能,而且T1和T2还有匹配比较中断,即定时器计数到设定的值时,产生中断并自动清零。若系统采用这种中断方式,其好处是在中断服务程序ISR中不需要重新装载定时器的值。但本文出于通用性的考虑,仍采用定时器计数溢出中断方式
2 μC/OS-Ⅱ的移植
2.1移植条件
要实现μC/OS-Ⅱ的移植,所用的处理器和编译器必须满足一定的条件:
(1) 所用的C编译器能产生可重入代码。
可重入代码是指可以被一个以上的任务调用,而不必担心其数据会被破坏的代码。可重入代码任何时候都可以被中断,一段时间以后又可以重新运行,而相应的数据不会丢失,不可重入代码则不行。本文所使用ImageCraft公司的ICCAVR V6.29编译器能产生可重入代码。
(2) 用C语言就可以打开和关闭中断。
本文所使用的ICCAVR V6.29编译器支持在C语言中内嵌汇编语句且提供专门开关中断的宏:CLI()和SEI()。这样,使得在C语言中开关中断非常方便。
(3) 处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)本文使用的ATmega128,有3个定时器,能产生μC/OS-Ⅱ所需的定时中断。
(4) 处理器支持能够容纳一定数量数据的硬件堆栈。本文使用的ATmega128有4K RAM,硬件堆栈可以开辟在这4K RAM中。
(5) 处理器有将堆栈指针和其它CPU寄存器从内存中读出和存储到堆栈或内存中的指令。一般的单片机都满足这个要求(如PUSH、POP指令),且ATmega128还具有直接访问I/O寄存器的指令(IN、OUT等),它比8051系列的单片机更容易实现上述要求。
2.2移植的实现
μC/OS-Ⅱ的移植工作包括以下几个内容:
用typedef声明与编译器相关的10个数据类型(OS_CPU.H)
用#define设置一个常量的值(OS_CPU.H)
#define声明三个宏(OS_CPU.H)
用C语言编写六个简单的函数(OS_CPU_C.C)
编写四个汇编语言函数(OS_CPU_A.S)
根据这几项内容,本文逐步来完成。
2.2.1 INCLUDES.H文件
是主头文件,在所有后缀名为.C的文件的开始都包含INCLUDES.H文件。使用INCLUDES.H的好处是所有的.C文件都只包含一个头文件,简洁,可读性强。缺点是.C文件可能会包含一些它并不需要的头文件,增加编译时间。我们是以增加编译时间为代价来换取程序的可移植性的。用户可以改写INCLUDES.H文件,增加自己的头文件,但必须加在文件末尾。
程序清单L2.2.1 INCLUDES.H.
#include // ATmega128的寄存器头文件
#include // ICCAVR的宏
#include
#include
#include
#include //一些C语言的标准库
/*
***************************************************************************
* μC/OS-Ⅱ 头文件
***************************************************************************
*/
#include "G:\Porting\ICCAVR\porting12_8\ATmega128\os_cpu.h"
#include "G:\Porting\ICCAVR\Porting12_8\EX1_mega128\os_cfg.h"
#include "G:\Porting\ICCAVR\Porting12_8\SOURCE\ucos_ii.h"
要注意,μC/OS-Ⅱ 的3个头文件的先后顺序是:os_cpu.h,os_cfg.h最后是ucos_ii.h。
2.2.2 OS_CPU.H文件
OS_CPU.H包括了用#define定义的与处理器相关的常量、宏和类型定义。其中需要注意以下三点:
一是堆栈的生长方向。正如前面所述,ATmega128的堆栈生长方向是向下生长,即从高地址到低地址,因此,OS_STK_GROWTH要被定义为1。
二是进入临界代码段(critical code section)的方法。μC/OS-II提供了三种进入临界代码段的方法,第一种方法是直接对中断允许位置1或清零,即进入临界代码段时,把中断允许位清零,退出临界代码段时,把中断允许位置1;第二种方法是进入临界代码段时,先将中断状态保存到堆栈中,然后关闭中断。与之对应的是,退出临界代码段时,从堆栈中恢复前面保存的中断状态。第三种方法是,由于某些编译提供了扩展功能,用户可以得到当前处理器状态字的值,并将其保存在C函的局部变量之中。这个变量可用于恢复状态寄存器SREG的值。由于ICCAVR不提供此项扩展功能,所以本文暂不考虑用第 三种方法进入临界代码段。第一种方法存在着一个小小的问题:如果在关闭中断后调用μC/OS-II的功能函数,当函数返 回后,中断可能会被打开。我们希望如果在调用μC/OS-II的功能函数前,中断是关着的,那么在函数返回后,中断仍然是关着的。方法1显然不满足要求。本文使用μC/OS-II的第二种方法——先将中断状态保存到堆栈中,然后关闭中断。
|
|
|
|
|
|