1.2 ARM嵌入式系统的开发环境

用户在开始进行ARM嵌入式系统开发之前,需要准备好相应的开发工具及其开发环境。选择一款适合实际工程应用的开发环境是非常重要的。一般而言,常见的ARM嵌入式系统开发环境配置有如下几项。

1.编译器/汇编器

通常情况下,当前几乎所有的C编译器套件都包含了相应的汇编/编译器,建议用户在进行ARM嵌入式系统开发过程中,软件代码使用C语言来实现;而在频繁与底层寄存器打交道时,则可以使用汇编语言。

2.指令系统模拟器

指令系统模拟器用于在软件开发早期的ARM嵌入式代码调试中。通过指令系统模拟器,用户可以避免由于硬件平台不成熟而带来的问题,从而大幅缩短软件代码的实现、调试周期。

3.在线仿真器或调试探测器

该系统的开发环境是连接在计算机和ARM开发板(或其他硬件调试平台)上的调试硬件。通常而言,与硬件调试平台之间的接口是JTAG或者SWD。

4.目标开发板

对于初次接触ARM嵌入式系统的用户而言,建议在早期开发的过程中使用目标开发板对需要开发的工程项目进行前期可行性验证,从而避免硬件电路设计中可能存在的问题,缩短项目开发周期。

5.跟踪捕捉仪

该开发环境是可选的硬件设备和周边软件,用户可以使用它来捕捉来自DWT及ITM的输出,并且以可读的形式显示出来。

6.嵌入式操作系统

嵌入式操作系统是指在ARM嵌入式芯片中运行的操作系统。需要注意的是,嵌入式操作系统也是一个可选的内容。一般情况下,在开发一些简单的嵌入式系统中可以不需要操作系统,而在开发一些复杂度比较高或者有高性能指标的ARM嵌入式系统时才需要运行相应的嵌入式操作系统。

目前,市场上常见的通用C编译器已经非常普遍。每一个编译器产品都具有各自的优势和应用场合,具体如表1.1所示。用户可以根据实际工程的需求,以及ARM处理芯片的类型来选择不同的C编译器产品。

表1.1 常见的ARM嵌入式系统C编译器

在上述几种ARM嵌入式开发环境中,最流行、最为通用的是Keil公司推出的RealView Microcontroller Development Kit,简称RealView MDK或RVMDK。该公司推出的此款ARM开发平台可以追溯到曾在8051系列单片机开发行业中具有盛誉的Keil开发套件,并且包含了丰富的功能组件,诸如μVision集成开发环境、调试器/模拟器、C/C++编译器、RTX实时内核、启动代码和Flash编程算法等。同样,Keil公司也推出了专为ARM的嵌入式开发平台,即RealView MDK。

1.2.1 Keil MDK简介

Keil开发环境是德国知名公司Keil(现已并入ARM公司)开发的嵌入式微控制器软件开发平台,也是目前ARM嵌入式单片机开发的主流工具。在该软件平台中,μVision的界面与微软VC++的界面类似,具有友好的人机交互环境,启动界面如图1.6所示,并且在调试程序、软件仿真方面有比较强大的功能。

图1.6 RealView MDK启动界面

值得说明的是,Keil公司推出的这款ARM嵌入式系统开发平台特别适合刚入门的ARM嵌入式新手,它甚至可以完全独立于外部的硬件开发环境。在μVision开发工具链中包含了指令模拟器,用户可以通过使用该功能来模拟“纯粹”的ARM嵌入式代码,即用户不需要外部硬件平台的支持也可以在软件平台中模拟代码的运行,基本的界面框架如图1.7所示。

图1.7 RealView MDK用户操作界面

RealView MDK开发环境所支持的单机脱离硬件的调试功能对学习和开发基于内核的系统软件有比较大的帮助,特别适用于缺乏硬件平台支撑的开发工程。除此之外,RealView MDK还可以与GNU工具链联合使用。

用户使用Keil平台开发ARM嵌入式工程软件,开发周期与其他平台环境的开发周期是类似的,大致有以下几个步骤:

