e-Paper icon indicating copy to clipboard operation
e-Paper copied to clipboard

Request: EPD_7in5_V2 Partial refresh (Python)

Open Sbatushe opened this issue 3 years ago • 14 comments

Hello, it's possible to implement partial refresh on a 7 inch display? Which steps are needed to implement this?

Sbatushe avatar Aug 04 '22 20:08 Sbatushe

Hello, there is currently no official way to implement partial refresh on thie screen.

SSYYL avatar Sep 15 '22 08:09 SSYYL

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.

Sbatushe avatar Sep 15 '22 08:09 Sbatushe

Please submit PR. I'd like to see it.

SSYYL avatar Sep 15 '22 08:09 SSYYL

@Sbatushe it would be great if you could share your work with us :-)

chellmann avatar Oct 07 '22 18:10 chellmann

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.

Sbatushe avatar Oct 10 '22 17:10 Sbatushe

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!

chellmann avatar Oct 10 '22 18:10 chellmann

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

Sbatushe avatar Oct 13 '22 14:10 Sbatushe

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 avatar Nov 28 '22 23:11 Sbatushe

@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.

Cylindric avatar Mar 14 '23 09:03 Cylindric

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.

Cylindric avatar Mar 16 '23 09:03 Cylindric

"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 avatar Oct 03 '23 12:10 takosalad

@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.

Cylindric avatar Oct 03 '23 15:10 Cylindric

@SSYYL, @waveshare Does the screen support partial update physicaly at all?

va1m avatar Jan 14 '24 21:01 va1m

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.

mbartlett21 avatar Jan 14 '24 21:01 mbartlett21