lovyan03 / LovyanGFX

SPI LCD graphics library for ESP32 (ESP-IDF/ArduinoESP32) / ESP8266 (ArduinoESP8266) / SAMD51(Seeed ArduinoSAMD51)
Other
1.03k stars 189 forks source link

Would like to add support for Cinread IT8951 module #242

Closed martinberlin closed 1 year ago

martinberlin commented 1 year ago

Hello Lovyan, I've been playing with this tiny new parallel controller that Good display is selling for the 9.7" (1200*825 pix) Fabricator of the PCB is https://www.cinread.com/EINK.html

After fighting like 3 days with it, I could send the framebuffer using an ESP32S3, but there is still a problem. It's looking like clipped. I'm waiting for Goodisplay support on this, but maybe you know any different initialization, that can make this work better?

For example sending this picture: strava

I'm getting this displayed on the epaper: 20220529_180657

Testing now signals IMG20220529160203

My test sketch: https://github.com/martinberlin/cale-idf/blob/feature/45-it8951/main/www-jpg-render/main/jpg-render-cinread8951.cpp

Why I would like to make this work? Because I know it should work with your library just as we could make work the Waveshare big 6" display on #147 And also because at 56 dollars price, plus 25 or 30 more for any generic 9.7" epaper in Aliexpress, could be a nice professional controller to use.

If you would like to get one @lovyan03 I'm shipping one to you and an additional Epaper directly from Aliexpress so you can try some other controller and have a bigger display than M5. Just tell me if you are interested.

lovyan03 commented 1 year ago

Hello @martinberlin
Perhaps that can be adjusted in the LovyanGFX panel config.

/// memory_width and memory_height should be set to the size supported by the driver IC. If you are unsure, I recommend that you leave it at the default value and do not set it yourself.
///   cfg.memory_width     =   EPD_WIDTH;  // ドライバICがサポートしている最大の幅
///   cfg.memory_height    =   EPD_HEIGHT;  // ドライバICがサポートしている最大の高さ

/// panel_width and panel_height are the actual width and height of the panels connected. This probably does not need to be changed.
      cfg.panel_width      =   EPD_WIDTH;  // 実際に表示可能な幅
      cfg.panel_height     =   EPD_HEIGHT;  // 実際に表示可能な高さ

/// It is this offset value that is important. Try adjusting this.
      cfg.offset_x         =   256;  // パネルのX方向オフセット量
      cfg.offset_y         =    32;  // パネルのY方向オフセット量

I went on a hunch and set the values cfg.offset_x=256 and cfg.offset_y=32, but there will probably still be some discrepancy. You should actually try the drawing to find the best value.

I have little understanding of IT8951. I certainly might be better off getting one of your recommendations.

lovyan03 commented 1 year ago

@martinberlin I have reviewed the specification of IT8951 and source code and have corrected the areas that I believe are relevant to this issue.

Forget for a moment about specifying the offset as described earlier. Instead, try using the develop branch of LovyanGFX.

Since M5Paper's image buffer address was 0x001236E0, I had specified a fixed value in my source code. But... perhaps there was a discrepancy in the image buffer address . This modification to the develop branch queries IT8951 for the image buffer address.

image

martinberlin commented 1 year ago

I have reviewed the specification of IT8951 and source code and have corrected the areas that I believe are relevant to this issue.

I'm so glad you could take a look on this one!

Forget for a moment about specifying the offset as described earlier.

Agree, tried just to change cfg.offset_x but it does not seem to affect at all in this case how the image is displayed.

I tried develop branch but there is still some glitch in the memory address where is writing the image: after_memory_tweak

One moment: I will try the Windows demo software and also do this query to check what is the ImgBufAddL / H. UPDATE: Here is what the USB interface says on the 0x0302 command:

Panel Width = 1200
Panel Height = 825
Image Buffer Address = 0x119FC0

Using 0x119FC0 as _tar_memaddr it works correctly so you are thinking in the right direction. Note: If I add esp_log.h header and enable the buf reading this is what is coming out in Serial:

E (889) debug: buf[0] = 0000
E (889) debug: buf[1] = 0000
E (889) debug: buf[2] = 0000
E (889) debug: buf[3] = 0000
E (899) debug: buf[4] = 0000
E (899) debug: buf[5] = 0000
E (899) debug: buf[6] = 0000
E (909) debug: buf[7] = 0000
E (909) debug: buf[8] = 0000
E (909) debug: buf[9] = 0000
E (919) debug: buf[10] = 0000
E (919) debug: buf[11] = 0000
E (919) debug: buf[12] = 0000
E (929) debug: buf[13] = 0000
E (929) debug: buf[14] = 0000
E (939) debug: buf[15] = 0000
E (939) debug: buf[16] = 0000
E (939) debug: buf[17] = 0000
E (949) debug: buf[18] = 0000
E (949) debug: buf[19] = 0000

Maybe it's not reading the command returned info?

lovyan03 commented 1 year ago

@martinberlin Okay, it seems that you are indeed not receiving from IT8951. Please change the following line in panel config.

cfg.spi_3wire  = false;        // 受信をMOSIピンで行う場合はtrueを設定

