2021-11-12 micropython esp32 中文显示 (不使用取模软件的一种方式)

写前面:micropython的oled0.96 的小液晶可以说是便宜又好用,SSD1306的库也很成熟,可以说玩microPython的人人都有几块小液晶,可是这个中文显示,就不怎么地。目前主方式还是使用取模软件把要显示的文字取好显示,不但繁琐总是觉得很闹心。这次记录一个不取模的方法,虽然也是很闹心,起码比取模的少费点力气,本着能省一点是一点的心态进行本次记录。

  1. 先把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()

  1. 需要一个字库放在同级目录下 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编码的汉字一个)

后记:这个类可以显示大段的文章,但是使用时字母和数字使用原版显示的8*8字符,所以比较小,凑合用吧。

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值