uCOSII移植LPC2119_Keil

【@.1 移植准备】

目标CPU:LPC2119。ARM7TDMI,最高主频60MHz,Flash128KB,RAM20KB,ARM7系列的中断机制可以参考我的这篇文章<uCOS-II的中断-ARM7实现中断嵌套的方法探究>。

OS:uCOS-II,具体内核版本参考官网。应该已经出到2.90以上了,不过移植时的版本可能没这么高。对于uCOS-II的任务切换机制可以参考我的这篇文章<uCOS-II中的任务切换机制>。

Toolchain:Keil,目前我使用的版本是MDK4.60,移植时也用MDK4.60来建立。

官网移植工程下载:不能仅仅下载内核,需要下载整个移植工程进行移植工作。参考官网移植工程时需要注意的是,移植目标CPU内核与自己的CPU一样或相近,uCOS版本不要太旧,编译器跟自己需要使用的编译器一样(比如Keil)。移植时我参考的是

NXP LPC2148(μC/OS-II μC/OS-II V2.85 μC/Probe V1.30, IAR (EWARM) V4.x IAR (EWARM) V5.x Keil MDK V3.x 2012/12/06)

由于官网不断更新,所以可能以后我参考的移植工程就不在网上了。

希望在移植系统时要仔细了解目标CPU的内核机制和对应OS的一些基本原理,即使自己不编写底层移植代码也不能照着下面的步骤,最后能用了事。这样是得不到锻炼的,如果需要移植到其他的CPU,或者移植其他OS时就又不会了,又上网找别人的工程伸手要。

【@.2 工程模板建立】

解压下载的移植工程,仔细阅读解压目录下的ReadMe.pdf,会有详细的目录结构讲解,这里我就不重复了。

uCOS的通用源代码在Micrium\Software下,此文件夹下的各个子文件夹就是uCOS的各个模块。其中uCOS-II是主要源代码,uC-CPU,uC-LIB是必要的模块,uCOSView和uC-Probe可以不用。若要使用别的模块也可以参考这里,一个模块一个文件夹,比如shell和uCGUI。

image

在这个文件夹下有示范工程例程,用MDK3.xx版本写的,路径这里就不列出来了,因为每次下载的移植工程可能不一样。

先不打开MDK,首先工程文件夹目录如下:


[根目录文件名取工程名字]
+---App
|       app.c        //拷贝自下载的移植工程同名文件,此文件为用户应用程序代码。此文件还需要修改
|       app_cfg.h    //拷贝自下载的移植工程同名文件,自己的任务相关配置(如优先级,堆栈大小)
|       includes.h   //拷贝自移植工程同名文件,源程序都需包含它作为include入口
|       os_cfg.h     //拷贝自移植工程同名文件,uCOS的功能配置文件,只需根据自己需要的功能修改宏定义
|      
+---Bsp
|       bsp.c        //拷贝自移植工程,但是需要修改
|       bsp.h        //拷贝自移植工程,但是需要修改
|       LPC21xx.h    //拷贝自MDK安装目录下\ARM\INC\Philips\ 的头文件
|       Startup.s    //拷贝自MDK安装目录下\ARM\Startup\Philips 的Startup.s。需要修改
|      
+---Libraries        //公司或自己的通用函数库。现在没有可以不放文件
+---Micrium          //uCOS的核心代码,主要拷贝自uCOS的各个模块
|   +---uC-CPU       //拷贝移植工程下整个文件夹。由于LPC2119跟移植CPU差不多所以不需要修改
|   |       cpu.h
|   |       cpu_a.s
|   |       cpu_def.h
|   |      
|   +---uC-Lib      //拷贝移植工程下整个文件夹,uCOS的常用预定义和内存、字符串库,一般不需要修改
|   |       lib_def.h
|   |       lib_mem.c
|   |       lib_mem.h
|   |       lib_str.c
|   |       lib_str.h
|   |      
|   \---uCOS-II     //拷贝移植工程下整个文件夹,
|       +---Port    //移植uCOS时按手册要求需要编写的代码。LPC2119跟移植CPU差不多,所以不需要修改
|       |       os_cpu.h
|       |       os_cpu_a.asm
|       |       os_cpu_c.c
|       |       os_dbg.c
|       |      
|       \---Source  //uCOS的内核代码,千万不要修改
|               os_core.c
|               os_flag.c
|               os_mbox.c
|               os_mem.c
|               os_mutex.c
|               os_q.c
|               os_sem.c
|               os_task.c
|               os_time.c
|               os_tmr.c
|               ucos_ii.h
|              
\---Solution      //与MDK工程相关的文件都放在这里
    |
    |  
    +---LIST      //MDK中的List输出文件夹,待MDK中修改
    +---MDK       //MDK的工程文件就放在这里,如下面文件名
    |      
    |       LPC2119_uCOS-II.uvproj
    |      
    \---OBJ       //MDK的目标输出文件夹,待MDK中修改

