Skip to content

How pixels are driven in a parallel epaper with epdiy

Martin F. edited this page Oct 2, 2024 · 14 revisions

This page will be created in several days, in sections, that will be slowly build while the code is analyzed. The basics have been outlined by Petteri in blog EssentialScrap I will try to describe here on my own words and research how this is done with epdiy v7 (ESP32S3 version). If you find mistakes please send me your critics, corrections or anything you want to make your point to martin(AT)cale.es

It's important to know that the Clock CKV in epdiy is controlled using the RMT driver. Relevant code section in lcd_driver.c

https://github.com/vroland/epdiy/blob/main/src/output_lcd/lcd_driver.c#L156-L162

In ESP32S3 the LCD module is used to send the parallel data using RGB LCD mode:

https://github.com/vroland/epdiy/blob/main/src/output_lcd/lcd_driver.c#L519-L526

Voltages are Applied though A Transistor Matrix

eink_panel electrical schematic

VCOM is the common electrode on the front side of the display, and it provides the reference voltage against which the driving voltages act. This is usually printed on a small adhesive label. If it's not don't worry it's usually a value between -1.5 V and -2.5 V. This value you can set in V7 code talking to the TPS65185 via I2C using:

// sets the VCOM to -1.96 V
epd_set_vcom(1960);

The Gate and Source drivers are shift registers, all electrical power (VCC) is 3.3 volts only, except the high voltages needed:

The +15 and -15 volt are used to drive the pixels (Darker or Lighter) The other two voltages that you can see that are generated by TPS65185 and a helping circuit in epdiy v7:

-20 and +22 V are to drive the Gate of each transistor pixel and let pass the +15 or -15 volt to the pixel itself.

Gate driver

The "Select row" driver is the easy one. Best explained in EssentialScrap:

How Gate driver works: You shift in a single "1" bit, and then "0" bits every time you want to advance to the next row. The "1" bit will shift through the register until it falls off the far end.

Gate driver in action (Video)

to start a frame, you toggle GMODE (gate output enable) high and activate SPV (start pulse vertical, active low) for one CKV clock cycle. After that you toggle CKV for each row you want to advance. It seems that the gate driver is only on while the CKV is high, and also that the on-off ratio of CKV is important. It seems necessary to have CKV off much longer than it is on, otherwise the image leaks to other rows also.

Source driver

This is the driver that actually drives the pixels itself applying the higher voltages. This part is for me the critical point about why this displays are particularly hard to drive:

You shift in 800 pixels of data, 2 bits per pixel. After all the data is in the shift register, you pulse the latch enable to store it into the latch. Then you pulse the output enable when you want to write out the row.

This part that is documented with more details in Petteri's website is the one that defines how the image is displayed and particularly the grays.

Bit 1 Bit 0 Action
0 0 No action
0 1 Darken
1 0 Lighten
1 1 No action

You can see also this lookup table reflected in lut.h

// Make a block of 4 pixels lighter on the EPD.
#define CLEAR_BYTE 0B10101010
// Make a block of 4 pixels darker on the EPD.
#define DARK_BYTE 0B01010101

As you can see you can only "darken" or "lighten" a specific pixel. All the rest should be done with many passes (Called Frames in epdiy). For example if you apply Darken to the first row pixels 4 times and the one below only 2 times, starting from pure white, while sending a 00 to all lines below: Then the first one will be darker than the one below. The other rows will be still white.

The same to do a partial upgrade, you will shift the Gate driver to the area it needs to be updated, then send 00 (No ops) till you find the beginning of the rectangle, and send only the relevant rows to that part of the line that needs to be updated. Repeating the same action in the row below and so on till the rectangle you want to update is covered.

Where the rendering takes place: /output_lcd/render_lcd.c render uses a line queue

The line queue logic: /output_common/line_queue.c