Badged 2040 UC8151 display driver: setup & refresh test.

This commit is contained in:
antirez
2024-03-14 17:43:08 +01:00
commit f5c595a874
2 changed files with 264 additions and 0 deletions

244
uc8151.py Normal file
View File

@@ -0,0 +1,244 @@
# MicroPython driver for the UC8151 e-paper display used in the
# Badger 2040.
#
# Copyright(C) 2024 Salvatore Sanfilippo <antirez@gmail.com>
# MIT license.
from machine import Pin
import time, framebuf
### Commands list.
# Commands are executed putting the DC line in command mode
# and sending the command as first byte, followed if needed by
# the data arguments (but with DC in data mode).
CMD_PSR = const(0x00)
CMD_PWR = const(0x01)
CMD_POF = const(0x02)
CMD_PFS = const(0x03)
CMD_PON = const(0x04)
CMD_PMES = const(0x05)
CMD_BTST = const(0x06)
CMD_DSLP = const(0x07)
CMD_DTM1 = const(0x10)
CMD_DSP = const(0x11)
CMD_DRF = const(0x12)
CMD_DTM2 = const(0x13)
CMD_LUT_VCOM = const(0x20)
CMD_LUT_WW = const(0x21)
CMD_LUT_BW = const(0x22)
CMD_LUT_WB = const(0x23)
CMD_LUT_BB = const(0x24)
CMD_PLL = const(0x30)
CMD_TSC = const(0x40)
CMD_TSE = const(0x41)
CMD_TSR = const(0x43)
CMD_TSW = const(0x42)
CMD_CDI = const(0x50)
CMD_LPD = const(0x51)
CMD_TCON = const(0x60)
CMD_TRES = const(0x61)
CMD_REV = const(0x70)
CMD_FLG = const(0x71)
CMD_AMV = const(0x80)
CMD_VV = const(0x81)
CMD_VDCS = const(0x82)
CMD_PTL = const(0x90)
CMD_PTIN = const(0x91)
CMD_PTOU = const(0x92)
CMD_PGM = const(0xa0)
CMD_APG = const(0xa1)
CMD_ROTP = const(0xa2)
CMD_CCSET = const(0xe0)
CMD_PWS = const(0xe3)
CMD_TSSET = const(0xe5)
### Register values
# PSR
RES_96x230 = const(0b00000000)
RES_96x252 = const(0b01000000)
RES_128x296 = const(0b10000000)
RES_160x296 = const(0b11000000)
LUT_OTP = const(0b00000000)
LUT_REG = const(0b00100000)
FORMAT_BWR = const(0b00000000)
FORMAT_BW = const(0b00010000)
SCAN_DOWN = const(0b00000000)
SCAN_UP = const(0b00001000)
SHIFT_LEFT = const(0b00000000)
SHIFT_RIGHT = const(0b00000100)
BOOSTER_OFF = const(0b00000000)
BOOSTER_ON = const(0b00000010)
RESET_SOFT = const(0b00000000)
RESET_NONE = const(0b00000001)
# PWR
VDS_EXTERNAL = const(0b00000000)
VDS_INTERNAL = const(0b00000010)
VDG_EXTERNAL = const(0b00000000)
VDG_INTERNAL = const(0b00000001)
VCOM_VD = const(0b00000000)
VCOM_VG = const(0b00000100)
VGHL_16V = const(0b00000000)
VGHL_15V = const(0b00000001)
VGHL_14V = const(0b00000010)
VGHL_13V = const(0b00000011)
# BOOSTER
START_10MS = const(0b00000000)
START_20MS = const(0b01000000)
START_30MS = const(0b10000000)
START_40MS = const(0b11000000)
STRENGTH_1 = const(0b00000000)
STRENGTH_2 = const(0b00001000)
STRENGTH_3 = const(0b00010000)
STRENGTH_4 = const(0b00011000)
STRENGTH_5 = const(0b00100000)
STRENGTH_6 = const(0b00101000)
STRENGTH_7 = const(0b00110000)
STRENGTH_8 = const(0b00111000)
OFF_0_27US = const(0b00000000)
OFF_0_34US = const(0b00000001)
OFF_0_40US = const(0b00000010)
OFF_0_54US = const(0b00000011)
OFF_0_80US = const(0b00000100)
OFF_1_54US = const(0b00000101)
OFF_3_34US = const(0b00000110)
OFF_6_58US = const(0b00000111)
# PFS
FRAMES_1 = const(0b00000000)
FRAMES_2 = const(0b00010000)
FRAMES_3 = const(0b00100000)
FRAMES_4 = const(0b00110000)
# TSE
TEMP_INTERNAL = const(0b00000000)
TEMP_EXTERNAL = const(0b10000000)
OFFSET_0 = const(0b00000000)
OFFSET_1 = const(0b00000001)
OFFSET_2 = const(0b00000010)
OFFSET_3 = const(0b00000011)
OFFSET_4 = const(0b00000100)
OFFSET_5 = const(0b00000101)
OFFSET_6 = const(0b00000110)
OFFSET_7 = const(0b00000111)
OFFSET_MIN_8 = const(0b00001000)
OFFSET_MIN_7 = const(0b00001001)
OFFSET_MIN_6 = const(0b00001010)
OFFSET_MIN_5 = const(0b00001011)
OFFSET_MIN_4 = const(0b00001100)
OFFSET_MIN_3 = const(0b00001101)
OFFSET_MIN_2 = const(0b00001110)
OFFSET_MIN_1 = const(0b00001111)
# PLL flags
HZ_29 = const(0b00111111)
HZ_33 = const(0b00111110)
HZ_40 = const(0b00111101)
HZ_50 = const(0b00111100)
HZ_67 = const(0b00111011)
HZ_100 = const(0b00111010)
HZ_200 = const(0b00111001)
class UC8151:
UPDATE_SPEED_DEFAULT=const(0)
UPDATE_SPEED_MEDIUM=const(1)
UPDATE_SPEED_FAST=const(2)
UPDATE_SPEED_TURBO=const(3)
def __init__(self,spi,*,cs,dc,rst,busy,speed=UPDATE_SPEED_DEFAULT,mirror_x=False,mirror_y=False,inverted=False):
self.spi = spi
self.cs = Pin(cs,Pin.OUT) if cs != None else None
self.dc = Pin(dc,Pin.OUT) if dc != None else None
self.rst = Pin(rst,Pin.OUT) if rst != None else None
self.busy = Pin(busy,Pin.IN) if busy != None else None
self.speed = speed
self.inverted = inverted
self.mirror_x = mirror_x
self.mirror_y = mirror_y
self.initialize_display()
self.raw_fb = bytearray(128*296//8)
self.fb = framebuf.FrameBuffer(self.raw_fb,128,296,framebuf.MONO_HLSB)
# While till the BUSY pin is high (chip no longer busy), and
# the chip completed its operation.
def wait_ready(self):
if self.busy == None: return
while not self.busy.value(): pass
# Perform hardware reset.
def reset(self):
self.rst.off()
time.sleep_ms(10)
self.rst.on()
time.sleep_ms(10)
self.wait_ready()
# Send just a command, just data, or a command + data, depending
# on cmd or data being both bytes() / bytearrays() or None.
def write(self,cmd=None,data=None):
self.cs.off()
self.dc.off() # Command mode
self.spi.write(bytes([cmd]))
if data:
if isinstance(data,int): data = bytes([data])
if isinstance(data,list): data = bytes(data)
self.dc.on() # Data mode
self.spi.write(data)
self.cs.on()
def initialize_display(self):
self.reset()
psr_settings = RES_128x296 | FORMAT_BW | BOOSTER_ON | RESET_NONE
# If we select the default update speed, we will use the
# lookup tables defined by the device. Otherwise the values for
# the lookup tables must be read from the registers we set.
if self.speed == UPDATE_SPEED_DEFAULT:
psr_settings |= LUT_OTP
else:
psr_settings |= LUT_REG
psr_settings |= SHIFT_LEFT if self.mirror_x else SHIFT_RIGHT
psr_settings |= SCAN_DOWN if self.mirror_y else SCAN_UP
self.write(CMD_PSR,psr_settings)
self.write(CMD_PWR, \
[VDS_INTERNAL|VDG_INTERNAL,
VCOM_VD|VGHL_16V,
0b101011,
0b101011,
0b101011])
self.write(CMD_PON)
self.wait_ready()
# Booster soft start configuration.
self.write(CMD_BTST, \
[START_10MS | STRENGTH_3 | OFF_6_58US,
START_10MS | STRENGTH_3 | OFF_6_58US,
START_10MS | STRENGTH_3 | OFF_6_58US])
self.write(CMD_PFS,FRAMES_1)
self.write(CMD_TSE,TEMP_INTERNAL | OFFSET_0)
self.write(CMD_TCON,0x22)
self.write(CMD_CDI,0b10_01_1100 if self.inverted else 0b01_00_1100)
self.write(CMD_PLL,HZ_100)
self.write(CMD_PON)
self.wait_ready()
def update(self):
self.write(CMD_PON)
self.write(CMD_PTOU)
self.write(CMD_DTM2,self.raw_fb)
self.write(CMD_DSP)
self.write(CMD_DRF) # Refresh
if __name__ == "__main__":
from machine import SPI
spi = SPI(0, baudrate=12000000, phase=0, polarity=0, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
eink = UC8151(spi,cs=17,dc=20,rst=21,busy=26)
eink.fb.ellipse(10,10,10,10,1)
eink.fb.ellipse(50,50,10,10,1)
eink.fb.text("SUKA",80,80,1)
eink.update()