集成开发环境CodeWarrior的使用方法

时间:2022-10-23 01:10:08

集成开发环境CodeWarrior的使用方法

摘要:介绍CodeWarrior集成开发环境,以及在飞思卡尔Mc9s12X系列单片机上的使用方法。CodeWarrior将调试技术与开发环境的简易性结合在一起,提供高度可视且自动化的框架。在嵌入式应用领域,CodeWarrior是使用最广泛的软件开发工具。使用CodeWarrior可以有效地提高软件开发效率,缩短开发周期。

关键词:集成开发环境(IDE);编译器;调试器

中图分类号:TP311 文献标识码:A 文章编号:1007-9599 (2012) 08-0000-04

一、前言

CodeWarrior系列集成开发环境(IDE)是Metrowerks公司为嵌入式微处理器设计的软件开发工具。该开发工具在商用嵌入式软件开发工具的使用率方面排名第一。

CodeWarrior包括构建平台和应用所必需的所有主要工具:IDE、编译器、调试器、编辑器、链接器、汇编程序等。CodeWarrior将尖端的调试技术与健全开发环境的简易性结合在一起,将C/C++源级别调试和嵌入式应用开发带入新的水平。开发工作提供高度可视且自动化的框架,可以加速甚至是最复杂应用的开发,因此对于各种水平的开发人员来说,创建应用都是简单而便捷的。

CodeWarrior能够自动地检查代码中的明显错误,然后编译并链接程序以便计算机能够理解并执行你的程序。使用CodeWarrior进行编程,你必须已经能够比较熟练地使用汇编语言和C/C++语言。

二、CodeWarrior使用方法

Freescale网站提供CodeWarrior软件免费下载,一般PC机基本上均可满足安装要求。安装过程也很简单,根据提示操作即可。使用免费版本的CodeWarrior时,如果你的软件代码量很大,编译可能会受限制,你需要去网站申请license来延长你的使用期限。

CodeWarrior是一个复杂的应用程序,你必须花点时间来了解它的各种各样的组件和功能。启动CodeWarrior后,在屏幕上方的菜单下面有一个工具条。这个工具条包含了一些常用菜单项的快捷方式。

(一)创建工程

为了使用CodeWarrior来创建一个应用程序,你必须创建许多文件来构成一个工程(Project)。该工程的设置和所有这些文件都被存放在一个工程文件中。这些设置包括编译和链接设置、源文件、库文件以及它们之间用于产生最终程序的相互关系。

创建工程过程很简单,你只需按照提示操作即可。其中有几点需要注意:

(1)选择S12X系列CPU,则会提示是否需要使用Xgate协处理器,并且选择它的代码存放区域,因为Xgate代码存放在RAM中比在FLASH中运行速度更快,但是会占用RAM空间。Xgate适合干一些短小精悍的工作,例如定时中断、串口通信等,但是一定要用C语言写,用汇编写实在太不方便。

(2)选择编辑语言类型。有时因具体项目要求,除了C编程外还需要编写独立的汇编语言模块,那就再加选汇编。也可以只选择C,需要时嵌入汇编即可。

(3)选择处理器专家(Processor Expert),它是CodeWarrior在自带的可实现芯片内部各种资源模块配置并自动生成相关代码的一个软件工具。不过只有专业版的CodeWarrior才支持该功能。通过处理器专家,用户可以快速实现芯片初始化代码的自动生成工作,而且还提供了大量的软件库可供用户开发时嵌入或调用。

(4)选择启动代码。编译器会自动生成一些启动代码。单片机复位后的指令运行将首先执行这些启动代码,然后再进入到你自己的程序模块main函数。这些启动代码主要完成堆栈指针初始化、全局和静态变量自动清零或赋初值、调用main函数等。

(5)选择浮点运算。当你的程序设计决定用浮点运算时就应该选择加入浮点运算库。毋庸置疑其运算精度将增加,但代码量也将增加,运算时间也会更长。用户可以按实际计算需求酌情选取。

(6)选择内存模式。当程序量较小时,小于64K,可以选择small类型,这时生成的代码和普通的代码一样,都是16位地址的。一般情况下,我们都是选择banked(分页)类型。

(7)选择调试方式。一般选择前两项就可以了。软仿真模式(Full Chip Simulation)。是芯片全功能模拟仿真,即无需任何目标系统的硬件资源,直接在你的PC机上模拟运行单片机的程序。硬件调试模式(P&E Multilink/Cyclone Pro)实现实时在线硬件调试。选这种模式需要另外购买BDM调试头,进行嵌入式应用开发,这个工具是必需的。

