e-Paper
e-Paper copied to clipboard
Request: EPD_7in5_V2 Partial refresh (Python)
Hello, it's possible to implement partial refresh on a 7 inch display? Which steps are needed to implement this?
Hello, there is currently no official way to implement partial refresh on thie screen.
Hi, i implemented this some months ago, now i paused the project, but i will make a merge request. My partial refresh works, but i don't know if it's safe for the display.
Please submit PR. I'd like to see it.
@Sbatushe it would be great if you could share your work with us :-)
I'm not a Github expert, how i can i send the code? Merge request? I need to power on My Pi and check the code.
First fork the Repo, place your altered files in there and then commit and push it to Github. Then you could provide your Fork here and may fill a Pull request.
You could also tell us how you achieved the partial refresh and post the code you changes as comment in this issue.
Anything would be really appreciated!
Ok ok, only problem is to clean up my project, i'll upload it when it's a bit cleaner, however to obtain the partial refresh i changed some parameter in the init phase (probably the clock speed) and then removed unnecessary steps in the lut, that's it
Sorry for the wait, this is my modified library. It's a mess and it uses 4inV2 code mixed with 7inV2
import logging
from . import epdconfig
# Display resolution
EPD_WIDTH = 800
EPD_HEIGHT = 480
class EPD:
lut_vcom0 = [
0x00, 0x17, 0x00, 0x00, 0x00, 0x02,
0x00, 0x17, 0x17, 0x00, 0x00, 0x02,
0x00, 0x0A, 0x01, 0x00, 0x00, 0x01,
0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_ww = [
0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
0x40, 0x0A, 0x01, 0x00, 0x00, 0x01,
0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bw = [
0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
0x40, 0x0A, 0x01, 0x00, 0x00, 0x01,
0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_wb = [
0x80, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
0x80, 0x0A, 0x01, 0x00, 0x00, 0x01,
0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bb = [
0x80, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
0x80, 0x0A, 0x01, 0x00, 0x00, 0x01,
0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
#-----------PARTIAL REFRESH HOMEMADE -----------------------------------------------------
EPD_4IN2_Partial_lut_vcom1 =[
0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]
EPD_4IN2_Partial_lut_ww1 =[
0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
EPD_4IN2_Partial_lut_bw1 =[
0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
EPD_4IN2_Partial_lut_wb1 =[
0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
EPD_4IN2_Partial_lut_bb1 =[
0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
#--------------------------------------------------------------------------------------------
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(2)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
logging.debug("e-Paper busy")
self.send_command(0x71)
busy = epdconfig.digital_read(self.busy_pin)
while(busy == 0):
self.send_command(0x71)
busy = epdconfig.digital_read(self.busy_pin)
epdconfig.delay_ms(200)
def set_lut(self):
self.send_command(0x20) # vcom
for count in range(0, 44):
self.send_data(self.lut_vcom0[count])
self.send_command(0x21) # ww --
for count in range(0, 42):
self.send_data(self.lut_ww[count])
self.send_command(0x22) # bw r
for count in range(0, 42):
self.send_data(self.lut_bw[count])
self.send_command(0x23) # wb w
for count in range(0, 42):
self.send_data(self.lut_bb[count])
self.send_command(0x24) # bb b
for count in range(0, 42):
self.send_data(self.lut_wb[count])
def Partial_SetLut(self):
self.send_command(0x20);
for count in range(0, 44):
self.send_data(self.EPD_4IN2_Partial_lut_vcom1[count])
self.send_command(0x21);
for count in range(0, 42):
self.send_data(self.EPD_4IN2_Partial_lut_ww1[count])
self.send_command(0x22);
for count in range(0, 42):
self.send_data(self.EPD_4IN2_Partial_lut_bw1[count])
self.send_command(0x23);
for count in range(0, 42):
self.send_data(self.EPD_4IN2_Partial_lut_wb1[count])
self.send_command(0x24);
for count in range(0, 42):
self.send_data(self.EPD_4IN2_Partial_lut_bb1[count])
def init(self):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.send_command(0x01) # POWER SETTING
self.send_data(0x07) # VSR_EN=1, VS_EN=1, VG_EN=1
self.send_data(0x07) # VGH=20V,VGL=-20V
self.send_data(0x3f) # VDH=15V
self.send_data(0x3f) # VDL=-15V
self.send_command(0x04) # POWER ON
epdconfig.delay_ms(100)
self.ReadBusy()
self.send_command(0X00) # PANNEL SETTING
self.send_data(0xBF) # KW-3f KWR-2F BWROTP 0f BWOTP 1f
self.send_command(0x30) # PLL setting
self.send_data(0x3c) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ
self.send_command(0x61) # tres
self.send_data(0x03) # source 800
self.send_data(0x20)
self.send_data(0x01) # gate 480
self.send_data(0xE0)
self.send_command(0X15) # DUAL SPI
self.send_data(0x00) # MM_EN, DUSPI_EN disabled
self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING
self.send_data(0x10)
self.send_data(0x07)
self.send_command(0X60) # TCON SETTING
self.send_data(0x22)
#self.set_lut()
self.Partial_SetLut()
# EPD hardware init end
return 0
def getbuffer(self, image):
# logging.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width/8) * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
# logging.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if(imwidth == self.width and imheight == self.height):
logging.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif(imwidth == self.height and imheight == self.width):
logging.debug("Horizontal")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
def display(self, image):
#self.set_lut()
self.Partial_SetLut()
self.send_command(0x13) #START TRASMISSION 2 (red)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(~image[i]);
self.send_command(0x12) #DISPLAY REFRESH
epdconfig.delay_ms(100)
self.ReadBusy()
def display_window(self, image, height):
self.send_command(0x13)
for i in range(0, int(self.width * height / 8)):
self.send_data(~image[i]);
self.send_command(0x12)
epdconfig.delay_ms(100)
self.ReadBusy()
def Clear(self):
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0x00)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0x00)
self.send_command(0x12)
epdconfig.delay_ms(100)
self.ReadBusy()
def sleep(self):
self.send_command(0x02) # POWER_OFF
self.ReadBusy()
self.send_command(0x07) # DEEP_SLEEP
self.send_data(0XA5)
epdconfig.module_exit()
### END OF FILE ###
@Sbatushe thanks for that code, it actually worked for me straight as it is.
Do you know if it's still necessary or desirable to do any sort of full-screen refresh in the old way too? And if so, is there an easy way of calling the "old" display method? I'm finding that manually sending a full-black screen followed by a full-while screen is much slower than the old process.
I take it back a bit. That modified lib doesn't always seem to work. I found that in many redraw scenarios it just results in a corrupted screen. I've not yet been able to work out the steps to reproduce though. Seems to be that I've drawn an image to the screen once just fine, but later changes result in only partial updates and then fading elements. I'll try and create a test-case not embedded in all my crap.
"For E-paper displays that support partial refresh, please note that you cannot refresh them with the partial refresh mode all the time. After refreshing partially several times, you need to fully refresh EPD once. Otherwise, the display effect will be abnormal, which cannot be repaired!"
Taken from https://www.waveshare.com/wiki/4.2inch_e-Paper_Module_(B)_Manual
@takosalad that doesn't really relate though. I can't do any partial refreshes without subsequent draws corrupting. That wasn't the case with the old code.
@SSYYL, @waveshare Does the screen support partial update physicaly at all?
The chip that is used to drive the display (GD7965) does have two bits of memory per pixel, so as long as you can get it into a mode where it reads from one bank of memory and sets the other to that (so it knows what the display is currently showing) that should work.