STM32Fgcc编译全纪录
使用Keil uVision编译的时候,有编译限制(32K,盗版的例外),正版的Keil,你懂的。为此想到了网上开源的编译器,大名鼎鼎的gcc编译器,大家都听过,但是对于嵌入式ARM的编译器,也在产生了很多变种(arm-none-eabi-gcc、arm-none-linux-eabi、arm-none-uclinuxeabi等),这都是开源世界的百花齐放的结果,以下区别要点仅供参考:
1)arm-none-eabi-: 这个是没有操作系统的,自然不可能支持那些跟操作系统关系密切的函数,比如fork(2),它使用的是newlib这个专用于嵌入式系统的C库
2)arm-none-linux-eabi: 针对于Linux的,使用Glibc。
3)arm-none-uclinuxeabi: 针对于uCLinux的,使用Glibc。
4)arm-none-symbianelf: 针对于symbian系统。
一、下载安装包并安装。
因为针对于无操作系统的应用,自然而然的选择了arm-none-eabi-gcc版本。
安装包: gcc-arm-none-eabi-4_7-2013q1-20130313-win32.exe
源码包:gcc-arm-none-eabi-4_7-2012q4-20121208-src.tar.bz2(喜欢研究源代码的可以下载)
安装过程一路next即可。
二、安装MinGW
下载并安装 mingw-get-inst-20120426
安装的时候将C编译器和Minsys选上,需要联网才可以安装。
三、熟悉Makefile
强力推荐熟悉网络牛人李云的51cto博客,有一篇驾驭Makefile的文章,另外一份参考资料也尤为重要《GNUmake中文手册-v3.80.pdf》,这都是先辈们开凿好的前路,对于后辈的我们只要沿路走就可以了。
熟悉Makefile主要还是实践,按照李云博客介绍的方法,多使用echo,或者使用make的调试命令make –D > 1.txt,这样可以熟悉其中的依赖关系和建立顺序。说的直白点就是建立对应依赖关系。
在熟悉的时候,可以一边对照实际工程编写对应的Makefile。
以下是简要学习Makefile的笔记:
1. %.a: 匹配所有以.a结尾的所有文件名
|--> 类似于通常意义的*.
2. $(addprefixsrc/,foo bar) -->
返回值为"src/foosrc/bar"。
3. vpath %.h../headers
Makefile中出现的.h文件:如果不能再当前目录中找到,则到目录"../headers"下寻找.
vpath与VPATH的区别在于后者指定全局的搜索路径
4. $(MAKE) -C$(STD_PERIPH_LIB)
make的递归执行-->4.6GNUmake中文手册-v3.8
当前目录下存在外设库文件,在编译的时候先对子目录进行编译.
它等价于命令
lib:
cd $(STD_PERIPH_LIB) && $(MAKE)
5. $@:代表规则中的目标文件名:可以使用@-->at 代表目标的意思
$%:规则的目标文件是一个静态库文件时,代表静态库的一个成员名.
$<:规则的第一个依赖文件名.
$^:规则的所有依赖文件列表.
$(*D): 代表"茎"中的目录部分
$(*F): 代表"茎"中的文件名部分
6. find ./ -name'*~' | xargs rm -f
当你尝试用rm 删除太多的文件,你可能得到一个错误信息:
/bin/rm Argument list too long. 用xargs 去避免这个问题
7. 将make调试信息保存到txt文档
make -d > 1.txt
8. main.c 中#include"defs.h"
命令 gcc -M main.c
out: main.o:main.c defs.h
若不需要依赖关系中不考虑标准头文件时
使用gcc -MM main.c
9. := 与 += 区别
:= --> 立即展开
+= --> 立即展开或延迟展开
对于所有条件语句均采用立即展开
10. 将make输出的错误信息输出到文件1.txt
make 2>1.txt
11. 从Shell获得当前文件夹位置
ROOT=$(shell pwd)
12. 包含依赖文件时,不要放在所有目标的前面,如下所示
其中原因就是make依然会将dep文件作为目标,从而后面的目标无法得到运行。
四、熟悉gcc编译
Gcc编译学习是一个长期的过程,主要参考文档见安装目录下的Documation中的所有pdf和readme.txt。下面是简短的学习笔记。
1. 参数解释:
-g(level): 在本地操作系统产生指定格式的调试消息(stabs,COFF,XCOFF,DWARF 2)
-ggdb(level): 产生gdb格式的调试信息
-gstabs(level):产生stabs格式的调试信息,不包括GDB扩展.-->BSD.
(level)-->默认级别为2
0:不产生调试信息
1:产生最少的调试信息
3:包括额外的信息,包括在程序中出现的宏定义等.
-gstabs+,-gcoff,-gxcoff,-gxcoff+,-gdwarf-version,-gvms
-c
只激活预处理,编译和汇编,生成obj文件.还需要通过ld链接器对所有
.o文件进行链接才能生成执行文件.
-S
只激活预处理和编译把文件汇编到汇编代码,相当于将程序生成.s文件.
-E
只进行预处理
-o
定制目标名称,缺省情况下生成a.out文件. gcc -ohello.exe test.c
-D(macro)
相当于#define macro 1比如DDEBUG相当于将DEBUG的宏定义为1
-C
在预处理的时候不删除注释信息.
-M
生成文件的关联信息,就可以知道源代码依赖了那些头文件.
-MM
同上,忽略#include造成的依赖关系
-MD
和-M相当,但是输出导入到.d文件中. gcc -MDtest.c 输出依赖关系
放在test.d文件里
-MF file
生成指定的默认依赖文件.
-MMD
和-MM相当,忽略#include造成的依赖关系
-l(library)
用于指定编译的时候使用的库. gcc-lgtk test.c 则程序使用gtk库进
行编译.不过需要注意的是gcc库一般以lib(name).a来命名库文件,使用
-l参数导入库文件的时候,直接使用-lname来引入,lib被省略.
-L(dir)
指定库文件所在的文件夹
-T script
指定空间分配文件
参考文档:GCC参数详解
2. -ffunction-sections,-fdata-sections:
因为GCC链接操作以section作为最小的处理单元,只要一个section中有某个符号被引用,该section就会被加入。如果我们的某个.c程序中所有function都加入同一个section.则如果用到这个.c生成的.o的其中任何一个function.则必须将所有function(符号)加入其中。如此,则使用-ffunction-sections 和 -fdata-sections将每个符号创建为一个sections. sections名与function,data名保持一致。则在link阶段,-Wl,--gc-sections 申明去掉不用的section。就可以去掉没用的function(符号)了。可以减少code size。
3. 调用标准库函数printf的编译错误。
a. 需要在选项中需要添加—specs= nano.specs用来优化代码尺寸,它使用了新的newlib-nano代码库,具体可以参考Documation/readme.txt和源码中的README.nano说明文档,需要注意的上这个选项上链接选项。
b. 支持浮点数打印–u _printf_float,当然相应的代码尺寸会增大。
c. 将安装文件夹中的example里面的retarget.c复制到源代码目录
d. 尽管选项设置都没问题,但依然出现以下错误:
c:/program files/gnu tools arm embedded/4.72013q1/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../../arm-none-eabi/lib/armv7-m\libc_s.a(lib_a-sbrkr.o):In function `_sbrk_r':
sbrkr.c:(.text._sbrk_r+0xc): undefinedreference to `_sbrk'
c:/program files/gnu tools armembedded/4.72013q1/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../../arm-none-eabi/lib/armv7-m\libc_s.a(lib_a-closer.o):In function `_close_r':
closer.c:(.text._close_r+0xc): undefinedreference to `_close'
查阅libc.pdf文档内容,也就是说在实现printf函数的时候,需要系统提供其它函数的实现,libc.pdfàpage164, and chapter12 syscalls.
经查问题原因如下:
将编译顺序调换一下即可,第一条为正确的编译顺序。
五、建立定制的应用程序框架
以下为参考的应用框架,针对于小型嵌入式的平台程序开发,设计如下框架的原因:在嵌入式开发中,存在一个电路板(硬件平台)应用在多个项目的情况。
FRAMEà 所有项目的总目录,一般为所有项目的源程序目录
FRAME /PROJ: 所有项目的根目录
FRAME /HAL: 硬件平台目录,添加一块新PCB版,编写对应的底层的接口文件
FRAME /LIB: 库文件:主要有对应CPU的库文件和本身应用程序的库文件。
FRAME /BUILD: 编译工作空间
FRAME /BUILD /make.rule:为所有源文件共有的makefile
FRAME /BUILD /Makefile: 配置当前项目工程和编译
FRAME /BUILD/configs: 包含当前编译工程文件名
FRAME
├─PROJ
│ ├─PROJ2
│ └─PROJ1
├─HAL
├─LIB
│ ├─STM32F10xP(st官方提供的库文件v3.5)
│ ├─CMSIS (ARM官方提供的库文件)
│ └─COMMON
└─BUILD
│ Makefile
│ make.rule
└─configs
具体详见附件内容。
使用方法:可以进入Build目录,配置对应的项目工程,然后make即可;或者可以直接进入项目工程的目录,直接make同样可以。
针对STM32F10xp外设库的依赖规则做以下简要分析,供大家参考:
参考文档:
1. 驾驭Makefile 2.GNUMakev3.8
3. gcc.pdf, ld.pdf libc.pdf…and so on
4. http://zaidpirwani.com/1160/stm32f3discovery-arm-gcc-environment-on-windows-part-1/
http://zaidpirwani.com/1187/stm32f3discovery-basic-project-template-part-2