Areas of the screen not in the rendering area will become lighter in color.
lanistor opened this issue · 30 comments
Areas of the screen that are not in the rendering area will become lighter in color.
For example, screen is 1024*758
, rendering area {x: 550, y: 30, width: 474, height: 60}
for several times, the area {x: 76, y: 90, width: 474, height: 668}
black color will fade.
Like this:
No matter how I adjust the voltage, I can't fix this problem.
Is there a way to keep areas' color of the screen that are not in the rendering area unaffected?
Testing environment:
- board:
v5
- screen:
060XD4
- waveform: tested
epdiy_ED060XC3
andepdiy_ED060SCT
, same behavior. - UI library:
lvgl
- mode:
MODE_DU
, usingepd_hl_update_area
- esp idf:
release 4.4
Demo code:
epd_poweron();
epd_hl_update_area(&hl, updateMode, temperature, first->area);
epd_poweroff();
My feedback w/Kindle paper white
https://youtu.be/HrZnDrv0QJ8?si=tgrA1ztUJoJH-S-3
My feedback w/Kindle paper white
Got it👍
Areas of the screen that are not in the rendering area will become lighter in color.
For example, screen is
1024*758
, rendering area{x: 550, y: 30, width: 474, height: 60}
for several times, the area{x: 76, y: 90, width: 474, height: 668}
black color will fade. Like this:No matter how I adjust the voltage, I can't fix this problem. Is there a way to keep areas' color of the screen that are not in the rendering area unaffected?
Testing environment:
- board:
v5
- screen:
060XD4
- waveform: tested
epdiy_ED060XC3
andepdiy_ED060SCT
, same behavior.- UI library:
lvgl
- mode:
MODE_DU
, usingepd_hl_update_area
- esp idf:
release 4.4
Demo code:
epd_poweron(); epd_hl_update_area(&hl, updateMode, temperature, first->area); epd_poweroff();
@martinberlin Is there a way to avoid color changes in non-rendering areas?
I think if no voltage is applied to an area, its color should not change.
Can you please post some program to replicate this?
I know this happens mostly with SPI epapers. But still never saw it in parallel. It might be that display has some ghosts after partial refreshes, that's why e-readers make a full refresh every 10th or so page turn, but other than that it works good for me.
But it can be that with this particular eink and program does not work for you. At least you should make a repository and post test cases so we can replicate it
@martinberlin I found the problem, it was a problem with the code I rewrote.
I'm using below code to replace epd_hl_update_area()
:
epd_clear_area_cycles(area, 1, _clear_cycle_time);
epd_hl_update_area_directly(&hl, updateMode, temperature, area);
enum EpdDrawError epd_hl_update_area_directly(EpdiyHighlevelState* state,
enum EpdDrawMode mode,
int temperature,
EpdRect area) {
assert(state != NULL);
// Not right to rotate here since this copies part of buffer directly
bool previously_white = true;
bool previously_black = false;
memset(state->dirty_lines + area.y, 1, area.height);
enum EpdDrawError err;
if (previously_white) {
err = epd_draw_base(epd_full_screen(), state->front_fb, area,
MODE_PACKING_2PPB | PREVIOUSLY_WHITE | mode,
temperature, state->dirty_lines, state->waveform);
}
for (int l = area.y; l < area.y + area.height; l++) {
if (state->dirty_lines[l] > 0) {
uint8_t* lfb = state->front_fb + EPD_WIDTH / 2 * l; // to
uint8_t* lbb = state->back_fb + EPD_WIDTH / 2 * l; // from
for (int x = area.x; x < area.x + area.width; x++) {
if (x % 2) {
*(lbb + x / 2) = (*(lfb + x / 2) & 0xF0) | (*(lbb + x / 2) & 0x0F);
} else {
*(lbb + x / 2) = (*(lfb + x / 2) & 0x0F) | (*(lbb + x / 2) & 0xF0);
}
}
}
}
return err;
}
This method will clear afterimages and ensure faster rendering speed, but the problem is: the area rendered by this function, will become lighter in color, when other area rendering.
I wan't to rewrite epd_hl_update_area_directly
, but not works, such as set state->difference_fb[l * EPD_WIDTH + x] = ((*(lfb + x / 2) & 0x0F) << 4) | (*(lbb + x / 2) & 0x0F);
will not work.
Could you tell me the wrong of this function epd_hl_update_area_directly
, and how to repair it?
Could you tell me the wrong of this function epd_hl_update_area_directly, and how to repair it?
I don’t really know since I never try to use this myself. I either use the high level library or I push the raw framebuffer and do a hl_update
Could you tell me the wrong of this function epd_hl_update_area_directly, and how to repair it?
I don’t really know since I never try to use this myself. I either use the high level library or I push the raw framebuffer and do a hl_update
It should be because of i don't known the rendering logic of epdiy clearly, i havn't got the side effect of this way.
@martinberlin I found the problem, it was a problem with the code I rewrote.
I'm using below code to replace
epd_hl_update_area()
:epd_clear_area_cycles(area, 1, _clear_cycle_time); epd_hl_update_area_directly(&hl, updateMode, temperature, area); enum EpdDrawError epd_hl_update_area_directly(EpdiyHighlevelState* state, enum EpdDrawMode mode, int temperature, EpdRect area) { assert(state != NULL); // Not right to rotate here since this copies part of buffer directly bool previously_white = true; bool previously_black = false; memset(state->dirty_lines + area.y, 1, area.height); enum EpdDrawError err; if (previously_white) { err = epd_draw_base(epd_full_screen(), state->front_fb, area, MODE_PACKING_2PPB | PREVIOUSLY_WHITE | mode, temperature, state->dirty_lines, state->waveform); } for (int l = area.y; l < area.y + area.height; l++) { if (state->dirty_lines[l] > 0) { uint8_t* lfb = state->front_fb + EPD_WIDTH / 2 * l; // to uint8_t* lbb = state->back_fb + EPD_WIDTH / 2 * l; // from for (int x = area.x; x < area.x + area.width; x++) { if (x % 2) { *(lbb + x / 2) = (*(lfb + x / 2) & 0xF0) | (*(lbb + x / 2) & 0x0F); } else { *(lbb + x / 2) = (*(lfb + x / 2) & 0x0F) | (*(lbb + x / 2) & 0xF0); } } } } return err; }This method will clear afterimages and ensure faster rendering speed, but the problem is: the area rendered by this function, will become lighter in color, when other area rendering.
I wan't to rewrite
epd_hl_update_area_directly
, but not works, such as setstate->difference_fb[l * EPD_WIDTH + x] = ((*(lfb + x / 2) & 0x0F) << 4) | (*(lbb + x / 2) & 0x0F);
will not work.Could you tell me the wrong of this function
epd_hl_update_area_directly
, and how to repair it?
@vroland May you help me? I want to get the side effect of this rendering logic, and repair it.
@martinberlin I found the problem, it was a problem with the code I rewrote.
I'm using below code to replaceepd_hl_update_area()
:epd_clear_area_cycles(area, 1, _clear_cycle_time); epd_hl_update_area_directly(&hl, updateMode, temperature, area); enum EpdDrawError epd_hl_update_area_directly(EpdiyHighlevelState* state, enum EpdDrawMode mode, int temperature, EpdRect area) { assert(state != NULL); // Not right to rotate here since this copies part of buffer directly bool previously_white = true; bool previously_black = false; memset(state->dirty_lines + area.y, 1, area.height); enum EpdDrawError err; if (previously_white) { err = epd_draw_base(epd_full_screen(), state->front_fb, area, MODE_PACKING_2PPB | PREVIOUSLY_WHITE | mode, temperature, state->dirty_lines, state->waveform); } for (int l = area.y; l < area.y + area.height; l++) { if (state->dirty_lines[l] > 0) { uint8_t* lfb = state->front_fb + EPD_WIDTH / 2 * l; // to uint8_t* lbb = state->back_fb + EPD_WIDTH / 2 * l; // from for (int x = area.x; x < area.x + area.width; x++) { if (x % 2) { *(lbb + x / 2) = (*(lfb + x / 2) & 0xF0) | (*(lbb + x / 2) & 0x0F); } else { *(lbb + x / 2) = (*(lfb + x / 2) & 0x0F) | (*(lbb + x / 2) & 0xF0); } } } } return err; }This method will clear afterimages and ensure faster rendering speed, but the problem is: the area rendered by this function, will become lighter in color, when other area rendering.
I wan't to rewriteepd_hl_update_area_directly
, but not works, such as setstate->difference_fb[l * EPD_WIDTH + x] = ((*(lfb + x / 2) & 0x0F) << 4) | (*(lbb + x / 2) & 0x0F);
will not work.
Could you tell me the wrong of this functionepd_hl_update_area_directly
, and how to repair it?@vroland May you help me? I want to get the side effect of this rendering logic, and repair it.
It seems the problem of epd_clear_area_cycles
.
Do you have an image / video of the area that gets lighter? Because to me that sounds a lot like improperly calibrated VCOM. Additionally, you could try if using the ED047TC2 waveform changes anything.
Also, why are you using your own epd_hl_update_area_directly
instead of epd_hl_update_area
?
Do you have an image / video of the area that gets lighter? Because to me that sounds a lot like improperly calibrated VCOM. Additionally, you could try if using the ED047TC2 waveform changes anything.
Also, why are you using your own
epd_hl_update_area_directly
instead ofepd_hl_update_area
?
- I tried
ED047TC2
, has same behavior. - I adjusted vcom to a suitable value, so it shouldn't be a problem with vcom.
- I use
epd_clear_area_cycles
+epd_hl_update_area_directly
to eliminate afterimages, it will renderingblack
->white
-> content image, this will clear the afterimage,epd_hl_update_area
cannot do this.
And, i tried not using epd_clear_area_cycles
+ epd_hl_update_area_directly
, instead using rendering black
-> white
-> content image, with epd_hl_update_area
, this problem disappears, so i think it may be epd_clear_area_cycles
Please answer this one:
Also, why are you using your own epd_hl_update_area_directly instead of epd_hl_update_area?
I also would like to understand what is the reason to use your own high level update area instead of the one that is already built in
Please answer this one:
Also, why are you using your own epd_hl_update_area_directly instead of epd_hl_update_area?
I also would like to understand what is the reason to use your own high level update area instead of the one that is already built in
This way can eliminate afterimages (epd_clear_area_cycles
+ epd_hl_update_area_directly
), while epd_hl_update_area
cannot.
Can you show a video of both methods? I don't really see why they would behave different, so seeing the outcome would help.
Can you show a video of both methods? I don't really see why they would behave different, so seeing the outcome would help.
Before
After
Video
https://github.com/lanistor/assets/blob/master/epaper/060XD4_out_paint_area.mp4
Can you show a video of both methods? I don't really see why they would behave different, so seeing the outcome would help.
Before
After
Video
https://github.com/lanistor/assets/blob/master/epaper/060XD4_out_paint_area.mp4
@vroland Is this helpful for resolving the problem?
That makes it more clear, thanks. Curious, I don't really know why that is. I'll mark it as a bug for now and try to reproduce it some time. Do you have a minimal example for reproducing the problem handy?
That makes it more clear, thanks. Curious, I don't really know why that is. I'll mark it as a bug for now and try to reproduce it some time. Do you have a minimal example for reproducing the problem handy?
I made a minimal example : https://github.com/lanistor/epaper-tester
I can reproduct problem with ED060XD4
, but cannot reproduct with XC3
, XCH
, do you have a ED060XD4
?
Argh, I think I only have an XC3... But I can try my luck.
I will check because Lanistor sent me 2 XD4 and still have one around so if I find it I can send it to you in next package 📦
Just checked so far have a 060XCD
I will check because Lanistor sent me 2 XD4 and still have one around so if I find it I can send it to you in next package 📦 Just checked so far have a 060XCD
As i remembered, i sent you 2 XCD, not XD4. I could send XD4
to @vroland .
Argh, I think I only have an XC3... But I can try my luck.
@vroland Could you give me your address? I could send you 2 XD4. Here is my email: lanistor@163.com
.
Hi, @vroland, @martinberlin,
I found another very interesting performance that will help solve this problem: when the circuit board is powered on again, it will slowly display the last rendered content.
Here is a video of my experiment:
https://github.com/lanistor/assets/blob/master/epaper/060XCD_2024-05-10.mp4
The test code for video recording is here https://github.com/lanistor/epaper-tester/tree/test/case-2, the branch is test/case-2.
And here is my experimental using case:
- Turn on the power and a black background will appear on the entire screen.
- Render the block a few times at the top, then a few times at the bottom
- Disconnect the power and then turn it on again. When rendering the top block, the shape of the block will appear at the bottom.
- Repeat the above steps, but turn the power off after 3 minutes and then turn it on again. At this time, the shape of the bottom block will fade; if you turn the power on again after 5 minutes, the shape of the bottom block will be almost invisible.
- If delay 2000ms between
epd_clear_area_cycles
andepd_hl_update_area_directly
, the problem almost disappeared. The longer the time, the better the effect.
epd_poweron();
epd_clear_area_cycles(area, 1, _clear_cycle_time);
vTaskDelay(pdMS_TO_TICKS(2000));
epd_hl_update_area_directly(&hl, updateMode, temperature, area);
epd_poweroff();
- I tested the brand new XCD and XCH and both had this problem; on the second-hand XD4, this problem will be more serious.
This is really a very strange behavior. The only explanation I can think of is: the electrode on the back of the screen has a weak capacitance, which stores a small amount of electricity. When the screen is powered on, these weak currents will cause the displacement of the particles. Because the second-hand screen (XD4) has been used for a long time, the capacitance will increase, causing the particle displacement to increase.
I found the problem, but I don't know how to solve it. This problem is really interesting, and it's beyond my knowledge. May you find out the way to resolve this problem?
Another test case
If call epd_push_pixels
to push white color instead of calling epd_clear_area_cycles
, the old area will became darker than expected. If count of white
and count of black
are not equal, it will aggravate the problem, but the behavior is opposite (the color becomes darker or lighter).
// for (int i = 0; i < 10; i++) {
// epd_push_pixels(area, _clear_cycle_time, 0);
// }
for (int i = 0; i < 10; i++) {
epd_push_pixels(area, _clear_cycle_time, 1);
}
Test behavior: (this case branch: test/case-3 ).
Interesting, looks like this particular display model is very sensitive to residual charge / charge buildup. Maybe if we could dump the on-chip waveform we can fix this? Or we have to experiment with the waveforms we have. But this would be my guess that with the waveforms we have there is some imbalanced charge buildup.
Interesting, looks like this particular display model is very sensitive to residual charge / charge buildup. Maybe if we could dump the on-chip waveform we can fix this? Or we have to experiment with the waveforms we have. But this would be my guess that with the waveforms we have there is some imbalanced charge buildup.
I got the knowledge here: #226 (comment) .
@lanistor I got your displays today, now I'm going to try replicating it when I have the time :) I already tried the display with a V7 board, there it didn't behave so different, but I'll try to reproduce it with a V5 board when I have the opportunity.
I tried to compile your example repo and I get an error with idf 4.4.5:
/home/vroland/dev/misc/epaper-tester/components/lvgl_epaper_drivers/lvgl_helpers.c: In function 'lvgl_driver_init':
/home/vroland/dev/misc/epaper-tester/components/lvgl_epaper_drivers/lvgl_helpers.h:48:25: error: 'LV_HOR_RES_MAX' undeclared (first use in this function); did you mean 'LV_HOR_RES'?
Any particular version of the dependencies I need?
Probably a different version of LVGL but here I packed a way to get all in a single git clone call:
https://github.com/martinberlin/lv_port_esp32-epaper/wiki/LVGL-9.0
LV_HOR_RES_MAX & LV_HOR_VER_MAX correspond if I remember well to display Width and Height.
I tried to compile your example repo and I get an error with idf 4.4.5:
/home/vroland/dev/misc/epaper-tester/components/lvgl_epaper_drivers/lvgl_helpers.c: In function 'lvgl_driver_init': /home/vroland/dev/misc/epaper-tester/components/lvgl_epaper_drivers/lvgl_helpers.h:48:25: error: 'LV_HOR_RES_MAX' undeclared (first use in this function); did you mean 'LV_HOR_RES'?
Any particular version of the dependencies I need?
@vroland Have you changed the version of lvgl? The default version is v8.3
, and it doesn't need the LV_HOR_RES_MAX
.
If using the following code to start project, it won't need LV_HOR_RES_MAX
, and i can start it correctly on a new folder:
git clone git@github.com:lanistor/epaper-tester.git
cd epaper-tester
git submodule update --init --recursive
idf.py build
And idf 4.4
is OK.
Ok, now it compiles but the screen doesn't update. Maybe I'll have to try with a different board when I have the chance.