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

Add support for 4.2 inch screen #17

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

thedevleon
Copy link
Contributor

@thedevleon thedevleon commented Sep 4, 2024

This adds support for the 4.2 inch screen. Currently stacked ontop #14, until merged, see diff here: thedevleon/weact-studio-epd@feat/sleep_and_wakeup...thedevleon:weact-studio-epd:feat/4.2_support

ToDo

  • Get partial refresh working
  • Extend sample with partial refresh test

Reference
https://github.com/ZinggJM/GxEPD2/blob/master/src/gdey/GxEPD2_420_GDEY042T81.cpp

@thedevleon
Copy link
Contributor Author

image

@avsaase
Copy link
Owner

avsaase commented Sep 4, 2024

Thanks! I don't have this screen to test it because it seems to go in and out of stock on AliExpress. Do you have a lick where I can order one?

I'll review this and your other PR later today.

@thedevleon
Copy link
Contributor Author

I got mine here, still seems to be in stock atm.

@thedevleon thedevleon mentioned this pull request Sep 4, 2024
4 tasks
@thedevleon
Copy link
Contributor Author

Partial update doesn't seem to quite work right, I will have to do some more digging as to why.

@thedevleon thedevleon marked this pull request as draft September 4, 2024 13:46
@capt41nkirk
Copy link

I found in the c driver for this display that this display seems to need other commands and data for fast partial refresh. Here you can see the commands. When changing the driver.rs code i get fast partial refresh working:

    /// Start a fast refresh of the display using the current in-screen buffers.
    ///
    /// If the display hasn't done a [`Self::full_refresh`] yet, it will do that first.
    pub async fn fast_refresh(&mut self) -> Result<()> {
        if !self.initial_full_refresh_done {
            // There a bug here which causes the new image to overwrite the existing image which then
            // fades out over several updates.
            self.full_refresh().await?;
        }

        if !self.using_partial_mode {
            self.command_with_data(command::WRITE_LUT, &lut::LUT_PARTIAL_UPDATE)
                .await?;
            self.using_partial_mode = true;
        }
        self.command_with_data(command::DISPLAY_UPDATE_CONTROL, &[0x00])
            .await?;
        self.data(&[0x00]).await?;
        self.command_with_data(command::UPDATE_DISPLAY_CTRL2, &[flag::UNDOCUMENTED])
            .await?;
        self.command(command::MASTER_ACTIVATE).await?;
        self.wait_until_idle().await;
        Ok(())
    }

This would prob break the other displays but i think it's possible to make display specific code.

diff --git a/src/driver.rs b/src/driver.rs
index 37ab26c..de25bd1 100644
--- a/src/driver.rs
+++ b/src/driver.rs
@@ -377,7 +377,10 @@ where
                 .await?;
             self.using_partial_mode = true;
         }
-        self.command_with_data(command::UPDATE_DISPLAY_CTRL2, &[flag::UNDOCUMENTED])
+        self.command_with_data(command::DISPLAY_UPDATE_CONTROL, &[0x00])
+            .await?;
+        self.data(&[0x00]).await?;
+        self.command_with_data(command::UPDATE_DISPLAY_CTRL2, &[0xfc])
             .await?;
         self.command(command::MASTER_ACTIVATE).await?;
         self.wait_until_idle().await;
Snapchat-886308807.mp4

@marsjo
Copy link

marsjo commented Feb 6, 2025

@capt41nkirk This looks promising. Seems that you are using an esp32 c3 as well. I struggle with clearing the display.
Sometimes it works but most of the time it does not.
Could you also share your example code ❤️

@capt41nkirk
Copy link

you can find my code in this repo:
https://github.com/capt41nkirk/weact-studio-epd

the example is in the espc6 folder, though it is actually rewritten for c3. Still i have some issues with the coordinates i think.

@marsjo
Copy link

marsjo commented Feb 6, 2025

My White is not updating properly. Looks like white in the buffer is treaded like transparent and just black dots are set.
But also not allways - sometimes it just works. The Arduino example from WeAct works fine. Probably I have to also invest some research into it.

IMG_4363.1.mov

Your code on my setup starting from a black screen ...
Actually doing partial updates - but not the full_screen_update

@patcher-ms
Copy link

