Skip to content

Implement 7.5 inch V2 driver#5

Open
SakiiCode wants to merge 18 commits into
MorganR:mainfrom
SakiiCode:epd7in5
Open

Implement 7.5 inch V2 driver#5
SakiiCode wants to merge 18 commits into
MorganR:mainfrom
SakiiCode:epd7in5

Conversation

@SakiiCode

@SakiiCode SakiiCode commented Jun 13, 2026

Copy link
Copy Markdown

This PR

  • Adds support for Waveshare 7.5 inch V2 e-Paper display
  • Adds the Clear trait
  • Replaces some debug_assert!() with assert() to make them const compatible
  • Unifies the resolution to u32 everywhere
  • Adds the send_iter() function which takes an iterator instead of a &[u8] buffer
  • Adds demo sequence for this display using RP2350

The demo sequence has been changed a bit because I could not fully decode what the existing demos should look like

Video
epd_demo.mp4

@MorganR MorganR left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thank you for this contribution!

It looks really good and I'll be happy to accept it. I think there is a slight misunderstanding currently on how I intended the DisplaySimple::write_framebuffer vs. DisplayPartial::write_base_framebuffer to work.

Would you be amenable to tweaking this?

Comment thread samples/rp/.cargo/config.toml Outdated
Comment thread samples/rp/src/lib.rs
Comment on lines +597 to +598
x_end / 256,
x_end % 256 - 1,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This looks potentially buggy, as the second parameter will overflow if x_end is a multiple of 256. Could you please try a partial display where that's true to confirm?

@SakiiCode SakiiCode Jun 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not sure, the result is casted to u8, x_end is a signed i32, if x_end is a multiple of 256, it becomes 256 % 256 - 1 = 255.

These calculations were taken from the official code

let buf2_value;
match self.state.mode {
RefreshMode::Fast | RefreshMode::Full | RefreshMode::Partial => {
buf1_value = BinaryColor::On as u8;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Should this be 0xFF? I think the u8 for BinaryColor::On is 0x01, which seems odd to use here

Comment thread epd-waveshare-async/src/lib.rs
Comment thread epd-waveshare-async/src/hw.rs
Comment on lines +592 to +607
self.send(spi, Command::PartialIn, &[]).await?;

let window: [u8; _] = [
x_start / 256,
x_start % 256,
x_end / 256,
x_end % 256 - 1,
y_start / 256,
y_start % 256,
y_end / 256,
y_end % 256 - 1,
0x01,
]
.map(|p| p as u8);

self.send(spi, Command::PartialWindow, &window).await?;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This looks more like the set_window operation (see epd2in9_v2). The idea for both write_framebuffer and write_base_framebuffer is that they're both supposed to support writing to an arbitrary window within the display's internal framebuffers. So they should both support this logic.

The difference between write_framebuffer and write_base_framebuffer is that write_framebuffer should only write the main framebuffer that's going to be displayed next. In black and white mode, that's Command::DisplayStartTrans2 for this 7.5" display.

write_base_framebuffer should only write to the "base" or "old" buffer that's used as the diff base for a partial update operation (in this case, DisplayStartTrans1). You don't actually have to use this, but it's exposed for more advanced control.

When you're in partial refresh mode, the display only tries to update the pixels that differ between these two buffers. In the default operating modes, the display automatically copies the data from the active display buffer to the old display buffer on each refresh. Even if you're writing the full buffer to the screen on each update, you can still benefit from a faster refresh time if only a few pixels have changed.

So write_base_framebuffer only exists in DisplayPartial because it would be meaningless for a display that had no partial refresh like this. But I still would expect most users of a partially updating display to only need write_framebuffer. That works fine, since DisplayPartial requires DisplaySimple.

Does that make this more clear? I'd love to hear if the docs can be improved to make this more obvious.

@SakiiCode

Copy link
Copy Markdown
Author

Sure, thanks for the detailed review, I will try to correct them in the upcoming days.

@SakiiCode

SakiiCode commented Jun 21, 2026

Copy link
Copy Markdown
Author

Maybe the framebuffer and base_framebuffer should be called front_buffer and back_buffer to make it clear what they do.

Although the video appears correct, I discovered that I am using the partial refresh function totally wrong. This will require more investigation. Unfortunately I broke my EPD so it can take a few weeks.

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.

2 participants