这个文件结构建好之后如图所示。不要忘了其中的需要拷贝/建立源代码。

image

之后打开MDK新建工程。打开MDK->新建工程,选择保存路径在刚才新建的文件夹下的\Solution\MDK\ 中,名字自己随便取。CPU选择NXP->LPC2119,当提示是否需要拷贝启动代码到工程文件夹下时选择否,因为我们自己拷贝过了。左侧的Project右击Target 1 -> Manage Components,之后需添加刚才所拷贝/新建的源文件。Project Targets改名随便,比如LPC2119_Target.将中间Group一栏改名为App,点击右侧Add Files可以将源文件添加到这一个Group中。按照之前的文件结构,添加Group的文件如下

image

可以看到MDK中的工程目录跟实际硬盘上的文件结构是基本一致的,这也便于对MDK工程的理解。右键工程,点击Options for ... 编辑工程属性。

Output选项卡点击Select folder for object,选为刚才新建的目录\Solution\OBJ,选中Create HEX file,Name for Excutable修改为自己需要的hex文件名。最好不要包含'-'等字符。

Listing选项卡点击Select folder for listing,选为刚才新建的目录\Solution\LIST

C/C++选项卡中的Including Path可以选择工程中所用到的头文件搜索路径,点击右侧可添加。这里添加如下

..\..\App

..\..\Bsp

..\..\Libraries

..\..\Micrium\uC-Lib

..\..\Micrium\uCOS-II\Port

..\..\Micrium\uCOS-II\Source

..\..\Micrium\uC-CPU

均为刚才所建工程目录下的文件。以后若新添加的头文件在别的目录也需要在这里添加。

Utilities选项卡 ->J-Link Settings->Add 找到LPC2000 IAP 128KB Flash即可用J-Link即可debug和下载

至此MDK工程设置结束,下面修改代码

【@.3 移植代码修改】

Startup.s是主要修改的文件。在MDK中的Startup.s比较简单,我们需要修改ISR中断入口函数使之与uCOS的中断通用处理函数连接起来即可。

找到 AREA RESET, CODE, READONLY 端, 这一段是此汇编的户口,下面的Vectors标签即所有的中断入口偏移表。在Vectors前面,添加如下代码

IMPORT OS_CPU_ARM_ExceptUndefInstrHndlr

IMPORT OS_CPU_ARM_ExceptSwiHndlr

IMPORT OS_CPU_ARM_ExceptPrefetchAbortHndlr

IMPORT OS_CPU_ARM_ExceptDataAbortHndlr

IMPORT OS_CPU_ARM_ExceptIrqHndlr

IMPORT OS_CPU_ARM_ExceptFiqHndlr

这表示以上IMPORT的函数在别的文件中有定义。而在uCOS中这些函数是在uCOS-II/Port中的os_cpu_a.asm中用汇编写好的。本来这些代码是需要移植时参考uCOS手册的要求和移植的芯片手册自己编写,但是我们下载的移植工程已经写好,我们只需要将这些函数与启动代码中的中断入口链接起来即可。

在Vectors中的IRQ中断一列,Keil可能是下面这样写的:

;LDR PC, IRQ_Addr

LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr

其实第一句被注释掉了,所以起作用的是第二句,这句话直接将IRQ的中断跳转到PC, #-0x0FF0,也就是地址位于0xFFFFF030的VICVectAddr寄存器,这是LPC的中断控制器,中断向量的入口地址,按照LPC2000系列的规定这里存放了我们在程序中注册的中断服务程序地址。因此采用第二句话实际上是将中断直接交给LPC的中断控制器进行中断控制。但是我们需要将IRQ中断交给uCOS一并管理所以需要修改,应该采用第一句的LDR PC, IRQ_Addr进行偏移,之后再进行如下程序的链接。

Reset_Addr DCD Reset_Handler

Undef_Addr DCD OS_CPU_ARM_ExceptUndefInstrHndlr

SWI_Addr DCD OS_CPU_ARM_ExceptSwiHndlr

PAbt_Addr DCD OS_CPU_ARM_ExceptPrefetchAbortHndlr

DAbt_Addr DCD OS_CPU_ARM_ExceptDataAbortHndlr

DCD 0 ; Reserved Address

IRQ_Addr DCD OS_CPU_ARM_ExceptIrqHndlr

