一 开发板
开发板推荐选择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 点击下载
原来图片的残影怎么出来了???
总体来说算成功了
四 进阶内容:墨水屏时钟摆件
资料:
时段 | 个数 |
---|---|
{{f.startingTime}}点 - {{f.endTime}}点 | {{f.fileCount}} |