最后,点击“完成”,即完成了一个工程的创建。剩下的一些项目设定将自动用缺省配置。如果你要自己需要调整,可以再进行相关菜单操作。

(二)工程文件

打开工程文件,可以看到里面包含有很多文件。一般情况下,你只需要在Sources里添加或直接编写程序代码,其它文件都是系统自动生成,不需要修改。

(1)Sources下包含所有你的源程序文件,可以是C,也可以是asm,或C++。你可以在此栏下点击鼠标右键在弹出菜单中选择Add Files添加其他源程序文件。文件后面的“Code”表示代码长度,“Data”表示变量长度。

(2)Startup Code下包含系统启动文件start12.c。是刚才建项目时自动生成的启动文件,你可以打开观察具体的程序代码,也可以在必要时自己添加或修改这些启动代码。

(3)Libraries所包含的是本项目开发用到的代码库,可以是目标代码型式或C源程序型式。

(4)prm所包含的是用于编程器下载的代码文件格式配置(bbl文件)、机器代码连接定位用的内存说明和配置文件(prm文件,见后面介绍)。

(5)map所包含的是目标代码在内存中的映射文件。这个文件可以方便用户在编译后,查看程序代码和变量在内存中的分配情况。

(三)编写代码

工程文件建立好以后,就可以编写程序代码了,在实际编写代码过程中有几个小技巧。

(1)在单片机程序设计中对于变量类型的选择能用短的变量就不用长的,能用无符号数就不用有符号数,这在很大程度上决定你代码的长度和效率。CodeWarrior的头文件已经将最常用的一些无符号变量类型做了类型名简化替换,这样在程序编写是可以节约点敲键盘的时间。

(2)单片机程序设计中经常会用到的位变量作为一些标志。CodeWarrior中没有特别的位变量定义关键词,位变量是用结构体和联合体的型式来定义的,这一点可以参考头文件中有关寄存器定义的范例。

(3)在工程编译后所定义的函数名和变量名,以及系统自带的关键字,按照默认的选项,都会变成浅蓝色。这时如果在这些名称上点击鼠标右键,选择“Go to…”可以直接跳到定义的地方,非常方便。

(4)点击在代码编辑器工具栏里的前两个按钮,可以实现文件跳转,快速切换到当前编辑的文件,而不需要到工程窗口去找。特别是当文件复杂,函数多的情况下,函数的跳转功能更为实用。

(5)在嵌入式应用中经常需要访问CPU寄存器等物理地址,这时可以在C代码中嵌入汇编指令,有以下几种方法:

?asm 汇编指令;

?asm (汇编指令);

?asm {

汇编指令;

汇编指令;

}

(四)编译和链接

选择“Compile”按钮和“Make”按钮进行编译和链接。编译后会看到错误和警告信息。双击这些信息,一般都会跳到错误或警告出处。

如果编译和链接成功,最后将生成用于源程序符号调试的abs文件,用于芯片烧写的s19文件,所有变量和函数模块在内存中的映射map文件。

(五)调试及仿真

在CodeWarrior主窗口中,选择“Debug”按钮就进入调试窗口。可以先使用软仿真模式测试应用程序,检查一下程序功能是否能正确实现,然后连接硬件,在线下载和调试程序。。

软仿真模式(Full Chip Simulation),是芯片全功能模拟仿真,即无需任何目标系统的硬件资源,直接模拟运行单片机的程序,在模拟运行过程中可以观察调试程序的各项控制和运行流程,分析代码运行的时间,观察各种变量。

硬件调试模式(P&E Multilink/Cyclone Pro),是基于P&E公司的硬件调试工具实现实时在线硬件调试。实际就是我们经常说的BDM调试。BDM调试是基于芯片本身内含的在线调试功能,可实现程序下载,单步/全速运行,可以设若干个断点,可以观察和修改任意寄存器或RAM内存空间。BDM调试几乎是开发Freescale单片机的标准调试模式,运用最为广泛。

调试窗口中有许多小的子窗口,包括源程序窗口、函数窗口、数据窗口、寄存器窗口、内存窗口等。调试窗口左上角的那些按钮,它们是用于帮助你进行调试工作的,包括运行、单步进入、单步跳过、单步跳出、汇编单步、停止和复位等,是调试软件最常用的几个按钮。

三、使用CodeWarrior要点

