micropython-waveshare-epaper icon indicating copy to clipboard operation
micropython-waveshare-epaper copied to clipboard

2.13 The screen does not display properly

Open LilyGO opened this issue 5 years ago • 21 comments

Board:ESP32

Display:Waveshare Ink 2.13inch/2.9inch

I modified your 4.2 part of the code and modified some of the pins. When it worked with epaper2in9.py, my display worked perfectly.

IMG_20190330_170122

When I repeat the same work, the 2.13 screen keeps refreshing, but it doesn't display properly.

eweq

1.I applied and checked epaper2in13, EPD_init is no problem, it can flash normally

2.I commented on "loading the image" and just let him fill_color(white/black). But when the last step is shown, he will fail.

3.I preliminarily speculated that the problem should occur in "framebuf"

image

LilyGO avatar Apr 01 '19 01:04 LilyGO

I'm facing the same issue here using an esp8266 with Waveshare ePaper 2.13inch display. 😢

alvarowolfx avatar Apr 06 '19 22:04 alvarowolfx

Display:Waveshare Ink 2.13inch/2.9inch (Wemos T5 V2 TTGO)

https://www.banggood.com/Wemos-T5-V2_0-TTGO-WiFi-Wireless-Module-bluetooth-Base-ESP-32-2_13-ePaper-Display-Development-Board-p-1332909.html?rmmds=myorder&cur_warehouse=CN

I have the same issue. The display blinks but always keeps the previous image. (It seems it is not clearing / setting the memory)

ygbr avatar Apr 12 '19 03:04 ygbr

Hi, @LilyGO have you managed to make it work on the 2.13” display?

ygbr avatar Apr 14 '19 17:04 ygbr

I have the same problem. Did anyone solved it?

Werner-G avatar Jan 13 '20 15:01 Werner-G

I have same problem with a 2.13" display.

If I'm doing e.display_frame() within a while block I'm able to see a gray rectangle and the e.init() it's clearing it. But I'm not able to draw anything on it. Any help on this would be appreciated .

fxmike08 avatar Feb 05 '20 13:02 fxmike08

There are three versions of the display in the field - GDEH0213B1 / B72 and B73 using different Controllers (SSD16XX or IL3897). I don't know if modifications of the driver are necessary. @ fxmike08: can you please post your complete code just to be sure to have the same setup?

Werner-G avatar Feb 07 '20 14:02 Werner-G

@Werner-G I think we have same hardware setup. Found a thread of you ... I have Lolin d32 with the above display connected view tft cable.

I have a display with IL3897 controller. Tested with following code and it's working. I'll try to port the arduino code to micropython.

fxmike08 avatar Feb 09 '20 07:02 fxmike08

Sounds good - I hope you will be successful!

Werner-G avatar Feb 09 '20 12:02 Werner-G

For IL3897 controller, working with Lolin D32 PRO connected via TFT : epaper2in13.py

from micropython import const
from machine import SPI, Pin
from time import sleep_ms
import ustruct

# Display resolution
EPD_WIDTH  = const(250)
EPD_HEIGHT = const(128)
# datasheet says 250x122 (increased to 128 to be multiples of 8)

# Display commands
DRIVER_OUTPUT_CONTROL                = const(0x01)
# Gate Driving Voltage Control       0x03
# Source Driving voltage Control     0x04
BOOSTER_SOFT_START_CONTROL           = const(0x0C) # not in datasheet
#GATE_SCAN_START_POSITION             = const(0x0F) # not in datasheet
DEEP_SLEEP_MODE                      = const(0x10)
DATA_ENTRY_MODE_SETTING              = const(0x11)
#SW_RESET                             = const(0x12)
#TEMPERATURE_SENSOR_CONTROL           = const(0x1A)
MASTER_ACTIVATION                    = const(0x20)
#DISPLAY_UPDATE_CONTROL_1             = const(0x21)
DISPLAY_UPDATE_CONTROL_2             = const(0x22)
# Panel Break Detection              0x23
WRITE_RAM                            = const(0x24)
WRITE_VCOM_REGISTER                  = const(0x2C)
# Status Bit Read                    0x2F
WRITE_LUT_REGISTER                   = const(0x32)
SET_DUMMY_LINE_PERIOD                = const(0x3A)
SET_GATE_TIME                        = const(0x3B)
#BORDER_WAVEFORM_CONTROL              = const(0x3C)
SET_RAM_X_ADDRESS_START_END_POSITION = const(0x44)
SET_RAM_Y_ADDRESS_START_END_POSITION = const(0x45)
SET_RAM_X_ADDRESS_COUNTER            = const(0x4E)
SET_RAM_Y_ADDRESS_COUNTER            = const(0x4F)
TERMINATE_FRAME_READ_WRITE           = const(0xFF) # not in datasheet, aka NOOP