When spi_3wire is set to true, send and receive are performed on the MOSI pin. When spi_3wire is set to false, send is performed on the MOSI pin and receive is performed on the MISO pin.

martinberlin commented 1 year ago

Hello Lovyan, great tip, now is reading from MISO! I've added an extra debug tag so it prints the memaddr:

ESP_LOGI("debug", "_tar_memaddr: %06x", _tar_memaddr);

This is what I'm getting now. Is still pointing to a wrong mem address, since I get only a gray image:

I (889) debug: buf[0] = 04b0
I (889) debug: buf[1] = 04b0
I (889) debug: buf[2] = 0339
I (889) debug: buf[3] = 9fc0
I (899) debug: buf[4] = 0011
I (899) debug: buf[5] = 4744
I (899) debug: buf[6] = 5f76
I (909) debug: buf[7] = 2e30
I (909) debug: buf[8] = 2e31
I (909) debug: buf[9] = 0000
I (919) debug: buf[10] = 0000
I (919) debug: buf[11] = 0000
I (919) debug: buf[12] = 0000
I (929) debug: buf[13] = 364d
I (929) debug: buf[14] = 3134
I (939) debug: buf[15] = 545f
I (939) debug: buf[16] = 4544
I (939) debug: buf[17] = 3039
I (949) debug: buf[18] = 3754
I (949) debug: buf[19] = 4332
I (949) debug: _tar_memaddr: 9fc00339

