1 字库
字库的概念:相应文字或字符的合集。
点阵字库:按字库顺序排列的字符/汉字字模的合集。
LVGL中字库使用Unicode编码,Unicode 是全球文字统一编码。它把世界上的各种文字的每一个字符指定唯一编码,实现跨语种、跨平台的应用。中文用户最常接触的是汉字 Unicode 编码。中文字符数量巨大,日常使用的汉字数量有数千个,再加上生僻字,数量达到数万个。
注意:一个汉字是需要两个字节
分别为:区码 + 位码
2 LVGL实现字库的两种方式
第一种方式:内部大数组
之前显示汉字采用的就是这种形式,如果只是显示少量汉字,用这种方式显示就很便捷,字体加载速度也会比较快。但是如果想要显示任意的汉字时,就需要把常用的字模全部取出,对应的保存字模数据的数组就会占用大量的空间。
第二种方式:外置字库
一般我们会通过软件将常用的汉字全部取模,保存到外部存储介质中,例如SD卡或者Flash。对于LVGL来说,网上有第三方提供的字库制作工具。
我们采用的是W25Q64外部Flash模块来用于储存字库。
2.1 字库制作准备工作
字体下载链接:等宽字体 — 百问网LVGL中文教程文档 文档 (100ask.net)
字体转换工具下载链接:
LvglFontTool字体工具更新V0.4版本 - LittlevGL - 里飞网 - Powered by Discuz! (lfly.xyz)
2.2 字库制作流程
在制作LVGL字库之前需要准备好上述文件,字库制作流程如下:
- 字库制作工具下载好之后解压,然后找到对应软件图标双击打开
- 选择想要的字体文件,然后点击确定,注意子模高度和实际生成的有偏差
- 添加常用汉字,并修改LVGL版本和字库类型,字体名称根据自己需求命名
- 点击开始转换,选择自己的想要保存字库的文件夹,等待转换完成即可
- 转换完成后生成的内容如下:myFont.bin是生成的字库文件,需要下载到外部Flash里,myFont.c是对应的字模获取源码,需要移植到自己的工程里。
至此字库文件就已经制作完成。
myFont.c :功能函数,要跟myFont.bin,调用功能函数获取字模信息
myFont.bin:放的是所有的字模信息
myFont.bin -- 相当于之前的大数组,我们为了节省单片机的空间,同时尽可能支持全部字体,所有需要把bin文件放到SD卡或者flash中,我们就是放到flash中
下面会介绍如何放入
2.3 将字库下载到外部FLASH中
流程示意图:
存在的风险:
简单来说就是,可能会出现两个传输的速率不相同导致一个传输的快,一个接收的慢,到时数据在传输的过程中出现问题。
在STM32中和W25Q64模块进行传输数据中,采用的是SPI协议,搭载在APB1总线,并经过分频,且分频后的频率为:18MHZ --也就是SPI的速率
串口发送1个字节包含起始位和停止位和8位数据,发送1个字节需要10位,串口1s发送11520字节/s==0.01M字节/s<18M.
2.4 字库下载代码实现流程
如果是第一次下载字库,需要先擦除整个芯片,或者按照所使用的扇区大小擦除。
1、确定下载位置,先擦除指定位置的扇区。
2、如使用16号字体,从0地址开始下载,24号字体字号开始的地址要避免覆盖16号字体。
3、在串口接收中断内,每接收一个字节数据,就保存一个字节数据。
4、每接收一个字节,保存地址偏移一下。
5、每用一个1ms自加一次,而每接收一个字节,再清一下变量,用于判断是否接受完毕,如果变量累加100ms,一直位接收到数据,就认为数据已经接收完毕。
2.5 代码流程
2.5.1 定义擦除函数
uint32_t Receive_Cont=0; //收到的字节数
uint8_t Receive_time=0; //用来表示超时时间 超过100ms未收到数据认为接收完成
uint8_t Receive_flag=0; //表示接收完成的标志位 0表示未接收完成 1表示接收完成
uint8_t Receive_ing=0; //表示是否正在接收 0表示未开始接收 1表示正在接收
//定义函数连续擦除多个扇区
//参数1 扇区的起始地址
//参数2 扇区的数量
void sFLASH_EraseSector_Cont(uint32_t SectorAddr,uint16_t Sector_Cont)
{
for(uint16_t i=0;i<Sector_Cont;i++)
{
sFLASH_EraseSector(SectorAddr+i*sFLASH_SPI_SECTORSIZE);
}
}
调用扇区擦除函数擦除对应区域
printf("开始擦除16号字库区域\r\n");
sFLASH_EraseSector_cont(Font16_ADDR,Font16_sector_Cont);
printf("擦除16号字库区域完成\r\n");
计算存储空间
用这个大小除上4096就得到占用的空间大小了。
W25Q64的位置分配
W25Q64的最小擦除函数扇区 1扇区=4096字节
扇区0 0x0
扇区1 0x1000
扇区2 0x2000
....
扇区238 0x1000*238
扇区239 0x1000*239
16号字体 877339字节 需要占用的扇区 877339/4096=214.19 需要扩大 所以分配 215个扇区
16号字体 扇区0(0x0)--扇区238(0x1000*238)
2.5.2 串口接收数据
void USART1_IRQHandler(void)
{
uint8_t Receive_Data=0;
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
{
Receive_Data=USART_ReceiveData(USART1);
//参数2一定要在字库起始地址的基础上个偏移已经接收的数量
sFLASH_WriteBuffer(&Receive_Data,Font16_ADDR+Font16_count,1);
Font16_count++;//接收到的数量
Font16_time=0;//接收完成的超时时间清0
Font16_lvgl=1;//表示正在接收数据
USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除 USARTx 的中断待处理位
}
}
在主函数中加入是否传输完毕检测函数,然后吧程序下载到开发板上,运行即可。
if(Font16_time>=100&&Font16_flag==0)
{
//超时溢出表示接收完成
Font16_flag=1;//表示接收完成
Font16_lvgl=0;
printf("接收的数量:%d\r\n",Font16_count);
}
导入字库