要完全掌握CodeWarrior的使用方法,是一件很困难的事。如果没有特殊要求,我们一般使用它的默认设置就够用了,如果想更多地了解它,那就得多用多试才行。下面介绍在实际使用中的一些注意事项。

(一)IDE设置

CodeWarrior IDE提供了许多设置以便让你定制你的工作环境。当你选择了编辑菜单中的Preferences项时,你将会看到一个设置对话框。在该对话框中,有控制CodeWarrior编辑器、调试器和其它许多功能的界面和行为的选项。

你可以在自己的CodeWarrior中试试上述这些设置项。你可以先点击问号标志,然后点击你感兴趣的项目,就可以得到一个有关该项目的用途的简短介绍,也可以从帮助菜单中得到更详细的信息。

(二)编译器优化

选择编辑菜单中的“P&E Multilink CyclonePro Settings”,可以进行有关的编译器设置,优化编译器。CodeWarrior编译器提供了几种从C源代码产生实际汇编代码的优化方法,我们可以选择不同的优化项,在生成代码时将会有差异。

完成编译过程中代码生成的各类选项设定,所含内容也很多,最需要关注的是优化栏Optimization。你可以按实际需要打开或关闭某些特定的优化选项,但我们一般通过Smart Sliders做综合的优化设定。用此Smart Sliders对话框则可以针对不同的优化侧重面,由系统自动配置。

你只需用鼠标拉动各项的滑块对其进行优化级别的设定:

Code Density针对生成代码的长度进行优化,设定越高,生成的代码长度越短,代码越高效紧凑;Execution Speed针对代码运行速度进行优化,设定越高,代码执行速度越快;Debug Complexity针对调试复杂度进行优化,设定越高,生成的的调试信息越丰富,调试越方便;Compilation Time针对编译时间进行优化,设定越高,表明编译过程所需时间越长(对应其他各项所做的优化程度越高);Information Level针对编译信息进行优化,设定越高,表明编译过程中产生的各类信息越丰富。

所有这些编译优化项目都是相互关联的。你移动任意一项滑块的位置,其他各项也会随之自动发生变化。你自己必须有针对性地改变某一项或两项的优化级别,从中作出平衡,无法将所有的优化级别都提到最高。最常用的优化侧重面是代码长度和代码速度这两项。

(三)连接器选项设定

在上面提到的编译器设置中,有关连接器的设定选项(Linker forHC12)。其中有一项对prm文件的选择。通过项目模板建立的项目其中必含有本项目专用的一个prm文件。缺省设置是利用此prm文件进行内存分配和连接定位。当你的项目用的是纯汇编单一文件且为绝对定位的编程模式,则不使用prm文件。

(四)prm文件介绍

prm文件已经在前面反复多次提到。我们可以看到在文件栏的prm下有两个prm文件,对应不同的调试模式。这个文件到底有什么作用,很多CodeWarrior初学者,开始都不容易理解。

prm文件一般情况下不需要修改,即可满足我们使用。它按所含的信息主要有五个组成部分构成。

(1)NAMES-END:用以指定在连接时加入除本项目文件列表之外的额外的目标代码模块文件,这些文件都是事先经C编译器或汇编器编译好的机器码目标文件而不是源代码文件。不过这种用法比较少见,因为我们可以在Libs一栏中添加这些目标代码文件来实现同样的任务,而且由项目列表管理这些模块文件比较直观方便。

(2)SEGMENTS-END:定义和划分芯片所有可用的内存资源,包括程序空间和数据空间。一般我们将程序空间定义成ROM或FLASH,把数据空间划分RAM,但实际上这些名字都不是系统保留的关键词,可以由用户随意修改。用户也可以把内存空间按地址和属性随意分割成大小不同的块,每块可以自由命名。

(3)PLACEMENT-END:将指派源程序中所定义的各种段,例如数据段DATA_SEG、CONST_SEG和代码段CODE_SEG被具体放置到哪一个内存块中。它是将源程序中的定义描述和实际物理内存挂钩的桥梁。INTO是系统保留的关键词,在这里可以理解为“放入”的意思。

(4)STACKSIZE:定义系统堆栈长度,其后给出的长度字节数可以根据实际应用需要进行修改。堆栈的实际定位取决于RAM内存的划分和使用情况。在常见的RAM线性划分变量连续分配的情况下,堆栈将紧挨在用户所定义的所有变量区域的高端。但如果你将RAM区分成几个不同的块,请确保其中至少有一个块能容纳已经定义的堆栈长度。

