STM32高级开发(10)-搭架你自己的libopencm3工程

在上一篇中,我们介绍了下载和使用libopencm3固件库的官方例程,同时我们也介绍了官方历程中的makefile等文件结构和引用方式,不知道大家注意到没有,实际上在我们使用的makefile中的目标里,是有flash等通过调试器下载的操作的,但是由于在rules.mk的文件下存在一些错误,所以可能工作并不正常。而且在我们实际的使用中不会涉及到那么多的MCU型号和开发板,所以在这篇中我们就来教大家,改写官方样例工程的makefile文件,来搭架自己的工程。


下参考我的样板工程

首先,同样的,cd进入工作目录,下载我再GitHub上托管的工程,并更新下载其中libopencm3的自模块,并编译libopencm3库。

$ cd ~/workspace 
$ git clone https://github.com/zhengyangliu/libopencm3-my-example.git
$ cd ./libopencm3-my-example
$ git submodule init
$ git submodule update
$ cd ./libopencm3
$ make

我的样例工程结构及makefile介绍

在我设计我的工程结构的时候,考虑到我们大多数时候也就是使用一个开发板进行学习,所以我把源代码目录和makefile结构进行了简化,并修改了其中的一些功能,下面我们就来看看我的样例工程结构。

-libopencm3-my-example
|-blink\
|-buildtool\
|-cdcacm\
|-libopencm3\
|-usart_printf\
|-usart_stdio\
|-LICENSE
|-makefile
|-README.md

同官方样例相同libopencm3为固件库本体,readme为GitHub初始化的说明文件,这里我没有写所以是空的。LICENSE文件是我的样板工程的开源许可,为GPL2。makefile是我的F429disco的通用设置。buildtool中存放的rules.mk是libopencm3工程通用的makefile配置,还有就是ld文件,其他的2个文件则是用来实现codestyle的功能的,就是代码格式化(与eclipse的代码格式化功能相同)。最后主目录下其他的文件夹就是一些样例性子工程了,如:blink、cdcacm、usart_stdio等。

接下来我们通过blink子工程来分析这个样例工程中的makefile和其他编译功能,来为大家配置自己的开发板工程提供一个指南。


子工程目录下makefile

先来看看bink文件夹下的makefile:

OBJS += delay.o
BINARY = blink

OPT     := -Os
CSTD    := -std=c99
CXXSTD  := -std=c++98

include ../makefile
  • 第一个设定除工程主文件blink之外的.c文件的编译对象。这里我们在主函数中调用了一个由system tick功能写成的延时函数,所以此处要在OBJS变量后添加一个delay.o的生成对象。

  • 而后在BINARY出设置工程执行文件的名称。

  • 接下来的三个变量分别是,代码优化等级,C和CPP语言标准的设置,此处我们设置为不优化,和C99与C++98的语言标准。

  • 在文件最后调用上层的文件夹下的makefile。


顶层的makefile

我们再来看看子工程目录上层的makefile。

##
##  STM32F429i-DISCO 开发板make设置
##

##############################################################################
# 工程设置
TOPDIR  := ..
OBJS +=
#BINARY = usart_stdio
LDSCRIPT = $(TOPDIR)/buildtool/stm32f429i-discovery.ld

##############################################################################
# 芯片设置

LIBNAME     = opencm3_stm32f4
DEFS        += -DSTM32F4

FP_FLAGS    := -mfloat-abi=hard -mfpu=fpv4-sp-d16
ARCH_FLAGS  = -mthumb -mcpu=cortex-m4 $(FP_FLAGS)

################################################################################
# OpenOCD specific variables   OpenOCD调试器配置

OOCD             := openocd
OOCD_INTERFACE   := /usr/local/share/openocd/scripts/interface/stlink-v2.cfg
OOCD_TARGET      := /usr/local/share/openocd/scripts/target/stm32f4x_stlink.cfg

################################################################################
OPENCM3_DIR := $(TOPDIR)/libopencm3

include $(TOPDIR)/buildtool/rules.mk 
  • 在这个文件的第一个区块,有一些残留的变量赋值操作,并没有发生作用
