1.μC/OS-Ⅱ简介
uC/OS-II读做“microCOS2”,意为“微控制器操作系统版本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.开发环境的搭建
操作系统:WindowsXP
集成开发环境: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:
;**--------------FileInfo---------------------------------------------
;**Filename: Startup.s
;**LastmodifiedDate:2009-08-17
;**LastVersion: 1.0
;**Descriptions:为LPC2100系列芯片的启动代码,从这里开始执行,包括初始化代码,为每一种模
;**式的进入口和任务的堆栈
;定义堆栈的大小
SVC_STACK_LEGTHEQU0
FIQ_STACK_LEGTHEQU0
IRQ_STACK_LEGTHEQU256
ABT_STACK_LEGTHEQU0
UND_STACK_LEGTHEQU0
NoIntEQU0x80 ;Bit7,I位
NoFIQ EQU 0x40 ;Bit6,F位
USR32ModeEQU0x10 ;M[4:0]=10000,用户模式
SVC32ModeEQU0x13 ;M[4:0]=10011,管理模式
SYS32ModeEQU0x1f ;M[4:0]=11111,系统模式
IRQ32ModeEQU0x12 ;M[4:0]=10010,IRQ中断
FIQ32ModeEQU0x11 ;M[4:0]=10001,快速中断
IMPORT__use_no_semihosting_swi
;引入的外部标号在这声明
IMPORTFIQ_Exception;快速中断异常处理程序
IMPORT__main;C语言主程序入口
IMPORTTargetResetInit;目标板基本初始化
;给外部使用的标号在这声明
EXPORTbottom_of_heap
EXPORTStackUsr
EXPORTReset
EXPORT__user_initial_stackheap
CODE32
AREAvectors,CODE,READONLY
ENTRY
;中断向量表
Reset
LDRPC,ResetAddr ;0x00,复位
LDRPC,UndefinedAddr ;0x04,未定义地址
LDRPC,SWI_Addr ;0x08,软件中断
LDRPC,PrefetchAddr ;0x0c,预取指中止
LDRPC,DataAbortAddr ;0x10,数据中止
DCD0xb9205f80 ;0x14,保留
LDRPC,[PC,#-0xff0] ;0x18,IRQ中断
LDRPC,FIQ_Addr ;0x1C,快速中断
ResetAddrDCDResetInit ;复位初始化处理程序地址
UndefinedAddrDCDUndefined ;未定义指令处理程序地址
SWI_AddrDCDSoftwareInterrupt ;软件中断处理程序地址
PrefetchAddrDCDPrefetchAbort ;预取指中止处理程序地址
DataAbortAddrDCDDataAbort ;数据中止处理程序地址
NouseDCD0 ;未使用
IRQ_AddrDCD0 ;IRQ中断,已在"LDR[PC,#-0xff0]"中处理
FIQ_AddrDCDFIQ_Handler
;未定义指令
Undefined
BUndefined ;死循环
;软中断
SoftwareInterrupt
;BSoftwareInterrupt
;//增加开/关中断处理
CMPR0,#4 ;判断传过来的参数是否大于4
LDRLOPC,[PC,R0,LSL#2] ;小于4(参数正确),进行查表
MOVSPC,LR ;大于或者等于4(参数出错),则返回
SwiFunction
DCDIRQDisable ;0号调用,禁止IRQ中断
DCDIRQEnable ;1号调用,使能IRQ中断
DCD FIQDisable ;2号调用,禁止FIQ中断
DCD FIQEnable ;3号调用,使能FIQ中断
IRQDisable
;关IRQ中断
MRSR0,SPSR ;读取SPSR的值
ORRR0,R0,#NoInt ;置位I位,设置关IRQ中断
MSRSPSR_c,R0 ;回写SPSR
MOVSPC,LR ;返回
IRQEnable
;开IRQ中断
MRSR0,SPSR
BICR0,R0,#NoInt ;清零I位,设置开IRQ中断
MSRSPSR_c,R0
MOVSPC,LR
FIQDisable
;关FIQ中断
MRSR0,SPSR
ORRR0,R0,#NoFIQ ;置位F位,设置关FIQ中断
MSRSPSR_c,R0
MOVSPC,LR
FIQEnable
;开FIQ中断
MRSR0,SPSR
BICR0,R0,#NoFIQ ;清零F位,设置开FIQ中断
MSRSPSR_c,R0
MOVSPC,LR
;取指令中止
PrefetchAbort
BPrefetchAbort ;死循环
;取数据中止
DataAbort
BDataAbort ;死循环
;快速中断
FIQ_Handler
STMFDSP!,{R0-R3,LR} ;寄存器R0~R3,LR入栈
BLFIQ_Exception ;调用FIQ处理程序
LDMFDSP!,{R0-R3,LR} ;寄存器R0~R3,LR出栈
SUBSPC,LR,#4 ;计算返回地址
;/*****************************************************************************************
;**unctionname 函数名称: InitStack
;**Descriptions 功能描述: 初始化堆栈
;**Createdby 作 者: 吴友强
;**CreatedDate 日 期: 2009/07/202009年7月20日
;*****************************************************************************************/
;初始化堆栈,此时禁止IRQ和FIQ中断,处于ARM状态
InitStack
MOVR0,LR
;BuildtheSVCstack
;设置管理模式堆栈
MSRCPSR_c,#0xd3
LDRSP,StackSvc
;BuildtheIRQstack
;设置中断模式堆栈
MSRCPSR_c,#0xd2
LDRSP,StackIrq
;BuildtheFIQstack
;设置快速中断模式堆栈
MSRCPSR_c,#0xd1
LDRSP,StackFiq
;BuildtheDATAABORTstack
;设置中止模式堆栈
MSRCPSR_c,#0xd7
LDRSP,StackAbt
;BuildtheUDFstack
;设置未定义模式堆栈
MSRCPSR_c,#0xdb
LDRSP,StackUnd
;BuildtheSYSstack
;设置系统模式堆栈
MSRCPSR_c,#0xdf ;切换到系统模式,之后将在系统模式下运行
LDRSP,=StackUsr ;除非进行模式切换
MOVPC,R0
;/*****************************************************************************************
;**unctionname 函数名称: ResetInit
;**Descriptions 功能描述: 复位入口
;**Createdby 作 者: 吴友强
;**CreatedDate 日 期: 2009/07/202009年7月20日
;**----------------------------------------------------------------------------------------
;*****************************************************************************************/
ResetInit
BLInitStack;初始化堆栈
BLTargetResetInit;目标板基本初始化
B__main;跳转到c语言入口
;/*****************************************************************************************
;**unctionname 函数名称: __user_initial_stackheap
;**Descriptions 功能描述: 库函数初始化堆和栈,不能删除
;**Createdby 作 者: 吴友强
;**CreatedDate 日 期: 2009/07/202009年7月20日
;*****************************************************************************************/
__user_initial_stackheap
LDRr0,=bottom_of_heap
;LDRr1,=StackUsr
MOVpc,lr
StackSvcDCDSvcStackSpace+(SVC_STACK_LEGTH-1)*4 ;管理模式堆栈
StackIrqDCDIrqStackSpace+(IRQ_STACK_LEGTH-1)*4 ;IRQ模式堆栈
StackFiqDCDFiqStackSpace+(FIQ_STACK_LEGTH-1)*4 ;FIQ模式堆栈
StackAbtDCDAbtStackSpace+(ABT_STACK_LEGTH-1)*4 ;中止模式堆栈
StackUndDCDUndtStackSpace+(UND_STACK_LEGTH-1)*4;未定义模式堆栈
;/*****************************************************************************************
;**unctionname 函数名称: CrpData
;**Descriptions 功能描述: 芯片加密,代码保护
;**inputparameters 输 入: None无
;**Returnedvalue 输 出: None无
;**Usedglobalvariables全局变量: None无
;**Callingmodules 调用模块: None无
;**
;**Createdby 作 者: 吴友强
;**CreatedDate 日 期: 2009/07/202009年7月20日
;*****************************************************************************************/
IF:DEF:EN_CRP
IF.>=0x1fc
INFO1,"/nThedataat0x000001fcmustbe0x87654321./nPleasedeletesomesourcebeforethisline."
ENDIF
CrpData
WHILE.<0x1fc ;循环用NOP填充,直到0x1FC
NOP
WEND
CrpData1
DCD0x87654321;/*当此数为0x87654321时,用户程序被保护*/
ENDIF
;/*分配堆栈空间*/
AREAMyStacks,DATA,NOINIT,ALIGN=2
SvcStackSpaceSPACESVC_STACK_LEGTH*4;管理模式堆栈空间
IrqStackSpaceSPACEIRQ_STACK_LEGTH*4;中断模式堆栈空间
FiqStackSpaceSPACEFIQ_STACK_LEGTH*4;快速中断模式堆栈空间
AbtStackSpaceSPACEABT_STACK_LEGTH*4;中止义模式堆栈空间
UndtStackSpaceSPACEUND_STACK_LEGTH*4;未定义模式堆栈
AREAHeap,DATA,NOINIT ;Heap通过分散加载文件定位
bottom_of_heapSPACE1
AREAStacks,DATA,NOINIT ;Stack通过分散加载文件定位
StackUsr
END
2.IRQ.S
NoIntEQU0x80
USR32ModeEQU0x10
SVC32ModeEQU0x13
SYS32ModeEQU0x1f
IRQ32ModeEQU0x12
FIQ32ModeEQU0x11
CODE32
AREAIRQ,CODE,READONLY
MACRO
$IRQ_LabelHANDLER$IRQ_Exception_Function
EXPORT$IRQ_Label;输出的标号
IMPORT$IRQ_Exception_Function;引用的外部标号
$IRQ_Label
SUBLR,LR,#4;计算返回地址
STMFDSP!,{R0-R3,R12,LR};保存任务环境
MRSR3,SPSR;保存状态
STMFDSP,{R3,LR}^;保存SPSR和用户状态的SP,注意不能回写
;如果回写的是用户的SP,所以后面要调整SP
NOP
SUBSP,SP,#4*2
MSRCPSR_c,#(NoInt|SYS32Mode);切换到系统模式
BL$IRQ_Exception_Function;调用c语言的中断处理程序
MSRCPSR_c,#(NoInt|IRQ32Mode);SwitchbaktoIRQmode切换回irq模式
LDMFDSP,{R3,LR}^;恢复SPSR和用户状态的SP,注意不能回写
;如果回写的是用户的SP,所以后面要调整SP
MSRSPSR_cxsf,R3
ADDSP,SP,#4*2
LDMFDSP!,{R0-R3,R12,PC}^
MEND
;/*以下可添加中断句柄,根据实际情况改变*/
;Timer0_HandlerHANDLERTimer0
END
3.Target.C
#defineIN_TARGET
#include"config.h"
void__irqIRQ_Exception(void)
{
while(1);//这一句替换为自己的代码
}
voidFIQ_Exception(void)
{
while(1);//这一句替换为自己的代码
}
voidTargetInit(void)
{
/*添加自己的代码*/
}
voidTargetResetInit(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功能
#ifFcclk<20000000 //根据Fcclk的大小来设置MAM定时寄存器
MAMTIM=1;
#else
#ifFcclk<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>
#pragmaimport(__use_no_semihosting_swi)
int__rt_div0(inta)
{
a=a;
return0;
}
intfputc(intch,FILE*f)
{
ch=ch;
f=f;
return0;
}
intfgetc(FILE*f)
{
f=f;
return0;
}
int_sys_close(FILEHANDLEfh)
{
fh=fh;
return0;
}
int_sys_write(FILEHANDLEfh,constunsignedchar*buf,unsignedlen,intmode)
{
fh=fh;
buf=buf;
len=len;
mode=mode;
return0;
}
int_sys_read(FILEHANDLEfh,unsignedchar*buf,
unsignedlen,intmode)
{
fh=fh;
buf=buf;
len=len;
mode=mode;
return0;
}
void_ttywrch(intch)
{
ch=ch;
}
int_sys_istty(FILEHANDLEfh)
{
fh=fh;
return0;
}
int_sys_seek(FILEHANDLEfh,longpos)
{
fh=fh;
return0;
}
int_sys_ensure(FILEHANDLEfh)
{
fh=fh;
return0;
}
long_sys_flen(FILEHANDLEfh)
{
fh=fh;
return0;
}
int_sys_tmpnam(char*name,intsig,unsignedmaxlen)
{
name=name;
sig=sig;
maxlen=maxlen;
return0;
}
void_sys_exit(intreturncode)
{
returncode=returncode;
}
char*_sys_command_string(char*cmd,intlen)
{
cmd=cmd;
len=len;
return0;
}
4.config.h
#ifndef__CONFIG_H
#define__CONFIG_H
#ifndefTRUE
#defineTRUE1
#endif
#ifndefFALSE
#defineFALSE0
#endif
typedefunsignedcharuint8;/*无符号8位整型变量*/
typedefsignedcharint8;/*有符号8位整型变量*/
typedefunsignedshortuint16;/*无符号16位整型变量*/
typedefsignedshortint16;/*有符号16位整型变量*/
typedefunsignedintuint32;/*无符号32位整型变量*/
typedefsignedintint32;/*有符号32位整型变量*/
typedeffloatfp32;/*单精度浮点数(32位长度)*/
typedefdoublefp64;/*双精度浮点数(64位长度)*/
#include"LPC2294.h"
/*系统设置,Fosc、Fcclk、Fcco、Fpclk必须定义*/
#defineFosc11059200//应当与实际一至晶振频率,10MHz~25MHz,应当与实际一至
#defineFcclk(Fosc*4)//系统频率,必须为Fosc的整数倍(1~32),且<=60MHZ
#defineFcco(Fcclk*4)//CCO频率,必须为Fcclk的2、4、8、16倍,范围为156MHz~320MHz
#defineFpclk(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
#ifndefIN_TARGET
externvoidReset(void); //复位
externvoidTargetInit(void); //目标板初始化
#endif
#ifdef__cplusplus
}
#endif
#endif
__swi(0x00)voidSwiHandle1(intHandle); //软件中断
#defineIRQDisable()SwiHandle1(0) //禁止IRQ中断
#defineIRQEnable()SwiHandle1(1) //使能IRQ中断
#defineFIQDisable()SwiHandle1(2) //禁止FIQ中断
#defineFIQEnable()SwiHandle1(3) //使能FIQ中断
6.LPC2294.H
/*向量中断控制器(VIC)的特殊寄存器*/
#defineVICIRQStatus(*((volatileunsignedlong*)0xFFFFF000))
#defineVICFIQStatus(*((volatileunsignedlong*)0xFFFFF004))
#defineVICRawIntr(*((volatileunsignedlong*)0xFFFFF008))
#defineVICIntSelect(*((volatileunsignedlong*)0xFFFFF00C))
#defineVICIntEnable(*((volatileunsignedlong*)0xFFFFF010))
#defineVICIntEnClr(*((volatileunsignedlong*)0xFFFFF014))
#defineVICSoftInt(*((volatileunsignedlong*)0xFFFFF018))
#defineVICSoftIntClear(*((volatileunsignedlong*)0xFFFFF01C))
#defineVICProtection(*((volatileunsignedlong*)0xFFFFF020))
。。。。。。省略
7.men_a.Scf适用于片内Flash发布
ROM_LOAD0x00000000 ;加载区,从0x00000000开始
{
ROM_EXEC0x00000000 ;执行区,起始地址,空间大小要和加载区一致
{
Startup.o(vectors,+First) ;startup.S中的向量表
*(+RO) ;其他代码
}
IRAM0x40000000 ;变量区,内部RAM开始
{
Startup.o(MyStacks) ;startup.S中的MyStacks
*(+RW,+ZI) ;文件中的其他变量
}
HEAP+0UNINIT ;系统堆空间
{
Startup.o(Heap) ;startup.S中的Heap
}
STACKS0x40002000UNINIT ;LPC2131片内RAM最高端
{ ;系统的堆空间
Startup.o(Stacks) ;startup.S中的Stacks
}
}
8.men_c.Scf适用于片内Flash调试
ROM_LOAD0x0
{
ROM_EXEC0x00000000
{
Startup.o(vectors,+First)
*(+RO)
}
IRAM0x40000000
{
Startup.o(MyStacks)
*(+RW,+ZI)
}
HEAP+0UNINIT
{
Startup.o(Heap)
}
STACKS0x40002000UNINIT
{
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公司。ARMArchitectureReferenceManual。
(2)对OS原理要有深入的了解;
参考资料:LabrosseJJean。嵌入式实时操作系统uC/OS-II(第二版)。邵贝贝等译。
(3)对所使用的编译器要有较深的了解;
参考资料:ADS自带的编译器和连接器的手册
(4)对需要移植的操作系统要有相当的了解;
参考资料:LabrosseJJean。嵌入式实时操作系统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 unsignedchar BOOLEAN;
typedef unsignedchar INT8U;
typedef signedchar INT8S;
typedef unsignedshort INT16U;
typedef signedshort INT16S;
typedef unsignedint INT32U;
typedef signedint INT32S;
typedef float FP32;
typedef double FP64;
typedef unsignedint OS_STK;
/******************************************************************************************
*与ARM7体系结构相关的一些定义
******************************************************************************************/
#define OS_CRITICAL_METHOD 2 /*选择开、关中断的方式*/
__swi(0x00)voidOS_TASK_SW(void); /*任务级任务切换函数*/
__swi(0x01)void_OSStartHighRdy(void); /*运行优先级最高的任务*/
__swi(0x02)voidOS_ENTER_CRITICAL(void); /*关中断*/
__swi(0x03)voidOS_EXIT_CRITICAL(void); /*开中断*/
__swi(0x80)voidChangeToSYSMode(void); /*任务切换到系统模式*/
__swi(0x81)voidChangeToUSRMode(void); /*任务切换到用户模式*/
__swi(0x82)voidTaskIsARM(INT8Uprio); /*任务代码是ARM代码*/
__swi(0x83)voidTaskIsTHUMB(INT8Uprio); /*任务代码是Thumb代码*/
__swi(0x40)void*GetOSFunctionAddr(intIndex); /*获取系统服务函数入口*/
__swi(0x41)void*GetUsrFunctionAddr(intIndex);/*获取自定义服务函数入口*/
__swi(0x42)voidOSISRBegin(void); /*中断开始处理*/
__swi(0x43)voidOSISRNeedSwap(void); /*判断中断是否需要切换*/
#defineOS_STK_GROWTH 1 /*堆栈是从上往下长的*/
#define USR32Mode 0x10
#define SYS32Mode 0x1f
#defineNoInt 0x80
#ifndef USER_USING_MODE
#define USER_USING_MODE USER32Mode /*任务缺省模式*/
#endif
#ifndef OS_SELF_EN
#defineOS_SELF_EN 0 /*允许返回OS与任务分别编译、固化*/
#endif
OS_CPU_EXTINT32U 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日
**描 述:增加函数
**-----------------------------------------------------------------------------------------
******************************************************************************************/
#defineOS_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,INT16Uopt)
{
OS_STK*stk;
opt=opt;/*'opt'isnotused,preventwarning*/
stk=ptos; /*Loadstackpointer*/
/*buildacontextforthenewtask*/
*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=(unsignedint)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 intconst_OSFunctionAddr[];
extern intconst_UsrFunctionAddr[];
#endif
voidSWI_Exception(intSWI_Num,int*Regs)
{
OS_TCB*ptcb;
switch(SWI_Num)
{
case0x02: /*关中断函数OS_ENTER_CRITICAL()*/
__asm
{
MRS R0,SPSR
ORRR0,R0,#NoInt
MSR SPSR_c,R0
}
OsEnterSum++;
break;
case0x03: /*开中断函数OS_EXIT_CRITICAL()*/
if(--OsEnterSum==0)
{
__asm
{
MRSR0,SPSR
BICR0,R0,#NoInt
MSRSPSR_c,R0
}
}
break;
#if OS_SELF_EN>0
case0x40: /*返回指定系统服务函数的地址*/
/*函数地址存于数组_OSFunctionAddr中*/
/*数组_OSFunctionAddr需要另外定义*/
/*Regs[0]为第一个参数,也是返回值*/
/*Regs[1]为第二个参数*/
/*Regs[2]为第三个参数*/
/*Regs[3]为第四个参数*/
/*仅有一个参数为系统服务函数的索引*/
Regs[0]=_OSFunctionAddr[Regs[0]];
break;
case0x41:
/*返回指定用户的服务函数的地址*/
/*函数地址存于数组_UsrFunctionAddr中*/
/*数组_UsrFunctionAddr需要另外定义*/
/*Regs[0]为第一个参数,也是返回值*/
/*Regs[1]为第二个参数*/
/*Regs[2]为第三个参数*/
/*Regs[3]为第四个参数*/
/*仅有一个参数为用户服务函数的索引*/
Regs[0]=_UsrFunctionAddr[Regs[0]];
break;
case0x42:/*中断开始处理*/
OSIntNesting++;
break;
case0x43:/*判断中断是否需要切换*/
if(OSTCBHighRdy==OSTCBCur)
{
Regs[0]=0;
}
else
{
Regs[0]=1;
}
break;
#endif
case0x80: /*任务切换到系统模式*/
__asm
{
MRSR0,SPSR
BICR0,R0,#0x1f
ORRR0,R0,#SYS32Mode
MSRSPSR_c,R0
}
break;
case0x81: /*任务切换到用户模式*/
__asm
{
MRSR0,SPSR
BICR0,R0,#0x1f
ORRR0,R0,#USR32Mode
MSRSPSR_c,R0
}
break;
case0x82: /*任务是ARM代码*/
if(Regs[0]<=OS_LOWEST_PRIO)
{
ptcb=OSTCBPrioTbl[Regs[0]];
if(ptcb!=NULL)
{
ptcb->OSTCBStkPtr[1]&=~(1<<5);
}
}
break;
case0x83: /*任务是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运行第一个任务,
**实质是产生swi1指令
**作 者:吴友强
**日 期:2009年7月28日
**-----------------------------------------------------------------------------------------
******************************************************************************************/
voidOSStartHighRdy(void)
{
_OSStartHighRdy();
}
/*以下为一些钩子函数,全部为空函数。*/
#ifOS_CPU_HOOKS_EN
#ifOS_VERSION>203
voidOSInitHookBegin(void)
{
}
#endif
#ifOS_VERSION>203
voidOSInitHookEnd(void)
{
}
#endif
voidOSTaskCreateHook(OS_TCB*ptcb)
{
ptcb=ptcb;/*Preventcompilerwarning*/
}
voidOSTaskDelHook(OS_TCB*ptcb)
{
ptcb=ptcb;/*Preventcompilerwarning*/
}
voidOSTaskSwHook(void)
{
}
voidOSTaskStatHook(void)
{
}
#ifOS_VERSION>203
voidOSTCBInitHook(OS_TCB*ptcb)
{
ptcb=ptcb;/*PreventCompilerwarning*/
}
#endif
#ifOS_VERSION>=251
voidOSTaskIdleHook(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
IMPORTOSTCBCur;指向当前任务TCB的指针
IMPORTOSTCBHighRdy;指向将要运行的任务TCB的指针
IMPORTOSPrioCur;当前任务的优先级
IMPORTOSPrioHighRdy;将要运行的任务的优先级
IMPORTOSTaskSwHook;任务切换的钩子函数
IMPORTOSRunning;uC/OS-II运行标志
IMPORTOsEnterSum;关中断计数器(关中断信号量)
IMPORTSWI_Exception;软中断异常处理程序
EXPORT__OSStartHighRdy
EXPORTOSIntCtxSw;中断退出时的入口,参见startup.s中的IRQ_Handler
EXPORTSoftwareInterrupt;软中断入口
;/*****************************************************************************************
;**函数名称:SoftwareInterrupt
;**功能描述:软件中断,用于提供一些系统服务
;**输 入:依功能而定
;**输 出:依功能而定
;**调用模块:SWI_Exception
;**作 者:吴友强
;**日 期:2009年7月29日
;**----------------------------------------------------------------------------------------
;*****************************************************************************************/
;软件中断
SoftwareInterrupt
LDRSP,StackSvc;重新设置堆栈指针
STMFDSP!,{R0-R3,R12,LR}
MOVR1,SP;R1指向参数存储位置
MRSR3,SPSR
TSTR3,#T_bit;中断前是否是Thumb状态
LDRNEHR0,[LR,#-2];是:取得Thumb状态SWI号
BICNER0,R0,#0xff00
LDREQR0,[LR,#-4];否:取得arm状态SWI号
BICEQR0,R0,#0xFF000000
;r0=SWI号,R1指向参数存储位置
CMPR0,#1
LDRLOPC,=OSIntCtxSw
LDREQPC,=__OSStartHighRdy;SWI0x01为第一次任务切换
BLSWI_Exception
LDMFDSP!,{R0-R3,R12,PC}^
StackSvcDCD(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
;下面为保存任务环境
LDRR2,[SP,#20];获取PC
LDRR12,[SP,#16];获取R12
MRSR0,CPSR
MSRCPSR_c,#(NoInt|SYS32Mode)
MOVR1,LR
STMFDSP!,{R1-R2};保存LR,PC
STMFDSP!,{R4-R12};保存R4-R12
MSRCPSR_c,R0
LDMFDSP!,{R4-R7};获取R0-R3
ADDSP,SP,#8;出栈R12,PC
MSRCPSR_c,#(NoInt|SYS32Mode)
STMFDSP!,{R4-R7};保存R0-R3
LDRR1,=OsEnterSum;获取OsEnterSum
LDRR2,[R1]
STMFDSP!,{R2,R3};保存CPSR,OsEnterSum
;保存当前任务堆栈指针到当前任务的TCB
LDRR1,=OSTCBCur
LDRR1,[R1]
STRSP,[R1]
BLOSTaskSwHook;调用钩子函数
;OSPrioCur<=OSPrioHighRdy
LDRR4,=OSPrioCur
LDRR5,=OSPrioHighRdy
LDRBR6,[R5]
STRBR6,[R4]
;OSTCBCur<=OSTCBHighRdy
LDRR6,=OSTCBHighRdy
LDRR6,[R6]
LDRR4,=OSTCBCur
STRR6,[R4]
OSIntCtxSw_1
;获取新任务堆栈指针
LDRR4,[R6]
ADDSP,R4,#68;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
LDRLR,[SP,#-8]
MSRCPSR_c,#(NoInt|SVC32Mode);进入管理模式
MOVSP,R4;设置堆栈指针
LDMFDSP!,{R4,R5};CPSR,OsEnterSum
;恢复新任务的OsEnterSum
LDRR3,=OsEnterSum
STRR4,[R3]
MSRSPSR_cxsf,R5;恢复CPSR
LDMFDSP!,{R0-R12,LR,PC}^;运行新任务
;/*****************************************************************************************
;**函数名称:__OSStartHighRdy
;**功能描述:uC/OS-II启动时使用OSStartHighRdy运行第一个任务,
;**OSStartHighRdy会调用__OSStartHighRdy
;**输 入:无
;**输 出:无
;**全局变量:OSRunning,OSTCBCur,OSTCBHighRdy,OsEnterSum
;**调用模块:OSTaskSwHook
;**作 者:吴友强
;**日 期:2009年7月29日
;**----------------------------------------------------------------------------------------
;*****************************************************************************************/
__OSStartHighRdy
MSRCPSR_c,#(NoInt|SYS32Mode)
;告诉uC/OS-II自身已经运行
LDRR4,=OSRunning
MOVR5,#1
STRBR5,[R4]
BLOSTaskSwHook;调用钩子函数
LDRR6,=OSTCBHighRdy
LDRR6,[R6]
BOSIntCtxSw_1
AREASWIStacks,DATA,NOINIT,ALIGN=2
SvcStackSpaceSPACESVC_STACK_LEGTH*4;管理模式堆栈空间
END
(4)IRQ.INC定义了一个宏汇编,并且是uC/OS-IIforARM7通用的中断服务程序的汇编与C语言接口代码,需要自己编写。源代码如下:
;/****************************************Copyright(c)************************************
;**FreshAir嵌入式软件开发团队
;**软件开发团队
;**技术部
;**http://www.freshiair.com
;**--------------文件信息------------------------------------------------------------------
;**文件名:irq.inc
;**创建人:吴友强
;**最后修改日期:2009年7月28日
;**描述:定义IRQ汇编接口代码宏
;**--------------历史版本信息--------------------------------------------------------------
;**创建人:吴友强
;**版本:v1.0
;**日 期:2009年7月29日
;*****************************************************************************************/
NoIntEQU0x80
USR32ModeEQU0x10
SVC32ModeEQU0x13
SYS32ModeEQU0x1f
IRQ32ModeEQU0x12
FIQ32ModeEQU0x11
;引入的外部标号在这声明
IMPORTOSIntCtxSw;任务切换函数
IMPORTOSIntExit;中断退出函数
IMPORTOSTCBCur
IMPORTOSTCBHighRdy
IMPORTOSIntNesting;中断嵌套计数器
IMPORTStackUsr
IMPORTOsEnterSum
CODE32
AREAIRQ,CODE,READONLY
MACRO
$IRQ_LabelHANDLER$IRQ_Exception_Function
EXPORT$IRQ_Label;输出的标号
IMPORT$IRQ_Exception_Function;引用的外部标号
$IRQ_Label
SUBLR,LR,#4;计算返回地址
STMFDSP!,{R0-R3,R12,LR};保存任务环境
MRSR3,SPSR;保存状态
STMFDSP,{R3,SP,LR}^;保存用户状态的R3,SP,LR,注意不能回写
;如果回写的是用户的SP,所以后面要调整SP
LDRR2,=OSIntNesting;OSIntNesting++
LDRBR1,[R2]
ADDR1,R1,#1
STRBR1,[R2]
SUBSP,SP,#4*3
MSRCPSR_c,#(NoInt|SYS32Mode);切换到系统模式
CMPR1,#1
LDREQSP,=StackUsr
BL$IRQ_Exception_Function;调用c语言的中断处理程序
MSRCPSR_c,#(NoInt|SYS32Mode);切换到系统模式
LDRR2,=OsEnterSum;OsEnterSum,使OSIntExit退出时中断关闭
MOVR1,#1
STRR1,[R2]
BLOSIntExit
LDRR2,=OsEnterSum;因为中断服务程序要退出,所以OsEnterSum=0
MOVR1,#0
STRR1,[R2]
MSRCPSR_c,#(NoInt|IRQ32Mode);切换回irq模式
LDMFDSP,{R3,SP,LR}^;恢复用户状态的R3,SP,LR,注意不能回写
;如果回写的是用户的SP,所以后面要调整SP
LDRR0,=OSTCBHighRdy
LDRR0,[R0]
LDRR1,=OSTCBCur
LDRR1,[R1]
CMPR0,R1
ADDSP,SP,#4*3;
MSRSPSR_cxsf,R3
LDMEQFDSP!,{R0-R3,R12,PC}^;不进行任务切换
LDRPC,=OSIntCtxSw;进行任务切换
MEND
END
2.注意事项:
(1)OS_STK_GROWTH中指定堆栈的生长方式:为1表示堆栈从下往上生长,0表示堆栈从上往下生长。
(2)在OSTaskStkInt初始化堆栈的函数必须保存CPU相关资源,如寄存器。
3.其他的都在移植源代码中有相关注释及说明。
三.完成项目
1.源代码编译通过,完成了所有的移植工作,项目也达到预期的效果,操作系统正常的跑起来了。
2.通过应用程序的测试,下面是运行成功后的第一个应用的主程序:
/****************************************Copyright(c)**************************************
** FreshAir嵌入式软件开发团队
**FreshAir
**http://www.FreshAir.com
**--------------FileInfo------------------------------------------------------------------
**Filename: main.c
**LastmodifiedDate:2009-07-22
**Createdby: 吴友强
**Createddate:2009年7月21日0:00
**Version: V1.0
**Descriptions:成功移植uCOSII操作系统后的第一个应用程序:GPIO输出实验
******************************************************************************************/
#include"config.h"
#include"stdlib.h"
#defineLED1 (1<<18)
#defineLED2 (1<<19)
#define TaskStkLengh 64 //定义用户任务0的堆栈长度
#defineTaskLED1StkSize 128 //定义用户任务1的堆栈长度
#defineTaskLED2StkSize128 //定义用户任务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);
intmain(void)
{
OSInit();
OSTaskCreate(Task0,(void*)0,&TaskStk[TaskStkLengh-1],2);
OSStart();
return0;
}
/****************************************************************************************** Task0任务0
******************************************************************************************/
voidTask0 (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);
}
}
voidTaskLED1(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);
}
}
voidTaskLED2(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.通过解决项目过程中遇到的很多问题,提高了自己解决问题的能力,也让自己相信,只要努力做一件事并且坚持不懈,最终是能够成功的