(5)VECTOR:定义中断入口向量地址。模板在生成prm文件时已经定义了复位的入口地址。对于各类中断向量,用户必须自己按向量编号与中断服务函数名相关联。如果中断函数的定义是用interrupt加上向量号,则无需在这里重复定义。

(五)编程要点

CodeWarrior中针对Freescale 8位、16位单片机C语言编程基本符合ANSI规范,因此关于标准C语言编程的话题就不再重复。这里主要介绍和单片机资源密切相关的一些编程要点。

(1)变量定义。在单片机程序设计中对于变量类型的选择确认有两条最基本的原则:能用短的变量就不用长的;能用无符号数就不用有符号数。这两条基本原则将在很大程度上决定你代码的长度和效率。由于是S12系列单片机的特点,对于多字节组成的变量,例如int、long等,C编译器缺省的变量内存排列方式是高字节在前(低地址),低字节在后(高地址)。

用volatile定义变量,其值是不随你的程序代码运行而随意改变的。它可以告诉编译器的优化处理器这些变量是实实在在存在的,在优化过程中不能无故消除。

用const定义变量,用以声明变量为永不变化的常数。一般来说这些变量都应该被放在ROM区(也就是Flash程序空间)以节约宝贵的RAM内存。但简单的一个const声明并不能保证变量最后会被分配到ROM区,安全的做法必须配合#pragma声明的。

(2)端口定义。端口定义在CodeWarrior中以变量绝对定位的方式定义,是特别针对芯片内部的硬件寄存器定义的。所有的硬件寄存器在编写C程序时均被视为变量,它们都已在头文件中预先定义。如:

#define PortA ( * ( volatile unsigned char * ) 0x0000 )

或者volatile unsigned char PortA @0x0000;

由于有些硬件资源,如某个端口PORTPRINT,它分配的地址是2100H。要定义它可用“volatile PORTPRINT @0x2100”,但要注意这个地址不能和其它变量地址重叠,所以要慎用。若非要定义这样的变量,需要适当修改prm文件的内容。

(六)#pragma声明介绍

#pragma声明是基于单片机开发的特点而对标准C语法的一个扩充。它对充分利用单片机内各类有限的资源起到不可或缺的关键作用。下面简单介绍几个最常用的#pragma声明。

(1)#pragma DATA_SEG。定义变量所处的数据段。其语法型式为

#pragma DATA_SEG名称

数据段名称可以自己任意命名,但习惯上有些约定的名称,其作用分别为:??DEFAULT:缺省的数据段,在S12X系列单片机中的地址为0x2000到0x3FFF。一般的变量定义可以放在这一区域。数据段名称必须和prm文件中的数据段配置说明相关联,才能真正发挥其定位作用。如果你自己命名的数据段在prm文件中没有特别说明,那此数据段的性质等同于DEFAULT。

数据段的可以缺省,它主要的目的是告诉编译器此段数据可适用的寻址模式。不同的寻址模式所花的指令数量和运行时间都不同。对于S12系列单片机,固定页的数据段可以用8位地址进行直接快速寻址,故对应此数据段应尽量指明其属性为__SHORT_SEG。对于一般数据段没有属性描述,其缺省是__FAR_SEG,将用16位地址间接寻址。

(2)#pragma CONST_SEG。定义一个常数数据段,必须和变量的const修饰关键词配合使用。其语法型式为

#pragma CONST_SEG 名称

该数据段下定义的所有数据将被放置在程序只读的ROM 区,也就是S12系列单片机内的Flash 程序空间区。常数段名称可以用户自由定义,但一般都用DEFAULT,让连接器按可用的ROM区域自由分配变量位置。如:

#pragma CONST_SEG DEFAULT

const byte name =‘hello world!’;

const word data = 0x0123;

#pragma CONST_SEG DEFAULT

word data = 0x0123; // 变量将被放置在RAM 区

#pragma DATA_SEG DEFAULT

const word data = 0x0123; // 变量将被放置在RAM 区

(3)#pragma INTO_ROM。功能类似于CONST_SEG,和变量修饰词const配合使用。但它只定义一个常数变量到ROM区,且只作用于紧接着的下一行定义。例如:

#pragma INTO_ROM

const byte name []=‘hello world!’; // 变量将被放置在ROM 区

word data = 0x0123; // 变量将被放置在缺省RAM 区

(4)#pragma CODE_SEG。用以定义程序段并赋以特定的段名,语法型式如下:

#pragma CODE_SEG 名称