● 创建新的项目工程,选择正确的目标芯片,并对工程项目参数进行配置;

● 编写C语言代码或汇编程序代码,并添加到项目工程文件中;

● 对项目工程进行编译、连接和调试;

● 修改源代码中的语法错误和逻辑错误,重新编译至正确;

● 与硬件联机调试至语法和逻辑都正确无误。

在RealView MDK开发环境中,开发平台附带了部分工程项目的示例程序,包括ST公司的STM32系列单片机产品,以及Luminary Micro公司的Stellaris系列单片机。在这些示例代码中都统一使用了ARM芯片生产厂商所提供的驱动固件库。

对于普通用户而言,这种做法也是必要的,因为用户使用生产厂家所提供的驱动固件库可以避免编写代码对芯片外部寄存器进行操作,也在一定程度上避免了代码中的错误,缩短了嵌入式系统的开发周期。

在实际的项目开发流程中,用户并不需要完全从代码的第一行开始写起,可以很方便地通过开发系统所自带的代码模板进行编写,提高代码实现的效率和准确率。

1.2.2 Keil MDK的开发步骤

用户在正确安装了RealView MDK开发环境后,就可以从开始菜单或者桌面图标启动μVision集成开发环境。在默认情况下,RealView MDK会自动加载上一次打开时所运行的工程文件。

1.创建/打开工程文件

在Keil MDK开发环境中,用户可以通过选择“New Project”下拉菜单来创建一个新的工程,如图1.8所示。

图1.8 在菜单中创建新的工程

此时,系统会弹出一个对话框,要求用户为新建的项目工程起一个名字。在这里,用户可以创建一个名为“test”的文件夹,如图1.9所示。在该工程文件夹中,主要用于存放有关test测试工程项目的所有文件。

图1.9 将新创建的工程命名为“test”

如果用户需要打开已经存在的项目工程文件,则可以通过选择菜单“Project”|“Open Project”来打开已经存在的工程。此时,系统会弹出一个打开文件对话框,供用户选择需要打开的项目工程文件的路径。

2.选择ARM嵌入式芯片

在创建完存放工程代码的文件夹后,用户就可以根据实际工程需要,选择需要的ARM嵌入式芯片。

需要注意的是,在项目开发过程中,并不是只需要一个源程序代码就可以完成对整个系统软件的开发,还需要在项目工程实现之前选择正确的ARM芯片的型号,加载对应的芯片启动代码(汇编语言实现,选择ARM芯片后系统自动加载),进而根据所选的具体芯片型号来确定项目工程编译、汇编和连接时的参数,并且指定具体的调试方式。

如果用户在系统芯片库中找不到实际所需要的处理器型号,也可以使用同一款兼容的处理器来代替。一般情况下,用户在选择具体的处理器芯片后系统会为芯片提供相应的启动代码,在弹出的对话框中单击“Yes”按钮就完成了对整个项目工程的建立。

3.添加源代码文件

在建立工程项目文件后,用户需要在该工程项目内添加源程序代码。通过选择菜单“File”|“New”或者单击工具栏上的“新建文件”按钮,就可以在项目工程窗口的右侧打开一个新的文本编辑窗口,用户可以在该窗口中输入程序代码。

需要说明的是,所谓ARM源程序代码就是一般的文本文件,只是后缀名根据编程语言的不同而各不相同。用户在编写源代码的时候不一定需要使用Keil软件来编写,也可以使用任意的文本编辑器甚至是记事本来编写,只要注意在完成源代码编写后需要将该文本保存为“.c”的后缀名文件就可以了。

通常情况下,Keil软件开发环境中的代码编辑器尚不能支持中文字符,因此如果用户需要在源代码中输入中文,建议使用UltraEdit之类的第三方字符编辑软件进行代码输入。

在编辑完代码之后,用户需要将其保存为源文件,选择菜单“File”|“Save”或者单击工具栏的“保存文件”按钮可以实现对源文件的保存操作。

如果用户是第一次对当前源文件进行保存的,系统会出现一个保存文件的对话框。用户在选择合适的保存路径后,输入相应的文件名就可以了。

