[经验分享] SDK范例工程的命令行编译(blinky)

cruelfox   2021-5-5 17:03 楼主

  RSL10官方的开发环境是基于Ecipse的,因此SDK支持GCC没有悬念。下载 RSL10 的 pack 以后(这是一个压缩文件,直接用工具解开就是),可以见到 source\samples 目录下有许多范例,除了ON IDE工程文件,还提供了Keil, IAR的工程文件。本人习惯用命令行来手动编译,所以要将范例工程进行解剖。先拿最简单的 blinky 下手(想必就是LED闪烁功能):

proj.PNG   blink工程代码少,就只有一个 C 源文件,以及 include 目录下还有一个 .h 头文件。但是编译依赖的头文件,以及调用的函数的实现等都在SDK里面。除了程序源文件外工程还有几个配置文件,是给 Eclipse 用的XML格式文件,也可以阅读。

 

  不妨先用 arm-none-eabi-gcc -c -march=cortex-m3 -mthumb 来对 app.c 进行试编译。GCC会报告找部不头文件 rsl10.h.  查找一下,在 SDK (就是那个 .pack 压缩文件内容) 的 include 目录下就有,于是复制过来,例如放到一个 inc 子目录里面。再编译,还会报错,又有另外的头文件找不到了。可以一次性将 SDK 的 include 目录下文件都复制过来,不管用不用到;也可以缺什么文件就复制什么文件,这样看看到底需要依赖多少。

  实际上还真不少,但即使把 include 目录的文件包含解决了,编译还是有很多错误,缺少宏定义。注意第一个错误是:

 error: #error RSL10_CID must be defined

  怎么定义 RSL10_CID 就要从工程文件里面找线索了,在 .cproject 文件中搜索到如下:

<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnuarmeclipse.managedbuild.cross.......

   <listOptionValue builtIn="false" value="RSL10_CID=101"/>
   <listOptionValue builtIn="false" value="_RTE_"/>
</option>

  看来需要定义这两个宏,在 GCC 的命令行上用 -D 参数指定即可。

  还有一个编译错误是 printf.h 找不到。搜索一下,在 SDK 的 source/firmware/printf 目录里面,一同还有一个 printf.c 文件。把这两个复制过来,放到一个 sys 子目录下。这样,用命令行

arm-none-eabi-gcc -c -Iinc -Isys -mcpu=cortex-m3 -mthumb -O2 -DRSL10_CID=101 -D_RTE_ app.c
就可以把 app.c 编译成 app.o 了。

  然后,仅使用 app.o 是得不到 app.elf 的,还需要SDK库的函数支持,比如链接会报错说 SystemCoreClock 不存在。SDK提供了这个函数的实现,在哪个 C 文件里面?可以用 find, grep 这些工具命令来找,并不难。不过工程文件已包含了这些信息,用IDE环境的时候自动就使用了。在 blinky.rteconfig 文件里面,有这几行:

      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_romvect.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_asrc.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_audio.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_clocks.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_crc.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_dma.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_flash.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_power.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_power_modes.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_rffe.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_timers.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_uart.c"/>
      <file category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_sys_version.c"/>
      <file attr="config" category="source" deviceDependent="1" name="source/firmware/syslib/code/rsl10_protocol.c" version="3.1.573"/>

  这就容易看明白了,SDK 的 source/firmware/syslib/code 目录下有若干文件,是需要编译并链接到一起的。用 GCC 命令行分别处理一下就可以了。当然一个一个手工编译没有必要,写成个 Makefile 最好。

  启动文件,包括中断向量表,也是要一并链接的。在 SDK的 firmware/cmsis/sources/GCC 目录下有 startup_rsl10.S 汇编文件,可以用 gcc 命令进行汇编。

  再把这些编译好的目标文件 (.o文件)链接到一起,用 gcc 间接调用 ld 实现,用 -T 参数指定 sections.ld 文件(在SDK的 firmware/cmsis/source/GCC 目录下。这个文件提供了代码存放的地址,RAM所在的地址等重要信息,MCU程序开发必须要用到)。这时候仍然报错:_exit 找不到。

  一般 MCU 程序里面不会用 exit 功能,这个函数是 C 运行库的。加上 -nostartfiles 选项,就可以不链接 C 库的几个默认目标模块。这样一来,又报告 _start 找不到的错误了。注意到工程文件里面还有

      <file category="source" condition="GCC_Condition" deviceDependent="1" name="source/firmware/cmsis/source/sbrk.c"/>
      <file category="source" condition="GCC_Condition" deviceDependent="1" name="source/firmware/cmsis/source/start.c"/>
      <file attr="config" category="source" deviceDependent="1" name="source/firmware/cmsis/source/system_rsl10.c" version="1.0.0"/>

  还需要把 sbrk.c start.c system_rsl10.c 三个文件分别编译,最后参与链接。

 

  写成一个 Makefile 如下:

default: app.elf

INC = -Iinc

APP_OBJ = app.o

APP_OPTS = -DRSL10_CID=101 -D_RTE_

SYSOBJ = rsl10_romvect.o \
	rsl10_sys_asrc.o \
	rsl10_sys_audio.o \
	rsl10_sys_clocks.o \
	rsl10_sys_crc.o \
	rsl10_sys_dma.o \
	rsl10_sys_flash.o \
	rsl10_sys_power.o \
	rsl10_sys_power_modes.o \
	rsl10_sys_rffe.o \
	rsl10_sys_timers.o \
	rsl10_sys_uart.o \
	rsl10_sys_version.o \
	rsl10_protocol.o \
	system_rsl10.o \
	startup_rsl10.o \
	start.o \
	sbrk.o

CFLAGS = $(INC) -mcpu=cortex-m3 -mthumb -O2 $(APP_OPTS)

LDFLAGS = -Tsections.ld -nostartfiles

%.o : %.c
	arm-none-eabi-gcc -c -Isys $(CFLAGS) $<

%.o : sys/%.c
	arm-none-eabi-gcc -c $(CFLAGS) $<

%.o : sys/%.S
	arm-none-eabi-gcc -c $(CFLAGS) $<

app.elf : $(APP_OBJ) $(SYSOBJ)
	arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $^ $(LDFLAGS) -o $@

 

  这样,执行 make 命令就可完成编译。

  再用 arm-none-eabi-objcopy 从 elf 文件产生 .hex 或 .bin 文件,由 Jlink 烧写到板子,就可以看到 LED 闪亮了。

 

  *实际上这个范例工程文件里面列的 C 文件有多余,其中 rsl10_sys_xxxx 若干文件,只需要有 rsl10_sys_dma.c 就够了,因为 system_rsl10.c 中调用了一个 DMA 的函数。如果自己修改一下,还可以去掉。

回复评论 (3)

写的Makefile 这个方法很好,

楼主经验很丰富,介绍的也细致

点赞  2021-5-5 20:58

make大法完美

默认摸鱼,再摸鱼。2022、9、28
点赞  2021-5-10 22:50
感谢楼主的分享
点赞  2021-5-14 18:42
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复