对于S12系列16位单片机程序将超过64KB,这样必须在内存空间中以页面型式映射到首64KB地址范围,其对应的程序段属性要特殊声明。某些特殊的设计需要将不同部分的程序分别定位到不同的地址空间,例如实现程序代码下载自动更新。这样的设计需要把负责应用程序下载更新的驱动代码固定放置在一个保留区域内,而把一般的应用程序放置在另外一个区域以便在需要时整体擦除后更新。这时就需要用CODE_SEG来分别指明不同的程序段,但还必须配合prm文件对程序空间进行分配和指派。

代码段的属性一般都用缺省的__FAR_SEG,表明所有的函数调用都是长调用(对应汇编指令为JSR)。但S12系列单片机支持效率更高的函数短调用(对应汇编指令为BSR),如果你的某一个功能模块含有多个相互调用的小函数且函数调用间距不超过+127或-128字节,则可以将这部分代码段声明为短调用属性__NEAR_SEG。但实际编程时由于C代码对应的汇编指令长度不是很容易就能估测得到,所以短调用属性很少使用。

(5)#pragma TRAP_PROC。用于定义一个函数为中断服务类型。此类型的函数编译器在将C代码编译成汇编指令时会在代码前后增加必要的现场保护和恢复汇编代码,同时函数的最后返回用汇编指令RTI,而不是针对普通函数的RTS。例如:

#pragma TRAP_PROC

void sci_ISR (void) {…}//定义SCI的中断服务程序

注意用TRAP_PROC定义的中断服务函数其实际中断矢量地址必须通过prm文件指派。

(七)编写中断服务程序

编写中断函数几乎是每一个单片机项目开发必需的一个内容。CodeWarrior针对S12系列单片机的中断函数编写有以下几种方式可以实现。

(1)用关键词interrupt和中断矢量编号定义中断函数这种方式最直观也最简单。缺点是程序的可移植性稍差。如:

void interrupt 7 RTI_ISR(void)

关键词interrupt说明此函数为中断服务函数,“7”是中断向量编号,中断编号可以在该芯片的数据手册中查到。

(2)用关键词interrupt定义中断函数,中断矢量入口由prm文件指定,以上面的中断服务函数为例,这是函数的定义方式为:

void interrupt RTI_ISR (void){...}

然后在项目对应的prm文件中添加一行矢量位置定义:

VECTOR 0 _Startup //系统缺省的复位矢量入口

VECTOR 7 RTI_ISR //指定的中断服务矢量入口

(3)用#pragma TRAP_PROC定义中断函数,中断矢量入口由prm文件指定实际上就是用前面介绍的#pragma TRAP_PROC定义中断函数,再按照和interrupt相同的方法在prm文件中指定矢量入口。

(4)直接建立中断向量表数组,你可以在freescale网站找到移植范例。本人常用这种方法,可读性和移植性好,不需要修改其它文件。这种中断向量表定义方式大致如下:

__interrupt void UnimplementedISR(void) { __asm BGND;}

__interrupt void ReservedISR(void) { __asm BGND;}

typedef void (*tIsrFunc)(void);

const tIsrFunc ResetVectorTable[] @0xFF10 ={

ReservedISR, /* 0xFF10 Spurious Interrupt */

ReservedISR, /* 0xFF12 Reserved */

……

UnimplementedISR, /* 0xFFF2 IRQ */

UnimplementedISR, /* 0xFFF4 XIRQ */

UnimplementedISR, /* 0xFFF6 SWI */

UnimplementedISR, /* 0xFFF8 Unimplemented instruction trap */

UnimplementedISR, /* 0xFFFA COP failure reset */

UnimplementedISR, /* 0xFFFC Clock monitor fail reset */

//_Startup /* Reset vector */

};

四、结束语

在嵌入式应用中,开发人员要处理的外设越来越多,外设越来越复杂,开发周期要求越来越短,性能要求越来越高。这些要求往往很难同时满足,开发人员必须需要高性能的工具来帮助他们,CodeWarrior给工程师提供了巨大的帮助。鉴于本人认识有限,不妥之处,请批评指正。

参考文献:

[1]孙同景.Freescale 9S12十六位单片机原理及嵌入式开发技术[M].北京:机械工业出版社,2008

[2]张阳.MC9S12XS单片机原理及嵌入式系统开发[M].北京:电子工业出版社,2011

上一篇:服装材料与隔热值关系探讨 下一篇:高校计算机学生创新能力培养的有效路径