穿越机源码剖析 [1]源码编译 [转载lofter乐山]
穿越机源码剖析 [1]源码编译
穿越机源码剖析 [1]源码编译
--------- 转载请注明出处
-------- 更多笔记请访问:merafour.blog.163.com
-------- 2016-9-6.冷月追风
-------- email:merafour@163.com
-------- 骑行也是一种修行
关键字:开源飞控、多轴飞行器、四轴、穿越机、CC3D、F3飞控
1. 源码编译
1.1 Makefile
1.2 startup
1.3 stm32_*.ld
1.4 target.h
在未特别说明的情况下,这里所说的穿越机均指运行Cleanflight软件的多轴飞行器。如 CC3D、NAZE32、F3等。编译环境为 ubuntu系统,或其衍生系统如xubuntu。
1. 源码编译
拿到源码我们要做的第一件事就是编译代码了。编译过程根据你所使用的操作系统各有不同。我个人习惯于在linux环境下执行make进行编译。
通过命令 “git clone https://github.com/cleanflight/cleanflight.git”可以在linux环境将源码从网络下载到本地。下载完成之后源码放在cleanflight目录中。其源码目录中包含如下内容:
build_docs.sh CONTRIBUTING.md docs fake_travis_build.sh JLinkSettings.ini lib LICENSE Makefile media Notes.md obj README.md src support Vagrantfile
我的源码是编译过的,所以包含 obj目录。下面我们就来分析它的第一个Makefile 文件。
17 # The target to build, see VALID_TARGETS below
18 TARGET ?= NAZE
19
20 # Compile-time options
21 OPTIONS ?=
22 export OPTIONS
23
24 # Debugger optons, must be empty or GDB
25 DEBUG ?=
26
27 # Serial port/Device for flashing
28 SERIAL_DEVICE ?= $(firstword $(wildcard /dev/ttyUSB*) no-port-found)
29
30 # Flash size (KB). Some low-end chips actually have more flash than advertised, use this to override.
31 FLASH_SIZE ?=
32
33 ###############################################################################
34 # Things that need to be maintained as the source changes
35 #
36
37 FORKNAME = cleanflight
38
39 64K_TARGETS = CJMCU
40 128K_TARGETS = ALIENFLIGHTF1 CC3D NAZE OLIMEXINO RMDO SPRACINGF1OSD
41 256K_TARGETS = ALIENFLIGHTF3 CHEBUZZF3 COLIBRI_RACE EUSTM32F103RC IRCFUSIONF3 LUX_RACE MOTOLAB PORT103R RCEXPLORERF3 SPARKY SPRACINGF3 SPRACINGF3EVO SPRACINGF3MINI STM32F3DISCOVERY SPRACINGF3OSD
42
43 F3_TARGETS = ALIENFLIGHTF3 CHEBUZZF3 COLIBRI_RACE IRCFUSIONF3 LUX_RACE MOTOLAB RCEXPLORERF3 RMDO SPARKY SPRACINGF3 SPRACINGF3EVO SPRACINGF3MINI STM32F3DISCOVERY SP RACINGF3OSD
44
45 VALID_TARGETS = $(64K_TARGETS) $(128K_TARGETS) $(256K_TARGETS)
46
47 VCP_TARGETS = CC3D ALIENFLIGHTF3 CHEBUZZF3 COLIBRI_RACE LUX_RACE MOTOLAB RCEXPLORERF3 SPARKY SPRACINGF3EVO SPRACINGF3MINI STM32F3DISCOVERY SPRACINGF1OSD SPRACINGF3 OSD
48 OSD_TARGETS = SPRACINGF1OSD SPRACINGF3OSD
从这一段脚本中我们可以知道cleanflight支持哪些飞控。截止到目前在我们看到只支持 F1跟 F3两个系列的 MCU。其中TARGET是我们编译的目标平台,默认为NAZE。64K_TARGETS是目标平台的 Flash大小。正如我们所看到的,CJMCU的Flash是最小的为 64K,但其实我们编译出来之后:
merafour@TheTravels:~/workspace/cleanflight$ ls -lh obj/cleanflight_CJMCU.bin
-rwxrwxr-x 1 merafour merafour 60K 9月 6 17:29 obj/cleanflight_CJMCU.bin
merafour@TheTravels:~/workspace/cleanflight$
代码大小为 60KB。当然相对的它的功能也有限。如果你想做一个低成本的四轴,那么你显然可以考虑CJMCU。F3_TARGETS后面列举的是基于 F3芯片的硬件平台,因为是 M4带硬件浮点,虽然都是 72M主频,所以能够获得更快的运算速度,可以实现更多的功能以及获得更快的响应。不过基于我个人的研究,通过 SRAM提速也可以获得更快的运算速度,前提是你的 SRAM剩余有足够的空间。
后面的VCP_TARGETS是支持 USB虚拟串口的飞控。因为飞控跟上位机连接通常使用串口,而 STM32,不论是 F1还是 F3现在都带有 USB接口,而使用 USB虚拟串口一方面可以减少硬件成本,另一方面可以缩小 PCB尺寸。当然,这个在玩具四轴里面会特别明显,毕竟做玩具的就算是一块钱的成本那都是能省就省。
然后:
50 # Configure default flash sizes for the targets
51 ifeq ($(FLASH_SIZE),)
52 ifeq ($(TARGET),$(filter $(TARGET),$(64K_TARGETS)))
53 FLASH_SIZE = 64
54 else ifeq ($(TARGET),$(filter $(TARGET),$(128K_TARGETS)))
55 FLASH_SIZE = 128
56 else ifeq ($(TARGET),$(filter $(TARGET),$(256K_TARGETS)))
57 FLASH_SIZE = 256
58 else
59 $(error FLASH_SIZE not configured for target $(TARGET))
60 endif
61 endif
这一段就是设置FLASH_SIZE的值。接下来:
65 # Working directories
66 ROOT := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
67 SRC_DIR = $(ROOT)/src/main
68 OBJECT_DIR = $(ROOT)/obj/main
69 BIN_DIR = $(ROOT)/obj
70 CMSIS_DIR = $(ROOT)/lib/main/CMSIS
71 INCLUDE_DIRS = $(SRC_DIR) \
72 $(ROOT)/src/main/target
73 LINKER_DIR = $(ROOT)/src/main/target
74
75 # Search path for sources
76 VPATH := $(SRC_DIR):$(SRC_DIR)/startup
77 USBFS_DIR = $(ROOT)/lib/main/STM32_USB-FS-Device_Driver
78 USBPERIPH_SRC = $(notdir $(wildcard $(USBFS_DIR)/src/*.c))
79
80 CSOURCES := $(shell find $(SRC_DIR) -name '*.c')
这里设置相关源码的路径以及编译后bin文件的路径。$(ROOT)就是你的源码路径,在我这里是:”~/workspace/cleanfligh”。并且把USB相关源文件列表保存在USBPERIPH_SRC中。 CMSIS_DIR实际上用的是ST官方提供的库。因为使用ST的库,那么相对来说比较容易一直到如 F4等平台上。实际上如果你下载cleanfligh早期源码你会发现它用就是用的keil编译器。
82 ifeq ($(TARGET),$(filter $(TARGET),$(F3_TARGETS)))
83 # F3 TARGETS
84
85 STDPERIPH_DIR = $(ROOT)/lib/main/STM32F30x_StdPeriph_Driver
86
87 STDPERIPH_SRC = $(notdir $(wildcard $(STDPERIPH_DIR)/src/*.c))
88
89 EXCLUDES = stm32f30x_crc.c \
90 stm32f30x_can.c
91
92 STDPERIPH_SRC := $(filter-out ${EXCLUDES}, $(STDPERIPH_SRC))
93
94 DEVICE_STDPERIPH_SRC = \
95 $(STDPERIPH_SRC)
96
97
98 VPATH := $(VPATH):$(CMSIS_DIR)/CM1/CoreSupport:$(CMSIS_DIR)/CM1/DeviceSupport/ST/STM32F30x
99 CMSIS_SRC = $(notdir $(wildcard $(CMSIS_DIR)/CM1/CoreSupport/*.c \
100 $(CMSIS_DIR)/CM1/DeviceSupport/ST/STM32F30x/*.c))
101
102 INCLUDE_DIRS := $(INCLUDE_DIRS) \
103 $(STDPERIPH_DIR)/inc \
104 $(CMSIS_DIR)/CM1/CoreSupport \
105 $(CMSIS_DIR)/CM1/DeviceSupport/ST/STM32F30x
106
107 ifeq ($(TARGET),$(filter $(TARGET),$(VCP_TARGETS)))
108 INCLUDE_DIRS := $(INCLUDE_DIRS) \
109 $(USBFS_DIR)/inc \
110 $(ROOT)/src/main/vcp
111
112 VPATH := $(VPATH):$(USBFS_DIR)/src
113
114 DEVICE_STDPERIPH_SRC := $(DEVICE_STDPERIPH_SRC)\
115 $(USBPERIPH_SRC)
116
117 endif
118
119 LD_SCRIPT = $(LINKER_DIR)/stm32_flash_f303_$(FLASH_SIZE)k.ld
120
121 ARCH_FLAGS = -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fsingle-precision-constant -Wdouble-promotion
122 DEVICE_FLAGS = -DSTM32F303xC -DSTM32F303
123 TARGET_FLAGS = -D$(TARGET)
124
125 else ifeq ($(TARGET),$(filter $(TARGET),EUSTM32F103RC PORT103R))
在这里根据你的硬件平台做不同的设置。如库肯定用F30x的,然后LD_SCRIPT很重要,这个是链接脚本,它决定的你的函数被编译到哪段地址空间。当然我们都知道 STM32的其实地址是0x08000000,但如果你使用了bootloader那么你显然不可以再链接到这个地址了,因为0x08000000地址存放的是bootloader,所以你需要为 bootloader让出一部分空间如0x08004000,这个时候你就需修改链接文件。然后ARCH_FLAGS是编译参数,”-mcpu=cortex-m4”一定要看得懂,F303是M4核。最后还有两个” -D”,实际上也是编译参数,用来定义宏。因为cleanfligh支持很多硬件平台,而这些宏正是用来控制我们的代码编译到哪个飞控板上使用的。而下面的 F103同样需要做这些设置。
当这些设置完成之后:
202 ifneq ($(FLASH_SIZE),)
203 DEVICE_FLAGS := $(DEVICE_FLAGS) -DFLASH_SIZE=$(FLASH_SIZE)
204 endif
205
206 TARGET_DIR = $(ROOT)/src/main/target/$(TARGET)
207 TARGET_SRC = $(notdir $(wildcard $(TARGET_DIR)/*.c))
208
209 # VARIANTS
210 ifeq ($(TARGET),ALIENFLIGHTF1)
211 # ALIENFLIGHTF1 is a VARIANT of NAZE
212 TARGET_FLAGS := $(TARGET_FLAGS) -DNAZE -DALIENFLIGHT
213 TARGET_DIR = $(ROOT)/src/main/target/NAZE
214 endif
215 ifeq ($(TARGET),CHEBUZZF3)
216 # CHEBUZZ is a VARIANT of STM32F3DISCOVERY
217 TARGET_FLAGS := $(TARGET_FLAGS) -DSTM32F3DISCOVERY
218 endif
219 ifeq ($(TARGET),$(filter $(TARGET),RMDO IRCFUSIONF3))
220 # RMDO and IRCFUSIONF3 are a VARIANT of SPRACINGF3
221 TARGET_FLAGS := $(TARGET_FLAGS) -DSPRACINGF3
222 endif
223
224 # OSDs
225 ifeq ($(TARGET),$(filter $(TARGET),$(OSD_TARGETS)))
226 TARGET_FLAGS := $(TARGET_FLAGS) -DOSD
227 endif
TARGET_DIR就是我们板子的源文件路径,但实际上我们通常只需要一个 .h文件即可,用来对飞控的资源进行定义。这一点跟 PX4是一样的,PX4也是硬件资源单独定义。当然这并不是说增加一个资源定义文件就可以增加一块新的硬件平台了,公共部分源码你还需要修改,除非你是基于一块已经被支持的飞控板稍作修改。
当我们顺着 Makefile继续往下阅读,我们会看到很多如:SYSTEM_SRC、FC_COMMON_SRC等是源文件列表。我们也会看到如:NAZE_SRC、CJMCU_SRC等,其对应的就是我们的目标硬件。我们可以在这里拿两个平台过来对比:
472 CJMCU_SRC = \
473 startup_stm32f10x_md_gcc.S \
474 $(STM32F10x_COMMON_SRC) \
475 drivers/accgyro_mpu.c \
476 drivers/accgyro_mpu6050.c \
477 drivers/compass_hmc5883l.c \
478 drivers/pwm_mapping.c \
479 drivers/pwm_output.c \
480 drivers/pwm_rx.c \
481 drivers/sound_beeper_stm32f10x.c \
482 drivers/timer.c \
483 drivers/timer_stm32f10x.c \
484 hardware_revision.c \
485 flight/gtune.c \
486 blackbox/blackbox.c \
487 blackbox/blackbox_io.c \
488 $(FC_COMMON_SRC) \
489 $(SYSTEM_SRC)
490
491 CC3D_SRC = \
492 startup_stm32f10x_md_gcc.S \
493 $(STM32F10x_COMMON_SRC) \
494 drivers/accgyro_mpu.c \
495 drivers/accgyro_spi_mpu6000.c \
496 drivers/barometer_bmp085.c \
497 drivers/barometer_ms5611.c \
498 drivers/bus_spi.c \
499 drivers/compass_hmc5883l.c \
500 drivers/display_ug2864hsweg01.c \
501 drivers/flash_m25p16.c \
502 drivers/inverter.c \
503 drivers/light_ws2811strip.c \
504 drivers/light_ws2811strip_stm32f10x.c \
505 drivers/pwm_mapping.c \
506 drivers/pwm_output.c \
507 drivers/pwm_rx.c \
508 drivers/serial_softserial.c \
509 drivers/sonar_hcsr04.c \
510 drivers/sound_beeper_stm32f10x.c \
511 drivers/timer.c \
512 drivers/timer_stm32f10x.c \
513 io/flashfs.c \
514 $(HIGHEND_SRC) \
515 $(FC_COMMON_SRC) \
516 $(SYSTEM_SRC) \
517 $(VCP_SRC)
最明显的区别是传感器不一样了,另外 CC3D支持串口,超声波模块,USB串口,HIGHEND_SRC中还包含有 GPS、telemetry等模块,而这些都是CJMCU_SRC所没有的功能。去掉了部分功能编译出来的代码自然就更小。那么可想而知, F3平台由于具有更大空间的 Flash以及更强的运算能力,那么也就拥有比 CC3D更多的功能。由于支持的硬件平台多样,设置源码列表占据相当大的篇幅。
当所有源码列表都设置好了以后,会有不小篇幅用于设置编译参数,如:
826 # Tool names
827 CC := $(CCACHE) arm-none-eabi-gcc
828 OBJCOPY := arm-none-eabi-objcopy
829 SIZE := arm-none-eabi-size
arm-none-eabi-gcc就是我们所使用的工具链。再往下:
900 # Things we will build
901 #
902 ifeq ($(filter $(TARGET),$(VALID_TARGETS)),)
903 $(error Target '$(TARGET)' is not valid, must be one of $(VALID_TARGETS))
904 endif
905
906 TARGET_BIN = $(BIN_DIR)/$(FORKNAME)_$(TARGET).bin
907 TARGET_HEX = $(BIN_DIR)/$(FORKNAME)_$(TARGET).hex
908 TARGET_ELF = $(OBJECT_DIR)/$(FORKNAME)_$(TARGET).elf
909 TARGET_OBJS = $(addsuffix .o,$(addprefix $(OBJECT_DIR)/$(TARGET)/,$(basename $($(TARGET)_SRC))))
910 TARGET_DEPS = $(addsuffix .d,$(addprefix $(OBJECT_DIR)/$(TARGET)/,$(basename $($(TARGET)_SRC))))
911 TARGET_MAP = $(OBJECT_DIR)/$(FORKNAME)_$(TARGET).map
912
913
914 ## Default make goal:
915 ## hex : Make filetype hex only
916 .DEFAULT_GOAL := hex
917
918 ## Optional make goals:
919 ## all : Make all filetypes, binary and hex
920 all: hex bin
921
922 ## binary : Make binary filetype
923 ## bin : Alias of 'binary'
924 ## hex : Make hex filetype
925 bin: $(TARGET_BIN)
926 binary: $(TARGET_BIN)
927 hex: $(TARGET_HEX)
Hex、 bin就是我们所需要的目标代码。其编译的详细过程为:
998 $(TARGET_HEX): $(TARGET_ELF)
999 $(OBJCOPY) -O ihex --set-start 0x8000000 $< $@
1000
1001 $(TARGET_BIN): $(TARGET_ELF)
1002 $(OBJCOPY) -O binary $< $@
1003
1004 $(TARGET_ELF): $(TARGET_OBJS)
1005 $(CC) -o $@ $^ $(LDFLAGS)
1006 $(SIZE) $(TARGET_ELF)
1007
1008 # Compile
1009 $(OBJECT_DIR)/$(TARGET)/%.o: %.c
1010 @mkdir -p $(dir $@)
1011 @echo %% $(notdir $<)
1012 @$(CC) -c -o $@ $(CFLAGS) $<
1013
1014 # Assemble
1015 $(OBJECT_DIR)/$(TARGET)/%.o: %.s
1016 @mkdir -p $(dir $@)
1017 @echo %% $(notdir $<)
1018 @$(CC) -c -o $@ $(ASFLAGS) $<
1019
1020 $(OBJECT_DIR)/$(TARGET)/%.o: %.S
1021 @mkdir -p $(dir $@)
1022 @echo %% $(notdir $<)
1023 @$(CC) -c -o $@ $(ASFLAGS) $<
熟悉Makefile的都知道$(TARGET_OBJS)最后通过 ” # Compile”后面的规则进行编译。而编译的源文件如 CC3D在CC3D_SRC列表中。
下面我们有几个源文件需要说明。