uCOS-II 在 ATmega128 上的移植 Step by Step
-ICC-AVR 编译器
(ICC7.14C)
ba_wang_mao
2009 年 5 月
ATmega128 内核特点
成都理工学院 ba_wang_mao 1
本文详细介绍了把 μC/OS-Ⅱ移植到 ATMEL 公司的 8 位微控制器 ATmega128 上的全过程(编译器为
ICC-AVR)。所谓移植,就是使一个实时内核能某个微处理器或微控制器上运行。在移植之前,希望读者
能熟悉所用微处理器和 C 编译器的特点。
1. ATmega128 内核特点
之所以要先介绍 ATmega128MCU 内核特点,是因为在 μ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 寄存器还能用作对程序存储空间进行间接寻
址的寄存器。有些 AVRC 语言编译器还把 Y 寄存器作为软件堆栈的堆栈指针,比如 ICC-AVR。
堆栈指针(SP)是一个指示堆栈顶部地址的 16 位寄存器。在 ICCAVR 中,它被用作指向硬件堆栈的
堆栈指针。AVR 单片机上电复位后,SP 指针的初始值为 0x0000,由于 AVR 单片机的堆栈是向下生长的
(从高地址向低地址生长),所以系统程序一开始必须对堆栈指针 SP 进行初始化,即将 SP 的值设为数据
存储空间的最高地址。ICCAVR 编译器在链接 C 程序文件的时候,会自动在程序头链入 startup 文件。startup
文件里面的程序将会去做初始化 SP 指针的工作。链入 tartup 文件是 ICCAVR 这个编译器的特点,在用其
它编译器的时候,希望读者确认所使用的编译器是否带有自动初始化 SP 的功能,若没有,应在用户程序
中初始化 SP。
1.2. 数据存储空间(仅内部)
AVR 单片机的数据存储器是线形的,从低地址到高地址依次是 CPU 寄存器区(32 个通用寄存器),
I/O 寄存器区,数据存储区 ICCAVR 编译器又将数据存储区划分为全局变量和字符串区,软件堆栈区和硬
件堆栈区三个空间。
ATmega128内核特点
成都理工学院 ba_wang_mao 2
ICCAVR 编译器将堆栈分成了两个功能不同的堆栈来处理(这一点与 8051 系列的单片机编译器处理
方式不同)。
硬件堆栈:用于储存子程序和中断服务子程序调用时的函数返回地址。这块数据区域由堆栈指针 SP 进
行寻址,数据的进栈和出栈有专门的汇编指令(POP,PUSH 等)支持,所以叫做硬件堆栈区。
软件堆栈:用于传递参数,储存临时变量和局部变量。这块数据区域是用软件模拟堆栈储存数据的方式
进行数据存储,用 Y 寄存器进行寻址,所以叫做软件堆栈区。
AVR 单片机的硬件堆栈的生长方向是向下的(从高地址向低地址生长),所以软件堆栈在定义的时候,
也采取相同的生长方向。
这里没有用 ATmega128 而采用 AVR 单片机的提法是因为 ATmega128 属于 AVR 系列单片机中的一
种,而所有的 AVR 单片机的数据存储器组织方式都是一致的。在创建 μC/OS-Ⅱ的任务栈时,需要了解所
用微处理器数据存储空间尤其是堆栈空间的组形式及相关的操作。读者应参阅所用微处理器的资料和编译
器的帮助文档,了解该部分知识。
1.3. Tmega128 的中断响应机制
ATmega128 有 34 个不同的中断源,每个中断源和系统复位在程序存储空间都有一个独立的中断向量
(中断入口地址)。每个中断源都有各自独立的中断允许控制位,当某个中断源的中断允许控制位为“1”
且全局中断允许位 I 也为“1”时,系统才响应该中断。
当系统响应一个中断请求后,会自动将全局中断允许位 I 清零,此时,后续中断响应被屏蔽。当系统
执行中断返回指令 RETI 时,会将全局中断允许位 I 置“1”,以允许响应下一个中断。若用户想实现中断
嵌套,必须在中断服务子程序中将全局中断允许位 I 置“1”。(这一点与 8051 系列的单片机不同)中断向
量表中,处于低地址的中断具有高的优先级。优先级高只是表明在多个中断同时发生的时候,系统先响应
优先级高的中断,并不含有高优先级的中断能打断低优先级的中断处理工程的意思。这与 8051 系列单片
机的中断优先级概念不同。
由于 μC/OS-Ⅱ的任务切换实际上是模拟一次中断,因此需要知道 CPU 的中断响应机制。中断发生
CPU 寄存器区 低地址
I/O 寄存器区
全局变量和字符串区
软件堆栈区
硬件堆栈区 高地址
ATmega128内核特点
成都理工学院 ba_wang_mao 3
时,ATmega128 按以下步骤顺序执行:
1、全局中断允许位 I 清零。
2、将指向下一条指令的 PC 值压入硬件堆栈,同时堆栈指针 SP 减 2。
3、选择最高优先级的中断向量装入 PC,程序从此地址继续执行中断处理。
4、当执行中断处理时,中断源的中断允许控制位清零。
中断结束后,执行 RETI 指令,此时
1、全局中断允许位 I 置“1”。
2、PC 从堆栈推出,程序从被中断的地方继续执行。
特别要注意的是:AVR 单片机在响应中断及从中断返回时,并不会对状态寄存器 SREG 和通用寄存
器自动进行保存和恢复操作,因此,对状态寄存器 SREG 和通用寄存器的中断保护工作必须由用户来完成
(只不过如果用户使用 C 语言编程,编译器已经帮你完成了上述寄存器入栈工作)。
1.4. Tmega128 的定时器中断
ATmega128 有三个定时器:T0,T1,T2,它们三者都有计数溢出中断功能,而且 T1 和 T2 还有匹配比
较中断,即定时器计数到设定的值时,产生中断并自动清零。若系统采用这种中断方式,其好处是在中断
服务程序 ISR 中不需要重新装载定时器的值。但本文出于通用性的考虑,仍采用定时器计数溢出中断方式。
μC/OS-Ⅱ的移植
成都理工学院 ba_wang_mao 4
2. μC/OS-Ⅱ的移植
2.1. 移植条件
要实现 μC/OS-Ⅱ的移植,所用的处理器和编译器必须满足一定的条件:
(1)、所用的 C 编译器能产生可重入代码。
可重入代码是指可以被一个以上的任务调用,而不必担心其数据会被破坏的代码。可重入代码任何时
候都可以被中断,一段时间以后又可以重新运行,而相应的数据不会丢失,不可重入代码则不行。本文所
使用 ImageCraft 公司的 ICCAVRV6.29 编译器能产生可重入代码。
(2)、用 C 语言就可以打开和关闭中断。
本文所使用的 ICCAVRV6.29 编译器支持在 C 语言中内嵌汇编语句且提供专门开关中断的宏:CLI()
和 SEI()。这样,使得在 C 语言中开关中断非常方便。
(3)、处理器支持中断,并且能产生定时中断(通常在 10 至 100Hz 之间)
本文使用的 ATmega128,有 3 个定时器,能产生 μC/OS-Ⅱ所需的定时中断。
(4)、处理器支持能够容纳一定数量数据的硬件堆栈。
本文使用的 ATmega128 有 4KRAM,硬件堆栈可以开辟在这 4KRAM 中。
(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)
根据这几项内容,本文逐步来完成。