""" MicroPython Good Display GDEQ042T81 (GDEY042T81) Based on MicroPython Waveshare 4.2" Black/White GDEW042T2 e-paper display driver https://github.com/mcauser/micropython-waveshare-epaper licensed under the MIT License Copyright (c) 2017 Waveshare Copyright (c) 2018 Mike Causer """ """ MicroPython Good Display GDEQ042T81 (GDEY042T81) e-paper display driver MIT License Copyright (c) 2024 Martin Maly Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from micropython import const from time import sleep_ms try: from buzzer import system_buzzer except ImportError: system_buzzer = None # Display resolution EPD_WIDTH = const(400) EPD_HEIGHT = const(300) BUSY = const(0) # 0=busy, 1=idle class EPD: def __init__(self, spi, cs, dc, rst, busy): self.spi = spi self.cs = cs self.dc = dc self.rst = rst self.busy = busy self.cs.init(self.cs.OUT, value=1) self.dc.init(self.dc.OUT, value=0) self.rst.init(self.rst.OUT, value=0) self.busy.init(self.busy.IN) self.width = EPD_WIDTH self.height = EPD_HEIGHT self.powered = False self.init_done = False self.hibernate = True self.use_fast_update = True # 添加刷新计数器,用于控制全屏刷新频率 self.refresh_count = 0 # 每N次局部刷新后执行一次全屏刷新,防止残影积累 self.partial_refresh_limit = 5 # 强制全屏刷新标志 self.force_full_refresh = False def _command(self, command, data=None): self.dc(0) self.cs(0) self.spi.write(bytearray([command])) self.cs(1) if data is not None: self._data(data) def _data(self, data): self.dc(1) self.cs(0) self.spi.write(data) self.cs(1) def _ndata(self, data): self._data(bytearray([data])) def pwr_on(self): if self.powered == False: self._command(0x22, b'\xe0') self._command(0x20) self.wait_until_idle() self.powered = True def pwr_off(self): if self.powered == True: self._command(0x22, b'\x83') self._command(0x20) self.wait_until_idle() self.powered = False #set partial def set_partial(self, x, y, w, h): self._command(0x11,b'\x03') self._command(0x44) self._ndata(x // 8) self._ndata((x + w - 1) // 8) self._command(0x45) self._ndata(y % 256) self._ndata(y // 256) self._ndata((y+h-1) % 256) self._ndata((y+h-1) // 256) self._command(0x4E) self._ndata(x // 8) self._command(0x4F) self._ndata(y % 256) self._ndata(y // 256) def init(self): if self.hibernate==True: self.reset() sleep_ms(100) #self.wait_until_idle() self._command(const(0x12)) #SWRESET self.wait_until_idle() # 优化驱动初始化参数,确保与GDEY042T81规格匹配 self._command(0x01,b'\x2B\x01\x00') #MUX 设置 self._command(0x21,b'\x40\x00') # 显示更新控制 self._command(0x3C,b'\x05') # 边界波形控制,减少残影 self._command(0x18,b'\x80') # 读取内部温度传感器 self._command(0x0C,b'\x8B\x00\x00') # 设置开始和结束阶段,优化刷新 self.set_partial(0, 0, self.width, self.height) self.init_done = True def wait_until_idle(self): while self.busy.value() == BUSY: sleep_ms(100) print("等待墨水屏空闲") def reset(self): self.rst(0) sleep_ms(200) self.rst(1) sleep_ms(200) def update_full(self): #update Full print("执行全屏更新") self._command(0x21,b'\x40\x00') if self.use_fast_update == False: self._command(0x22,b'\xf7') else: self._command(0x1A, b'\x64') # 快速刷新设置 self._command(0x22,b'\xd7') self._command(0x20) self.wait_until_idle() print("更新完成", self.busy.value()) # 添加专门的全屏刷新方法,用于清除残影 def clear_screen(self, double_refresh=True): """执行全屏刷新以清除残影 参数: double_refresh: 是否执行两次刷新以彻底清除残影,默认为True """ if double_refresh: print("执行双次全屏刷新以彻底清除残影") else: print("执行单次全屏刷新以清除残影") # 创建全白缓冲区 white_buffer = bytearray(self.width * self.height // 8) for i in range(len(white_buffer)): white_buffer[i] = 0xFF # 全白 # 先写入全白数据 self.set_partial(0, 0, self.width, self.height) self.write_image(0x24, white_buffer, True, True) # 执行第一次全屏刷新 self._command(0x21,b'\x40\x00') self._command(0x22,b'\xf7') # 使用完整刷新模式 self._command(0x20) self.wait_until_idle() # 如果启用双次刷新,再执行一次 if double_refresh: print("执行第二次全屏刷新") self._command(0x21,b'\x40\x00') self._command(0x22,b'\xf7') # 使用完整刷新模式 self._command(0x20) self.wait_until_idle() # 重置刷新计数器 self.refresh_count = 0 self.force_full_refresh = False print("全屏刷新完成") def write_image(self, command, bitmap, mirror_x, mirror_y): sleep_ms(1) h = self.height w = self.width bpl = w // 8 # bytes per line self._command(command) for i in range(0, h): for j in range(0, bpl): idx = ((bpl-j-1) if mirror_x else j) + ((h-i-1) if mirror_y else i) * bpl self._ndata(bitmap[idx]) def write_value(self, command, value): sleep_ms(1) h = self.height w = self.width bpl = w // 8 # bytes per line self._command(command) for i in range(0, h): for j in range(0, bpl): self._ndata(value) # 修改显示方法,添加刷新控制逻辑 def display_frame(self, frame_buffer, partial=False, x=0, y=0, w=None, h=None, global_refresh=False): """显示帧缓冲区内容 参数: frame_buffer: 要显示的帧缓冲区数据 partial: 是否使用局部刷新模式,默认为False x: 局部刷新的起始X坐标,默认为0 y: 局部刷新的起始Y坐标,默认为0 w: 局部刷新的宽度,默认为全屏宽度 h: 局部刷新的高度,默认为全屏高度 global_refresh: 是否使用全局刷新模式,默认为False """ print("显示帧缓冲区") if self.init_done==False: self.init() # 如果未指定宽高,则使用全屏 if w is None: w = self.width if h is None: h = self.height # 检查是否需要强制全屏刷新 need_clear_screen = self.force_full_refresh or self.refresh_count >= self.partial_refresh_limit # 如果使用全局刷新模式,则设置全屏刷新区域 if global_refresh: self.set_partial(0, 0, self.width, self.height) elif need_clear_screen: self.clear_screen() # 设置局部刷新区域 self.set_partial(x, y, w, h) else: # 设置局部刷新区域 self.set_partial(x, y, w, h) # 写入图像数据 self.write_image(0x24, frame_buffer, True, True) # 播放等待音效 (异步) if system_buzzer: system_buzzer.play_process_async() # 执行刷新 if global_refresh: # 全局刷新模式 print("执行全局刷新模式") self._command(0x21,b'\x40\x00') self._command(0x22,b'\xf7') # 使用完整刷新模式 self._command(0x20) self.wait_until_idle() self.refresh_count = 0 print("全局刷新完成,重置计数器") elif need_clear_screen: # 已经通过clear_screen()执行了刷新,这里不需要再次刷新 print("已通过clear_screen完成全屏刷新,跳过重复刷新") self.refresh_count = 0 elif partial and not self.force_full_refresh and self.refresh_count < self.partial_refresh_limit: # 局部刷新模式 print("执行局部刷新模式") self._command(0x21,b'\x40\x00') self._command(0x1A, b'\x64') # 快速刷新设置 self._command(0x22,b'\xd7') # 局部刷新命令 self._command(0x20) self.wait_until_idle() self.refresh_count += 1 print(f"局部刷新完成,刷新计数: {self.refresh_count}/{self.partial_refresh_limit}") else: # 全屏刷新模式 print("执行全屏刷新模式") self.update_full() self.refresh_count = 0 print("全屏刷新完成,重置计数器") # 添加强制全屏刷新的方法 def force_refresh(self): """强制执行下一次全屏刷新,清除所有残影""" self.force_full_refresh = True print("已设置强制全屏刷新标志") # to wake call reset() or init() def sleep(self): self.pwr_off() self._command(0x10, b'\x01') self.init_done = False self.hibernate = True