楼主用墨水屏的刷屏帧率大概能到多少呢?无明显残影的情况下。盖格计一类的省电仪器,搭配墨水屏也是不错的组合。
残影是墨水屏的固有问题,好像是需要间隔刷白屏才能完全消除,网上看过一些新工艺墨水屏可以把残影控制的比较好。
一 开发板
开发板推荐选择NodeMCU-32s
micropython固件烧写请看:固件烧写与测试
(固件,Tonny,驱动和串口工具在文末附件里有)
二 墨水屏及驱动
墨水屏我使用的是微雪的2.9寸屏
也可以选择其他的,只要兼容微雪就行
然后是整个项目里最难的部分,写驱动。由于微雪官方给的驱动只有arduino和树莓派的,所以micropython的驱动需要自己编写。我写了大概快两周。
from micropython import const
from time import sleep_ms
import ustruct
from machine import Pin
import framebuf
EPD_WIDTH = 128
EPD_HEIGHT = 296
先导入需要的库并定义屏幕宽高
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类并初始化(初始化函数在后面)
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引脚一个低电平脉冲
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写入
def ReadBusy(self):
while self.busy == 1: # 0: idle, 1: busy
sleep_ms(10)
由于墨水屏刷新需要一段时间,所以需要是否忙,如果busy脚是高电平就需要等待
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()
全局刷新和局部刷新的函数
def SetLut(self, lut):
self.send_command(0x32)
for i in range(0, 153):
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表在附件里的驱动有,作用是设置全刷还是局刷模式
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()
这两个函数主要作用是设置写那一块区域的图像缓存,说白了就是设置你的图片显示在哪里
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(0, 0, self.width-1, self.height-1)
self.send_command(0x21) # Display update control
self.send_data(0x00)
self.send_data(0x80)
self.SetCursor(0, 0)
self.ReadBusy()
self.SetLut_by_host(self.WS_20_30)
# EPD hardware init end
return 0
初始化函数,具体流程图可以在墨水屏的说明书中找到
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(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_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(0, 0,self.width - 1,self.height - 1)
self.SetCursor(0, 0)
self.send_command(0x24)
for i in range(0, self.width/8*self.height):
self.send_data(image_buffer[i])
self.send_command(0x26)
for i in range(0, self.width/8*self.height):
self.send_data(image_buffer[i])
设置帧内存,分别对应全刷,局刷和显示背景,背景图片必须是296*128像素的
注意:这里缩进由于复制出问题了
这是清空帧内存,类似于清屏
def ClearFrameMemory(self,color):
self.SetWindow(0, 0,self.width - 1,self.height - 1)
self.SetCursor(0, 0)
self.send_command(0x24)
for i in range(0,self.width/8*self.height):
self.send_data(color)
这个color输入的必须是0xFF或0x00(前者是全部刷白,后者是全部刷黑)
睡眠模式:长时间不刷新需要调为睡眠模式
def sleep(self):
self.send_command(0x10) # DEEP_SLEEP_MODE
self.send_data(0x01)
注意:唤醒后需要重新进行初始化
到这里驱动就写完了,毫不夸张的说,网上能找到的驱动很少,功能也没这么全(基本上都不支持局刷),但刚入门编写驱动难度较大,大家可以根据这套编写其他尺寸墨水屏的驱动。毕竟授之以鱼不如授之以渔。
三 显示图片
墨水屏显示的内容必须转成十六进制形式,需要用到图片取模软件(附件里也有)
首先用Windows自带的画图创一个128*296大小的图片,画完后另存为.bmp(单位色图)格式。
打开附件里的PCtoLCD2002,先要设置输出格式
提示:请仔细检查设置的内容和上图是否一致,否则可能导致报错或显示错误
然后点击文件->打开,打开前面保持的图像点击生成字模,然后在micropython设备中新建一个bg_XXXXXXX,将输出的内容放在一个列表里
新建XXXXXXX
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()
运行,这时屏幕上应该已经显示出了内容
f0b23016c484ebd27a2e2a1852b4f3fb.mp4 点击下载
然后试试局刷
5d01ffd4c26e90f2d6fae196e977f14f.mp4 点击下载
原来图片的残影怎么出来了???
总体来说算成功了
四 进阶内容:墨水屏时钟摆件
资料:
200字以内,仅用于支线交流,主线讨论请采用回复功能。