FIQ_Addr DCD OS_CPU_ARM_ExceptFiqHndlr

删掉源文件中Keil对中断地址的定义,修改为如上的uCOS的中断入口。其中IRQ_Addr将跳转到OS_CPU_ARM_ExceptIrqHndlr,也就是交给uCOS对IRQ中断进行控制。所以不管移植到哪种CPU上,所有的异常和中断的入口都要像上面一样进行重新定位,到自己编写的os_cpu_a.asm中的对应函数。最终的RESET代码段的代码修改如下:

clip_image002

clip_image002[4]

另外,由于uCOS要求在LCP2xxx上应该运行在Supervisor模式,而Keil默认的启动代码在结束startup后是进入的User模式,权限太低不能进行一些特定的汇编指令操作,因此在Startup.s的下面,初始化堆栈中Enter User Mode and set its Stack Pointer下面的代码全部删除或注释掉,需要删除或注释的代码如下

; MSR CPSR_c, #Mode_USR

; IF :DEF:__MICROLIB

; EXPORT __initial_sp

; ELSE

; MOV SP, R0

; SUB SL, SP, #USR_Stack_Size

; ENDIF

这里我们不再进入用户模式对堆栈初始化了,因为进入了用户模式之后要切换到Supervisor模式需要使用软件中断,不能简单的进行切换,比较麻烦,所以干脆舍弃用户模式,我们永远不进入用户模式也不需要对其堆栈初始化,这里只需要保证堆栈初始化时最后一个是对Supervisor模式进行的堆栈初始化即可保证进入main函数之后不在其他模式下进行的。所以像STM32这样没有那么多复杂的模式的CPU移植起来就简单得多。

下面不是具体的代码操作,但是还是说一些注意事项

@->app.c中保留下列函数用于application hook 功能,里面空即可,否则编译时找不到函数定义,

#if (OS_APP_HOOKS_EN > 0)

void App_TaskCreateHook (OS_TCB *ptcb) { ;}

void App_TaskDelHook (OS_TCB *ptcb){ ;}

#if OS_VERSION >= 251

void App_TaskIdleHook (void){ ;}

#endif

void App_TaskStatHook (void){ ;}

#if OS_TASK_SW_HOOK_EN > 0

void App_TaskSwHook (void){ ;}

#endif

#if OS_VERSION >= 204

void App_TCBInitHook (OS_TCB *ptcb){ (void)ptcb; }

#endif

#if OS_TIME_TICK_HOOK_EN > 0

void App_TimeTickHook (void){ ;}

#endif

#endif

所以可以看到其实也可以在os_cfg.h中将OS_APP_HOOKS_EN设置为0关掉这个功能。不过为了了解uCOS-II的所有特性以免井底之蛙,现在暂时留着。

主函数和任务函数现在先保留,可以根据自己的任务需要另作修改

@->app_cfg.h中包含的是任务堆栈大小和优先级,以及一些其他应用程序相关的定义。

@->bsp.c中的Tmr_TickISR_Handler(void)需要保留,这是uCOS ticker的服务代码,这个ticker使用的是LPC中的Timer0中断,因此这个服务函数必须保留。

OS_CPU_ExceptHndlr(CPU_INT32U)这个函数必须保留,这是所有异常发生时都将进入的函数,这里可能只写了IRQ发生时的代码,其他异常发生时都将进入死循环(除了Reset)。也可以自己添加其他异常代码,不过小的应用也不需要。

【@.4 移植工程测试代码】

下面仅仅列出app.c中的用户代码,注意,实际对硬件操作的功能函数都在bsp.c中,所以每个移植工程的实际bsp.c是不一样的。

clip_image002[6]

clip_image002[8]

主函数中需首先对OS初始化,再新建至少一个任务,最后调用OSStart()之后则完成整个OS的启动过程。一般新建的一个任务叫做启动任务(Task Starter),其中需进行OS所需的系统时钟中断的启动(这里包含在了BSP_Init()硬件初始化函数中),之后再新建其他任务。这里的启动任务中不建立新的任务,仅仅进行通过延时对小灯进行亮、灭的控制。将整个工程编译后下载即可观察到运行效果。

最后附工程模板的下载链接

LPC2119_uCOSPrj.7z

另外添加一个实现了中断机制的工程模板,利用两个外部中断控制流水灯的启停。可以参考这个源文件看看uCOS-II中的中断是怎么编写的。

LPC2119_uCOSPrj_LEDSample.7z

@.[FIN]      @.date->Mar 28, 2013      @.author->apollius

原文地址:https://www.cnblogs.com/apollius/p/2986810.html