目的:找到一种适合的多平台的开发环境
在本文后将详细描述ECLIPSE+GCC的免费开源环境搭建 和一系列demo工程
(时间紧凑,先摘录原文,回头会来写体会)
开发工具
在开始使用Cortex ‐ M3之前,需要准备好一些开发工具,典型的如:
z 编译器/ 汇编器:把C 和汇编源程序转换成目标文件。几乎所有的C 编译器套件都
包含了对应的汇编器。
z 指令系统模拟器:模拟指令的执行,用于在软件开发早期的调试。
z 在线仿真器(ICE )或者调试探测器(probe):连接到电脑和目标板上的调试硬件,
与目标板的接口通常是JTAG 或SW。
z 一块开发板。
z 跟踪捕捉仪:可选的硬件设备和周边软件,可以用它来捕捉来自 DWT 以及ITM 的
输出,并且以可读的形式显示出来。
z 嵌入式操作系统:在单片机上运行的操作系统。这也是一个可选件,许多简单的应
用程序不需要操作系统。但是在开发复杂度较高或者有高性能指标的系统时,常常
需要使用。
C 编译器
截止到目前,已经有若干个C 编译器套件可以使用了,如表17.2 所列。
表17.2 支持Cortex ‐ M3的开发工具
使用GNU工具链开始Cortex-M3开发
背景
GNU 工具链在ARM 产品开发中使用得很广泛,并且有些为ARM 打造的开发工具也是
基于GNU 工具链的。在目前,支持CM3 的GNU 工具链可以由CodeSour c er y 处免费下载到
(ww w.c odesourcer y.com)。而GNU 的主打C 编译器则在以后支持CM3 (在2008年3 月31
日以后,主流的GNU 工具链已经支持Cortex‐ M3,对应的开发工具为WinARM——译者注)。
本章只介绍使用GNU 工具链的基础知识,更详细的信息还需要参阅联机帮助文档。值
得一提的是,GNU 的汇编语法(GNU 工具链中的AS程序)与ARM 的汇编语法是有些不同
的。这些不同点包括变量定义、编译指示字、以及the lik e。因此,使用ARM Rea lView 工具
的汇编代码在使用GNU 工具前,还需要一些(很枯燥的)修改工作。
获取GNU工具链
编译好的GNU 工具链可以从w w w.codesourcer y.com/gnu_toolchains/a rm/ 处下载。有一系
列的二进制构建版本。对于最简单的使用,可以使用EABI(EABI表示嵌入式应用程序二进制
接口。可执行目标文件必须符合该规格,从而可以跨开发工具集使用),并且没有嵌入式OS
的版本。这个工具链既有在Windows 上使用的版本,也有在Linux上使用的版本。本章给出的
示例程序可以用于任何一个版本上。
开发流程
和ARM 开发工具的相似,GNU 工具链也包含了编译器、汇编器和连接器,从而使得源
代码既可以使用C,也可以使用汇编写成,如图19.1 所示。
不同的应用程序环境中也有不同版本的工具链(Symbian, Linux, EABI 等)。取决于工具
链的目标平台,相应的可执行文件通常有一个前缀。例如,如果使用了EABI 环境,则GCC
命令为arm ‐ xxxx ‐ eabi ‐ gcc。本章的目标代码使用 CodeSourcery 的GNU ARM 工具链,如表19.1
所示。
表19.1 winARM20080331 GNU 工具链的命令名称
在开发流程图中,连接脚本是可选的。但是当存储器映射比较复杂时,常常是必需的。
例 5:纯 C 程序
想必大家已经受够了在汇编下过的日子吧!在GNU 工具链中的一个主要组件就是C 编
译器。在本例中,整个可执行程序——甚至是复位向量和MSP 初值都由C 写成。此外,还
添加了一个连接器脚本,用来把各段放到正确的位置。那么,先让我们看一看C 程序文件。
========== example5.c ==========
#define STACK_TOP 0x20000800
#define NVIC_CCR ((volatile unsigned long *)(0xE000ED14))
// 声明函数原型
void myputs(char *string1);
void myputc(char mychar);
int main(void);
void nmi_handler(void);
void hardfault_handler(void);
// 定义向量表
__attribute__ (( section(“vectors”) )) void (* const VectorArray[])(void) =
{
STACK_TOP,
main,
nmi_handler,
hardfault_handler
};
// 主程序入口点
242
Cortex ‐ M3权威指南 初稿 第19章
int main(void)
{
const char *helloworld[]="Hello world\n";
*NVIC_CCR = *NVIC_CCR | 0x200; /* 设置NVIC 的STKALIGN */
myputs(*helloworld);
while(1);
return(0);
}
// 函数
void myputs(char *string1)
{
char mychar;
int j;
j=0;
do
{
mychar = string1[j];
if (mychar!=0)
{
myputc(mychar);
j++;
}
} while (mychar != 0);
return;
}
void myputc(char mychar)
{
#define UART0_DATA ((volatile unsigned long *)(0x4000C000))
#define UART0_FLAG ((volatile unsigned long *)(0x4000C018))
// Wait until busy fl ag is clear
while ((*UART0_FLAG & 0x20) != 0);
// Output character to UART
*UART0_DATA = mychar;
return;
}
//空的服务例程
void nmi_handler(void)
{
return;
}
void hardfault_handler(void)
243
Cortex ‐ M3权威指南 初稿 第19章
{
return;
}
========== end of file ==========
注意粗体字显示的部分,它使用 __attribute(( )) (注意,是双小括号)来指定特殊的属
性。在这里则指出那个函数指针数组是放到vectors 段中的。然而,这个C 程序并没有指定
vectors 段在何处。那么在哪里指定 vectors 段的位置呢?现在该请出我们的连接器脚本文件
了,工作就在这里完成。本例的连接器脚本文件为simple.ld ,内容如下:
========== simple.ld ==========
/* MEMORY 命令:定义允许的存储器区域 */
/* 本部分定义了连接器允许放入数据的各存储器区域,这是 */
/* 一个可选的功能,但是对于开发很有益,它使连接器在在 */
/* 程序太大时能给你警告 */
MEMORY
{
/* ROM 是可读的(r) 和可执行的(x) */
rom (rx) : ORIGIN = 0, LENGTH = 2M
/* RAM 是可读的(r) ,可写的(w) ,可执行的(x) */
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 4M
}
/* SECTIONS 命令 : 定义各输入段到输出段的映射 */
SECTIONS
{
. = 0x0; /* 从0x00000000开始 */
.text : {
*(vectors) /* 向量表 */
*(.text) /* 程序代码 */
*(.rodata) /* 只读数据 */
}
. = 0x20000000; /* 从0x20000000开始 */
.data : {
*(.data) /* 数据存储器 */
}
.bss : {
*(.bss) /* 预留的数据存储器,必须初始化为零 */
}
}
========== end of file ==========
为使用连接脚本,需要在编译阶段把simple.ld 传给编译器。
$> arm-none-eabi-gcc -mcpu_cortex-m3 -mthumb example5.c -nostartfiles
-T simple.ld -o example5.o
在CodeSourcery 安装后,已经包含了一系列的连接脚本,可以从 codesourcery/sourcery
g++/arm‐ none‐ eabi/lib目录下找到。在下例中,我们就使用了 lm3s8xx ‐ rom.ld 文件。这个连
接器脚本顾名思义,是用于LM3S8XX 系列芯片的。
GNU C编译器的内联汇编
GNU 的ARM C编译器是支持内联汇编的,但此时的汇编语法看起来有点怪:
__asm (" inst1 op1, op2... \n"
" inst2 op1, op2... \n"
...
" inst op1, op2... \n"
: 输出操作数s /* 可选 */
: 输入操作数s /* 可选 */