class EPD:
    def __init__(self):
        self.spi = SPI(2, baudrate=20000000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
        #self.spi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
        self.spi.init()

        dc = Pin(27)
        cs = Pin(14)
        rst = Pin(33)

        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.width = EPD_WIDTH
        self.height = EPD_HEIGHT

        self.size = self.width * self.height // 8
        self.buf = bytearray(self.size)

    LUT_FULL_UPDATE    = bytearray(b'\x80\x60\x40\x00\x00\x00\x00\x10\x60\x20\x00\x00\x00\x00\x80\x60\x40\x00\x00\x00\x00\x10\x60\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x03\x00\x00\x02\x09\x09\x00\x00\x02\x03\x03\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x41\xA8\x32\x30\x0A')
    LUT_PARTIAL_UPDATE = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x41\xA8\x32\x30\x0A')

    def clearBuffer(self):
        self._command(b'\x24')
        for i in range(0, len(self.buf)):
            self.buf[i] = 255
            self._data(bytearray([self.buf[i]]))

    def displayBuffer(self, buf):
        self._command(b'\x24')
        for i in range(0, len(buf)):
            self._data(bytearray([buf[i]]))
        self._command(b'\x22')
        self._command(b'\xC7')
        self._command(b'\x20')
        self._command(bytearray([TERMINATE_FRAME_READ_WRITE]))
        self.wait_until_idle()

    def _command(self, command, data=None):
        self.cs(1) # according to LOLIN_EPD
        self.dc(0)
        self.cs(0)
        self.spi.write(command)
        self.cs(1)
        if data is not None:
            self._data(data)

    def _data(self, data):
        self.cs(1) # according to LOLIN_EPD
        self.dc(1)
        self.cs(0)
        self.spi.write(data)
        self.cs(1)

    def init(self):
        self.reset()

        self.wait_until_idle();
        self._command(b'\x12'); # soft reset
        self.wait_until_idle();


        self._command(b'\x74', b'\x54'); #set analog block control
        self._command(b'\x7E', b'\x3B'); #set digital block control
        self._command(b'\x01', b'\xF9\x00\x00'); #Driver output control
        self._command(b'\x11', b'\x01'); #data entry mode
        #set Ram-X address start/end position
        self._command(b'\x44', b'\x00\x0F'); #0x0C-->(15+1)*8=128
        #set Ram-Y address start/end position
        self._command(b'\x45', b'\xF9\x00\x00\x00'); # 0xF9-->(249+1)=250

        self._command(b'\x3C', b'\x03'); # BorderWavefrom
        self._command(b'\x2C', b'\x55'); # VCOM Voltage

        self._command(b'\x03', bytes([self.LUT_FULL_UPDATE[70]])); # ??

        self._command(b'\x04')
        self._data(bytes([self.LUT_FULL_UPDATE[71]])); # ??
        self._data(bytes([self.LUT_FULL_UPDATE[72]])); # ??
        self._data(bytes([self.LUT_FULL_UPDATE[73]])); # ??


        self._command(b'\x3A', bytes([self.LUT_FULL_UPDATE[74]])); # Dummy Line
        self._command(b'\x3B', bytes([self.LUT_FULL_UPDATE[75]])); # Gate time

        self.set_lut(self.LUT_FULL_UPDATE)

        self._command(b'\x4E', b'\x00'); # set RAM x address count to 0;
        self._command(b'\x4F', b'\xF9\x00'); # set RAM y address count to 0X127;
        self.wait_until_idle()

    def wait_until_idle(self):
        sleep_ms(1000)

    def reset(self):
        self.rst(1)
        sleep_ms(1)

        self.rst(0)
        sleep_ms(10)

        self.rst(1)

    def set_lut(self, lut):
        self._command(bytearray([WRITE_LUT_REGISTER]), lut)

    # put an image in the frame memory
    def set_frame_memory(self, image, 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 (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.set_memory_area(x, y, x_end, y_end)
        self.set_memory_pointer(x, y)
        self._command(bytearray([WRITE_RAM]), image)

    # replace the frame memory with the specified color
    def clear_frame_memory(self, color):
        self.set_memory_area(0, 0, self.width - 1, self.height - 1)
        self.set_memory_pointer(0, 0)
        self._command(bytearray([WRITE_RAM]))
        # send the color data
        for i in range(0, (self.width * self.height)//8):
            self._data(bytearray([color]))

    # draw the current frame memory and switch to the next memory area
    def display_frame(self):
        self._command(bytearray([DISPLAY_UPDATE_CONTROL_2]), b'\xC7')
        self._command(bytearray([MASTER_ACTIVATION]))
        self._command(bytearray([TERMINATE_FRAME_READ_WRITE]))
        self.wait_until_idle()

    # specify the memory area for data R/W
    def set_memory_area(self, x_start, y_start, x_end, y_end):
        self._command(bytearray([SET_RAM_X_ADDRESS_START_END_POSITION]))
        # x point must be the multiple of 8 or the last 3 bits will be ignored
        self._data(bytearray([(x_start >> 3) & 0xFF]))
        self._data(bytearray([(x_end >> 3) & 0xFF]))
        self._command(bytearray([SET_RAM_Y_ADDRESS_START_END_POSITION]), ustruct.pack("<HH", y_start, y_end))

    # specify the start point for data R/W
    def set_memory_pointer(self, x, y):
        self._command(bytearray([SET_RAM_X_ADDRESS_COUNTER]))
        # x point must be the multiple of 8 or the last 3 bits will be ignored
        self._data(bytearray([(x >> 3) & 0xFF]))
        self._command(bytearray([SET_RAM_Y_ADDRESS_COUNTER]), ustruct.pack("<H", y))
        self.wait_until_idle()

    # to wake call reset() or init()
    def sleep(self):
        self._command(bytearray([DEEP_SLEEP_MODE]))
        self.wait_until_idle()

Working example:

import epaper2in13
e = epaper2in13.EPD()
e.init()
e.clearBuffer()

import framebuf
buf = bytearray(128 * 250 // 8)
fb = framebuf.FrameBuffer(buf, 128, 250, framebuf.MONO_HLSB)
black = 0
white = 1
fb.fill(white)
fb.text('Hello World',30,10,black)
fb.pixel(30, 10, black)
fb.hline(30, 30, 10, black)
fb.vline(30, 50, 10, black)
fb.line(30, 70, 40, 80, black)
fb.rect(30, 90, 10, 10, black)
fb.fill_rect(30, 110, 10, 10, black)
for row in range(0,37):
	fb.text(str(row),0,row*8,black)
fb.text('Line 36',0,288,black)
e.displayBuffer(buf)

For some reason the displayed image is mirrored. In case someone solve this before me please share with us :). IL3897

fxmike08 avatar Feb 09 '20 14:02 fxmike08

Thanks for doing this. Still, I cannot write something. First of all, I had to import framebuf otherwise the working example gaves me an error. Then, the display is flashing black and white several times, but no sign left on the display. I have no idea what makes the difference between your and my code. The reason for the mirroring might be 'framebuf.MONO_VLSB' instead of 'framebuf.MONO_HLSB' - but it's just an idea, I cannot test it...

Werner-G avatar Feb 09 '20 16:02 Werner-G

@Werner-G Added that import to the above example. If I use framebuf.MONO_VLSB is showing some random lines.

Not sure why is not working for you. Do you have same hardware as me? d32 pro with tft cable and display with IL3897 driver?

Example:

import epaper2in13
e = epaper2in13.EPD()
e.init()
e.clearBuffer()
e.display_frame()
import framebuf
buf = bytearray(128 * 250 // 8)
fb = framebuf.FrameBuffer(buf, 128, 250, framebuf.MONO_HLSB)
black = 0
white = 1
fb.fill(white)
fb.text('Hello World',30,10,black)
e.displayBuffer(buf)

You should see this. 1581271375444

fxmike08 avatar Feb 09 '20 17:02 fxmike08

Again, the display is flashing several times, but no content. So, what could be the difference since I use the same H/W (LOLIN D32_pro V2.0.0 and EPD_2.13 V1.0.0), and the Arduino-code works fine?

  • My firmware is esp32spiram-idf3-20200202-v1.12-120-g4ab8bee82.bin
  • boot.py (and nothing else is on the root): import esp import uos, machine import gc gc.collect()

Werner-G avatar Feb 09 '20 18:02 Werner-G

Yes, arduino-code works fine and its working full and partial update.

I have MicroPython v1.11 . This was compiled by me . I need to be able to compile micropython myself so I can use frozen modules (precompile some python modules) to save some memory allocation.

Micropython v1.11 require to have esp_idf with following git hash: 5c88c5996dbde6208e3bec05abc21ff6cd822d26 And I used xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0

Could you try it with micropython v1.11 ?

fxmike08 avatar Feb 09 '20 19:02 fxmike08

mpython1.1.tar.gz The archive contain the firmware v1.11 and main.py and the epaper2in13.py Please try first without reflash by using main.py and epaper2in13.py (I think it was a problem somewhere).

In case is still not working paste it directly to repl

import epaper2in13
e = epaper2in13.EPD()
e.init()
e.clearBuffer()

import framebuf
buf = bytearray(128 * 250 // 8)
fb = framebuf.FrameBuffer(buf, 128, 250, framebuf.MONO_HLSB)
black = 0
white = 1
fb.fill(white)
fb.text('Hello World',30,30,black)
e.displayBuffer(buf)

In case is still not working reflash esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-idf3-20190529-v1.11.bin

Then copy those file main.py and the epaper2in13.py do a soft or hard reset and it should work.

fxmike08 avatar Feb 09 '20 19:02 fxmike08

Great! Thanks for your patience: now it works! No need to flash Micropython v1.11 - it was just that unnecessary line e.display_frame() responsible for that misfunction. Even your other working example works. But both are still mirrored. Hope somebody can find out why!

Werner-G avatar Feb 10 '20 12:02 Werner-G

For those that are struggling with mirroring problem, I think there is a problem with framebuf. I've found a thread about this.

fxmike08 avatar Feb 10 '20 15:02 fxmike08

And nobody had this prob before? Quite funny.

Werner-G avatar Feb 10 '20 15:02 Werner-G

Hi, is there anybody with a working solution for the mirroring problem?

Werner-G avatar Feb 19 '20 16:02 Werner-G

Here is a link to the code for epaper2in13.py that removes the mirroring effect for the Lolin 2.13 inch EPD @Werner-G, the problem lay in the RAM addressing:

https://forum.micropython.org/viewtopic.php?f=18&t=7547&p=46928#p46928

T-Wilko avatar Apr 28 '20 06:04 T-Wilko

Hi,some body please help me to see this post。Thanks! https://forum.micropython.org/viewtopic.php?f=18&t=8709

nanchaoren avatar Jul 20 '20 06:07 nanchaoren

@T-Wilko Do we have a solution for mirroring issue on the Waveshare1.54 Black/White e-paper display?

lasithf avatar Feb 20 '23 13:02 lasithf