在stm32上使用二进制字库的简单方法
2015-03-13 by Stavros标题其实可以写成"在可执行程序里嵌入二进制资源的方法", 不过这个题目大了点, 还是原样吧.
以前如果我们用到不带字库的点阵LCD, 一般都是把字库按16进制写成一个大数组, 再和其他源程序一起编译. 有没有方便一点的办法呢?这里给出两个方案.
-
使用objcopy把字库转成目标文件(.o/.obj)
假设我们需要嵌入的是5x7的ascii点阵字库, 文件名是asc5x7.bin.
在arm-gcc环境下, 命令如下:
arm-none-eabi-objcopy -B arm -I binary -O elf32-littlearm --rename-section .data=.rodata asc5x7.bin asc5x7.o
需要注意的是, objcopy生成的.o文件默认是把数据放在.data段的. 因此这里需要加个--rename-section的选项, 把.data改成.text或者.rodata, 不然单片机可怜的一点点RAM根本不够用. 如果是在PC上运行, 这里改不改就无所谓了, 不过内存还是能省点就省点的好.
在.o里会生成三个符号:
_binary_asc5x7_bin_start, _binary_asc5x7_bin_end, binary_asc5x7_bin_size.
在程序里这么调用:
extern const int _binary_asc5x7_bin_start; uint8_t *start = (uint8_t *) (&_binary_asc5x7_bin_start);
这样我们就得到了字库的起始地址. 在AVR上可能会稍复杂一些, 因为数据存储在FLASH里, 所以需要象PROGMEM数组一样, 用pgm_read_byte之类函数来读取.
链接时会有一堆错误提示: "Conflicting CPU architectures ....", 这是因为.o里还缺少一个叫.ARM.attributes的段. 不过实际上也没啥影响, 程序是可以正确运行的.
-
直接利用GNU汇编的.incbin功能
这是minux@bdwm提供的方法, 解决得明显更漂亮一些.
首先建立一个asc5x7.s的源文件,内容如下:
.section .rodata,"",%progbits .global _binary_asc5x7_bin_start, _binary_asc5x7_bin_end, _binary_asc5x7_bin_size _binary_asc5x7_bin_start: .incbin "asc5x7.bin" _binary_asc5x7_bin_end: _binary_asc5x7_bin_size = _binary_asc5x7_bin_end - _binary_asc5x7_bin_start
其实_binary_asc5x7_bin_size和_binary_asc5x7_bin_end这两个符号不要也可以, 因为字库的存储格式我们事先已经知道了.
然后建立一个空的.c文件:
touch empty.c, arm-none-eabi-gcc -S empty.c
, 这样我们得到了一个empty.s, 内容如下:.cpu arm7tdmi-s .fpu softvfp .eabi_attribute 20, 1 .eabi_attribute 21, 1 .eabi_attribute 23, 3 .eabi_attribute 24, 1 .eabi_attribute 25, 1 .eabi_attribute 26, 1 .eabi_attribute 30, 6 .eabi_attribute 18, 4 .file "empty.c" .ident "GCC: (Sourcery G++ Lite 2011.03-42) 4.5.2"
把这些东西复制到之前那个asc5x7.s里, 部分内容可能要按需调整一下, 比如.cpu这里应该改成cortex-m0或者cortex-m3之类. 这样在链接时就不会出现前面那个"Conflicting CPU architectures ...."的报错信息了.
如果要使用几个字库呢? 可以把它们全都放在一个段里, 但是如果只用到了某一个或几个字库, 也会把它们全都链接进去, 导致最终的可执行文件体积过大. 因此最好是拆成几个段. 假设我们用到了asc5x7.bin, asc12.bin, asc16.bin和asc24.bin, 写出来应该是这样:
.section .rodata.asc5x7,"",%progbits .global _binary_asc5x7_bin_start _binary_asc5x7_bin_start: .incbin "asc5x7.bin" .section .rodata.asc12,"",%progbits .global _binary_asc12_bin_start _binary_asc12_bin_start: .incbin "asc12.bin" .section .rodata.asc16,"",%progbits .global _binary_asc16_bin_start _binary_asc16_bin_start: .incbin "asc16.bin" .section .rodata.asc24,"",%progbits .global _binary_asc24_bin_start _binary_asc24_bin_start: .incbin "asc24.bin"
然后Makefile里需要增加几个参数, 假如之前没有的话.
CFLAGS += -ffunction-sections --data-sections, LDFLAGS += -Wl,--gc-sections
这样就只会把实际用到的段链接到最终可执行文件了.
如果要在可执行文件里嵌入图片, 音频之类, 也可以用同样的方法.
以上程序均在Windows 7 & Sourcery G++ & STM32F103/F030环境下测试通过. 在Keil/IAR等环境下请自行类推.
补充:注意asc5x7.bin的字节数是奇数, 这样会使得后面几个字库的起始地址都是奇数, stm32进行某些操作时会进入HardFault. 解决办法是在每个.section之前的位置加个.align(3).