Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions boards/arm/reel_board/reel_board.dts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@
sdv = [19];
vcom = <0xa8>;
border-waveform = <0x71>;
dummy-line = <0x1a>;
gate-line-width = <0x08>;
lut-initial = [
22 55 AA 55 AA 55 AA 11
00 00 00 00 00 00 00 00
Expand Down
2 changes: 2 additions & 0 deletions boards/arm/reel_board/reel_board_v2.dts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
sdv = [41 a8 32];
vcom = <0x26>;
border-waveform = <0x03>;
dummy-line = <0x30>;
gate-line-width = <0x0a>;
Comment on lines +58 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes contrast worse than with default values on GDEH0213B72 displays. Can you counter test or remove it for GDEH0213B72 displays?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to use the values from the reference code or the values that produce better contrast here? I'm generally a bit reluctant to mess with these values since some of them (mainly voltages) presumably affect display reliability.

I can double check the values tonight and update to whatever we decide.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think I know what's going on.

Broadly speaking, there are two generations of ssd16xx devices. These operate in subtly different ways, especially when it comes to partial refresh and some control registers (dummy line seems to be gen1 specific). I didn't really understand why some devices load two LUTs until recently. It seems like we are configuring most(?) gen 1 devices to use partial refresh using the second LUT. This is very strange though since we first configure the device for full refresh to clear the screen and then irreversibly enable partial refresh (but a lot of registers are incorrectly left in their full refresh state).

This causes two issues. The first problem is that there is no way to cause a full refresh to remove ghosting. As far as I understand, this will cause panel degradation. The second issue is that we potentially don't use the right voltages / timings for one of the LUTs. Looking at the example code from Waveshare, it seems like they got it wrong as well and don't correctly program the various voltages correctly when entering partial refresh.

The Waveshare example code use 0x30/0x0a for full refresh. Weirdly, the LUT table for partial refresh specifies 0x30/0x0a, but the code never writes that to the control registers but that's probably not a problem since they seem to correspond to the reset values. The old driver used 0x1a/0x08 which I have no idea where it came from.

I would suggest that we keep the new values since they are correct with respect to the LUT. You can find the corresponding example here: https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/c/lib/e-Paper/EPD_2in13_V2.c

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broadly speaking, there are two generations of ssd16xx devices. These operate in subtly different ways, especially when it comes to partial refresh and some control registers (dummy line seems to be gen1 specific). I didn't really understand why some devices load two LUTs until recently. It seems like we are configuring most(?) gen 1 devices to use partial refresh using the second LUT. This is very strange though since we first configure the device for full refresh to clear the screen and then irreversibly enable partial refresh (but a lot of registers are incorrectly left in their full refresh state).

The driver supports several similar controllers.
You probably mean the controllers on displays GDEH0213B1 (label HINK-E0213) and GDEH0213B72 (label HINK-E0213A22), both have been discontinued for a long time. Yes they use different controllers, also HINK-E0213 has no values in OTP at all. HINK-E0213A22 has values in OTP that produce very good results (but IIRC are too slow for reel board demo). Manufacturer for this EPDs (electrophoretic displays) is Good Display not Waveshare.

This causes two issues. The first problem is that there is no way to cause a full refresh to remove ghosting. As far as I understand, this will cause panel degradation. The second issue is that we potentially don't use the right voltages / timings for one of the LUTs. Looking at the example code from Waveshare, it seems like they got it wrong as well and don't correctly program the various voltages correctly when entering partial refresh.

The driver makes full refresh at initialization. IMHO ghosting is not avoidable when using partial refresh (faster/simpler wave-forms) and the only correct way is to use setting from OTP (if they are available). "right voltages" depends also on the circuit and components.

The Waveshare example code use 0x30/0x0a for full refresh. Weirdly, the LUT table for partial refresh specifies 0x30/0x0a, but the code never writes that to the control registers but that's probably not a problem since they seem to correspond to the reset values. The old driver used 0x1a/0x08 which I have no idea where it came from.

Timings 0x1a/0x08 are for HINK-E0213, and come from Good Display sample code. And your are right, 0x30/0x0a are for HINK-E0213A22.

Copy link
Contributor Author

@andysan andysan Jul 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broadly speaking, there are two generations of ssd16xx devices. These operate in subtly different ways, especially when it comes to partial refresh and some control registers (dummy line seems to be gen1 specific). I didn't really understand why some devices load two LUTs until recently. It seems like we are configuring most(?) gen 1 devices to use partial refresh using the second LUT. This is very strange though since we first configure the device for full refresh to clear the screen and then irreversibly enable partial refresh (but a lot of registers are incorrectly left in their full refresh state).

The driver supports several similar controllers. You probably mean the controllers on displays GDEH0213B1 (label HINK-E0213) and GDEH0213B72 (label HINK-E0213A22), both have been discontinued for a long time. Yes they use different controllers, also HINK-E0213 has no values in OTP at all. HINK-E0213A22 has values in OTP that produce very good results (but IIRC are too slow for reel board demo). Manufacturer for this EPDs (electrophoretic displays) is Good Display not Waveshare.

As far as I can tell, the devices I referred to as gen2 devices are mostly SSD168x and SSD1675B (but not SSD1675A) based panels. I think the only gen2 device we have in-tree is the GDEH0154D67 (HINK-E0154A07). Some of the SSD167x (at least SSD1673) seem to be gen 1 while the SSD1675B seems to be a gen 2. I think there might be a class of gen0 devices as well judging by the differences in the data sheets I have seen.

The important difference between the generations isn't the OTP. It's how the device looks up LUTs in the OTP and how to enable partial refresh. It seems like gen2 devices have introduced a new flag in SSD16XX_CMD_UPDATE_CTRL2 that enables what is referred to as "mode 2". Gen 1 devices seem to use the same bit for something else, but it isn't clear what it does from the datasheet. Gen 1 devices, similar to UC81xx devices, seem to require software overriding the LUT to enable fast partial / differential updates.

I think one of the reasons they made this change is that it makes it possible for gen2 devices to use a single master activation to load either a full refresh or a partial refresh LUT from OTP by looking at the mode bit.

This causes two issues. The first problem is that there is no way to cause a full refresh to remove ghosting. As far as I understand, this will cause panel degradation. The second issue is that we potentially don't use the right voltages / timings for one of the LUTs. Looking at the example code from Waveshare, it seems like they got it wrong as well and don't correctly program the various voltages correctly when entering partial refresh.

The driver makes full refresh at initialization. IMHO ghosting is not avoidable when using partial refresh (faster/simpler wave-forms) and the only correct way is to use setting from OTP (if they are available). "right voltages" depends also on the circuit and components.

Yes, it does make a full refresh at initialisation. However, the manual for many of these panels specify that a full refresh is needed at a regular interval. Typically daily or after a fixed number of partial updates. This is currently not possible.

As for right voltages, the my understanding is that VCOM/VGH/VSH/VSL values are strongly dependant on the panel, LUT, and the frame rate (controlled by dummy line and friends). A higher voltage differential between the back and the front of the panel results in quicker movement, which means that a shorter update can be achieved. The LUT tables assume a specific frame rate and specifics voltages. If these don't match, you potentially end up driving the pixels too hard and/or too long which can result in permanent damage.

In this case, we seem to be using the VCOM voltage (0x26 / -0.95V) from the partial refresh instead of a full refresh (0x55 / -2.1V).

The Waveshare example code use 0x30/0x0a for full refresh. Weirdly, the LUT table for partial refresh specifies 0x30/0x0a, but the code never writes that to the control registers but that's probably not a problem since they seem to correspond to the reset values. The old driver used 0x1a/0x08 which I have no idea where it came from.

Timings 0x1a/0x08 are for HINK-E0213, and come from Good Display sample code. And your are right, 0x30/0x0a are for HINK-E0213A22.

So, should we go with the values matching the panel here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, should we go with the values matching the panel here?

Let me test it again please.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have always seen variation in contrast among the same display types. It seems to be higher with these values, though I no longer have access to significant numbers of panels for an experiment.

lut-initial = [
/*
* Waveform Composition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@
dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */
reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */
busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */
gdv = [00];
sdv = [41 a8 32];
vcom = <0x00>;
border-waveform = <0x05>;
border-waveform = <0x3c>;
tssv = <0x80>;
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
sdv = [19];
vcom = <0xa8>;
border-waveform = <0x71>;
dummy-line = <0x1a>;
gate-line-width = <0x08>;
lut-initial = [
22 55 AA 55 AA 55 AA 11
00 00 00 00 00 00 00 00
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
sdv = [41 a8 32];
vcom = <0x26>;
border-waveform = <0x03>;
dummy-line = <0x30>;
gate-line-width = <0x0a>;
lut-initial = [
80 60 40 00 00 00 00
10 60 20 00 00 00 00
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
vcom = <0xa8>;
border-waveform = <0x33>;
softstart = [d7 d6 9d];
dummy-line = <0x1a>;
gate-line-width = <0x08>;
lut-initial = [
50 AA 55 AA 11 00 00 00
00 00 00 00 00 00 00 00
Expand Down
Loading