基于ARM7(LPC2131)平台的μC/OS-Ⅱ的移植
1.μC/OS-Ⅱ简介
uC/OS-II读做“micro COS2”, 意为“微控制器操作系统版本2”。uC/OS-II是著名的,源代码公开的实时内核,可用于各类8位,16位和32位单片机或DSP。从uC/OS算起,该内核已有十多年应用历史,在诸多领域得到广泛应用。
2.μC/OS-Ⅱ的特点
(1)提供源代码
(2)可固化
(3)可裁剪
(4)可剥夺
(5)多任务
(6)可确定性
(7)任务栈
(8)系统服务
(9)中断管理
(10)稳定性与可靠性
3.编译器的选择
ARM处理器核的C编译器有很多,大概有SDT,ADS1.2,IAR,TASKING和GCC等。我最终选择的是ADS1.2编译程序和调试。
4.ARM7简介
ARM7处理器核具有用户,系统,管理,中止,未定义,中断和快中断7中工作模式。其中除了用户模式外其他均为特权模式。同时支持两个指令集:16位Thumb指令集和32位ARM指令集。
5.开发环境的搭建
操作系统:Windows XP
集成开发环境:ADS1.2
调试环境:AXD(ADS1.2配套的软件)
串口调试工具
6.项目中所运用到的技术创新
(1)实现了加密程序,保护了用户应用程序代码,利用在特定的地址0x1fc处写入特定的值0x87654321实现加密
(2)通过工程配置文件加载程序代码,实现了写应用程序的模板工程,不用每次配置工程设置
(3)初始化程序和操作系统代码都留有很多扩充的接口,方便不同的用户需要实现不同的启动代码功能和扩充操作系统的功能
7.项目的特色
(1)在本文档中有完整的源代码,并且有很详细的注释
(2)只要具备同样的软硬件环境,根据我的《过程与结果总结报告》完全可以实现这个项目
(3)同时完成了引导程序和操作系统的移植
(4)涉及到软硬件的知识,不仅仅只是软件
二.移植过程
1.编写bootloader(ARM启动代码)
1.启动代码综述
在一般32位ARM应用系统中,软件大多数采用C语言进行编程,并且以嵌入式操作系统为开发平台,这样大大的提高了开发效率及软件性能。为了能够进行系统初始化,通常会用一个汇编文件作为启动代码。它可以实现向量表定义、堆栈初始化、系统变量初始化、中断系统初始化、I/O初始化、外围初始化和地址重映射等操作。
ARM公司之设计内核,不生产芯片,只是把内核授权给其他厂商。其他厂商购买了授权后加入自己的外设,生产出各具特色的芯片。这样就促进了基于ARM处理器核的芯片的多元化,但也使得各种芯片的启动代码差别很大,不易编写出统一的启动代码。
2.文件的组成
汇编文件1.Startup.S:异常向量表、各模式堆栈初始化和跳到main()函数入口等
2.IRQ.S :负责管理中断嵌套
C文件 Target.C :初始化目标板
头文件 Config.H :系统配置和类型定义
Target.H :一些和目标板相关的声明
LPC2294.H :LPC2000系列芯片特殊寄存器定义
分散加载文件mem_a.Scf:在片内Flash发布,RelInFLASH
Mem_b.Scf:在片内RAM调试,DebugInRAM,在uCOSII中不使用这种布局模式
Mem_c.Scf:在片内Flash调试,DebugInFLASH
3.各源代码文件内容及其注释
1.Startup.S:
;**--------------File Info---------------------------------------------
;** File name: Startup.s
;** Last modified Date: 2009-08-17
;** Last Version: 1.0
;** Descriptions: 为 LPC2100系列芯片的启动代码,从这里开始执行,包括初始化代码,为每一种模
;** 式 的进入口和任务的堆栈
;定义堆栈的大小
SVC_STACK_LEGTH EQU 0
FIQ_STACK_LEGTH EQU 0
IRQ_STACK_LEGTH EQU 256
ABT_STACK_LEGTH EQU 0
UND_STACK_LEGTH EQU 0
NoInt EQU 0x80 ;Bit7,I位
NoFIQ EQU 0x40 ;Bit6,F位
USR32Mode EQU 0x10 ;M[4:0]=10000,用户模式
SVC32Mode EQU 0x13 ;M[4:0]=10011,管理模式
SYS32Mode EQU 0x1f ;M[4:0]=11111,系统模式
IRQ32Mode EQU 0x12 ;M[4:0]=10010,IRQ中断
FIQ32Mode EQU 0x11 ;M[4:0]=10001,快速中断
IMPORT __use_no_semihosting_swi
;引入的外部标号在这声明
IMPORT FIQ_Exception ; 快速中断异常处理程序
IMPORT __main ; C语言主程序入口
IMPORT TargetResetInit ;目标板基本初始化
;给外部使用的标号在这声明
EXPORT bottom_of_heap
EXPORT StackUsr
EXPORT Reset
EXPORT __user_initial_stackheap
CODE32
AREA vectors,CODE,READONLY
ENTRY
;中断向量表
Reset
LDR PC, ResetAddr ;0x00,复位
LDR PC, UndefinedAddr ;0x04,未定义地址
LDR PC, SWI_Addr ;0x08,软件中断
LDR PC, PrefetchAddr ;0x0c,预取指中止
LDR PC, DataAbortAddr ;0x10,数据中止
DCD 0xb9205f80 ;0x14,保留
LDR PC, [PC, #-0xff0] ;0x18,IRQ中断
LDR PC, FIQ_Addr ;0x1C,快速中断
ResetAddr DCD ResetInit ;复位初始化处理程序地址
UndefinedAddr DCD Undefined ;未定义指令处理程序地址
SWI_Addr DCD SoftwareInterrupt ;软件中断处理程序地址
PrefetchAddr DCD PrefetchAbort ;预取指中止处理程序地址
DataAbortAddr DCD DataAbort ;数据中止处理程序地址
Nouse DCD 0 ;未使用
IRQ_Addr DCD 0 ;IRQ中断,已在"LDR [PC,#-0xff0]"中处理
FIQ_Addr DCD FIQ_Handler
;未定义指令
Undefined
B Undefined ;死循环
;软中断
SoftwareInterrupt
; B SoftwareInterrupt
;//增加开/关中断处理
CMP R0, #4 ;判断传过来的参数是否大于4
LDRLO PC, [PC, R0, LSL #2] ;小于4(参数正确),进行查表
MOVS PC, LR ;大于或者等于4(参数出错),则返回
SwiFunction
DCD IRQDisable ;0号调用,禁止IRQ中断
DCD IRQEnable ;1号调用,使能IRQ中断
DCD FIQDisable ;2号调用,禁止FIQ中断
DCD FIQEnable ;3号调用,使能FIQ中断
IRQDisable
;关IRQ中断
MRS R0, SPSR ;读取SPSR的值
ORR R0, R0, #NoInt ;置位I位,设置关IRQ中断
MSR SPSR_c, R0 ;回写SPSR
MOVS PC, LR ;返回
IRQEnable
;开IRQ中断
MRS R0, SPSR
BIC R0, R0, #NoInt ;清零I位,设置开IRQ中断
MSR SPSR_c, R0
MOVS PC, LR
FIQDisable
;关FIQ中断
MRS R0, SPSR
ORR R0, R0, #NoFIQ ;置位F位,设置关FIQ中断
MSR SPSR_c, R0
MOVS PC, LR
FIQEnable
;开FIQ中断
MRS R0, SPSR
BIC R0, R0, #NoFIQ ;清零F位,设置开FIQ中断
MSR SPSR_c, R0
MOVS PC, LR
;取指令中止
PrefetchAbort
B PrefetchAbort ;死循环
;取数据中止
DataAbort
B DataAbort ;死循环
;快速中断
FIQ_Handler
STMFD SP!, {R0-R3, LR} ;寄存器R0~R3,LR入栈
BL FIQ_Exception ;调用FIQ处理程序
LDMFD SP!, {R0-R3, LR} ;寄存器R0~R3,LR出栈
SUBS PC, LR, #4 ;计算返回地址
;/*****************************************************************************************
;** unction name 函数名称: InitStack
;** Descriptions 功能描述: 初始化堆栈
;** Created by 作 者: 吴友强
;** Created Date 日 期: 2009/07/20 2009年7月20日
;*****************************************************************************************/
;初始化堆栈,此时禁止IRQ和FIQ中断,处于ARM状态
InitStack
MOV R0, LR
;Build the SVC stack
;设置管理模式堆栈
MSR CPSR_c, #0xd3
LDR SP, StackSvc
;Build the IRQ stack
;设置中断模式堆栈
MSR CPSR_c, #0xd2
LDR SP, StackIrq
;Build the FIQ stack
;设置快速中断模式堆栈
MSR CPSR_c, #0xd1
LDR SP, StackFiq
;Build the DATAABORT stack
;设置中止模式堆栈
MSR CPSR_c, #0xd7
LDR SP, StackAbt
;Build the UDF stack
;设置未定义模式堆栈
MSR CPSR_c, #0xdb
LDR SP, StackUnd
;Build the SYS stack
;设置系统模式堆栈
MSR CPSR_c, #0xdf ;切换到系统模式,之后将在系统模式下运行
LDR SP, =StackUsr ;除非进行模式切换
MOV PC, R0
;/*****************************************************************************************
;** unction name 函数名称: ResetInit
;** Descriptions 功能描述: 复位入口
;** Created by 作 者: 吴友强
;** Created Date 日 期: 2009/07/20 2009年7月20日
;**----------------------------------------------------------------------------------------
;*****************************************************************************************/
ResetInit
BL InitStack ;初始化堆栈
BL TargetResetInit ;目标板基本初始化
B __main ;跳转到c语言入口
;/*****************************************************************************************
;** unction name 函数名称: __user_initial_stackheap
;** Descriptions 功能描述: 库函数初始化堆和栈,不能删除
;** Created by 作 者: 吴友强
;** Created Date 日 期: 2009/07/20 2009年7月20日
;*****************************************************************************************/
__user_initial_stackheap
LDR r0,=bottom_of_heap
; LDR r1,=StackUsr
MOV pc,lr
StackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4 ;管理模式堆栈
StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4 ;IRQ模式堆栈
StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4 ;FIQ模式堆栈
StackAbt DCD AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4 ;中止模式堆栈
StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH - 1)* 4;未定义模式堆栈
;/*****************************************************************************************
;** unction name 函数名称: CrpData
;** Descriptions 功能描述: 芯片加密,代码保护
;** input parameters 输 入: None 无
;** Returned value 输 出 : None 无
;** Used global variables 全局变量: None 无
;** Calling modules 调用模块: None 无
;**
;** Created by 作 者: 吴友强
;** Created Date 日 期: 2009/07/20 2009年7月20日
;*****************************************************************************************/
IF :DEF: EN_CRP
IF . >= 0x1fc
INFO 1,"/nThe data at 0x000001fc must be 0x87654321./nPlease delete some source before this line."
ENDIF
CrpData
WHILE . < 0x1fc ;循环用NOP填充,直到0x1FC
NOP
WEND
CrpData1
DCD 0x87654321 ;/* 当此数为0x87654321时,用户程序被保护 */
ENDIF
;/* 分配堆栈空间 */
AREA MyStacks, DATA, NOINIT, ALIGN=2
SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ; 管理模式堆栈空间
IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ; 中断模式堆栈空间
FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ; 快速中断模式堆栈空间
AbtStackSpace SPACE ABT_STACK_LEGTH * 4 ; 中止义模式堆栈空间
UndtStackSpace SPACE UND_STACK_LEGTH * 4 ; 未定义模式堆栈
AREA Heap, DATA, NOINIT ;Heap通过分散加载文件定位
bottom_of_heap SPACE 1
AREA Stacks, DATA, NOINIT ;Stack通过分散加载文件定位
StackUsr
END
2.IRQ.S
NoInt EQU 0x80
USR32Mode EQU 0x10
SVC32Mode EQU 0x13
SYS32Mode EQU 0x1f
IRQ32Mode EQU 0x12
FIQ32Mode EQU 0x11
CODE32
AREA IRQ,CODE,READONLY
MACRO
$IRQ_Label HANDLER $IRQ_Exception_Function
EXPORT $IRQ_Label ; 输出的标号
IMPORT $IRQ_Exception_Function ; 引用的外部标号
$IRQ_Label
SUB LR, LR, #4 ; 计算返回地址
STMFD SP!, {R0-R3, R12, LR} ; 保存任务环境
MRS R3, SPSR ; 保存状态
STMFD SP, {R3,LR}^ ; 保存SPSR和用户状态的SP,注意不能回写
; 如果回写的是用户的SP,所以后面要调整SP
NOP
SUB SP, SP, #4*2
MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式
BL $IRQ_Exception_Function ; 调用c语言的中断处理程序
MSR CPSR_c, #(NoInt | IRQ32Mode) ; Switch bak to IRQ mode 切换回irq模式
LDMFD SP, {R3,LR}^ ; 恢复SPSR和用户状态的SP,注意不能回写
; 如果回写的是用户的SP,所以后面要调整SP
MSR SPSR_cxsf, R3
ADD SP, SP, #4*2
LDMFD SP!, {R0-R3, R12, PC}^
MEND
;/* 以下可添加中断句柄,根据实际情况改变 */
;Timer0_Handler HANDLER Timer0
END
3.Target.C
#define IN_TARGET
#include "config.h"
void __irq IRQ_Exception(void)
{
while(1); // 这一句替换为自己的代码
}
void FIQ_Exception(void)
{
while(1); // 这一句替换为自己的代码
}
void TargetInit(void)
{
/* 添加自己的代码 */
}
void TargetResetInit(void)
{
#ifdef __DEBUG_RAM //如果在片内RAM调试
MEMMAP = 0x2; //映射到片内RAM
#endif
#ifdef __DEBUG_FLASH //如果在片内Flash调试
MEMMAP = 0x1; //映射到片内FLASH
#endif
#ifdef __IN_CHIP //如果在片内Flash发布(将会被加密)
MEMMAP = 0x1; //映射到片内FLASH
#endif
/* 设置系统各部分时钟 */
PLLCON = 1; //设置激活但未连接PLL
#if (Fpclk / (Fcclk / 4)) == 1
VPBDIV = 0;
#endif
#if (Fpclk / (Fcclk / 4)) == 2
VPBDIV = 2;
#endif
#if (Fpclk / (Fcclk / 4)) == 4
VPBDIV = 1;
#endif
//设定PLL的乘因子M和除因子P的值
#if (Fcco / Fcclk) == 2
PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);
#endif
#if (Fcco / Fcclk) == 4
PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);
#endif
#if (Fcco / Fcclk) == 8
PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);
#endif
#if (Fcco / Fcclk) == 16
PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);
#endif
PLLFEED = 0xaa; //发送PLL馈送序列,执行设定PLL的动作
PLLFEED = 0x55;
while((PLLSTAT & (1 << 10)) == 0); //等待PLL锁定
PLLCON = 3; //设置激活并连接PLL
PLLFEED = 0xaa; //发送PLL馈送序列,执行激活和链接动作
PLLFEED = 0x55;
/* 设置存储器加速模块 */
MAMCR = 0; //禁止MAM功能
#if Fcclk < 20000000 //根据Fcclk的大小来设置MAM定时寄存器
MAMTIM = 1;
#else
#if Fcclk < 40000000
MAMTIM = 2;
#else
MAMTIM = 3;
#endif
#endif
MAMCR = 2; //使能MAM
/* 初始化VIC */
VICIntEnClr = 0xffffffff; //清零所有中断
VICVectAddr = 0;
VICIntSelect = 0;
/* 添加其他的代码 */
}
/***************************************************************************************
** 以下为一些与系统相关的库函数的实现
***************************************************************************************/
#include <rt_sys.h>
#include <stdio.h>
#pragma import(__use_no_semihosting_swi)
int __rt_div0(int a)
{
a = a;
return 0;
}
int fputc(int ch,FILE *f)
{
ch = ch;
f = f;
return 0;
}
int fgetc(FILE *f)
{
f = f;
return 0;
}
int _sys_close(FILEHANDLE fh)
{
fh = fh;
return 0;
}
int _sys_write(FILEHANDLE fh, const unsigned char * buf, unsigned len, int mode)
{
fh = fh;
buf = buf;
len =len;
mode = mode;
return 0;
}
int _sys_read(FILEHANDLE fh, unsigned char * buf,
unsigned len, int mode)
{
fh = fh;
buf = buf;
len =len;
mode = mode;
return 0;
}
void _ttywrch(int ch)
{
ch = ch;
}
int _sys_istty(FILEHANDLE fh)
{
fh = fh;
return 0;
}
int _sys_seek(FILEHANDLE fh, long pos)
{
fh = fh;
return 0;
}
int _sys_ensure(FILEHANDLE fh)
{
fh = fh;
return 0;
}
long _sys_flen(FILEHANDLE fh)
{
fh = fh;
return 0;
}
int _sys_tmpnam(char * name, int sig, unsigned maxlen)
{
name = name;
sig = sig;
maxlen = maxlen;
return 0;
}
void _sys_exit(int returncode)
{
returncode = returncode;
}
char *_sys_command_string(char * cmd, int len)
{
cmd = cmd;
len = len;
return 0;
}
4.config.h
#ifndef __CONFIG_H
#define __CONFIG_H
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef unsigned char uint8; /* 无符号8位整型变量 */
typedef signed char int8; /* 有符号8位整型变量 */
typedef unsigned short uint16; /* 无符号16位整型变量 */
typedef signed short int16; /* 有符号16位整型变量 */
typedef unsigned int uint32; /* 无符号32位整型变量 */
typedef signed int int32; /* 有符号32位整型变量 */
typedef float fp32; /* 单精度浮点数(32位长度) */
typedef double fp64; /* 双精度浮点数(64位长度) */
#include "LPC2294.h"
/* 系统设置, Fosc、Fcclk、Fcco、Fpclk必须定义*/
#define Fosc 11059200 //应当与实际一至晶振频率,10MHz~25MHz,应当与实际一至
#define Fcclk (Fosc * 4) //系统频率,必须为Fosc的整数倍(1~32),且<=60MHZ
#define Fcco (Fcclk * 4) //CCO频率,必须为Fcclk的2、4、8、16倍,范围为156MHz~320MHz
#define Fpclk (Fcclk / 4) * 1 //VPB时钟频率,只能为(Fcclk / 4)的1、2、4倍
#include "target.h"
#endif
5.target.h
#ifndef __TARGET_H
#define __TARGET_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef IN_TARGET
extern void Reset(void); //复位
extern void TargetInit(void); //目标板初始化
#endif
#ifdef __cplusplus
}
#endif
#endif
__swi(0x00) void SwiHandle1(int Handle); //软件中断
#define IRQDisable() SwiHandle1(0) //禁止IRQ中断
#define IRQEnable() SwiHandle1(1) //使能IRQ中断
#define FIQDisable() SwiHandle1(2) //禁止FIQ中断
#define FIQEnable() SwiHandle1(3) //使能FIQ中断
6.LPC2294.H
/* 向量中断控制器(VIC)的特殊寄存器 */
#define VICIRQStatus (*((volatile unsigned long *) 0xFFFFF000))
#define VICFIQStatus (*((volatile unsigned long *) 0xFFFFF004))
#define VICRawIntr (*((volatile unsigned long *) 0xFFFFF008))
#define VICIntSelect (*((volatile unsigned long *) 0xFFFFF00C))
#define VICIntEnable (*((volatile unsigned long *) 0xFFFFF010))
#define VICIntEnClr (*((volatile unsigned long *) 0xFFFFF014))
#define VICSoftInt (*((volatile unsigned long *) 0xFFFFF018))
#define VICSoftIntClear (*((volatile unsigned long *) 0xFFFFF01C))
#define VICProtection (*((volatile unsigned long *) 0xFFFFF020))
。。。。。。省略
7.men_a.Scf适用于片内Flash发布
ROM_LOAD 0x00000000 ;加载区,从0x00000000开始
{
ROM_EXEC 0x00000000 ;执行区,起始地址,空间大小要和加载区一致
{
Startup.o (vectors, +First) ;startup.S中的向量表
* (+RO) ;其他代码
}
IRAM 0x40000000 ;变量区,内部RAM开始
{
Startup.o (MyStacks) ;startup.S中的MyStacks
* (+RW,+ZI) ;文件中的其他变量
}
HEAP +0 UNINIT ;系统堆空间
{
Startup.o (Heap) ;startup.S中的Heap
}
STACKS 0x40002000 UNINIT ;LPC2131片内RAM最高端
{ ;系统的堆空间
Startup.o (Stacks) ;startup.S中的Stacks
}
}
8.men_c.Scf适用于片内Flash调试
ROM_LOAD 0x0
{
ROM_EXEC 0x00000000
{
Startup.o (vectors, +First)
* (+RO)
}
IRAM 0x40000000
{
Startup.o (MyStacks)
* (+RW,+ZI)
}
HEAP +0 UNINIT
{
Startup.o (Heap)
}
STACKS 0x40002000 UNINIT
{
Startup.o (Stacks)
}
}
2.启动代码工作流程
1.芯片复位后,PC=0x00000000,在异常向量表0x00000000处
2.芯片根据异常处理程序地址表,得到复位程序的地址
3.跳转到复位处理程序处
4.调用InitStack()函数,初始化个模式的堆栈
5.调用Target.c中的TargetResetInit函数,初始化目标板:设置Remap,各部分时钟,存储器加速模块和VIC等
6.跳转到用户C程序入口main()处
3.修改uC/OSII代码(完成移植)
1.要移植一个操作系统到一个特定的CPU体系结构上,并不是一件很容易的事情。对移植者有以下要求:
(1)对目标体系结构要有深入的了解:
参考资料:ARM公司。ARM Architecture Reference Manual。
(2)对OS原理要有深入的了解;
参考资料:Labrosse J Jean。嵌入式实时操作系统uC/OS-II(第二版)。邵贝贝等译。
(3)对所使用的编译器要有较深的了解;
参考资料:ADS自带的编译器和连接器的手册
(4)对需要移植的操作系统要有相当的了解;
参考资料:Labrosse J Jean。嵌入式实时操作系统uC/OS-II(第二版)。邵贝贝等译。
(5)对具体使用的芯片也要一定的了解;
参考资料:LPC2131的数据手册和使用手册
2.需要移植的文件有:OS_CPU.H,OS_CPU_A.ASM,OS_CPU_C.C,IRQINC
(1).OS_CPU.H文件:定义与编译器无关的数据类型和堆栈的数据类型,以及一些开关中断的宏代码,例如:BOOLEAN,INT8U等数据类型,OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()开关中断宏,OS_STK_GROWTH堆栈的增长方向,OS_TASLK_SW任务切换时执行的代码。源代码如下:
/****************************************Copyright (c)************************************
** FreshAir嵌入式软件开发团队
** 软件开发团队
** 技术部
** http://www.freshiair.com
**--------------文件信息-------------------------------------------------------------------
**文 件 名: os_cpu.h
**创 建 人: 吴友强
**最后修改日期: 2009年7月28日
**描 述: μCOS-II在LPC210x上的移植代码CPU配置部分,用ADS1.2编译
**--------------当前版本修订---------------------------------------------------------------
** 修改人: 吴友强
** 日 期: 2009年7月18日
** 描 述: 增加函数
**-----------------------------------------------------------------------------------------
******************************************************************************************/
#ifdef OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT extern
#endif
/******************************************************************************************
* 定义与编译器无关的数据类型
******************************************************************************************/
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
typedef unsigned int OS_STK;
/******************************************************************************************
* 与ARM7体系结构相关的一些定义
******************************************************************************************/
#define OS_CRITICAL_METHOD 2 /*选择开、关中断的方式*/
__swi(0x00) void OS_TASK_SW(void); /*任务级任务切换函数*/
__swi(0x01) void _OSStartHighRdy(void); /*运行优先级最高的任务*/
__swi(0x02) void OS_ENTER_CRITICAL(void); /*关中断*/
__swi(0x03) void OS_EXIT_CRITICAL(void); /*开中断*/
__swi(0x80) void ChangeToSYSMode(void); /*任务切换到系统模式*/
__swi(0x81) void ChangeToUSRMode(void); /*任务切换到用户模式*/
__swi(0x82) void TaskIsARM(INT8U prio); /*任务代码是ARM代码*/
__swi(0x83) void TaskIsTHUMB(INT8U prio); /*任务代码是Thumb代码*/
__swi(0x40) void *GetOSFunctionAddr(int Index); /*获取系统服务函数入口*/
__swi(0x41) void *GetUsrFunctionAddr(int Index);/*获取自定义服务函数入口*/
__swi(0x42) void OSISRBegin(void); /*中断开始处理*/
__swi(0x43) void OSISRNeedSwap(void); /*判断中断是否需要切换*/
#define OS_STK_GROWTH 1 /*堆栈是从上往下长的*/
#define USR32Mode 0x10
#define SYS32Mode 0x1f
#define NoInt 0x80
#ifndef USER_USING_MODE
#define USER_USING_MODE USER32Mode /*任务缺省模式*/
#endif
#ifndef OS_SELF_EN
#define OS_SELF_EN 0 /*允许返回OS与任务分别编译、固化*/
#endif
OS_CPU_EXT INT32U OsEnterSum; /*关中断计数器(开关中断的信号量)*/
(2).OS_CPU_C.C文件:OSTaskStkInit()任务堆栈初始化函数和一些uC/OS-II在执行某些操作时调用的用户函数,一般为空,但是需要,避免编译警告。源代码如下:
/****************************************Copyright (c)************************************
** FreshAir嵌入式软件开发团队
** 软件开发团队
** 技术部
** http://www.freshiair.com
**--------------文件信息-------------------------------------------------------------------
**文 件 名: os_cpu_c.c
**创 建 人: 吴友强
**最后修改日期: 2009年7月28日
**描 述: μCOS-II在LPC210x上的移植代码CPU配置部分,用ADS1.2编译
**--------------当前版本修订---------------------------------------------------------------
** 修改人: 吴友强
** 日 期: 2009年7月18日
** 描 述: 增加函数
**-----------------------------------------------------------------------------------------
******************************************************************************************/
#define OS_CPU_GLOBALS
#include "config.h"
/******************************************************************************************
** 函数名称: OSTaskStkInit
** 功能描述: 任务堆栈初始化代码,本函数调用失败会使系统崩溃
** 输 入: task : 任务开始执行的地址
** pdata :传递给任务的参数
** ptos :任务的堆栈开始位置
** opt :附加参数,当前版本对于本函数无用,具体意义参见OSTaskCreateExt()的opt参数
** 输 出: 栈顶指针位置
** 作 者: 吴友强
** 日 期: 2009年7月28日
**-----------------------------------------------------------------------------------------
******************************************************************************************/
OS_STK *OSTaskStkInit(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
{
OS_STK *stk ;
opt = opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
/* build a context for the new task */
*stk = (OS_STK)task; /* pc */
*--stk = (OS_STK)task; /* lr */
*--stk = 0; /* r12 */
*--stk = 0; /* r11 */
*--stk = 0; /* r10 */
*--stk = 0; /* r9 */
*--stk = 0; /* r8 */
*--stk = 0; /* r7 */
*--stk = 0; /* r6 */
*--stk = 0; /* r5 */
*--stk = 0; /* r4 */
*--stk = 0; /* r3 */
*--stk = 0; /* r2 */
*--stk = 0; /* r1 */
*--stk = (unsigned int) pdata; /* r0 ,第一个参数使用R0传递*/
*--stk = (USER_USING_MODE | 0x00); /* spsr, 允许IRQ,FIQ中断*/
*--stk = 0; /* 关中断计数器OsEnterSum */
return (stk);
}
/******************************************************************************************
** 函数名称: SWI_Exception
** 功能描述: 软中断异常处理程序,提供一些系统服务
**
** 输 入: SWI_Num:功能号
** Regs[0] 为第一个参数,也是返回值
** Regs[1] 为第二个参数
** Regs[2] 为第三个参数
** Regs[3] 为第四个参数
** 输 出: 根据功能而定
** 作 者: 吴友强
** 日 期: 2009年7月19日
**-----------------------------------------------------------------------------------------
******************************************************************************************/
#if OS_SELF_EN > 0
extern int const _OSFunctionAddr[];
extern int const _UsrFunctionAddr[];
#endif
void SWI_Exception(int SWI_Num, int *Regs)
{
OS_TCB *ptcb;
switch (SWI_Num)
{
case 0x02: /* 关中断函数OS_ENTER_CRITICAL()*/
__asm
{
MRS R0,SPSR
ORR R0,R0,#NoInt
MSR SPSR_c,R0
}
OsEnterSum++;
break;
case 0x03: /* 开中断函数OS_EXIT_CRITICAL()*/
if (--OsEnterSum == 0)
{
__asm
{
MRS R0,SPSR
BIC R0,R0,#NoInt
MSR SPSR_c,R0
}
}
break;
#if OS_SELF_EN > 0
case 0x40: /* 返回指定系统服务函数的地址 */
/* 函数地址存于数组_OSFunctionAddr中*/
/* 数组_OSFunctionAddr需要另外定义 */
/* Regs[0] 为第一个参数,也是返回值 */
/* Regs[1] 为第二个参数 */
/* Regs[2] 为第三个参数 */
/* Regs[3] 为第四个参数 */
/* 仅有一个参数为系统服务函数的索引 */
Regs[0] = _OSFunctionAddr[Regs[0]];
break;
case 0x41:
/* 返回指定用户的服务函数的地址 */
/* 函数地址存于数组_UsrFunctionAddr中*/
/* 数组_UsrFunctionAddr需要另外定义 */
/* Regs[0] 为第一个参数,也是返回值 */
/* Regs[1] 为第二个参数 */
/* Regs[2] 为第三个参数 */
/* Regs[3] 为第四个参数 */
/* 仅有一个参数为用户服务函数的索引 */
Regs[0] = _UsrFunctionAddr[Regs[0]];
break;
case 0x42: /* 中断开始处理 */
OSIntNesting++;
break;
case 0x43: /* 判断中断是否需要切换 */
if (OSTCBHighRdy == OSTCBCur)
{
Regs[0] = 0;
}
else
{
Regs[0] = 1;
}
break;
#endif
case 0x80: /* 任务切换到系统模式 */
__asm
{
MRS R0,SPSR
BIC R0,R0,#0x1f
ORR R0,R0,#SYS32Mode
MSR SPSR_c,R0
}
break;
case 0x81: /* 任务切换到用户模式 */
__asm
{
MRS R0,SPSR
BIC R0,R0,#0x1f
ORR R0,R0,#USR32Mode
MSR SPSR_c,R0
}
break;
case 0x82: /* 任务是ARM代码 */
if (Regs[0] <= OS_LOWEST_PRIO)
{
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb->OSTCBStkPtr[1] &= ~(1 << 5);
}
}
break;
case 0x83: /* 任务是THUMB代码 */
if (Regs[0] <= OS_LOWEST_PRIO)
{
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb->OSTCBStkPtr[1] |= (1 << 5);
}
}
break;
default:
break;
}
}
/******************************************************************************************
** 函数名称: OSStartHighRdy
** 功能描述: uC/OS-II启动时使用OSStartHighRdy运行第一个任务,
** 实质是产生swi 1指令
** 作 者: 吴友强
** 日 期: 2009年7月28日
**-----------------------------------------------------------------------------------------
******************************************************************************************/
void OSStartHighRdy(void)
{
_OSStartHighRdy();
}
/* 以下为一些钩子函数,全部为空函数。*/
#if OS_CPU_HOOKS_EN
#if OS_VERSION > 203
void OSInitHookBegin (void)
{
}
#endif
#if OS_VERSION > 203
void OSInitHookEnd (void)
{
}
#endif
void OSTaskCreateHook (OS_TCB *ptcb)
{
ptcb = ptcb; /* Prevent compiler warning */
}
void OSTaskDelHook (OS_TCB *ptcb)
{
ptcb = ptcb; /* Prevent compiler warning */
}
void OSTaskSwHook (void)
{
}
void OSTaskStatHook (void)
{
}
#if OS_VERSION > 203
void OSTCBInitHook (OS_TCB *ptcb)
{
ptcb = ptcb; /* Prevent Compiler warning */
}
#endif
#if OS_VERSION >= 251
void OSTaskIdleHook (void)
{
}
#endif
#endif
(3)OS_CPU_A.ASM文件:进入多任务环境时,应运行优先级最高的任务OSStartHighRdy()函数,中断退出时的任务切换函数OSIntCtxSw()和时钟节拍中断服务程序;为使用ADS1.2编译器的管理这个文件改名为OS_CPU_A.S。源代码如下:
;/****************************************Copyright (c)************************************
;** FreshAir嵌入式软件开发团队
;** 软件开发团队
;** 技术部
;** http://www.freshiair.com
;**--------------文件信息------------------------------------------------------------------
;**文 件 名: os_cpu_a.s
;**创 建 人: 吴友强
;**最后修改日期: 2009年7月19日
;**--------------当前版本修订--------------------------------------------------------------
;** 修改人: 吴友强
;** 日 期: 2009年7月29日
;**----------------------------------------------------------------------------------------
;*****************************************************************************************/
;定义系统模式堆栈的大小
SVC_STACK_LEGTH EQU 32
NoInt EQU 0x80
USR32Mode EQU 0x10
SVC32Mode EQU 0x13
SYS32Mode EQU 0x1f
IRQ32Mode EQU 0x12
FIQ32Mode EQU 0x11
;T_bit用于检测进入异常前cpu是否处于THUMB状态
T_bit EQU 0x20
CODE32
AREA |subr|,CODE,READONLY
IMPORT OSTCBCur ;指向当前任务TCB的指针
IMPORT OSTCBHighRdy ;指向将要运行的任务TCB的指针
IMPORT OSPrioCur ;当前任务的优先级
IMPORT OSPrioHighRdy ;将要运行的任务的优先级
IMPORT OSTaskSwHook ;任务切换的钩子函数
IMPORT OSRunning ;uC/OS-II运行标志
IMPORT OsEnterSum ;关中断计数器(关中断信号量)
IMPORT SWI_Exception ;软中断异常处理程序
EXPORT __OSStartHighRdy
EXPORT OSIntCtxSw ;中断退出时的入口,参见startup.s中的IRQ_Handler
EXPORT SoftwareInterrupt ;软中断入口
;/*****************************************************************************************
;** 函数名称: SoftwareInterrupt
;** 功能描述: 软件中断,用于提供一些系统服务
;** 输 入: 依功能而定
;** 输 出 : 依功能而定
;** 调用模块: SWI_Exception
;** 作 者: 吴友强
;** 日 期: 2009年7月29日
;**----------------------------------------------------------------------------------------
;*****************************************************************************************/
;软件中断
SoftwareInterrupt
LDR SP, StackSvc ; 重新设置堆栈指针
STMFD SP!, {R0-R3, R12, LR}
MOV R1, SP ; R1指向参数存储位置
MRS R3, SPSR
TST R3, #T_bit ; 中断前是否是Thumb状态
LDRNEH R0, [LR,#-2] ; 是: 取得Thumb状态SWI号
BICNE R0, R0, #0xff00
LDREQ R0, [LR,#-4] ; 否: 取得arm状态SWI号
BICEQ R0, R0, #0xFF000000
; r0 = SWI号,R1指向参数存储位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw
LDREQ PC, =__OSStartHighRdy ; SWI 0x01为第一次任务切换
BL SWI_Exception
LDMFD SP!, {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
;/*****************************************************************************************
;** 函数名称: OSIntCtxSw
;** 功能描述: 中断退出时的入口
;** 输 入: R3 :当前任务的状态寄存器CPSR(即SPSR的值)
;** R4-R12:当前任务的R4-R11
;** 当前处理器模式的堆栈结构(出栈次序):R0-R3、R12、PC(当前任务的)
;** 输 出 : 无
;** 全局变量: OSPrioCur,OSPrioHighRdy,OSPrioCur,OSPrioHighRdy
;** 作 者: 吴友强
;** 日 期: 2009年7月29日
;**----------------------------------------------------------------------------------------
;*****************************************************************************************/
OSIntCtxSw
;下面为保存任务环境
LDR R2, [SP, #20] ;获取PC
LDR R12, [SP, #16] ;获取R12
MRS R0, CPSR
MSR CPSR_c, #(NoInt | SYS32Mode)
MOV R1, LR
STMFD SP!, {R1-R2} ;保存LR,PC
STMFD SP!, {R4-R12} ;保存R4-R12
MSR CPSR_c, R0
LDMFD SP!, {R4-R7} ;获取R0-R3
ADD SP, SP, #8 ;出栈R12,PC
MSR CPSR_c, #(NoInt | SYS32Mode)
STMFD SP!, {R4-R7} ;保存R0-R3
LDR R1, =OsEnterSum ;获取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum
;保存当前任务堆栈指针到当前任务的TCB
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1]
BL OSTaskSwHook ;调用钩子函数
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [R4]
;OSTCBCur <= OSTCBHighRdy
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4]
OSIntCtxSw_1
;获取新任务堆栈指针
LDR R4, [R6]
ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式
MOV SP, R4 ;设置堆栈指针
LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum
;恢复新任务的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 ;恢复CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务
;/*****************************************************************************************
;** 函数名称: __OSStartHighRdy
;** 功能描述: uC/OS-II启动时使用OSStartHighRdy运行第一个任务,
;** OSStartHighRdy会调用__OSStartHighRdy
;** 输 入: 无
;** 输 出 : 无
;** 全局变量: OSRunning,OSTCBCur,OSTCBHighRdy,OsEnterSum
;** 调用模块: OSTaskSwHook
;** 作 者: 吴友强
;** 日 期: 2009年7月29日
;**----------------------------------------------------------------------------------------
;*****************************************************************************************/
__OSStartHighRdy
MSR CPSR_c, #(NoInt | SYS32Mode)
;告诉uC/OS-II自身已经运行
LDR R4, =OSRunning
MOV R5, #1
STRB R5, [R4]
BL OSTaskSwHook ;调用钩子函数
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
B OSIntCtxSw_1
AREA SWIStacks, DATA, NOINIT,ALIGN=2
SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆栈空间
END
(4)IRQ.INC定义了一个宏汇编,并且是uC/OS-II for ARM7通用的中断服务程序的汇编与C语言接口代码,需要自己编写。源代码如下:
;/****************************************Copyright (c)************************************
;** FreshAir嵌入式软件开发团队
;** 软件开发团队
;** 技术部
;** http://www.freshiair.com
;**--------------文件信息------------------------------------------------------------------
;**文 件 名: irq.inc
;**创 建 人: 吴友强
;**最后修改日期: 2009年7月28日
;**描 述: 定义IRQ汇编接口代码宏
;**--------------历史版本信息--------------------------------------------------------------
;** 创建人: 吴友强
;** 版 本: v1.0
;** 日 期: 2009年7月29日
;*****************************************************************************************/
NoInt EQU 0x80
USR32Mode EQU 0x10
SVC32Mode EQU 0x13
SYS32Mode EQU 0x1f
IRQ32Mode EQU 0x12
FIQ32Mode EQU 0x11
;引入的外部标号在这声明
IMPORT OSIntCtxSw ;任务切换函数
IMPORT OSIntExit ;中断退出函数
IMPORT OSTCBCur
IMPORT OSTCBHighRdy
IMPORT OSIntNesting ;中断嵌套计数器
IMPORT StackUsr
IMPORT OsEnterSum
CODE32
AREA IRQ,CODE,READONLY
MACRO
$IRQ_Label HANDLER $IRQ_Exception_Function
EXPORT $IRQ_Label ; 输出的标号
IMPORT $IRQ_Exception_Function ; 引用的外部标号
$IRQ_Label
SUB LR, LR, #4 ; 计算返回地址
STMFD SP!, {R0-R3, R12, LR} ; 保存任务环境
MRS R3, SPSR ; 保存状态
STMFD SP, {R3, SP, LR}^ ; 保存用户状态的R3,SP,LR,注意不能回写
; 如果回写的是用户的SP,所以后面要调整SP
LDR R2, =OSIntNesting ; OSIntNesting++
LDRB R1, [R2]
ADD R1, R1, #1
STRB R1, [R2]
SUB SP, SP, #4*3
MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式
CMP R1, #1
LDREQ SP, =StackUsr
BL $IRQ_Exception_Function ; 调用c语言的中断处理程序
MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式
LDR R2, =OsEnterSum ; OsEnterSum,使OSIntExit退出时中断关闭
MOV R1, #1
STR R1, [R2]
BL OSIntExit
LDR R2, =OsEnterSum ; 因为中断服务程序要退出,所以OsEnterSum=0
MOV R1, #0
STR R1, [R2]
MSR CPSR_c, #(NoInt | IRQ32Mode) ; 切换回irq模式
LDMFD SP, {R3, SP, LR}^ ; 恢复用户状态的R3,SP,LR,注意不能回写
; 如果回写的是用户的SP,所以后面要调整SP
LDR R0, =OSTCBHighRdy
LDR R0, [R0]
LDR R1, =OSTCBCur
LDR R1, [R1]
CMP R0, R1
ADD SP, SP, #4*3 ;
MSR SPSR_cxsf, R3
LDMEQFD SP!, {R0-R3, R12, PC}^ ; 不进行任务切换
LDR PC, =OSIntCtxSw ; 进行任务切换
MEND
END
2.注意事项:
(1)OS_STK_GROWTH中指定堆栈的生长方式:为1表示堆栈从下往上生长,0表示堆栈从上往下生长。
(2)在OSTaskStkInt初始化堆栈的函数必须保存CPU相关资源,如寄存器。
3.其他的都在移植源代码中有相关注释及说明。
三.完成项目
1.源代码编译通过,完成了所有的移植工作,项目也达到预期的效果,操作系统正常的跑起来了。
2.通过应用程序的测试,下面是运行成功后的第一个应用的主程序:
/****************************************Copyright(c)**************************************
** FreshAir嵌入式软件开发团队
** FreshAir
** http://www.FreshAir.com
**--------------File Info------------------------------------------------------------------
** File name: main.c
** Last modified Date: 2009-07-22
** Created by: 吴友强
**Created date:2009年7月21日0:00
** Version: V1.0
** Descriptions:成功移植uCOSII操作系统后的第一个应用程序:GPIO输出实验
******************************************************************************************/
#include "config.h"
#include "stdlib.h"
#define LED1 (1 << 18)
#define LED2 (1 << 19)
#define TaskStkLengh 64 //定义用户任务0的堆栈长度
#define TaskLED1StkSize 128 //定义用户任务1的堆栈长度
#define TaskLED2StkSize 128 //定义用户任务2的堆栈长度
OS_STK TaskStk [TaskStkLengh]; //定义用户任务0的堆栈
OS_STK TaskLED1Stk[TaskLED1StkSize];//定义用户任务1的堆栈
OS_STK TaskLED2Stk[TaskLED2StkSize];//定义用户任务2的堆栈
void Task0(void *pdata); //Task0 任务0
void TaskLED1(void *data);
void TaskLED2(void *data);
int main (void)
{
OSInit ();
OSTaskCreate (Task0,(void *)0, &TaskStk[TaskStkLengh - 1], 2);
OSStart ();
return 0;
}
/****************************************************************************************** Task0 任务0
******************************************************************************************/
void Task0 (void *pdata)
{
pdata = pdata;
TargetInit ();
OSTaskCreate(TaskLED1, (void *)0, &TaskLED1Stk[TaskLED1StkSize - 1], 3);
OSTaskCreate(TaskLED2, (void *)0, &TaskLED2Stk[TaskLED2StkSize - 1], 4);
while (1)
{
OSTimeDly(10);
}
}
void TaskLED1(void *pdata)
{
pdata = pdata;
PINSEL2 = PINSEL2 & (~0x08);
IO1DIR |= LED1;
IO1SET = LED1;
for (; ;)
{
IO1CLR = LED1;
OSTimeDly(OS_TICKS_PER_SEC / 4);
IO1SET = LED1;
OSTimeDly(OS_TICKS_PER_SEC / 2);
}
}
void TaskLED2(void *pdata)
{
pdata = pdata;
PINSEL2 = PINSEL2 & (~0x08);
IO1DIR |= LED2;
IO1SET = LED2;
for (; ;)
{
IO1CLR = LED2;
OSTimeDly(OS_TICKS_PER_SEC / 2);
IO1SET = LED2;
OSTimeDly(OS_TICKS_PER_SEC / 1);
}
}
四.项目经验总结及心得
1.通过长期的学习相关知识和不断的尝试编码及测试,终于成功的所有的源代码通过编译,在看到没有错误提示的那一刻是多么的兴奋无法形容。
2.虽然这个项目成功并且是出色的完成了,但这只是开始,是以后做项目的开端,可以把这个项目运用到以后项目中去
3.通过自学和不断的摸索让自己的自学能力有很大幅度提高,项目的成功完成也让我对自己未来的学习更有信心,为自己下一步学习linux操作系统和ARM9及ARM10或ARM11打下了良好的基础。
4.通过<<嵌入式实时操作系统μC/OS-Ⅱ(第二版)>>阅读让我基本掌握了μC/OS-Ⅱ这个操作系统,主要学习里面的设计思想,这个操作系统属于RTOS,对于这一类设计思想有了一个很好的了解,更重要的一点在阅读这个系统的源代码时吸收了许多很好的编程风格和编程思路,当然还有更多的编程技巧。
5.移植过程中遇到主要问题解决:
(1)编译器产生#error这个地方错误,通过查看编译器的文档,通过加入-Ep编译选项成功解决。
(2)头文件包含错误,主要是文件组织结构没有弄好,最后还是下定决心从新开始,通过解决一个两个等的编译错误最终成功并且运行
(3)移植的过程中对于很多宏定义出现错误,要不是没有定义,要不是重复定义,最终通过耐心修改源代码得以解决
6.通过解决项目过程中遇到的很多问题,提高了自己解决问题的能力,也让自己相信,只要努力做一件事并且坚持不懈,最终是能够成功的