写前面:micropython的oled0.96 的小液晶可以说是便宜又好用,SSD1306的库也很成熟,可以说玩microPython的人人都有几块小液晶,可是这个中文显示,就不怎么地。目前主方式还是使用取模软件把要显示的文字取好显示,不但繁琐总是觉得很闹心。这次记录一个不取模的方法,虽然也是很闹心,起码比取模的少费点力气,本着能省一点是一点的心态进行本次记录。
- 先把ssd1306的库贴上,
ssd1306.py
标准库。
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
from micropython import const
import time
import framebuf
import sys
currentBoard=""
if(sys.platform=="esp8266"):
currentBoard="esp8266"
elif(sys.platform=="esp32"):
currentBoard="esp32"
elif(sys.platform=="pyboard"):
currentBoard="pyboard"
import pyb
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xa4)
SET_NORM_INV = const(0xa6)
SET_DISP = const(0xae)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xa0)
SET_MUX_RATIO = const(0xa8)
SET_COM_OUT_DIR = const(0xc0)
SET_DISP_OFFSET = const(0xd3)
SET_COM_PIN_CFG = const(0xda)
SET_DISP_CLK_DIV = const(0xd5)
SET_PRECHARGE = const(0xd9)
SET_VCOM_DESEL = const(0xdb)
SET_CHARGE_PUMP = const(0x8d)
class SSD1306:
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
self.framebuf = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MVLSB)
self.poweron()
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR, 0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO, self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET, 0x00,
SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV, 0x80,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
SET_VCOM_DESEL, 0x30, # 0.83*Vcc
# display
SET_CONTRAST, 0xff, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)
def fill(self, col):
self.framebuf.fill(col)
def pixel(self, x, y, col):
self.framebuf.pixel(x, y, col)
def scroll(self, dx, dy):
self.framebuf.scroll(dx, dy)
def text(self, string, x, y, col=1):
self.framebuf.text(string, x, y, col)
def hline(self, x, y, w, col):
self.framebuf.hline(x, y, w, col)
def vline(self, x, y, h, col):
self.framebuf.vline(x, y, h, col)
def line(self, x1, y1, x2, y2, col):
self.framebuf.line(x1, y1, x2, y2, col)
def rect(self, x, y, w, h, col):
self.framebuf.rect(x, y, w, h, col)
def fill_rect(self, x, y, w, h, col):
self.framebuf.fill_rect(x, y, w, h, col)
def blit(self, fbuf, x, y):
self.framebuf.blit(fbuf, x, y)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
#IF SYS :
global currentBoard
if currentBoard=="esp8266" or currentBoard=="esp32":
self.i2c.writeto(self.addr, self.temp)
elif currentBoard=="pyboard":
self.i2c.send(self.temp,self.addr)
#ELSE:
def write_data(self, buf):
self.temp[0] = self.addr << 1
self.temp[1] = 0x40 # Co=0, D/C#=1
global currentBoard
if currentBoard=="esp8266" or currentBoard=="esp32":
self.i2c.start()
self.i2c.write(self.temp)
self.i2c.write(buf)
self.i2c.stop()
elif currentBoard=="pyboard":
#self.i2c.send(self.temp,self.addr)
#self.i2c.send(buf,self.addr)
self.i2c.mem_write(buf,self.addr,0x40)
def poweron(self):
pass
class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
global currentBoard
if currentBoard=="esp8266" or currentBoard=="esp32":
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
elif currentBoard=="pyboard":
self.spi.init(mode = pyb.SPI.MASTER,baudrate=self.rate, polarity=0, phase=0)
self.cs.high()
self.dc.low()
self.cs.low()
global currentBoard
if currentBoard=="esp8266" or currentBoard=="esp32":
self.spi.write(bytearray([cmd]))
elif currentBoard=="pyboard":
self.spi.send(bytearray([cmd]))
self.cs.high()
def write_data(self, buf):
global currentBoard
if currentBoard=="esp8266" or currentBoard=="esp32":
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
elif currentBoard=="pyboard":
self.spi.init(mode = pyb.SPI.MASTER,baudrate=self.rate, polarity=0, phase=0)
self.cs.high()
self.dc.high()
self.cs.low()
global currentBoard
if currentBoard=="esp8266" or currentBoard=="esp32":
self.spi.write(buf)
elif currentBoard=="pyboard":
self.spi.send(buf)
self.cs.high()
def poweron(self):
self.res.high()
time.sleep_ms(1)
self.res.low()
time.sleep_ms(10)
self.res.high()
- 需要一个字库放在同级目录下
HZK16S
就这个玩意
字库下载,收几个积分,虽然不是我写的起码是我搬运的,嘿嘿
3. 关于这个现实中文的玩意。。。原理也就是按字差模取点位显示其实万变不离取模只是用字库代替了取模软件手动取模。
import machine,ssd1306
i2c=machine.I2C(scl=machine.Pin(22),sda=machine.Pin(19)) #这里是引脚改成自己的,不能偷懒
#i2c.scan()
oled=ssd1306.SSD1306_I2C(128,64,i2c)
################################################################
oled.fill(0)
def chchar(oled,x,y,btxt,origin=1,color = 1):
# if ((x+16)>= self.width) or ((y+16)>= self.height):
# return
for i in range(16):
t1 = btxt[i<<1]
t1 = int(bin(t1)[2:])
t1 = "%08d" % t1
t2 = btxt[(i<<1)+1]
t2 = int(bin(t2)[2:])
t2 = "%08d" % t2
t = t1+t2
for j in range(16):
if t[j]=='1':
oled.pixel((x+j),(y+i),color)
oled.show()
#end
def get_gb2312(a):
f = open('HZK16S','rb')
#a = a.encode('gb2312')
area = a[0]-160
index = a[1]-160
offset = (94*(area-1)+(index-1))*32
try:
f.seek(offset)
btxt=f.read(32)
except:
print("out range chinese")
btxt=None
f.close()
return btxt
############
#核心奥义: 要显示的字存在文件里需要用gb2312编码格式存储!
#################################
a = "高"
b = a.encode('gb2312')
print(a,b)
btxt = get_gb2312(b'\xb8\xdf')
chchar(oled,0,0,btxt)
#################################
#btxt = get_gb2312(b'\xb8\xdf') #esp32的micropython 应该使用这种用法,就是在括号里边直接输入编码过的2312格式的中文码。
#chchar(oled,0,0,btxt)
以上就是主程序,但是也存在一些问题,1. 字库很大很占地方,运行速度也不快,带着这么个玩意就别想干啥快速的应用了,反正显示汉字比较奢侈。2. 这程序写的时候忽略了一个问题,如果是树莓派,使用这个程序则没有问题可以显示汉字,因为树莓派是完整PYTHON ,esp32使用microPython这种精简PYTHON,a.encode('gb2312')
在编码的时候是无效的统统给你搞成utf8
的编码,也就是说,能显示汉字但绝对不是你输入那几个,如下:
为此 ,解决办法为:
1.通过网络服务器构建转码,通过网络把汉字发给自建的网络服务器,然后变成GB2312给模块发回来。这个我尝试了下,用我自建的网站开个了API提供服务确实能搞定。。。。。虽然过程很彪但是确实可以。
2.把所需文字写到一个txt文件里边然后这个文件保存的时候采用gb2312编码,文字之间用空格,取回文件用sqlit转列表再一个一个仍里边显示。这里务必借用NOTEPAD++这个编辑器对txt文件进行编码约束。
在这里设定编码为GB2312后再往里存文字,文字之间留个空,有助于分段,修正后的代码段如下:
import machine,ssd1306
i2c=machine.I2C(scl=machine.Pin(22),sda=machine.Pin(19))
#i2c.scan()
oled=ssd1306.SSD1306_I2C(128,64,i2c)
################################################################
oled.fill(0)
def chchar(oled,x,y,btxt,origin=1,color = 1):
# if ((x+16)>= self.width) or ((y+16)>= self.height):
# return
for i in range(16):
t1 = btxt[i<<1]
t1 = int(bin(t1)[2:])
t1 = "%08d" % t1
t2 = btxt[(i<<1)+1]
t2 = int(bin(t2)[2:])
t2 = "%08d" % t2
t = t1+t2
for j in range(16):
if t[j]=='1':
oled.pixel((x+j),(y+i),color)
oled.show()
#end
def get_gb2312(a):
f = open('HZK16S','rb')
#a = a.encode('gb2312')
area = a[0]-160
index = a[1]-160
offset = (94*(area-1)+(index-1))*32
try:
f.seek(offset)
btxt=f.read(32)
except:
print("out range chinese")
btxt=None
f.close()
return btxt
############
#核心奥义:需要用gb2312编码格式存储!
with open ('data.txt','rb') as f:# 比如我这个data.txt里边就是上边那几个字方式是2312存的
b=f.read()
b=b.split(b' ')## 这里注意切片的时候要用同类型的切,切二进制,空格前面也得用b这种二进制格式
x=0 #横向
y=0 #纵向
for i in b:#现在是个列表了按个取出来
btxt = get_gb2312(i) #转成像素显示数据
chchar(oled,x,0,btxt)#按像素显示
x=x+16 #16位一个字,写一个右移动一次
if x>116:#写一个自动换行
y=y+16
x=0
至此,本次中文显示基本算是打穿了,其实如果你不需要显示很多东西完全可以像这样
import machine,ssd1306
i2c=machine.I2C(scl=machine.Pin(22),sda=machine.Pin(19)) #这里是引脚改成自己的,不能偷懒
#i2c.scan()
oled=ssd1306.SSD1306_I2C(128,64,i2c)
################################################################
oled.fill(0)
def chchar(oled,x,y,btxt,origin=1,color = 1):
# if ((x+16)>= self.width) or ((y+16)>= self.height):
# return
for i in range(16):
t1 = btxt[i<<1]
t1 = int(bin(t1)[2:])
t1 = "%08d" % t1
t2 = btxt[(i<<1)+1]
t2 = int(bin(t2)[2:])
t2 = "%08d" % t2
t = t1+t2
for j in range(16):
if t[j]=='1':
oled.pixel((x+j),(y+i),color)
oled.show()
#end
def get_gb2312(a):
f = open('HZK16S','rb')
#a = a.encode('gb2312')
area = a[0]-160
index = a[1]-160
offset = (94*(area-1)+(index-1))*32
try:
f.seek(offset)
btxt=f.read(32)
except:
print("out range chinese")
btxt=None
f.close()
return btxt
data=[b'\xb8\xdf',b'\xb8\xdf',b'\xd0\xcb'] #存个列表,用标准python翻译好的2312编码列表。
x=0
for i in data:
btxt = get_gb2312(i)
chchar(oled,x,0,btxt)
x=x+16
补充:一天过去了,睡醒后觉得昨天做的还不够好,毕竟文本还得切片,本次修改下输出,一不做二不休,写个类在下面。这个类的输入文本就不用切片了,直接输入就行标点符号运行,空格也行,只要文件看起来是个正经文件都差不多。这个类我没测试几次,目前显示都很正常。
import machine,ssd1306
import struct
import time
################################################################
class oled_zw(object):
def __init__(self,oled):
self.oled=oled
def chchar(self,x,y,btxt,origin=1,color = 1):
# if ((x+16)>= self.width) or ((y+16)>= self.height):
# return
for i in range(16):
t1 = btxt[i<<1]
t1 = int(bin(t1)[2:])
t1 = "%08d" % t1
t2 = btxt[(i<<1)+1]
t2 = int(bin(t2)[2:])
t2 = "%08d" % t2
t = t1+t2
for j in range(16):
if t[j]=='1':
self.oled.pixel((x+j),(y+i),color)
self.oled.show()
#end
def get_gb2312(self,a):
f = open('HZK16S','rb')
#a = a.encode('gb2312')
area = a[0]-160
index = a[1]-160
offset = (94*(area-1)+(index-1))*32
try:
f.seek(offset)
btxt=f.read(32)
except:
print("out range chinese")
btxt=None
f.close()
return btxt
def oled_show_only(self,x,y,data):#只显示一个汉字
btxt = self.get_gb2312(data)
print(btxt)
self.chchar(x,y,btxt)
#核心奥义: 需要用gb2312编码格式存储!
def oled_show_txt(self,path): #输入显示文件名
with open (path,'rb') as f:
textdata=f.read()
textdata=textdata.replace(b' ',b'') # 抛弃空格
x=0
y=0
count=[]
print(textdata)
for i in textdata:
print(i)
if i <= 127 and i !=13 and i != 10:#检测类型,如果小于127应该是ASCII的就直接处理了,不等于13和10是因为这俩是\r和\n换行符号,处理一下筛出去。
y=y+8
self.oled.text(chr(i),x,y) #用CHR(i)显示
self.oled.show() #直接显示出来
y=y-8
x=x+8 #坐标右移动
if x>116:#换行
y=y+16
x=0
if y>48: #翻页
y=0
x=0
time.sleep(2)#延时5秒
self.oled.fill(0) #清屏
print(chr(i))
else:
if len(count)<1:# 接受数据,2个一组
count.append(i)
else:
count.append(i)
counts=struct.pack('2b',*count) #二进制不能拼接,采用封包模式
print(count)
try :
btxt = self.get_gb2312(counts)
self.chchar(x,y,btxt)
except:
print('有不识别字符,已抛弃') #字库没有的显示不了的全扔了
x=x+16
if x>116:#换行
y=y+16
x=0
if y>48: #翻页
y=0
x=0
time.sleep(2)#延时5秒
self.oled.fill(0) #清屏
count=[]
if __name__=='__main__':
i2c=machine.I2C(scl=machine.Pin(22),sda=machine.Pin(19)) #定义引脚,搞硬件这个绕不开
oled=ssd1306.SSD1306_I2C(128,64,i2c) #嗯,驱动
oled.fill(0) # 清屏
oled_run = oled_zw(oled) # 类实例化 , 传入驱动
oled_run.oled_show_txt('data.txt') #执行显示,传入要显示的gb2312编码的中文文本
# oled.fill(0)
# oled_run.oled_show_only(0,0,b'\xce\xc2') #这个方法显示一个汉字,(输入横坐标、纵坐标、2312编码的汉字一个)