第2章:IAR EWARM 开发环境基础操作
2.1 创建新工程
2.1.1 选择目标芯片
在 IAR EWARM 中创建新工程的第一步是选择目标芯片。这一步至关重要,因为不同的芯片具有不同的硬件资源和特性,选择正确的芯片能确保开发环境针对目标硬件进行适配。
- 启动芯片选择界面:打开 IAR EWARM 开发环境后,选择菜单栏中的 “Project” -> “Create New Project…” 选项,弹出 “Create New Project” 对话框。在该对话框中,“Device” 选项卡用于选择目标芯片。
- 芯片筛选与选择:IAR EWARM 支持众多芯片厂商的产品,如 ARM、STMicroelectronics、NXP 等。在 “Device” 下拉列表中,首先选择芯片厂商,例如,如果要开发基于 STM32 的项目,选择 “STMicroelectronics”。然后,在厂商对应的芯片列表中,根据项目需求选择具体的芯片型号。例如,对于一些低功耗、资源需求不高的项目,可能选择 STM32F0 系列中的某一款芯片;而对于对性能要求较高、需要丰富外设的项目,则可能选择 STM32F4 系列芯片。选择芯片时,需综合考虑芯片的性能(如主频、内存大小)、外设资源(如 GPIO 数量、串口数量、ADC 精度等)以及成本等因素。
- 查看芯片信息:在选择芯片型号后,对话框下方会显示该芯片的简要信息,包括芯片的内核、内存大小、外设等关键参数。这些信息有助于开发者确认所选芯片是否符合项目要求。例如,若项目对存储容量有较高要求,通过查看芯片信息中的内存大小参数,可判断该芯片是否满足需求。
2.1.2 工程模板介绍与选择
选择完目标芯片后,接下来需要选择合适的工程模板。工程模板为项目提供了一个初始的框架,包含了一些基本的配置和文件,可大大加快项目开发的初始阶段。
-
工程模板类型:
- Empty project:空工程模板。此模板仅创建一个基本的工程框架,不包含任何源文件或库文件。适用于开发者希望完全自主构建项目,从最基础的代码编写开始,对项目的架构和文件组织有高度自定义需求的情况。例如,在开发一些高度定制化的底层驱动程序时,可能选择此模板。
- C - Executable:C 可执行文件工程模板。该模板创建的工程主要用于开发基于 C 语言的可执行程序,默认包含了一些必要的启动代码和链接脚本,适用于以 C 语言为主要开发语言,开发独立运行的可执行应用程序的项目。比如,开发一个简单的基于 ARM 芯片的传感器数据采集程序,可选择此模板。
- C++ - Executable:C++ 可执行文件工程模板。与 C - Executable 类似,但专门用于开发基于 C++ 语言的可执行程序。它为 C++ 开发提供了必要的环境配置,如 C++ 标准库的链接等。当项目需要利用 C++ 的面向对象特性、模板等功能时,如开发一个复杂的图形界面应用程序,可选择此模板。
- Static Library:静态库工程模板。若项目旨在开发一个可被其他工程复用的静态库,选择此模板。静态库是一组已编译的目标文件的集合,其他工程在链接时可将其包含进来。例如,开发一个通用的数学计算库,供多个项目使用,就可基于此模板进行开发。
- Dynamic Library:动态库工程模板。用于开发动态链接库,动态库在程序运行时被加载,可节省内存空间,且方便库的更新。但在嵌入式系统中,由于资源限制和实时性要求,动态库的使用相对较少。不过,在一些资源较为丰富且对库的动态更新有需求的嵌入式项目中,仍可能会用到。
-
模板选择依据:选择工程模板时,需根据项目的性质和需求来决定。如果项目是一个简单的基于 C 语言的应用程序,且对代码结构没有特殊的自定义需求,C - Executable 模板是一个不错的选择;若项目需要面向对象编程的特性,C++ - Executable 模板更为合适;而如果项目的目的是开发一个可复用的库,则应选择 Static Library 或 Dynamic Library 模板,具体选择哪种库模板取决于项目对库的使用方式和资源限制等因素。
2.1.3 工程配置选项说明
在选择完目标芯片和工程模板后,还需要对工程的一些配置选项进行设置,这些选项会影响项目的编译、链接和运行等方面。
-
General Options(常规选项):
- Target(目标):在这里可以再次确认和修改目标芯片的选择。此外,还可以设置目标硬件的一些特性,如是否使用硬件浮点运算单元(FPU)。如果目标芯片支持硬件浮点运算,且项目中有大量的浮点运算需求,启用 FPU 可显著提高运算速度。
- Output(输出):指定工程输出文件的路径和名称。默认情况下,输出文件会保存在工程目录下的 Debug 或 Release 文件夹中,具体取决于当前的工程配置。开发者可以根据需要修改输出路径,例如,将输出文件保存到特定的共享文件夹,方便团队成员获取。
- Library Configuration(库配置):用于选择使用的标准库版本。IAR EWARM 提供了不同优化级别的标准库,开发者可根据项目对代码体积和执行效率的要求进行选择。例如,对于资源受限的项目,可选择体积较小的精简版标准库;而对于对性能要求较高的项目,可选择优化程度更高的标准库版本。
-
C/C++ Compiler(C/C++ 编译器选项):
- Preprocessor(预处理器):在此处可以定义预处理宏,这些宏可用于在代码中进行条件编译。例如,定义一个宏 “DEBUG”,在代码中通过 “#ifdef DEBUG” 和 “#endif” 来控制调试信息的输出。此外,还可以设置包含文件的搜索路径,指定编译器查找头文件的位置。
- Optimization(优化):设置代码的优化级别,从无优化(None)到高度优化(High)等多个级别可供选择。优化级别越高,生成的代码执行效率越高,但可能会导致代码可读性和调试难度增加。一般在开发初期,为了便于调试,可选择较低的优化级别;在项目后期,为了提高性能,可逐步提高优化级别。
- Code(代码生成):可以选择代码生成的目标架构特性,如指令集版本、是否支持 Thumb 指令集等。Thumb 指令集是 ARM 架构的一种 16 位指令集,具有代码密度高的特点,适用于对代码体积要求较高的项目。
-
Assembler(汇编器选项):
- Preprocessor(预处理器):与 C/C++ 编译器的预处理器类似,用于定义汇编语言中的预处理宏和设置包含文件的搜索路径。在汇编代码中,也经常会使用宏来简化代码编写和实现条件汇编。
- Listing(列表文件):控制是否生成汇编列表文件,列表文件包含了汇编代码以及对应的机器码等信息,有助于开发者分析汇编代码的生成情况,特别是在优化代码或排查底层问题时非常有用。
-
Linker(链接器选项):
- Output(输出):设置链接生成的可执行文件的格式,如 elf、hex 等。不同的格式适用于不同的应用场景,elf 格式通常用于调试和开发阶段,它包含了丰富的调试信息;hex 格式则常用于将程序烧录到目标硬件中,许多编程器只支持 hex 格式的文件。
- Config(配置):指定链接器的配置文件,链接器配置文件定义了内存布局、符号定义等重要信息。通过修改链接器配置文件,可以根据目标芯片的内存分布情况,合理安排代码和数据在内存中的存储位置,以提高系统性能。
2.2 工程文件管理
2.2.1 文件的添加与移除
在项目开发过程中,需要不断添加和移除各种文件,以满足项目的功能需求。
-
添加文件:
- 添加源文件:在 IAR EWARM 工程窗口中,右键点击项目名称,在弹出的菜单中选择 “Add -> Add Files…” 选项。在弹出的文件选择对话框中,导航到源文件所在的目录,选择要添加的源文件(如. c 或. cpp 文件),然后点击 “Open” 按钮,即可将源文件添加到项目中。另外,也可以在菜单栏中选择 “Project” -> “Add -> Add Files…” 来完成相同操作。
- 添加头文件:添加头文件的方法与添加源文件类似。头文件通常包含函数声明、宏定义等信息,是项目中不可或缺的一部分。在添加头文件时,需注意头文件的路径设置。如果头文件与源文件在同一目录下,直接添加即可;若不在同一目录,需在工程的 C/C++ 编译器选项的 “Preprocessor” 中设置头文件的搜索路径,确保编译器能够找到头文件。
- 添加库文件:若项目需要使用外部库文件,同样可以通过右键点击项目名称,选择 “Add -> Add Files…” 来添加库文件(如. a 或. lib 文件)。库文件为项目提供了已编译好的代码模块,可复用这些模块来加快开发进度。添加库文件后,还需在链接器选项中设置库文件的搜索路径,以便链接器在链接过程中能够找到库文件。
-
移除文件:在工程窗口中,选中要移除的文件,右键点击,在弹出的菜单中选择 “Remove” 选项,即可将文件从项目中移除。需要注意的是,此操作仅将文件从项目中移除,并不会删除文件在磁盘上的实际存储。若要彻底删除文件,需在操作系统的文件管理器中手动删除。
2.2.2 文件分组与组织
随着项目规模的扩大,文件数量会逐渐增多,合理的文件分组与组织能提高项目的可维护性和可读性。
- 创建文件组:在工程窗口中,右键点击项目名称,选择 “Add -> Add Group…” 选项,在弹出的对话框中输入文件组的名称,如 “Source Files”(用于存放源文件)、“Header Files”(用于存放头文件)、“Libraries”(用于存放库文件)等,然后点击 “OK” 按钮,即可创建一个新的文件组。
- 移动文件到文件组:创建好文件组后,可将文件拖放到相应的文件组中。例如,将所有的源文件拖放到 “Source Files” 文件组中,将头文件拖放到 “Header Files” 文件组中。这样,工程中的文件就按照类型进行了分类组织,便于查找和管理。此外,也可以通过右键点击文件,在弹出的菜单中选择 “Move to Group”,然后选择目标文件组来完成文件的移动操作。
- 文件组的嵌套与层次结构:对于复杂的项目,可能需要创建多层嵌套的文件组结构。例如,在 “Source Files” 文件组下,再创建子文件组,如 “Driver Files”(用于存放驱动程序源文件)、“Application Files”(用于存放应用程序源文件)等,进一步细化文件的组织。通过合理设计文件组的嵌套和层次结构,能清晰地反映项目的架构和功能模块划分。
2.3 编辑源文件
2.3.1 代码编辑功能与快捷键
IAR EWARM 提供了丰富的代码编辑功能,同时支持多种快捷键操作,以提高代码编写效率。
-
代码编辑功能:
- 语法高亮显示:根据代码的语法结构,对不同类型的代码元素(如关键字、变量、注释等)显示不同的颜色,使代码结构更加清晰易读。例如,C 语言中的关键字通常显示为蓝色,注释显示为绿色,这样在阅读和编写代码时能快速区分不同类型的代码元素。
- 代码折叠:对于较长的代码块,如函数体、循环体等,可以将其折叠起来,只显示代码块的头部,使代码编辑窗口更加简洁。当需要查看或编辑代码块内部内容时,再展开折叠的代码块。在代码块的左侧会有一个折叠标记,点击该标记即可实现代码块的折叠与展开操作。
- 书签功能:开发者可以在代码的关键位置设置书签,方便快速跳转到这些位置。例如,在函数的入口处、重要的变量声明处等设置书签。设置书签后,在工程窗口的 “Bookmarks” 选项卡中会列出所有的书签,点击书签即可快速定位到对应的代码行。
-
常用快捷键:
- Ctrl + N:新建一个源文件。在开发过程中,当需要创建新的源文件来实现新的功能模块时,使用此快捷键可快速打开新建文件对话框。
- Ctrl + O:打开一个已有的文件。方便在项目中快速打开所需的源文件、头文件等进行查看或编辑。
- Ctrl + S:保存当前编辑的文件。养成经常保存文件的习惯,可防止因意外情况(如程序崩溃、断电等)导致代码丢失。
- Ctrl + Z:撤销上一步操作。当进行了错误的编辑操作(如误删除代码行)时,使用此快捷键可撤销操作,恢复到上一步的状态。
- Ctrl + F:查找文本。在代码中查找特定的字符串非常有用,特别是在大型项目中查找某个函数、变量或特定的代码片段时。
- Ctrl + H:替换文本。不仅可以查找文本,还能将查找到的文本替换为指定的内容,常用于批量修改代码中的变量名、函数名等。
- Ctrl + /:注释或取消注释当前行或选中的代码块。在调试代码或暂时不需要某些代码执行时,使用此快捷键可快速注释掉代码;当需要恢复代码执行时,再次使用该快捷键取消注释。
2.3.2 代码自动完成与语法检查
IAR EWARM 的代码自动完成和语法检查功能有助于提高代码编写的准确性和效率。
-
代码自动完成:当在代码编辑窗口中输入变量名、函数名或关键字的前几个字符时,IAR EWARM 会自动弹出一个列表,显示与输入字符匹配的所有可能选项。例如,当输入 “printf” 的前几个字符 “pri” 时,列表中会显示 “printf” 以及其他以 “pri” 开头的函数或变量(如果有的话)。通过上下箭头键选择所需的选项,然后按下 “Enter” 键,即可自动完成代码输入。代码自动完成功能不仅节省了代码输入时间,还能减少因拼写错误导致的编译错误。此外,对于自定义的变量和函数,只要在项目中已经定义,也能通过代码自动完成功能快速输入。
-
语法检查:IAR EWARM 会实时对输入的代码进行语法检查。当输入的代码存在语法错误时,会在错误位置下方显示一条红色波浪线,并在 “Problems” 窗口中详细列出错误信息,包括错误类型、错误所在的文件和行号等。例如,当遗漏了分号、括号不匹配等语法错误发生时,能立即在代码编辑窗口中直观地看到错误提示,方便及时修正。同时,通过双击 “Problems” 窗口中的错误信息,可快速定位到代码中的错误位置,提高错误排查效率。
2.4 编译与链接工程
2.4.1 编译过程与错误提示分析
-
编译过程
- 预处理:这是编译的第一个阶段。预处理器会处理源文件中的预处理指令,如
#include
、#define
、#ifdef
等。- 对于
#include
指令,它会将指定的头文件内容插入到源文件的相应位置。例如,若有#include "myHeader.h"
,预处理器会找到myHeader.h
文件,并将其内容复制到源文件中该指令所在处。这使得源文件能够使用头文件中定义的函数声明、宏定义等。 #define
指令用于定义宏。预处理器会在源文件中把所有宏的标识符替换为其定义的内容。例如,#define MAX_VALUE 100
,在源文件后续代码中,所有MAX_VALUE
都会被替换为100
。#ifdef
、#ifndef
、#endif
等条件编译指令,可根据定义的宏来决定是否编译某段代码。例如,#ifdef DEBUG #define DEBUG_PRINT(x) printf(x) #else #define DEBUG_PRINT(x) do{}while(0) #endif
,当定义了DEBUG
宏时,DEBUG_PRINT
会被定义为printf
函数调用,否则被定义为空操作。
- 对于
- 编译:经过预处理后的源文件进入编译阶段。编译器会对代码进行词法分析、语法分析和语义分析。
- 词法分析:将源文件的字符流转换为一个个单词(token)。例如,对于代码
int num = 10;
,词法分析会将其分解为 “int”(关键字)、“num”(标识符)、“=”(运算符)、“10”(常量)、“;”(界符)等单词。 - 语法分析:基于词法分析得到的单词序列,检查代码是否符合编程语言的语法规则。例如,检查语句是否以分号结尾、括号是否匹配等。如果代码
int num = 10
缺少分号,语法分析就会检测到错误。 - 语义分析:在语法正确的基础上,检查代码的语义是否正确。例如,检查变量是否在使用前声明、函数调用的参数类型和数量是否与函数定义一致等。如果代码中使用了未声明的变量,语义分析就会报错。
- 在通过这些分析后,编译器将源文件转换为目标芯片架构对应的汇编语言代码。不同的芯片架构(如 ARM、x86 等)有不同的汇编指令集,编译器会根据目标芯片的指令集生成相应的汇编代码。
- 词法分析:将源文件的字符流转换为一个个单词(token)。例如,对于代码
- 汇编:汇编器将编译生成的汇编代码转换为目标机器的机器代码(目标文件,通常扩展名为
.o
)。汇编器会把汇编指令翻译成对应的机器指令,并处理符号地址等信息。每个源文件经过编译和汇编后,都会生成一个对应的目标文件。例如,汇编指令MOV R0, #10
(将立即数 10 传送到寄存器 R0)会被翻译成目标机器能识别的二进制代码。
- 预处理:这是编译的第一个阶段。预处理器会处理源文件中的预处理指令,如
-
错误提示分析
当编译过程出现错误时,IAR EWARM 会在 “Problems” 窗口中显示错误信息。- 错误类型:明确指出错误的本质,常见的如 “syntax error”(语法错误),表示代码不符合编程语言的语法规则,可能是遗漏标点符号、关键字拼写错误等;“undefined symbol”(未定义符号错误),意味着使用了未声明的变量、函数等;“redefinition”(重定义错误),表示某个变量或函数被重复定义。例如,错误提示 “syntax error: unexpected end of file” 表示在文件结束时发现了不符合语法规则的情况,可能遗漏了某些语句结尾的符号。
- 错误所在文件和行号:清晰指出错误发生在哪个源文件以及具体的行号。例如,“main.c(10): error:…” 表明错误发生在
main.c
文件的第 10 行,方便开发者快速定位到代码中的错误位置。 - 错误描述:对错误进行简要描述,帮助开发者理解错误原因。例如,“undefined reference to ‘functionName’” 表示在代码中引用了一个未定义的函数
functionName
,开发者可以据此检查函数的声明和定义是否正确。
2.4.2 链接设置与常见问题处理
- 链接设置
- 输出文件设置:在 IAR EWARM 的链接器选项中,可以指定链接生成的可执行文件的格式,常见的有 ELF(Executable and Linkable Format)和 HEX 格式。ELF 格式文件包含了丰富的调试信息,便于在开发过程中进行调试;HEX 格式文件则常用于将程序烧录到目标硬件中,许多编程器只支持 HEX 格式的文件。此外,还能设置输出文件的名称和保存路径。
- 内存布局设置:链接器配置文件(通常为
.icf
文件)定义了目标芯片的内存布局,包括代码段、数据段、堆栈段等在内存中的位置。开发者需要根据目标芯片的内存分布情况,合理设置内存布局。例如,对于一款具有片上 Flash 和 SRAM 的芯片,需要将代码段放置在 Flash 区域,数据段和堆栈段放置在 SRAM 区域,以确保程序能够正确运行。 - 库文件链接:如果项目使用了外部库文件(如标准库、第三方库),需要在链接器选项中设置库文件的搜索路径,以便链接器在链接过程中能够找到这些库文件。同时,要指定链接哪些库文件,例如,若项目使用了数学运算库,就需要链接相应的数学库文件。
- 常见问题处理
- 未定义引用错误:当链接器无法找到某个符号(变量、函数等)的定义时,会出现 “undefined reference” 错误。这可能是因为:
- 忘记包含定义该符号的源文件或库文件。例如,在代码中调用了一个自定义函数,但该函数所在的源文件未添加到项目中,此时需将该源文件添加到项目,并确保其在编译路径中。
- 函数或变量的声明和定义不匹配。比如,声明函数时参数类型与定义函数时参数类型不一致,需要检查并修正声明和定义。
- 多重定义错误:如果同一个符号在多个地方被定义,会出现 “multiple definition” 错误。常见原因是在多个源文件中定义了同名的全局变量或函数。解决方法可以是将全局变量的定义放在一个源文件中,并在其他源文件中使用
extern
关键字声明;对于函数,如果是相同功能的函数重复定义,可以将其合并为一个函数。 - 内存分配错误:如果内存布局设置不合理,可能导致程序运行时出现内存访问错误。例如,将代码段放置在数据段的地址范围内,或者堆栈溢出等。此时需要仔细检查链接器配置文件中的内存布局设置,确保各个段在正确的内存区域,并且为堆栈分配足够的空间。
- 未定义引用错误:当链接器无法找到某个符号(变量、函数等)的定义时,会出现 “undefined reference” 错误。这可能是因为:
2.4.3 生成可执行文件的格式与用途
- ELF 格式
- 格式特点:ELF 格式是一种可执行和可链接的文件格式,常用于 Linux 系统以及嵌入式开发中的调试阶段。它包含了丰富的元数据,如符号表、重定位信息、调试信息等。符号表记录了程序中定义和引用的符号(变量、函数等)及其对应的地址;重定位信息用于在链接时调整符号的地址;调试信息则方便开发者在调试过程中查看变量的值、函数调用关系等。
- 用途:在开发过程中,ELF 格式文件主要用于调试。IAR EWARM 的调试器可以利用 ELF 文件中的调试信息,实现断点调试、变量观察、堆栈跟踪等功能。例如,在调试时,调试器可以根据符号表找到变量在内存中的地址,从而显示变量的值;通过重定位信息正确定位代码和数据在内存中的位置。此外,ELF 文件也可用于分析程序的结构和性能,例如通过分析符号表来了解程序中函数和变量的使用情况。
- HEX 格式
- 格式特点:HEX 格式文件是一种十六进制文本文件,它以特定的格式记录了程序的二进制代码和数据。每一行以 “:” 开头,后面跟着数据长度、地址、数据内容以及校验和等信息。例如,“:100000000C0000000000000000000000000000000039” 表示这一行包含 16 个字节的数据(数据长度为 10H),起始地址为 00000000H,后面跟着 16 个字节的数据内容,最后 “39” 是校验和。
- 用途:HEX 格式文件主要用于将程序烧录到目标硬件中。大多数嵌入式系统的编程器只支持 HEX 格式的文件进行烧录操作。在将程序烧录到目标芯片的 Flash 存储器等存储介质时,编程器会根据 HEX 文件中的地址和数据信息,将相应的代码和数据写入到芯片的指定位置,使芯片能够执行该程序。