使用MicroPython 开发板
读取自定义字库并显示
因为项目中使用了子模块 FontMaker Client 的binary
分支 和 OLED Research,所以要获取完整项目代码需要如下操作
$ git clone --recursive https://gitee.com/walkline/micropython-new-fontlib.git
或者克隆时未使用--recursive
参数的,使用如下代码更新子模块
$ cd micropython-new-fontlib
$ git submodule update --init --recursive
生成字库文件详细说明参考 FontMaker Client 项目文档
直接运行
$ client/youyuan_16.cmd
会生成一个combined.bin
文件,字库文件信息如下
参数 | 数值 | 说明 |
---|---|---|
字体 | 幼圆 | |
字号 | 16 | 像素 |
字重 | 普通 | 不加粗、不斜体、无下划线 |
字符宽度 | 固定 | |
宽度 | 16 | 像素 |
高度 | 16 | 像素 |
水平偏移 | 0 | 像素 |
垂直偏移 | 0 | 像素 |
扫描方式 | 垂直扫描 | |
字节顺序 | 低位在前 |
直接运行
$ python fontlib.py
会显示相关信息,包括:
- 字库信息
- 获取的字模数据
- 字模打印预览
完整输出内容如下
HZK Info: .//client/combined.bin
file size : 261972
font height : 16
data size : 32
scan mode : Vertical
byte order : LSB
characters : 7710
!: b'\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x00\x00\x08\x00\x08\x00\x00\x00'
☆: b'\x00\x00\x00\x00\x00\x80\x01\x80\x01\x80\x01@\x02@~? \x04\x10\x08\x0c\x10\x08\x10\x08\x90\x0bH\x148\x18\x08'
⒉: b'\x00\x00\x00\x00\x00\x00\x07\xc0\x08@\x00@\x00@\x00@\x00\x80\x01\x00\x01\x00\x02\x00\x04\x00\x0c\x08\x0f\xcc\x00\x00'
,: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x000\x00 \x00'
我: b'\x00\x00\x00@\x07PxH\x08D\x08D\x7f\xfe\x08D\x08D\t(\x0e0x0\x080\x08R\t\x8ax\x06'
ㄘ: b'\x00\x00\x00\x00\x00\x00\x01\x00\x01\x80\x00\x80\x01\x00\x03\xf0\x1d\x00\x01\x00\x02\xe0\x03`\x00@\x00\x80\x00\x00\x00\x00'
■: b'\x00\x00\x00\x00\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff'
B: b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x060\x068\x060\x06p\x07\xf0\x06\x18\x06\x18\x06\x18\x07\xf0\x00\x00\x00\x00'
中: b'\x00\x00\x01\x00\x01\x00\x01\x00?\xfc!\x04!\x04!\x04!\x04!\x04!\x04?\xfc\x01\x00\x01\x00\x01\x00\x01\x00'
爱: b'\x00\x00\x00\x00?\xf8\x11\x10\t\x10?\xfcD\x02B\x02\x1f\xf8\x04\x00\x07\xf8\x0e\x08\x13\x10 \xe0\x01\xe0\x1e\x1e'
β: b'\x00\x00\x00\x00\x00\x00\x01\xe0\x01\x10\x030\x02 \x02\xc0\x02`\x06 \x04 \x04`\x04`\x07\xc0\x0c\x00\x08\x00'
あ: b"\x00\x00\x00\x00\x03\x00\x03\x00\x02\xc0\x1f\x80\x02\x00\x06\x80\x07\xf0\x0c\x98\x15\x0c'\x0c&\x0c/\x088\x10\x01\xe0"
H: b'\x00\x00\x00\x00\x00\x00\xe7\x00B\x00B\x00B\x00B\x00~\x00B\x00B\x00B\x00B\x00\xe7\x00\x00\x00\x00\x00'
华: b'\x00\x00\x00\x00\x08@\x08H\x18p(\xc0+BHB\x08~\x01\x00\x01\x00\x7f\xfe\x01\x00\x01\x00\x01\x00\x01\x00'
ǚ: b"\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x08\x004\x00C\x00C\x00C\x00C\x00C\x00C\x00'\x00\x18\x00\x00\x00"
e: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00B\x00~\x00@\x00@\x00B\x00<\x00\x00\x00\x00\x00'
l: b'\x00\x00\x00\x00\x00\x00p\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00|\x00\x00\x00\x00\x00'
o: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00B\x00B\x00B\x00B\x00B\x00<\x00\x00\x00\x00\x00'
⑴: b'\x00\x00\x00\x00\x08\x00\x10\x08!\x84 \x82@\x82@\x83@\x81@\x81@\x81@\x82 \x82 \x84\x13\xc4\x10\x08'
................ ................ ................ ................ ................ ................
................ ................ ................ ................ .........@...... ................
................ ........@....... ................ ................ .....@@@.@.@.... ................
....@........... .......@@....... .....@@@@@...... ................ .@@@@....@..@... .......@........
....@@.......... .......@@....... ....@....@...... ................ ....@....@...@.. .......@@.......
....@........... .......@.@...... .........@...... ................ ....@....@...@.. ........@.......
....@........... ......@..@...... .........@...... ................ .@@@@@@@@@@@@@@. .......@........
....@........... .@@@@@@...@@@@@@ .........@...... ................ ....@....@...@.. ......@@@@@@....
....@........... ..@..........@.. ........@....... ................ ....@....@...@.. ...@@@.@........
....@........... ...@........@... .......@........ ................ ....@..@..@.@... .......@........
....@........... ....@@.....@.... .......@........ ................ ....@@@...@@.... ......@.@@@.....
....@........... ....@......@.... ......@......... ................ .@@@@.....@@.... ......@@.@@.....
................ ....@...@..@.... .....@.......... ................ ....@.....@@.... .........@......
....@........... ....@.@@.@..@... ....@@......@... .@@@............ ....@....@.@..@. ........@.......
....@........... ...@.@....@@@... ....@@@@@@..@@.. ..@@............ ....@..@@...@.@. ................
................ ...@@.......@... ................ ..@............. .@@@@........@@. ................
................ ................ ................ ................ ................ ................
................ ................ .......@........ ................ ................ ................
.@@@@@@@@@@@@@@@ ................ .......@........ ..@@@@@@@@@@@... ................ ......@@........
.@@@@@@@@@@@@@@@ ................ .......@........ ...@...@...@.... .......@@@@..... ......@@........
.@@@@@@@@@@@@@@@ .....@@@@@@@.... ..@@@@@@@@@@@@.. ....@..@...@.... .......@...@.... ......@.@@......
.@@@@@@@@@@@@@@@ .....@@...@@.... ..@....@.....@.. ..@@@@@@@@@@@@.. ......@@..@@.... ...@@@@@@.......
.@@@@@@@@@@@@@@@ .....@@...@@@... ..@....@.....@.. .@...@........@. ......@...@..... ......@.........
.@@@@@@@@@@@@@@@ .....@@...@@.... ..@....@.....@.. .@....@.......@. ......@.@@...... .....@@.@.......
.@@@@@@@@@@@@@@@ .....@@..@@@.... ..@....@.....@.. ...@@@@@@@@@@... ......@..@@..... .....@@@@@@@....
.@@@@@@@@@@@@@@@ .....@@@@@@@.... ..@....@.....@.. .....@.......... .....@@...@..... ....@@..@..@@...
.@@@@@@@@@@@@@@@ .....@@....@@... ..@....@.....@.. .....@@@@@@@@... .....@....@..... ...@.@.@....@@..
.@@@@@@@@@@@@@@@ .....@@....@@... ..@@@@@@@@@@@@.. ....@@@.....@... .....@...@@..... ..@..@@@....@@..
.@@@@@@@@@@@@@@@ .....@@....@@... .......@........ ...@..@@...@.... .....@...@@..... ..@..@@.....@@..
.@@@@@@@@@@@@@@@ .....@@@@@@@.... .......@........ ..@.....@@@..... .....@@@@@...... ..@.@@@@....@...
.@@@@@@@@@@@@@@@ ................ .......@........ .......@@@@..... ....@@.......... ..@@@......@....
.@@@@@@@@@@@@@@@ ................ .......@........ ...@@@@....@@@@. ....@........... .......@@@@.....
................ ................ ................ ................ ................ ................
................ ................ ................ ................ ................ ................
................ ....@....@...... ................ ................ ................ ................
@@@..@@@........ ....@....@..@... ................ ................ .@@@............ ................
.@....@......... ...@@....@@@.... ...@.@.......... ................ ...@............ ................
.@....@......... ..@.@...@@...... ....@........... ................ ...@............ ................
.@....@......... ..@.@.@@.@....@. ..@@.@.......... ................ ...@............ ................
.@....@......... .@..@....@....@. .@....@@........ ..@@@@.......... ...@............ ..@@@@..........
.@@@@@@......... ....@....@@@@@@. .@....@@........ .@....@......... ...@............ .@....@.........
.@....@......... .......@........ .@....@@........ .@@@@@@......... ...@............ .@....@.........
.@....@......... .......@........ .@....@@........ .@.............. ...@............ .@....@.........
.@....@......... .@@@@@@@@@@@@@@. .@....@@........ .@.............. ...@............ .@....@.........
.@....@......... .......@........ .@....@@........ .@....@......... ...@............ .@....@.........
@@@..@@@........ .......@........ ..@..@@@........ ..@@@@.......... .@@@@@.......... ..@@@@..........
................ .......@........ ...@@........... ................ ................ ................
................ .......@........ ................ ................ ................ ................
................
................
....@...........
...@........@...
..@....@@....@..
..@.....@.....@.
.@......@.....@.
.@......@.....@@
.@......@......@
.@......@......@
.@......@......@
.@......@.....@.
..@.....@.....@.
..@.....@....@..
...@..@@@@...@..
...@........@...
推荐使用 AMPY Batch Tool (ab
工具) 进行文件上传和调试操作
# 上传文件
$ ab
# 进入 repl 模式
$ ab --repl
# 运行开发板上的文件
# 使用快捷键 Ctrl-T
>>>
Run onboard file
[1] /boot.py
[2] /drivers/ssd1306.py
[3] /fontlib.py
Choose a file: 3
>>>
# 在开发板上运行本地文件
# 使用快捷键 Ctrl-R
>>> Run local file
[1] fontlib.py
[2] drivers\ssd1306.py
Choose a file: 1
>>>
这是把fontlib.py
文件中非MicroPython
代码精简掉,并编译为字节码再运行测试的方法
# 上传文件
$ ab abconfig-mpy
因为上传文件中已经包含了main.py
,所以直接复位根据提示进行选择,就可以看到效果了
方法参见 使用图标字体生成字库
GB2312
收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个全角字符
可以用键盘输入它们,但是并不一定简单,好在我们还可以从 GB2312简体中文编码表 页面中直接查看复制它们
以当前字库文件举例:
- 文件名:
combined.bin
- 文件大小:
261,972
字节GB2312
索引表大小:17,672
字节- 点阵大小:
16x16
像素- 字符数据大小:
32
字节- 字符总数:
7710
个(包含ASCII
和GB2312
)
- 实例化
FontLib
时打开字库文件,读取文件头信息,大约 12 ms - 同时读取
?
的字符数据作为占位符,大约 5 ms - 打印字库信息,大约 39 ms(简直无语,建议实际使用时不要打印)
- 检索字符数据前再次打开字库文件,大约 12 ms
- 检索字符数据,每字符大约 12 ms
- 字符数据使用
Dict
存储,字符Unicode
值作为关键字,取值耗时可以忽略 - 显示字符耗时未统计
决定显示速度的因素有两方面:
- 从字库中读取指定字符的字模数据
- 在 oled 中显示
显示部分,MicroPython
已经提供了SSD1306
驱动,使用FrameBuffer
进行数据管理,我相信官方的实力,所以显示部分的速度假设已经没有提升空间了
数据部分,最开始的思路就是“完成功能”,所以在读取的时候是这个流程:
- 打开字库文件
- 取一个字符
分段
查找字符在索引表
中的偏移量定位
到偏移量读取字模数据- 从第
2
步开始循环,直到读取所有字符数据
分段
的意思是:在Python
中打开文件,它不提供查找功能,需要先读取文件内容再进行查找,对于开发板来说,一次性读取全部文件肯定会导致内存溢出
,所以需要分段读取一段合理长度的数据再进行查找,在查找到之前就需要不停的分段读取
这个方法的问题在于,每个字符都要从索引表
的开始位置进行查找,比较浪费
提速的思路就是,在每次分段
读取数据的时候查找所有字符,减少分段
次数
继续查找资料,有人说使用read
代替seek
可以提高定位
的效率,在电脑上进行了简单测试,300M 的文件使用read
比seek
快大约0.2 ms
,但是这个方法有个致命问题,虽然read
读取到的数据并不使用,但也会分配内存空间,在开发板上运行的结果就是,更慢了
对比一下三种方式的读取速度(毫秒)
字库文件信息:
- 文件大小:500024 字节
- 字符总数:8932 个
- 数据大小:54 字节
原方法 | 新方法(seek) | 新方法(read) | |
---|---|---|---|
打开字库 | 27.51 | 26.22 | 61.21 |
字符数 | 原方法 | 新方法(seek) | 新方法(read) |
---|---|---|---|
240 | 13.09 | 6.50 | 159.28 |
227 | 16.05 | 8.22 | / |
767 | 内存溢出 | 内存溢出 | / |
注:统计结果为平均时间
这里的字符数是包含了重复字符的总数,实际获取数据时是要去重的
读取速度还与字符在索引表
中的相对位置有关,相对位置靠前的字符当然能更快被找到,反之则更慢
一次性读取太多的数据也会导致内存溢出
,所以在实际使用时不建议一次性读取太多
手动调用垃圾回收(gc.collect())的时机一定要掌握好,并不是任何时候手动调用都能起到预期的作用,有时还会起到反作用
对于能够自动回收的,如函数内部的变量,可以省去手动调用的操作
统计运行时间使用的装饰器
from utime import ticks_us, ticks_diff
def cal_time(fn):
def wrapper(*args,**kwargs):
starTime = ticks_us()
f = fn(*args,**kwargs)
print('%s() runtime: %s ms' % (fn.__name__, ticks_diff(ticks_us(), starTime) / 1000))
return f
return wrapper
又增加了一个单独测速的文件fontlib_test.py
,发现一个现象,虽然知道和缓存有关,但是具体怎么实现的并不了解
这里测速使用了两个方法
- 一次获取所有字符的字模数据
- 分段获取所有字符的字模数据
单独测试这两种方法,得到的结果如下
### method: load all
### 240 chars, get 111 chars: 1145.765 ms, avg: 10.32221 ms
### method separated
### 0 get 18 chars: 201.403 ms, avg: 11.18906 ms
...
### 9 get 22 chars: 169.531 ms, avg: 7.705955 ms
### 240 chars, get 197 chars: 1760.472 ms, avg: 9.978033 ms
但是同时测试两种方法的结果却是这样的
### method: load all
### 240 chars, get 111 chars: 1145.765 ms, avg: 10.32221 ms
### method separated
### 0 get 18 chars: 110.468 ms, avg: 6.137111 ms
...
### 9 get 22 chars: 125.306 ms, avg: 5.695727 ms
### 240 chars, get 197 chars: 1170.494 ms, avg: 6.635651 ms
调换顺序的结果也是类似
### method separated
### 0 get 18 chars: 201.403 ms, avg: 11.18906 ms
...
### 9 get 22 chars: 169.531 ms, avg: 7.705955 ms
### 240 chars, get 197 chars: 1760.472 ms, avg: 9.978033 ms
### method: load all
### 240 chars, get 111 chars: 565.726 ms, avg: 5.096631 ms
结论就是,不管是一次性还是分段,只要提前获取了所有字符的字模数据,下次再获取的时候速度就会快很多,不过显然这种提速并没有实际意义
- 联系邮箱:walkline@163.com
- QQ 交流群:
- 走线物联:163271910
- 扇贝物联:31324057