Right one seems to be 0x119FC0 (Not sure how's read, but seems buf[3] & buf[4] ?)

lovyan03 commented 1 year ago

@martinberlin I see, perhaps the read clock was too fast. Earlier I had set the read clock to 8 MHz, but your report suggests that the same value was received twice in buf[0] and buf[1], suggesting that the IT8951 was not processed in time. I re-read the spec and discovered that it is written to be down to 2MHz to safely read it out.

Once again, I have updated the develop branch, so please try this.

martinberlin commented 1 year ago

Thanks Lovyan, I just updated develop and compiled this again, just added the debug to preview what is reading:

I (889) debug: buf[0] = 04b0
I (889) debug: buf[1] = 04b0
I (889) debug: buf[2] = 0339
I (889) debug: buf[3] = 9fc0
I (899) debug: buf[4] = 0011
I (899) debug: buf[5] = 4744
I (899) debug: buf[6] = 5f76
I (909) debug: buf[7] = 2e30
I (909) debug: buf[8] = 2e31
I (909) debug: buf[9] = 0000
I (919) debug: buf[10] = 0000
I (919) debug: buf[11] = 0000
I (919) debug: buf[12] = 0000
I (929) debug: buf[13] = 364d
I (929) debug: buf[14] = 3134
I (939) debug: buf[15] = 545f
I (939) debug: buf[16] = 4544
I (939) debug: buf[17] = 3039
I (949) debug: buf[18] = 3754
I (949) debug: buf[19] = 4332

It seems to me that changed the speed it's not affecting how it's read. What I don't get correctly is that in specs says this is data[2] and data[3], but what I see if the reading is correctly (Since it does not seem to be a speed issue after last update)

Image Buffer Address = 0x119FC0 is coming on

I (889) debug: buf[3] = 9fc0 I (899) debug: buf[4] = 0011 (Or I'm seeing this wrong?)

lovyan03 commented 1 year ago

@martinberlin I am convinced that the problem is that the readout timing is too fast. At least with the IT8951 in the M5Paper, buf[2] and buf[3] get the address correctly.

As far as I have tried, when the clock is increased, the same value appears in buf more often in succession. This suggests that if the IT8951 is not ready for new data in time, the previous value will be received again. I assume that the fact that only the first data is being received twice is probably affected by the speed at which it switches from sending to receiving.

I have once again updated the develop branch. I have reduced the send speed and added delay(1) before receiving.

martinberlin commented 1 year ago

Confirmed! You where right and it's exactly that, just a timing issue. Here is how it comes now:

E (889) debug: buf[0] = 04b0
E (889) debug: buf[1] = 0339
E (889) debug: buf[2] = 9fc0
E (889) debug: buf[3] = 0011

Some statistics with S3:

125439 bytes read from http://img.cale.es/jpg/fasani/5e636b0f39aac I (12339) decode: 630 ms - 1200x825 image MCUs:520 I (12339) www-dw: 1405 ms - download I (12339) render: 426 ms - render I (12349) total: 2461 ms - total time spent

Is quite fast, since it's downloading a 125 Kb JPG and rendering it on the display in 2.5 seconds (After it connects to WiFi) Will make a short video on this one. Thanks a lot @lovyan03 Now this component can be used with any IT8951 controller, no matter what size it has!

martinberlin commented 1 year ago

Video demo: https://twitter.com/martinfasani/status/1531153415530848256 :tada: Thanks!

lovyan03 commented 1 year ago

@martinberlin Thanks for confirming it works and reporting ! I am pleased that the fix worked . Please let me know if there are any more problems.

I have come to think that LovyanGFX could have something like EPD's gamma correction process.

martinberlin commented 1 year ago

I have come to think that LovyanGFX could have something like EPD's gamma correction process.

That would be nice addition! I still need to try to load the JPG using your library. It's so far working everytime and it didn't add any noticeable delay to read this address from SPI. Only thing that needs to be mentioned, or maybe a warning signal should be raised, in case it reads all 0x00 in case the spi_3wire config parameter is set to true.

By the way this Issue is solved. @lovyan03 you can close it once it's merged in master branch. Thanks a lot for this!

martinberlin commented 1 year ago

New Hardware project started after getting this to work: https://github.com/martinberlin/H-cinread-it8951

Will be a small HAT that can be connected on top from DEXA-C097 and make it easier for developers to send the image buffer using fast ESP32S3. If you have any wishes for this small PCB just tell it to me over twitter and I will consider adding it.

martinberlin commented 1 year ago

Hello @lovyan03 I have an additional issue with this Cinread IT8951 PCB and the component. I'm looking for ways to optimize consumption since when it's on I see that on 5V USB is consuming around 290 mA (Even when my ESP32S3 board is on deepsleep)

So I tried turning it off using the sleep method from your library. That works and sends the consumption back to 3 mA. But the problem is that when it wakes up after two minutes to update the clock, even if I try display.wakeup() and wait some milliseconds, the display is completely gray, so it has some problem to wake up. Can you tell me if there is anything extra that needs to happen for a normal wake up? After that first correct refresh, the only way to bring it back to normal is a hardware reset, pulling the USB out and again it will work for one time more. As always I thank you in advance for your help on resolving this matter. GRAY Wakeup

UPDATE Discovered that doing this:

    // Turn on the 3.7 to 5V step-up
    gpio_set_level(GPIO_ENABLE_5V, 1);
    display.init();
    vTaskDelay(pdMS_TO_TICKS(100));
    display.wakeup();

It works back again but still shows a strange gray, like if the clearDisplay() would stop before being finished.

lovyan03 commented 1 year ago

@martinberlin There may be a flaw in the implementation regarding the display wake-up. The current implementation of the wakeup() function is to rerun init function. I will need to modify my code, but before I do, I would appreciate your help in experimenting.

It may be possible to wake up by using the powerSaveOff() function, please try it and let me know the result.

The following functions can be used to change modes.

// IT8951 changed to sleep mode
display.sleep();

// IT8951 changed to standby mode
display.powerSaveOn();

// IT8951 changed to active mode
display.powerSaveOff();
martinberlin commented 1 year ago

I tried using powerSaveOff() replacing the wakeup code I posted before. The IT8951 is not refreshing the display when I do that. Only with the wakeup() that is triggering the init() again as you commented is waking up and refreshing again the display.

What I can confirm is that looking at consumption, both display.sleep() and display.powerSaveOn() seem to have the same effect, leaving my cheap USB multimeter at 3 mA.

But let me know if I need to test any other cases, like doing powerSaveOn before deepsleep and powerSaveOff when the S3 wakes up. My entire demo sketch is here in the epaper-weather-station repository.

lovyan03 commented 1 year ago

@martinberlin note:

Try placing a 1 second delay immediately after boot, then run init_without_reset instead of init. LovyanGFX fills the entire screen to white (or black for LCDs) when init is called. Using init_without_reset omits the reset pin operation and fill process. In other words, it can be activated without changing any of the contents of the IT8951's image memory.

If the image memory is already volatiled, the user's code must redraw the image.

Try executing a simple fillCircle in this state. The rectangular area including the circle area should be refreshed, but since the memory of the area outside the circle is not changed, strange stripes will be reproduced in the four corners.

martinberlin commented 1 year ago

I understand. But even doing a:

    // Turn on the 3.7 to 5V step-up
    gpio_set_level(GPIO_ENABLE_5V, 1);

    display.wakeup(); // changing this for init() does not change anything
    vTaskDelay(pdMS_TO_TICKS(800));

Does not work good. Only if I do:

    display.init();         // Followed by another init() also works
    display.wakeup();
    // No wait needed. 

But even if I do only one init() does not work. I don't get why. Is like if it would need more time but adding a wait 800 ms does not make it. One thing that could be maybe enhanced is the reaction time, since it needs like 1 second from wake-up to update the epaper display. But that's not an important subject right now, what I would like to do is to keep consumption at minimum to do something that developers can take profit for a client's project. I'm going to send you one of this boards to you soon, hope you can get a nice 9.7" parallel epaper from Ali ;)

One curious thing is that calling wakeup first:

    display.wakeup();
    display.init();

It won't work. Only if you call init() first works right. But if after sleep I call init() just one time, it won't refresh alright, I need to call it two times.

martinberlin commented 1 year ago

Last comments where related to also the slow SPI, not only after setVCOM, but it seems also after waking up. This has nothing to do with your component but is an issue with Cinread.com IT8951 controller. It's working fine for me and refreshing the display with the RTC clock each 2 minutes since 20 days non-stop with a 3000 mA battery.

github-actions[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

github-actions[bot] commented 1 year ago

This issue has been automatically closed because it has not had recent activity. Thank you for your contributions.