加载中
加载中
表情图片
评为精选
鼓励
加载中...
分享
加载中...
文件下载
加载中...
修改排序
加载中...
所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
Micropython+ESP32驱动墨水屏
XZH1002023/03/12原创 电子技术 IP:上海

一  开发板

开发板推荐选择NodeMCU-32s

捕获.jpg

micropython固件烧写请看:固件烧写与测试

(固件,Tonny,驱动和串口工具在文末附件里有)

二  墨水屏及驱动

墨水屏我使用的是微雪的2.9寸屏

捕获.jpg

也可以选择其他的,只要兼容微雪就行

然后是整个项目里最难的部分,写驱动。由于微雪官方给的驱动只有arduino和树莓派的,所以micropython的驱动需要自己编写。我写了大概快两周。

Python
from micropython import const from time import sleep_ms import ustruct from machine import Pin import framebuf EPD_WIDTH       = 128 EPD_HEIGHT      = 296

先导入需要的库并定义屏幕宽高

Python
class EPD(framebuf.FrameBuffer):     def __init__(self, spi, cs, dc, rst, busy): self.spi = spi self.cs = Pin(cs,Pin.OUT,value = 1) self.dc = Pin(dc,Pin.OUT,value = 0) self.rst = Pin(rst,Pin.OUT,value = 0) self.busy = Pin(busy,Pin.IN) self.width = EPD_WIDTH self.height = EPD_HEIGHT self.pages = self.height//8 self.buffer = bytearray(self.width*self.pages) super().__init__(self.buffer, self.width, self.height,                 framebuf.MONO_HLSB) self.init()

创建EPD类并初始化(初始化函数在后面)


Python
def reset(self):     self.rst.value(1)      sleep_ms(20)      self.rst.value(0)     sleep_ms(5)     self.rst.value(1)     sleep_ms(20)

然后是硬件重置函数,说白了就是给rst引脚一个低电平脉冲


Python
def send_command(self, command):     self.dc.value(0)     self.cs.value(0)     self.spi.write(command.to_bytes(1,'big'))     self.cs.value(1) def send_data(self, data):     self.dc.value(1)     self.cs.value(0)     self.spi.write(data.to_bytes(1,'big'))     self.cs.value(1)

然后是写入数据和命令

写入命令或数据前通过dc脚表示输入的是命令还是数据,然后cs脚低电平表示有效,通过spi写入


Python
def ReadBusy(self):     while self.busy == 1:      #  0: idle, 1: busy sleep_ms(10)

由于墨水屏刷新需要一段时间,所以需要是否忙,如果busy脚是高电平就需要等待