(OBJS +=
#BINARY = usart_stdio

其他两个,前者用于后方设定一些文件结构的目录路径,后者用于指定工程编译时的ld文件。

  • 在文件第二个区块,我们设定了编译时的与MCU有关的参数,这是直接从官方的样例工程拷贝的,大家可以到官方的样例工程中查找自己的MCU型号并更具官方的样例改写。

  • 在第三个区块中,定义了OPENOCD的参数,包括他的启动指令,以及配置文件。

  • 在最后一个区块,我定义了libopencm3固件库的路径和引用了在buildtool文件夹下的makefile文件。


rules. mk

打开buildtool下的文件,我们来看

##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <[email protected]>
## Copyright (C) 2010 Piotr Esden-Tempski <[email protected]>
## Copyright (C) 2013 Frantisek Burian <[email protected]>
##
## This library is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This library is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this library.  If not, see <http://www.gnu.org/licenses/>.
##
## 2017.1.17  改写
## 作者:杨
## 

# 默认是不输出编译信息的,但如果使用 'make V=1' 则会显示所有编译调用
ifneq ($(V),1)
Q       := @
NULL        := 2>/dev/null
endif

###############################################################################
# Executables

PREFIX      ?= arm-none-eabi

CC      := $(PREFIX)-gcc
CXX     := $(PREFIX)-g++
LD      := $(PREFIX)-gcc
AR      := $(PREFIX)-ar
AS      := $(PREFIX)-as
OBJCOPY := $(PREFIX)-objcopy
OBJDUMP := $(PREFIX)-objdump
GDB     := $(PREFIX)-gdb

STFLASH = $(shell which st-flash)

OPT     ?= -Os
CSTD    ?= -std=c99
CXXSTD  ?= -std=c++98

###############################################################################
# Source files
OBJS        += $(BINARY).o

#如果未制定OPENCM3_DIR路径
ifeq ($(strip $(OPENCM3_DIR)),)
$(warning Cannot find libopencm3 library in the standard search paths.)
$(error Please specify it through OPENCM3_DIR variable!)
endif

ifeq ($(V),1)
$(info Using $(OPENCM3_DIR) path to library)
endif

DEFS        += -I$(OPENCM3_DIR)/include
LDFLAGS     += -L$(OPENCM3_DIR)/lib
LDLIBS      += -l$(LIBNAME)
LDSCRIPT    ?= $(BINARY).ld

SCRIPT_DIR  = $(OPENCM3_DIR)/scripts

###############################################################################
# C flags

TGT_CFLAGS  += $(OPT) $(CSTD) -g
TGT_CFLAGS  += $(ARCH_FLAGS)
TGT_CFLAGS  += -Wextra -Wshadow -Wimplicit-function-declaration
TGT_CFLAGS  += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes
TGT_CFLAGS  += -fno-common -ffunction-sections -fdata-sections

###############################################################################
# C++ flags

TGT_CXXFLAGS    += $(OPT) $(CXXSTD) -g
TGT_CXXFLAGS    += $(ARCH_FLAGS)
TGT_CXXFLAGS    += -Wextra -Wshadow -Wredundant-decls  -Weffc++
TGT_CXXFLAGS    += -fno-common -ffunction-sections -fdata-sections

###############################################################################
# C & C++ preprocessor common flags

TGT_CPPFLAGS    += -MD
TGT_CPPFLAGS    += -Wall -Wundef
TGT_CPPFLAGS    += $(DEFS)

###############################################################################
# Linker flags

TGT_LDFLAGS     += --static -nostartfiles
TGT_LDFLAGS     += -T$(LDSCRIPT)
TGT_LDFLAGS     += $(ARCH_FLAGS)
TGT_LDFLAGS     += -Wl,-Map=$(*).map
TGT_LDFLAGS     += -Wl,--gc-sections
ifeq ($(V),99)
TGT_LDFLAGS     += -Wl,--print-gc-sections
endif

###############################################################################
# Used libraries

LDLIBS      += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group

###############################################################################
# Debug flags

OOCDFLAGS := -f $(OOCD_INTERFACE) -f $(OOCD_TARGET)

###############################################################################
###############################################################################
###############################################################################

.SUFFIXES: .elf .bin .hex .list .map .images
.SECONDEXPANSION:
.SECONDARY:

all: elf

elf: $(BINARY).elf
bin: $(BINARY).bin
hex: $(BINARY).hex
list: $(BINARY).list

flash: $(BINARY).flash
stlink-flash: $(BINARY).stlink-flash
debug: $(BINARY).debug

style-code:
    sh $(TOPDIR)/buildtool/stylecode.sh

%.images: %.bin %.hex %.list %.map
    @#printf "*** $* images generated ***\n"

%.bin: %.elf
    @#printf "  OBJCOPY $(*).bin\n"
    $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin

%.hex: %.elf
    @#printf "  OBJCOPY $(*).hex\n"
    $(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex

%.list: %.elf
    @#printf "  OBJDUMP $(*).list\n"
    $(Q)$(OBJDUMP) -S $(*).elf > $(*).list

%.elf %.map: $(OBJS) $(LDSCRIPT)
    @#printf "  LD      $(*).elf\n"
    $(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $(*).elf

%.o: %.c
    @#printf "  CC      $(*).c\n"
    $(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).c

%.o: %.cxx
    @#printf "  CXX     $(*).cxx\n"
    $(Q)$(CXX) $(TGT_CXXFLAGS) $(CXXFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).cxx

%.o: %.cpp
    @#printf "  CXX     $(*).cpp\n"
    $(Q)$(CXX) $(TGT_CXXFLAGS) $(CXXFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).cpp

clean:
    @#printf "  CLEAN\n"
    $(Q)$(RM) *.o *.d *.elf *.bin *.hex *.srec *.list *.map generated.* ${OBJS} ${OBJS:%.o:%.d}

# 使用stlink驱动下载bin程序
%.stlink-flash: %.bin
    @printf "  ST-link FLASH  $<\n"
    $(Q)$(STFLASH) write $(*).bin 0x8000000

# 使用OpenOCD下载hex程序
%.flash: %.hex
    @printf "  OPEN_OCD FLASH $<\n"
    $(Q)$(OOCD) $(OOCDFLAGS) -c "program $(*).hex reset verify exit" 

# 使用GDB 通过sdtin/out管道与OpenOCD连接 并在main函数处打断点后运行
%.debug: %.elf
    @printf "  GDB DEBUG $<\n"
    $(Q)$(GDB) -iex 'target extended | $(OOCD) $(OOCDFLAGS) -c "gdb_port pipe"' \
    -iex 'monitor reset halt' -ex 'load' -ex 'break main' -ex 'c' $(*).elf

# a test to try to add a directory to gdb : it's seems that is effect.
debug2: $(BINARY).elf
    @printf "  GDB DEBUG TEST $<\n"
    $(Q)$(GDB) -iex 'target extended | $(OOCD) $(OOCDFLAGS) -c "gdb_port pipe"' \
    -iex 'monitor reset halt' -ex 'load' -d '/home/yang/workspace/blink_cm3/libopencm3/lib/cm3' $(BINARY).elf

.PHONY: images clean elf bin hex list debug stlink-flash style-code flash

-include $(OBJS:.o=.d)
  • 在最前方设定了一个编译时可选择的参数 V,在这个makefile的文件配置中,执行目标指令的时候是不在终端讯有任何显示的,如果我们想要看到执行过程中的指令,则需要我们make时,在指令的后方加入V=1即:
$ make V=1
$ make clean V=1

这样在执行make后,就可以看到make工具在终端中执行的指令了。

  • 而后文件的中部设定了编译器等繁杂的参数,大家可以自己阅读并学习理解,我们在这篇中不做具体分析。

  • 在文件的最后,生成的目标中,我改写了官方rules.mk文件的一些内容,我们来看看你有哪些最终目标:

    • images ,生成.hex .bin
    • clean ,清除工程中间及最终生成的文件
    • elf ,生成 .elf (也是默认的make目标)
    • bin , 略
    • hex , 略
    • list ,生成 .list文件(描述了.elf的代码结构,大家打开看看就明白了)
    • debug ,启动GDB调试程序并加载代码同时执行到main函数
    • stlink-flash , 使用stlink的驱动下载固件
    • style-code ,格式化子工程目录下的代码
    • flash , 使用OpenOCD下载固件

到这里,我们就把我的样例工程的makefile结构及文件结构介绍完毕了,接下来的我们来看看最后被改写的rules.mk文件是怎样产生效果的。

make目标指令分析

images 、elf 、bin、hex、list

对于这几个目标,我们可以类比于之前的关于makefile的教程,很容易就能理解,即使这里使用了%.hex的自动依赖,大家根据makefile的语法也很容易就能理解。

clean

同样也不必多讲,就是个系统自动搜索移除特定扩展名的指令

%.stlink-flash: %.bin
    @printf "  ST-link FLASH  $<\n"
    $(Q)$(STFLASH) write $(*).bin 0x8000000

此处STFLASH由文件上方提供,意为检测系统是否有st-flash这个指令,如果有就返回,没有则放回空。这里,由于我们之前的教程中安装过st-flash,所以该变量会被赋值为st-flash。

STFLASH = $(shell which st-flash)

在输入make stlink-flash指令后,make工具会执行stlink-flash write xx.bin 0x8000000的指令,来通过st-link驱动将工程的bin文件下载到MCU的0x8000000的地址,即STM32FLASH的起始地址处。

style-code

style-code:
    sh $(TOPDIR)/buildtool/stylecode.sh

style-code目标会调用执行buildtool目录下的stylecode.sh脚本,脚本将会执行astyle工具来格式化子工程目录下的源代码文件。astyle是Linux下的一个代码格式化工具,我们可以使用指令来安装(晕…我也忘了之前的教程交过大家没有,如果没有那就用指令安装上一个吧)。

$ sudo apt-get install astyle

stylecode.sh中包含着执行astyle的代码,首先它使用find指令搜索源代码文件,而后通过一个循环来为每个文件执行astyle格式化操作,在格式化操作中输入了astyle.conf文件,用来为astyle工具提供格式化规则,其中具体的参数设定大家可以自行百度学习,这里我们设定的是一组最常见的代码格式。

flash

%.flash: %.hex
    @printf "  OPEN_OCD FLASH $<\n"
    $(Q)$(OOCD) $(OOCDFLAGS) -c "program $(*).hex reset verify exit"

与stlink-flash相似,我们这里调用OPENOCD指令来通过,openOCD连接调试器来下载固件,这里我们重点讲一下调用时,配置文件后面的输入参数。其中 -c代表参数输入,其后跟着的“program $(*).hex reset verify exit“其实是一长串的序列指令,首先是烧写操作,而后校验文件正确性,最后复位MCU并退出OpenOCD。

总结

移植过程中的注意事项

大家在看完两个样例工程后,必然的就会开始想要配置自己的工程,不过在移植这些样例工程中我们必然会遇到一些问题,这里我就列出一些我当时遇到的或是觉得大家会遇到的情况来为大家提前的提个醒。

  • libopencm3固件库提供的MCU ld文件在它的lib目录下,我们只需要安装样例工程下的ld 文件设置ROM RAM大小 并调用该MCU主信号对应ld文件即可。

  • 大家可以将 样例工程下的makefile合并, 但一定要注意makefile和脚本下的路径,在移动工程文件结构后,有一些路径可能需要随之更改。

  • 在多个.c文件情况下,一定记得在OBJ变量中添加其.o文件,否则该目录下除BINARY变量的.c之外的文件不会被编译,结果就会报错。

  • 编译时,使用V=1设置来显示所有编译过程中的指令,观察他们的执行有助于学习makefile的执行过程。

学习libopencm3固件库的使用

由于libopencm3固件库中的函数名称输入参数及与我们之前使用的ST StdPeriph 库和 HAL 库不同,所以必然的我们要重新学习libopencm3 API的使用,不过别担心他们的差别不大,通过样例工程及说明书我们很快就能了解它的使用。大家可以在libopencm3文件夹下的doc文件夹下找到libopencm3通过doxygen工具生成的说明文档,使用网页打开doc目录下的index文件就可以阅读。

debug指令

说道最后,大家可能会注意到我们在上面的目标说明中漏掉了一个debug目标,其实我是故意的。因为在这所有的目标中debug它涉及到的东西和指令是最多的,所以我们将它放置在下一篇:”STM32高级开发(11)-使用GDB调试你的工程 ”中介绍,敬请期待吧~。

猜你喜欢

转载自blog.csdn.net/zhengyangliu123/article/details/54931449