特别需要提醒用户注意的是,在保存文件的时候一定要输入文件的扩展名,如果是C程序编写的文件,其扩展名为“.c”;如果是汇编语言编写的程序,扩展名为“.asm”;另外,代码中出现的注解说明文件可以单独保存为“.txt”扩展名。

在源文件编写完成之后,用户还需要将编写好的源文件添加到新建的项目工程中去。在新建项目工程的左侧界面中,单击“Target1”选项,前面有一个“+”号,表示当前选项还包含了子选项,单击即可展开。

当用户单击“+”号展开下一层的子选项后,可以看到“Source Gode”选项,编辑代码的源文件将被添加到该目录结构中。具体操作为:单击选中“Source Gode”,再右键单击鼠标,出现一个下拉菜单,如图1.10所示。

图1.10 添加源文件到项目工程中

选择其中的“Add Files to Group‘Source Gode’”,系统弹出一个文件对话框,用户可以在对话框中输入源代码文件存放的系统路径,单击“确定”按钮后就完成了对源文件的添加操作,具体如图1.11所示。

图1.11 添加源代码对话框

一般而言,在添加源文件操作的过程中,对话框下面的“源文件类型”默认的是C Source File(*.c)。在实际工程项目中,用户除了需要添加源代码文件外,还需要添加其他格式的文件,如汇编格式的源代码、系统库文件,以及目标文件等。

● 如果是选择添加“目标文件”,用户需要在对话框的下拉菜单中将文件类型选择为“Object File”;

● 如果是选择添加“汇编格式的源代码”,用户需要在对话框的下拉菜单中将文件类型选择为“asm source file”;

● 如果是选择添加“系统库文件”,用户需要在对话框的下拉菜单中将文件类型选择为“Library File”。

需要提醒用户注意的是,用户在单击“Add”按钮添加文件后,添加文件对话框不会消失,以方便用户连续添加多个文件。在添加文件结束后,单击对话框中的“Close”按钮关闭当前窗口。

在添加文件的时候,由于对话框不会自动关闭,经常会被初学者误认为“添加文件操作没有成功”而再次双击添加同一个文件,引起不必要的错误。发生这种错误的时候,RealView MDK系统也会弹出相应的错误提示对话框,如图1.12所示。

图1.12 重复添加源代码后的错误提示

用户在成功添加源代码文件后,单击“Source Group1”前面的“+”号就可以看到已经添加进去的源文件。双击文件名,则可以在代码区打开源程序代码文件,对其进行修改和编辑。

4.编译程序代码

在程序代码编辑完之后就可以进入编译阶段,用户可以通过菜单、工具栏和浮动菜单等多种方式对源代码进行编译。同时,在Keil MDK开发平台中,用户还可以通过批处理文件进行相应的编译操作。一般情况下,实际的项目工程需要进行批处理编译的操作不是很多,有兴趣的读者可以通过Keil MDK的技术支持文档查看具体的操作步骤,在这里不再赘述。

在“Project”下拉菜单中,分别列出了对应的菜单编译命令和工具栏编译命令。其各自的功能定义如下。

● Clear Target:清除当前编译的结果。

● Build Target:编译当前被修改的文件并且对当前源代码文件进行编译操作。

● Rebuild All Target Files:重新编译所有的项目工程源文件并且编译应用程序。

● Batch Build:通过前面输出的批处理文件进行编译。

● Translate **.*:编译某一个源文件代码,其中**.*表示需要编译的源代码文件。

● Stop Build:停止编译当前文件。该选项按钮只有在编译运行的过程中才有效。

除了上述相关的代码编译菜单之外,用户可以在工程窗口“Target1”上单击右键,系统同样也会弹出相应的编译菜单,也包含了跟上述一样意义和功能的命令操作。

用户在单击运行编译命令后,Keil MDK界面中的输出窗口会输出相应的编译结果,如图1.13所示。当出现“0 Errors(s),0 Warning(s)”时,表示当前工程项目中的源代码已经通过了编译器的语法检查,并正确无误。

图1.13 运行编译后的输出窗口信息

