背景
使用STM32使用液晶屏时,对于一些点阵屏,预先需定义好相应的字模,对于12864的话,使用的自身的FLASH空间就可以存放下所需的字模,几十KB的空间。
如果使用大的点阵屏,或者点阵密度较大时,且使用到的字体较多,如果只是用STM32自身的FLASH,对于一些小空间的芯片就不太够有空间去存放字模了。
当前在使用的一块320x170的屏幕,用到的字体又有好几种,显示的内容就不少,对自身的FLASH空间不足于存放相应的字库,因此需要将字库存放于其他位置。
外部SPI-FLASH就是其中一种,也可以存放于NOR-FLASH,NAND-FLASH,EEPROM等,此文使用外部SPI-FLASH,W25Q128。
原理构图
这里罗列了大致的操作流程
- FLASH字库数据烧录
- MCU字库数据读取
- MCU屏幕显示
各个环节的具体实际操作
烧录行为
外部SPI-FLASH,数据烧录方式
- 通过直接向芯片烧录,需要专门的芯片烧录工具,且在板上后无法修改
- 通过SWD烧录,只要保留SWD烧录口,就可以随时通过SWD更新SPI-FLASH内的数据
此文使用STM32CubeProgrammer通过SWD接口向SPI-FLASH更新数据
字库制作
使用PCtoLCD2002完美版这个软件,当然用其他的软件也没有问题
字模选项的设置,软件内有说明,哪种方式都是可以的,只是选择了相应的设置,在显示数据的时候,驱动的代码需要做相应的适配
设置完字模选项,与字体后,可以通过软件的生成字库功能直接生成相应的字库,ASCII码字库与GB2312字库
生成所需的字库二进制文件后,可以将多个文件拼成一个文件,并记录所需每个文件的相对偏移量,这个偏移量在MCU读取SPI-FLASH字库的时候,是被需要的
拼接成的字库文件有几MB,对于STM32自身的FLASH就无法存放这么多的数据
索引 文件名 大小 偏移量
1 E:\Font\ascii_16.bin 0x00000800 0x00000000
2 E:\Font\ascii_24.bin 0x00001800 0x00000800
3 E:\Font\ascii_32.bin 0x00002000 0x00002000
4 E:\Font\ascii_48.bin 0x00004800 0x00004000
5 E:\Font\ascii_64.bin 0x00008000 0x00008800
6 E:\Font\gb2312_16.bin 0x0003FE40 0x00010800
7 E:\Font\gb2312_24.bin 0x0008FC10 0x00050640
8 E:\Font\gb2312_32.bin 0x000FF900 0x000E0250
9 E:\Font\gb2312_48.bin 0x0023F040 0x001DFB50
10 E:\Font\gb2312_64.bin 0x003FE400 0x0041EB90
字库内部偏移量计算
当找到每种字体的起始地址后,我们通过字符自身的编码去计算相应的单个字模数据存放地址
计算公式如下
GB2312编码
offsetAddr = ((CodeH - 0xA1) * 94 + (CodeL - 0xA1)) * FONT_HEIGHT * FONT_WIDTH / 8;
// 16 x 16 字体
offsetAddr = ((CodeH - 0xA1) * 94 + (CodeL - 0xA1)) * 16 * 16 / 8
那这个公式是如何得出来的呢,那就跟GB2312的编码相关
GB2312的编码的两个字节都是从0xA1到0xFE,总共是94的编码,这个就是上述的94的由来,
而字库生成的时候只根据存在的字符生成点阵字库,可以用计算公式得到对应的偏移量换算
GB2312默认包含的字符是从0xA1A1开始生成,默认的GB2312编码生成的字库,其中也包含了很多无用的空格,只是为了保证编码与计算公式的连续性。但我们使用到的中文可以从0xB0A1开始,如果为了减少字库的大小,可以考虑删除一些字,重新调整偏移计算公式也是可以的
ASCII编码的字库则不同,根据ASCII码的偏移计算地址
GB2312内也可以生成英文与数字,不过跟直接用英文生成的字库形状会不一样,英文生成的只占GB2312一半的点阵
STM32CUBEPROGRAMMER烧录数据
字库的生成后通SWD接口往外部SPI-FLASH中烧录数据
STM32CUBEPROGRAMMER,需要根据当前线路板的硬件进行调整来进行数据烧录,这个调整即所谓的烧录算法
适配当前硬件的烧录算法,也采用根据官方提供的例子进行修改,此文基于M25P64_STM3210E-EVAL进行修改
M25P64与W25Q128的驱动基本相似,只是W25Q128支持的操作命名更多,但使用M25Q64的命令也可以完成相应的读写操作
基于这个工程,只要重新配置SPI及FLASH的空间参数设定好,就可以用于STM32CubeProgrammer进行烧录
修改如下:
重新配置SPI
#define sFLASH_SPI_SCK_PIN GPIO_Pin_3 /* PA.03 */
#define sFLASH_SPI_SCK_GPIO_PORT GPIOB /* GPIOB */
#define sFLASH_SPI_SCK_GPIO_CLK RCC_APB2Periph_GPIOB
#define sFLASH_SPI_MISO_PIN GPIO_Pin_4 /* PA.04 */
#define sFLASH_SPI_MISO_GPIO_PORT GPIOB /* GPIOB */
#define sFLASH_SPI_MISO_GPIO_CLK RCC_APB2Periph_GPIOB
#define sFLASH_SPI_MOSI_PIN GPIO_Pin_5 /* PA.05 */
#define sFLASH_SPI_MOSI_GPIO_PORT GPIOB /* GPIOB */
#define sFLASH_SPI_MOSI_GPIO_CLK RCC_APB2Periph_GPIOB
#define sFLASH_CS_PIN GPIO_Pin_6 /* PB.06 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SPI1, ENABLE);
Dev_Inf.c
#if defined (__ICCARM__)
__root struct StorageInfo const StorageInfo = {
#else
struct StorageInfo const StorageInfo = {
#endif
"W25Q128_STM32F103CB", // Device Name + version number
SPI_FLASH, // Device Type
0x00000000, // Device Start Address
0x01000000, // Device Size in Bytes (16MBytes/128Mbits)
0x00000100, // Programming Page Size 256Bytes
0xFF, // Initial Content of Erased Memory
// Specify Size and Address of Sectors (view example below)
0x00000100, 0x00010000, // Sector Num : 256 ,Sector Size: 64KBytes
0x00000000, 0x00000000,
};
这样生成的W25Q128_STM32F103CB.stldr,这个烧录算法,就可以跟当前的硬件相适配。
从烧录算法的源文件中,并没有找到MAIN函数这样的程序入口,不知道SWD如果具体运行这样的算法,直接烧录程序到自身FLASH,也是通过这样的方式,暂时没有找到相关说明资料
算法生成后,将文件复制到STM32CubeProgrammer的目录下
C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
再打开STM32CubeProgrammer,就可以找到相应的算法选项
选择相应的二进制文件烧录数据即可,数据可以正常烧录,不过烧录的速度并不快。
实际效果
总结
可以实现外部SPI-FLASH的字库用于显示不同的字体,来节省空间
个别具体细节还不太了解,比如SWD具体是如何实现烧录算法的加载与操作,没有MAIN函数这样的入口,暂时没有找到相关的资料
参考资料
- 野火-液晶显示中英文
- GB2312编码
- 第85章 STM32H7的SPI 总线应用之SPI Flash的STM32CubeProg下载算法制作
- 通过STM32CubeMX制作外部Flash的烧写驱动(.stdlr)
- ARM分散加载