Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.13 The screen does not display properly #5

Open
LilyGO opened this issue Apr 1, 2019 · 21 comments
Open

2.13 The screen does not display properly #5

LilyGO opened this issue Apr 1, 2019 · 21 comments

Comments

@LilyGO
Copy link

LilyGO commented Apr 1, 2019

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

@alvarowolfx
Copy link

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

@ygbr
Copy link

ygbr commented Apr 12, 2019

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
Copy link

ygbr commented Apr 14, 2019

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

@Werner-G
Copy link

I have the same problem. Did anyone solved it?

@fxmike08
Copy link

fxmike08 commented Feb 5, 2020

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 .

@Werner-G
Copy link

Werner-G commented Feb 7, 2020

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?

@fxmike08
Copy link

fxmike08 commented Feb 9, 2020

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

@Werner-G
Copy link

Werner-G commented Feb 9, 2020

Sounds good - I hope you will be successful!

@fxmike08
Copy link

fxmike08 commented Feb 9, 2020

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

@Werner-G
Copy link

Werner-G commented Feb 9, 2020

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

@fxmike08
Copy link

fxmike08 commented Feb 9, 2020

@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

@Werner-G
Copy link

Werner-G commented Feb 9, 2020

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()

@fxmike08
Copy link

fxmike08 commented Feb 9, 2020

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
Copy link

fxmike08 commented Feb 9, 2020

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.

@Werner-G
Copy link

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!

@fxmike08
Copy link

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

@Werner-G
Copy link

And nobody had this prob before? Quite funny.

@Werner-G
Copy link

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

@T-Wilko
Copy link

T-Wilko commented Apr 28, 2020

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

@nanchaoren
Copy link

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

@lasithf
Copy link

lasithf commented Feb 20, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants