机缘
最近意外知道了有树莓派Pico单片机,价格低廉,而且是micropyth就可以开发,感觉很有意思,就拼多多上买了一片来玩一下。
配合SSD1306的单色OLED显示屏输出时,发现默认的固件只能显示英文,而且网上搜了一下大部分都是用现取字模的方式来显示中文。这点感觉不是很方便,正好多年前也在unix上用C提取过gb2312字模,就想着试一下,看看micropython是否也可以实现。
网上找了一下,发现有ESP32的开发高手已经实现了在固件里集成了中文字体,可以直接调用输出,于是再变更了一下目标,实现一个.py,可以给其他micropython调用来显示gb2312中文文章。(这里的考虑是,用点阵字体来展示中文,网上已有一些micropython的代码可以借鉴了。gb2312的字数也有6、7千个,基本可以满足常见的中文展示了。gbk和utf-8的字数虽然多,但是在单片机有限容量的前提下还是能省则省吧。另外集成到固件中去,虽然很棒,但对笔者来说可能需要更多的时间去翻看Pico的源码,本身就是闲余时间玩一下,也就不作考虑了。能先实现一个.py,也可以给更多开发者参考借鉴,也是有帮助的。)
下面是完成的代码:
import machine
import ssd1306
import framebuf
import time
from utf2gb import utf8_gb2312
i2c = machine.I2C(0, sda=machine.Pin(8), scl=machine.Pin(9), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
def genword(cw1, ff="HZK16", fpt=16) :
""" 读取一个汉字
"""
try:
f = open(ff,'rb')
except:
print("read file ",ff," error!")
else:
fsize=fpt*2
offset = (94*(cw1[0]-0xa0-1)+(cw1[1]-0xa0-1))*fsize;
f_c1 = f.seek(offset)
f_c2 = f.read(fsize)
f_c1 = 0
bmp1=bytearray(f_c2)
f_c2 = 0
#print(bmp1)
f.close()
key =bytearray(b'\x80\x40\x20\x10\x08\x04\x02\x01')
for k in range(fpt):
for j in range(2):
for i in range(8):
flag= bmp1[k*2+j] & key[i]
if flag > 0 :
print('●',end='')
else :
#print('○',end='')
print(' ',end='')
print('')
return bmp1
def show_gb2312(cword,fpt,w,h):
pattern=cword
fw=16
if fpt==16:
fh=16
else:
fh=12
buf = framebuf.FrameBuffer(bytearray(pattern), fw, fh, framebuf.MONO_HLSB)
oled.blit(buf, w, h)
oled.show()
def clear_gb2312(w,h,fpt=12):
fw=128
if fpt==16:
fh=16
else:
fh=12
pattern = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
buf = framebuf.FrameBuffer(bytearray(pattern), fw, fh, framebuf.MONO_HLSB)
oled.blit(buf, w, h)
oled.show()
if __name__ == '__main__':
font = utf8_gb2312()
v_width=128-1
v_height=64-1
#读取需要显示的UTF-8文件
f_in = "poem-utf8.txt"
#字号,现有GB2312的字库是12、16两种
fpt = 16
v_lines=int(v_height/fpt)
pos_x=0
pos_y=0
pos_line=0
try:
f = open(f_in,'rb')
except:
print("read file ",f_in," error!")
else:
try:
while True :
text_line = f.readline()
if text_line:
tmpr1 = text_line[0:-2]
tmpr2=str(tmpr1,'utf-8')
pos_line = pos_line + 1
pos_x = 0
pos_y = (pos_line - 1) * fpt
""" position of current cword in view
"""
pos_c = 0
if pos_y > v_height - fpt :
oled.scroll(0,-(fpt))
pos_line = pos_line - 1
pos_y = (pos_line -1) * fpt
pos_x=0
pos_c=0
clear_gb2312(0,pos_y,fpt)
r=font.str(tmpr2)
v1=r
if len(v1) == 0 :
pos_x = 0
pos_c = 0
clear_gb2312(0,pos_y,fpt)
else:
time.sleep(1)
for i in range(len(v1)/2):
vy=v1[i*2: i*2+2]
if fpt==16:
c1=genword(vy,"HZK16",fpt)
else:
c1=genword(vy,"HZK12",fpt)
print('')
show_gb2312(c1,fpt,pos_x,pos_y )
#time.sleep(1)
pos_c = pos_c + 1
pos_x = pos_c * fpt
if pos_x > v_width - fpt :
pos_x = 0
pos_c = 0
pos_line = pos_line + 1
pos_y = (pos_line - 1) * fpt
if pos_y > v_height - fpt :
oled.scroll(0,-(fpt))
pos_line = pos_line - 1
pos_y = (pos_line -1) * fpt
#pos_x=0
#pos_c=0
clear_gb2312(0,pos_y,fpt)
else:
break
finally:
f.close()
说明:
1、使用了常见的ssd1306驱动: ssd1306.py
2、发现Pico支持UTF-8,要输出gb2312的话,还需要先把utf-8的中文编码转换为gb2312的编码。还好找到了高手已经实现的转码模块,https://github.com/LC044/MCU 就先拿来调用一下了。(调用时为了方便,改了一下原文件名main,py为utf2gb,py)
3、gb2312字库的话,找到了以前用的12位和16位字库,在上面的代码里可以通过字号参数来选择使用,因为不清楚是否涉权,所以也不提供了。12位的字库194KB,16位的是262KB。
4、代码简单说明: 先把需要显示的中文,在Windows等平台上保存为utf-8的文本格式(这段代码里的文件名是poem-utf8.txt),然后再用main.py调用这个文件进行逐行展示(代码里使用了16位字号,也可改为12)。每行展示时先调用utf2gb转码为gb2312编码,然后逐字调用genword来取点阵字模(获取后会print输出,方便调试),再用show_gb2312在oled 1306上进行显示。(显示时会根据屏幕的宽高进行自动换行和滚动,代码里倒是这部分代码较多了)
5、限制:目前只是简单实现了展示中文,所以对于混合了英文或其他非utf-8编码的中文都还不支持,有参考这个代码的同好需要注意一下。
多年来一直在潜水吸取网上高手们的经验,也没有些回报,有些惭愧。这次想着也能稍尽些绵薄之力,给有需要的同好,也算一件幸事。