在部分复杂的工程文件源代码中,编译之后可能会出现“0 Errors(s),* Warning(s)”的编译结果(*表示相应的阿拉伯数字),这表示当前工程文件已经通过系统编译器的编译,但仍存在部分警告。

一般情况下,这些警告(Warning)不会影响程序的正常执行,但也需要慎重对待。通常可能引起Warning警告的原因有“声明/定义变量后在代码中没有使用”等。

无论是错误还是警告,用户都可以双击输出窗口中有关错误/警告信息内容所在的行,快速跳转定位到错误/警告的代码位置。

5.程序代码的调试

一般而言,项目工程文件的源代码在通过编译器的编译后,只能说明当前的代码没有语法错误。但在实际的调试过程中,除了编译器能识别的语法错误外,还可能存在其他错误,如逻辑功能错误等,而从实际的工程开发经验来说,除非软件代码的功能非常简单,否则几乎所有的源程序代码都可能存在逻辑上的错误。用户必须通过调试才能发现这些逻辑上的问题并解决。

事实上,除了极为简单的程序代码外,绝大部分程序都要通过反复调试才能得到最终正确的结果。因此,从某种角度而言,源程序代码的调试是用来验证用户所编写的代码是否能够达到预期目的和功能的重要手段,往往也是程序开发过程中最为艰难,且耗时最长的阶段。

在一些功能比较复杂的程序代码中,特别是ARM嵌入式系统的代码的调试过程中,不仅与软件的源程序代码本身相关,还与外部的硬件环境、操作时序等密切相关。因此,只有完成对代码的软、硬件调试,才能实现嵌入式系统的最终开发。

在对工程项目成功进行汇编、连接之后,用户可以通过菜单“Debug”|“Start/Stop Debug Session”或者“Ctrl+F5”进入代码调试状态。此时,调试的界面与原工程项目在编辑状态下的界面有明显变化:Debug菜单中原来不能使用的命令选项(灰色)可以在调试状态下使用(黑色),RealView MDK的工具栏上也会弹出一个用于运行和调试的工具栏,如图1.14所示。其中,Debug菜单上的绝大部分命令都可以在工具栏上找到对应的快捷按钮。

图1.14 MDK调试界面

在Keil MDK开发平台中,常用的Debug菜单命令如下。

● Start/Stop Debug Session:开始或停止调试操作。

● Run:一直运行到下一个活动的断点(停止点)。如果没有断点,则一直循环运行。

● Step:以行为单位,单步执行代码。

● Step Over:跳过函数体继续执行,即将函数作为一条语句执行。

● Step out of current Function:跳出当前正在执行的函数。

● Run to Cursor Line:执行到光标所在的代码行。

● Stop Running:停止运行当前正在调试的代码。

● Breakpoints:在当前的调试代码行打开断点设置对话框。

● Insert/Remove Breakpoints:在当前的调试代码行插入/删除一个断点。

● Enable/Disable Breakpoints:激活当前代码行的断点或使得当前的断点无效。

● Disable All Breakpoints:使得当前调试代码中所有的断点都无效。

● Kill All Breakpoints:删除程序中所有的断点。

用户在使用程序调试的过程中,必须明确两个重要概念,即全速执行和单步执行。全速执行是指一行程序代码执行完了后紧接着执行下一行程序代码,中间不停留。全速执行的方式具有较快的执行速度,并且可以看到当前调试代码的总体效果,即可以直观地评价最终运行结果是正确的还是错误的,但如果调试的结果是错误的,则用户很难确认错误出现在具体的哪一行代码中。

单步执行则是在调试过程中每次只执行一行程序代码,执行完该行程序代码后立即停止,等待命令执行下一行程序代码,此时,所有寄存器的参数值都保持当前运行的状态。用户可以方便地去观察当前代码执行完以后得到的结果是否与当初软件功能的设计思路一致。在调试结果出现错误的情况下,可以比较方便地发现代码中的错误地方。

在实际项目工程调试过程中,这两种调试方法都需要使用,并且灵活地将两者结合在一起可以提高代码的调试效率。