在linux下搭建STM32工程
转载自康神博客:http://blog.csdn.net/u013298300/article/details/50243935
在LINUX下开始一个STM32工程
一、安装工具
- 安装交叉编译工具链arm-none-eabi
- 安装make
- 安装vim
- 安装git
二、 如何工作
2.1 需要作的工作
- 下载stm32固件库
- 编写连接器脚本
- 编写makefile
2.2 工作分析
2.2.1 makefile分析
当我们开发STM32时,会有两部分源码,一部分是由意法半导体提供的固件库,一部分是我们自己编写的用户层代码。 在开发过程中,除非我们进行版本更新否则固件库是不会改变的。而用户代码是会在开发过程当中增删减的。这样,我们可以首先把不变的固件库编译成静态库,之后的每次编译过程中只需要编译用户代码然后再把已经编译好的固件静态库链接进来就可以了,这样做可以大大节省编译时间,因为固件库结构本身是比较庞大和复杂的。
2.2.2 链接器脚本分析
在总控makefile编译完用户代码之后,就会将静态固件库链接进来,形成最终的镜像文件。这个链接过程由是链接器脚本控制完成的。
连接器脚本主要是用于安排不同代码段在内存中的位置的,程序中的段属性可以大致分为只读的代码段(.textd段)和可读可写的数据段(.data段和.bss段)。而在嵌入式微控制器中,内存也主要分为两种:只读的FLASH和可读可写的RAM。我们可以把代码段放到FLASH中,而数据段放到RAM中。当然,如果RAM空间足够大的话,我们可以把整个镜像都放到RAM里面去。这里需要解释一下,一般来说我们烧录程序是烧录到FLASH内的,而不是RAM内,因为RAM是掉电易失的,而FLASH为非易失储存器。如果代码烧录到RAM内,在关机之后代码就没了。所以我们需要把整个镜像烧录到FLASH内,而后在BOOT阶段再拷贝那些原本被链接器安排到RAM的代码段拷贝到RAM里面去。总而言之,链接器脚本主要完成的工作是:安排好不同.o文件不同段所在的位置,并且解决符号引用问题。
(注:关于链接的详细介绍,可以看我的另外一篇博文)
2.3 分析结果
我们的工程应该需要编写:
* 负责固件库编译的makefile
* 负责用户代码编译和链接生成最终镜像的makefile
* 负责链接过程中安排各个程序段的链接器脚本
三、开始工作
3.1 创建文工程目录
工程目录结构如图所示
|---project
|---arch
|---debug
|---doc
|---include
|---ldscripts
|---lib
|---OS
|---src
下载固件库到lib文件夹中去并且解压备用
cd lib wget www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/firmware/stm32f4_dsp_stdperiph_lib.zip unzip stm32f4_dsp_stdperiph_lib.zip -d stm32f4_dsp_stdperiph_lib
###3.2 编写连接器脚本 首先创建一个.lds文件并且编辑之
cd ldscripts/ && touch STM32F407.lds && vim STM32F407.lds
定义机器架构为ARM,入口符号为Reset_Handler
1 OUTPUT_ARCH(arm) 2 ENTRY(Reset_Handler)
定义RAM区和FLASH区的起始地址与长度
3 MEMORY
4 {
5 RAM : ORIGIN = 0x20000000, LENGTH = 112K
6 FLASH : ORIGIN = 0x08000000, LENGTH = 1M
7 }
8
定义各个段的位置。
为了加快程序运行速度,将中断向量表所在的isr段放到flash内,其他所有段都放到RAM内。并且安排整个镜像的加载地址是连续摆放的,这样可以避免镜像中出现空洞
9 SECTIONS
10 {
11 . = ALIGN(4);
12
13 .isr_vectors :
14 {
15 KEEP(\*(.isr_vectors))
16 . = ALIGN(4);
17 _eisr = .;
18 } > FLASH
19
20 .text : AT (_eisr)
21 {
22 _stext = .;
23 \*(.text)
24 . = ALIGN(4);
25 _etext = .;
26 } > RAM
27
28 .data : AT (__eisr+SIZEOF(.text))
29 {
30 _sdata = .;
31 \*(.data\*)
32 . = ALIGN(4);
33 _edata = .;
34 } > RAM
35
36 .bss :
37 {
38 . = ALIGN(4);
39 _sbss = .;
40 \*(.bss)
41 . = ALIGN(4);
42 _ebss = .;
43 } > RAM
44
45 .stack :
46 {
47 . = ALIGN(4);
48 _sstack = .;
49 \*(.stack);
50 . = ALIGN(4);
51 _estack = .;
52 } > RAM
53 }
3.3 编写Makefile
整个工程目录下,只有src、lib、arch三个子目录下有于源文件需要编译。为了结构层次清晰明了,这里采用总控Makefile配合各个源码子目录下Makefile的方式来完成整个编译过程。所以整个工程内一共有四份Makefile:
* 工程根目录下的总控Makefile
* src、lib、arch 子目录下的Makefile
在开始编写之前,先在工程目录和src、lib、arch目录下分别都创建一个名为Makefile的文件,再在工程目录下创建一个rules.mk文件,用于提炼四个Makefile文件中的相同代码。
cd project && touch Makefile src/Makefile lib/Makefile arch/Makefile
####3.3.1 总控Makfile 总控Makefile的主要职责是定义所有Makefile都要使用到的全局变量,并且控制各个子目录下的Makefile行为。
1 #定义通用符号 2 Q = @ 3 TypeOfMCU = STM32F40_41xxx 4 PROJNAME = STM32F407 5
定义各个通用符号,Q = @表示编译过程中执行的命令不显示出来。TypeOfMCU定义了处理器系列。PROJNAME则为工程名字。
6 #定义源码目录 7 TOPDIR = $(shell pwd) 8 LIBDIR = $(TOPDIR)/lib/stm32f4_dsp_stdperiph_lib 9 DRIVER_LIB_DIR = $(LIBDIR)/STM32F4xx_DSP_StdPeriph_Lib/Libraries/STM32F4xx_StdPeriph_Driver 10 CMSIS_LIB_DIR = $(LIBDIR)/STM32F4xx_DSP_StdPeriph_Lib/Libraries/CMSIS 11 ARCHDIR = $(TOPDIR)/arch 12 13 USERSRC = $(TOPDIR)/src 14 DRIVER_LIB_SRC = $(DRIVER_LIB_DIR)/src 15 CMSIS_SRC = $(CMSIS_LIB_DIR)/Device/ST/STM32F4xx/Source/Templates
为了方便起见,这里定义了各个源码目录,以免每次都要重写各个路径。
16 #定义交叉编译器 17 CROSS_COMPILE = arm-none-eabi- 18 CC = $(CROSS_COMPILE)gcc 19 LD = $(CROSS_COMPILE)ld 20 AR = $(CROSS_COMPILE)ar 21 OBJCOPY = $(CROSS_COMPILE)objcopy
这里需要定义交叉编译器,一来方便读写,二来可以方便以后拓展,如果换到其它的编译器,只需要在这里修改即可。
23 INCLUDE = -I $(TOPDIR)/inc 24 INCLUDE += -I $(CMSIS_LIB_DIR)/Include 25 INCLUDE += -I $(DRIVER_LIB_DIR)/inc 26 INCLUDE += -I $(CMSIS_LIB_DIR)/Device/ST/STM32F4xx/Include 27 28 OBJCFLAGS = --gap-fill=0xff 29 CFLAGS = $(INCLUDE) -g -O2 -Wall -mcpu=cortex-m4 -mthumb 30 31 #编译宏选项 32 CFLAGS += -D$(TypeOfMCU) 33 CFLAGS += -DVECT_TAB_FLASH 34 CFLAGS += -D"assert_parm(expr)=((void)0)" 35 CFLAGS += -DUSE_STDPERIPH_DRIVER 36 ARFLAGS = cr 37 LDFLAGS = -Bstatic -T $(TOPDIR)/ldscripts/$(PROJNAME).lds -N 38
以上定义了编译选项、链接选项,格式转化选项。因为在这个工程里大量使用了GNU make的隐含依赖链,所以编译选项必须定义为CFLAGS。
39 LIBS = $(LIBDIR)/libstm32.a 40 LIBS += $(USERSRC)/libapp.a 41 OBJS := $(ARCHDIR)/startup.o 42 43
这三句定义了lib、src、arch目录下的Makefile生成的目标。总控Makefile通过控制执行各个子目录下的Makefile从而得到这三个目标,最后将这三个目标链接形成最终的镜像。
44 export 45 46 .PHONY:all $(PROJNAME).bin $(PROJNAME).elf $(LIBS) $(OBJS)
第44行的export将所有定义的变量导出,以便各个子目录的Makefile使用。第46行则定义了将各个目标声明为伪目标,从而能地在每次执行make时强制检测检测各个子目录下的依赖关系变化。
47 all: $(PROJNAME).bin 48 49 $(PROJNAME).bin : $(PROJNAME).elf 50 $(OBJCOPY) $(OBJCFLAGS) @@bodylt; $@ 51 52 $(PROJNAME).elf : $(OBJS) $(LIBS) 53 $(LD) $(LDFLAGS) $(OBJS) \ 54 --start-group $(LIBS) --end-group -o $@ 55 56 $(OBJS) $(LIBS): 57 $(Q) make -C $(dir $@) 58
总控Makefile最终生成的目标是(PROJNAME).elf通过格式转化得来。而(OBJS) (OBJS)和$(LIBS)都由相应子目录下的Makefile生成。
3.3.2 src子目录Makefile
src/Makefile的唯一任务就是将src下的所有.c文件编译成为一个静态库libapp.a。具体代码如下:
1 SRC = $(wildcard $(USERSRC)/*.c) 2 3 OBJ = $(SRC:%.c=%.o) 4 DEP = $(SRC:%.c=%.d) 5 6 .PHONY:libapp.a 7 libapp.a : $(DEP) $(OBJ) 8 $(AR) $(ARFLAGS) $(USERSRC)/$@ $(OBJ) 9 10 sinclude $(TOPDIR)/rules.mk 11 sinclude $(DEP)
该Makefile比较简单,唯一需要说明的是第10行和第11行。第10行包含了工程目录下的rules.mk文件,第11行则包含了所有与每个.c文件对应的.d文件。rules.mk用于生成.d文件,rules.mk会为每个.c文件生成其对应的.d文件,.d文件记录了利用.c文件编译成.o文件的依赖关系。.o文件由GNU Make的隐含规则根据.d文件中的依赖关系编译生成,而最终生成libapp.a时只需要将所有.o文件打包即可。rules.mk代码如下:
1 %.d : %.c 2 $(Q) set -e;rm -f $@;\ 3 $(CC) -MM $(CFLAGS) @@bodylt; > $@.$$;\ 4 sed 's/\($(notdir $*)\)\.o[ :]*/\1.o $(notdir $@) : /g'
3.3.3 arch子目录Makefile
arch子目录下的Makefile与src/Makefile原理上一致,这里不做分析地列出代码如下:
1 obj = $(notdir $(OBJS)) 2 dep = $(obj:%.o=%.d) 3 4 .PHONY:all 5 all: $(dep) $(obj) 6 7 sinclude $(TOPDIR)/rules.mk 8 sinclude $(dep)
3.3.4 lib子目录Makefile
arch子目录下的Makefile与src/Makefile原理上一致,这里不做分析地列出代码如下:
1 LIBSRC = $(wildcard $(DRIVER_LIB_SRC)/*.c) 2 LIBSRC += $(wildcard $(CMSIS_SRC)/*.c) 3 4 LIBOBJ = $(LIBSRC:%.c=%.o) 5 LIBDEP = $(LIBSRC:%.c=%.d) 6 7 libstm32.a: $(LIBDEP) $(LIBOBJ) 8 $(Q) $(AR) $(ARFLAGS) $(LIBDIR)/$@ $(LIBOBJ) 9 11 sinclude $(LIBDEP)
3.3.5 Makefile总结
该工程下的GNU make文件一共有五分,包括四份Makefile和一份rules.mk,其中工程目录下的Makefile作为总控Makefile,其它Makefile作为子Makefile被总控Makefile控制执行。每个Makefile的责任都是生成一个子模块,而总控Makefile的责任是将所有子模块链接成为最终的镜像文件。当用户在工程目录下键入一个make并且摁下回车键时,总控Makefile依次控制进入各个子目录并且在子目录下执行make指令,每个子目录下执行make指令时,会调用rules.mk生成.d文件,再根据.d文件描述的依赖关系去将该目录下的所有.c文件编译成.o文件,最后利用.o文件生成该子Makefile负责的模块。当所有子目录下的make指令都成功执行后,得到startup.o
libapp.a libstm32.a三个模块,总控Makefile这时通过LD将三个模块链接,并且进行格式转化,就得到了最终的.bin镜像。
在linux下搭建STM32工程的更多相关文章
- [编译] 3、在Linux下搭建51单片机的开发烧写环境(makefile版)
星期二, 10. 七月 2018 01:01上午 - beautifulzzzz 一.SDCC(Small Device C Compiler)编译环境搭建 SDCC是一个小型设备的C语言编译器,该编 ...
- .Net Core实战教程(一):Linux下搭建项目
.Net Core实战教程(一):Linux下搭建项目 附言 .net core 1.0的时候就开始关注了,一直没有用于项目.真正用于项目我是2.0开始使用的.这几年也总结出一些经验.最近有空就写出来 ...
- [编译] 7、在Linux下搭建安卓APP的开发烧写环境(makefile版-gradle版)—— 在Linux上用命令行+VIM开发安卓APP
April 18, 2020 6:54 AM - BEAUTIFULZZZZ 目录 0 前言 1 gradle 安装配置 1.1 卸载系统默认装的gradle 1.2 下载对应版本的二进制文件 1.3 ...
- Linux下开发STM32单片机
一开始学习51单片机就是用的MDK这个IDE软件,IDE软件虽然看起来直观好像更加容易入门(因为有界面看起来很形象),但是实际上IDE却是向我们这些入门人员隐藏了背后真实存在的过程,让我们以为编译就是 ...
- [编译] 8、在Linux下搭建 stm8 单片机的开发烧写环境(makefile版)
目录 一.SDCC(Small Device C Compiler)编译环境搭建 1.1.下载 1.2.编译 1.3.测试 二.Hex2Bin+命令行烧写工具配置使用 2.1.下载工具安装配置 2.2 ...
- MongoDB学习笔记—Linux下搭建MongoDB环境
1.MongoDB简单说明 a MongoDB是由C++语言编写的一个基于分布式文件存储的开源数据库系统,它的目的在于为WEB应用提供可扩展的高性能数据存储解决方案. b MongoDB是一个介于关系 ...
- Linux下搭建个人网站
前不久在阿里买了一个服务器,然后开始第一次尝试搭建自己的个人网站.前端采用了bootstrap框架,后端采用的是PHP,数据库使用的是Mysql.新手第一次在linux下搭建遇见很多问题,在这里分享一 ...
- Linux下搭建PHP环境
转载于: http://www.uxtribe.com/php/405.html 该站下有系列PHP文章. 在Linux下搭建PHP环境比Windows下要复杂得多.除了安装Apache,PHP等软件 ...
- Java学习心得之 Linux下搭建Java环境
作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Java学习心得之 Linux下搭建Java环境 1.前言2.JDK安装3.配置环境变量4. ...
随机推荐
- 安卓程序员要拿到5000和1w的薪资,分别需要掌握哪些技术?
这个是我在逛知乎的时候发现的一个帖子,在这里小小的整理了一下,收集了一些评论,然后我分享出来,希望对自己还有同行有所帮助. 著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 链接 ...
- 5、Android Service测试
如果你在应用中使用了Service,你应该来测试这个Service来确保它正常工作.你可以创建仪表测试来验证Service的行为是否正确:比如,service保存和返回有效的数值并正常的处理数据. A ...
- 线程在Linux中的实现
早在以前,我们就知道,CPU调度的基本单位是线程,而进程是拥有资源的基本单位,进程是用进程描述符表示的,那么线程是怎么实现和表示的呢? 线程机制是现代编程技术中常用的一种抽象概 ...
- java模拟链表
java语言不存在指针,但是我们仍可以用相应的逻辑模拟链表的实现,下面这段代码就是我的一个小伙伴实现的: package com.brucezhang.test; public class ...
- JavaI/O体系详解
Java中IO操作主要是指使用Java进行输入,输出操作,Java中所有的IO操作类都存放在Java.io包中,在使用时需要导入此包. 在整个Java.io包中最重要的就是5个类和一个接口.5个类指的 ...
- HDFS追本溯源:体系架构详解
Hadoop是一个开发和运行处理大规模数据的软件平台,是Apache的一个用Java语言实现开源软件框架,实现在大量计算机组成的集群中对海量数据进行分布式计算.用户可以在不了解分布式底层细节的情况下, ...
- pyinstaller相关错误
http://blog.csdn.net/pipisorry/article/details/50620495 目录结构 将主文件testMain.py转换成exe可执行文件 主文件调用了自定义包 ...
- Java中类的创建及类与对象的关系
//import java.util.Scanner; //创建一个类 class Person{ //属性和方法的定义不是必须的 //属性 String name ; int age ; //方法 ...
- Cocos2D:塔防游戏制作之旅(七)
用这3个变量,你可以创建多种不同类型的炮塔,它们可以有着不同的攻击属性,比如长距离重型攻击力,但是慢速攻击的炮塔,或者是渴望快速攻击但是攻击范围近的炮塔. 最后,代码包括了一个draw方法,它在炮塔周 ...
- Andoird Crash的跟踪方法,使用腾讯Bugly来捕捉一些疑难杂症,让我们APP稳定上线
Andoird Crash的跟踪方法,使用腾讯Bugly来捕捉一些疑难杂症,让我们APP稳定上线 我们在开发中常常会注意到一些Crash,这正是很头疼的,而且Crash会带来很多意想不到的状态,很恶心 ...