Hello, I have one of these 4.2" displays and I'd be interested in this PR to land. Is there any testing I can help with?
For now I am depending on thedevleon's fork, using full updates, and everything works well for me.

@capt41nkirk
Copy link

@patcher-ms you could clone my code from above and use the ESP32-C3 example if you have that chip to test whether this works (including the full update at beginning and partial fast refresh afterwards).

@marsjo
Copy link

marsjo commented Feb 7, 2025

So full refresh didi not work for me. If I run it twice with different (long) text I get a mess. Anyhow I stepped through the code comparing it with GxEPD2 and I fixed it for me with the following patch (full_update) that's shares some common stuff with your partial fix.

diff --git a/src/driver.rs b/src/driver.rs
--- a/src/driver.rs	(revision 932d92e5d22f017ed3f64ee6ea9316eefd4f8f80)
+++ b/src/driver.rs	(date 1738971139965)
@@ -216,6 +216,10 @@
         self.initial_full_refresh_done = true;
         self.using_partial_mode = false;
 
+
+        self.command_with_data(command::DISPLAY_UPDATE_CONTROL, &[0x40, 0x00])
+            .await?;
+
         self.command_with_data(command::UPDATE_DISPLAY_CTRL2, &[flag::DISPLAY_MODE_1])
             .await?;
         self.command(command::MASTER_ACTIVATE).await?;

@capt41nkirk
Copy link

@marsjo thanks for the update. Your change doesn't break my display, so it seems this is the more general solution. I updated my fork accordingly. Now we need to rewrite the code to make the functions screen specific for general usage of the driver for all screen sizes.

@marsjo
Copy link

marsjo commented Feb 8, 2025

I do not have the others Display currently - thinking of ordering them.
I think the main difference should be between B/W and B/W and Red. Maybe also the controller chip?

I dislike the partial_buffer approach - but I have to find out more about the code.

@capt41nkirk
Copy link

@marsjo I find it strange that your 4.2 screen did not work before your code change in full_refresh but mine did. Maybe there are also other controller chips/revisions. I attach a pic of the backside, maybe you can spot some differences.
I currently have bw 2.13, 2.9 and 4.2 screen as well as 2.9 bwr one.
pic

@marsjo
Copy link

marsjo commented Feb 8, 2025

I do not have the sticker and the text on the cable - but the rest looks equal.
Did you try to run the full_update example from this PR with differente sized text?

I added the command 21 (see SSD1683 page 28) telling the chip to bypass Red RAM content as 0 (do not paint) while using BW normally.

During Initalisation the options are set to Normal for Red and Inverse for B/W so I have no clue how this should work if we do not set it.

By the way DISPLAY_UPDATE_CONTROL expects two data values instead of

        self.command_with_data(command::DISPLAY_UPDATE_CONTROL, &[0x00])
            .await?;
        self.data(&[0x00]).await?;

we should write:

        self.command_with_data(command::DISPLAY_UPDATE_CONTROL, &[0x00, 0x00])
            .await?;

I think I should read how fast_update is done because I read that it uses the Red Buffer in some way.

BTW: Is there a easy way to get the current millis is esp-rs?

@marsjo
Copy link

marsjo commented Feb 8, 2025

Next question: I read more inside the code and I wondering why do we write to the Red and B/W Buffer after the refresh?

    /// Update the screen with the provided partial frame buffer at the given position using a fast refresh.
    ///
    /// `x`, and `width` must be multiples of 8.
    pub async fn fast_partial_update_from_buffer(
        &mut self,
        buffer: &[u8],
        x: u32,
        y: u32,
        width: u32,
        height: u32,
    ) -> Result<()> {
        self.write_partial_bw_buffer(buffer, x, y, width, height)
            .await?;
        self.fast_refresh().await?;
        self.write_partial_red_buffer(buffer, x, y, width, height)
            .await?;
        self.write_partial_bw_buffer(buffer, x, y, width, height)
            .await?;
        Ok(())
    }

This might make sense for the red_buffer because we store the old image in it to enhance fast_update. But why would we write the same information into the b/w Buffer again?

Removing those two lines I can reduce the update time of the example from 1912ms to 877ms.
But actually we should initialize SPI with 20MHz and then it would not matter anyhow.

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

Successfully merging this pull request may close these issues.

5 participants