Python
def TurnOnDisplay(self):     self.send_command(0x22# DISPLAY_UPDATE_CONTROL_2     self.send_data(0xc7)     self.send_command(0x20# MASTER_ACTIVATION     self.ReadBusy() def TurnOnDisplay_Partial(self):     self.send_command(0x22# DISPLAY_UPDATE_CONTROL_2     self.send_data(0x0F)     self.send_command(0x20# MASTER_ACTIVATION     self.ReadBusy()

全局刷新和局部刷新的函数


Python
def SetLut(self, lut):     self.send_command(0x32)     for i in range(0153):         self.send_data(lut[i])     self.ReadBusy()      def SetLut_by_host(self, lut):     self.SetLut(lut)     self.send_command(0x3f)     self.send_data(lut[153])     self.send_command(0x03); # gate voltage     self.send_data(lut[154])     self.send_command(0x04); # source voltage     self.send_data(lut[155]) # VSH     self.send_data(lut[156]) # VSH2     self.send_data(lut[157]) # VSL     self.send_command(0x2c); # VCOM     self.send_data(lut[158])

设置lut表,lut表在附件里的驱动有,作用是设置全刷还是局刷模式


Python
def SetWindow(self, x_start, y_start, x_end, y_end):     self.send_command(0x44# SET_RAM_X_ADDRESS_START_END_POSITION     # x point must be the multiple of 8 or the last 3 bits will be ignored     self.send_data((x_start>>3) & 0xFF)     self.send_data((x_end>>3) & 0xFF)     self.send_command(0x45# SET_RAM_Y_ADDRESS_START_END_POSITION     self.send_data(y_start & 0xFF)     self.send_data((y_start >> 8) & 0xFF)     self.send_data(y_end & 0xFF)     self.send_data((y_end >> 8) & 0xFF) def SetCursor(self, x, y):     self.send_command(0x4E# SET_RAM_X_ADDRESS_COUNTER     # x point must be the multiple of 8 or the last 3 bits will be ignored     self.send_data((x >> 3) & 0xFF)       self.send_command(0x4F# SET_RAM_Y_ADDRESS_COUNTER     self.send_data(y & 0xFF)     self.send_data((y >> 8) & 0xFF)     self.ReadBusy()

这两个函数主要作用是设置写那一块区域的图像缓存,说白了就是设置你的图片显示在哪里


Python
def init(self):     # EPD hardware init start          self.reset()          self.ReadBusy()     self.send_command(0x12)  #SWRESET     self.ReadBusy()           self.send_command(0x01#Driver output control           self.send_data(0x27)     self.send_data(0x01)     self.send_data(0x00)             self.send_command(0x11#data entry mode            self.send_data(0x03)          self.SetWindow(00self.width-1self.height-1)          self.send_command(0x21#  Display update control     self.send_data(0x00)     self.send_data(0x80)              self.SetCursor(00)     self.ReadBusy()     self.SetLut_by_host(self.WS_20_30) # EPD hardware init end return 0

初始化函数,具体流程图可以在墨水屏的说明书中找到

捕获.jpg

Python
def SetFrameMemory(self,image_buffer,x,y,w,h): # x point must be the multiple of 8 or the last 3 bits will be ignored x = x & 0xF8 w = w & 0xF8 if image_buffer==[] or x < 0 or w < 0 or y < 0 or h < 0: return if (x + w >= self.width): x_end = self.width - 1 else: x_end = x + w - 1 if (y + h >= self.height): y_end = self.height - 1 else: y_end = y + h - 1 self.SetWindow(x, y, x_end, y_end) self.SetCursor(x, y) self.send_command(0x24) for j in range(0int(y_end - y + 1)): for i in range(0int((x_end - x + 1) / 8)): self.send_data(image_buffer[i + j * int(w / 8)]) def SetFrameMemory_Partial(self,image_buffer,x,y,w,h): if image_buffer==[] or x < 0 or w < 0 or y < 0 or h < 0: return x &= 0xF8 w &= 0xF8 if (x + w >= self.width): x_end = self.width - 1 else: x_end = x + w - 1 if (y + h >= self.height): y_end = self.height - 1 else: y_end = y + h - 1 self.rst.value(0) sleep_ms(5) self.rst.value(1) sleep_ms(5) self.SetLut(self._WF_PARTIAL_2IN9) self.send_command(0x37) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x40) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_command(0x3C#BorderWavefrom self.send_data(0x80) self.send_command(0x22) self.send_data(0xC0) self.send_command(0x20) sleep_ms(100) #self.ReadBusy() self.SetWindow(x, y, x_end, y_end) self.SetCursor(x, y) self.send_command(0x24) for j in range(0,int(y_end - y + 1)): for i in range(0,int((x_end - x + 1) / 8)): self.send_data(image_buffer[i + j * int(w / 8)]) def SetFrameMemory_Base(self, image_buffer): self.SetWindow(00,self.width - 1,self.height - 1) self.SetCursor(00) self.send_command(0x24) for i in range(0self.width/8*self.height): self.send_data(image_buffer[i]) self.send_command(0x26) for i in range(0self.width/8*self.height): self.send_data(image_buffer[i])

设置帧内存,分别对应全刷,局刷和显示背景,背景图片必须是296*128像素的

注意:这里缩进由于复制出问题了

这是清空帧内存,类似于清屏

Python
def ClearFrameMemory(self,color):     self.SetWindow(00,self.width - 1,self.height - 1)     self.SetCursor(00)     self.send_command(0x24)     for i in range(0,self.width/8*self.height):     self.send_data(color)

这个color输入的必须是0xFF或0x00(前者是全部刷白,后者是全部刷黑)

睡眠模式:长时间不刷新需要调为睡眠模式

Python
def sleep(self):     self.send_command(0x10# DEEP_SLEEP_MODE     self.send_data(0x01)

注意:唤醒后需要重新进行初始化

到这里驱动就写完了,毫不夸张的说,网上能找到的驱动很少,功能也没这么全(基本上都不支持局刷),但刚入门编写驱动难度较大,大家可以根据这套编写其他尺寸墨水屏的驱动。毕竟授之以鱼不如授之以渔。

三  显示图片

捕获.jpg

墨水屏显示的内容必须转成十六进制形式,需要用到图片取模软件(附件里也有)

首先用Windows自带的画图创一个128*296大小的图片,画完后另存为.bmp(单位色图)格式。

捕获.jpg

打开附件里的PCtoLCD2002,先要设置输出格式

捕获.jpg

提示:请仔细检查设置的内容和上图是否一致,否则可能导致报错或显示错误

然后点击文件->打开,打开前面保持的图像点击生成字模,然后在micropython设备中新建一个bg_data.py,将输出的内容放在一个列表里

捕获.jpg

新建test.py

Python
from machine import SPI,Pin from e2in9 import EPD from bg_data import BG_DATA spi=SPI(1,32000,sck=Pin(14),mosi=Pin(13)) display=EPD(spi,22,21,17,16) display.SetFrameMemory_Base(BG_DATA) display.TurnOnDisplay()

运行,这时屏幕上应该已经显示出了内容

00:00
00:00
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
f0b23016c484ebd27a2e2a1852b4f3fb.mp4  点击下载

然后试试局刷

00:00
00:00
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
5d01ffd4c26e90f2d6fae196e977f14f.mp4  点击下载

原来图片的残影怎么出来了???

总体来说算成功了


四 进阶内容:墨水屏时钟摆件

TI.jpg


资料:

attachment icon epaper.zip 23.63MB ZIP 329次下载


来自:电子信息 / 电子技术严肃内容:教程/课程
1
8
新版本公告
~~空空如也
Ma3.02的守望
2年1个月前 IP:安徽
917975

楼主用墨水屏的刷屏帧率大概能到多少呢?无明显残影的情况下。盖格计一类的省电仪器,搭配墨水屏也是不错的组合。


残影是墨水屏的固有问题,好像是需要间隔刷白屏才能完全消除,网上看过一些新工艺墨水屏可以把残影控制的比较好。

引用
评论(1)
4
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
XZH100
进士 机友 笔友
文章
13
回复
81
学术分
0
2022/03/20注册,19时0分前活动

愚者千虑,必有一得。屡战屡败,终有一胜。

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:上海
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的