8000 Add LCD ili9225 by darkautism · Pull Request #181 · almindor/mipidsi · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add LCD ili9225 #181

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

Merged
merged 16 commits into from
Apr 22, 2025
Merged

Add LCD ili9225 #181

merged 16 commits into from
Apr 22, 2025

Conversation

darkautism
Copy link
Contributor

First of all, I want to thank the maintainer of mipidsi. This library has been incredibly helpful.

I successfully used mipidsi to illuminate an LCD based on the ili9225 controller and wrote a new initialization procedure. However, I noticed that the commands for this controller differ significantly from other modules. Unfortunately, these commands are not implemented as traits, so I'm unsure how to handle them effectively.

For now, I've implemented the changes using conditional compilation with features to avoid impacting the existing codebase. While I admit this approach is not particularly elegant, it's the best solution I could come up with. I’d love to hear your thoughts on this PR and whether you think there's room for improvement.

@darkautism darkautism requested a review from almindor as a code owner April 9, 2025 19:06
@almindor
Copy link
Owner
almindor commented Apr 10, 2025

First of all, I want to thank the maintainer of mipidsi. This library has been incredibly helpful.

I successfully used mipidsi to illuminate an LCD based on the ili9225 controller and wrote a new initialization procedure. However, I noticed that the commands for this controller differ significantly from other modules. Unfortunately, these commands are not implemented as traits, so I'm unsure how to handle them effectively.

For now, I've implemented the changes using conditional compilation with features to avoid impacting the existing codebase. While I admit this approach is not particularly elegant, it's the best solution I could come up 8000 with. I’d love to hear your thoughts on this PR and whether you think there's room for improvement.

Interesting, DCS should be universal. Do you have a link to the documentation you used for this?

If we do need to have differing DCS between models, I think as you said, traits might be the best approach, or we could hide things in submodules conditionally, similar to your approach but module level to keep things nicer.

@rfuest what do you think?

@darkautism
Copy link
Contributor Author

According to [TFT Drivers](https://github.com/Bodmer/TFT_eSPI/tree/master/TFT_Drivers) and [this specific line in TFT_eSPI.cpp](https://github.com/Bodmer/TFT_eSPI/blob/5793878d24161c1ed23ccb136f8564f332506d53/TFT_eSPI.cpp#L3360), it's clear that the instructions are different, even the instruction widths vary. My implementation is based on these references.

@rfuest
Copy link
Collaborator
rfuest commented Apr 10, 2025

@rfuest what do you think?

I'm on the fence about adding this. The ILI9225 doesn't follow the DCS command set at all and uses something custom. Adding support for this controller would extend the scope of this crate beyond DCS displays (excerpt from the README: "This crate provides a generic display driver to connect to TFT displays that implement the MIPI Display Command Set."

But on the other hand adding something with a completely different command set would ensure that our code is flexible enough to handle more display types.

If we decide to add this it shouldn't require conditional compilation. The dcs module implements a standard command set and it shouldn't be modified by changing a feature. As an alternative it would be possible to add more methods to the Model trait, that have default implementations for DCS displays, but can be overridden to support other displays. For example:

struct Display<...> {
    ...
    fn set_address_window<DI: ...>(&mut self, di: &mut DI, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), DI::Error> {
        // add clipping offsets if present
        let mut offset = self.options.display_offset;
        let mapping = MemoryMapping::from(self.options.orientation);
        if mapping.reverse_columns {
            offset.0 = M::FRAMEBUFFER_SIZE.0 - (self.options.display_size.0 + offset.0);
        }
        if mapping.reverse_rows {
            offset.1 = M::FRAMEBUFFER_SIZE.1 - (self.options.display_size.1 + offset.1);
        }
        if mapping.swap_rows_and_columns {
            offset = (offset.1, offset.0);
        }

        let (sx, sy, ex, ey) = (sx + offset.0, sy + offset.1, ex + offset.0, ey + offset.1);

        MODEL::update_address_window(&mut self.di, sx, sy, se, ey)
    }
    ...
}

trait Model {
    ...
    // Default impl for DCS compliant models.
    fn update_address_window<DI: ...>(di: &mut DI, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), DI::Error> {
        di.write_command(dcs::SetColumnAddress::new(sx, ex))?;
        di.write_command(dcs::SetPageAddress::new(sy, ey))
    }
    ...
}

impl Model for ILI9225Rgb565 {
    ...
    fn update_address_window<DI: ...>(di: &mut DI, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), DI::Error> {
        di.write_raw(0x37, &sx.to_be_bytes())?;
        di.write_raw(0x36, &ex.to_be_bytes())?;
        di.write_raw(0x39, &sy.to_be_bytes())?;
        di.write_raw(0x38, &ey.to_be_bytes())?;
        di.write_raw(0x20, &sx.to_be_bytes())?;
        di.write_raw(0x21, &sy.to_be_bytes())
    }
    ...
}

@darkautism
Copy link
Contributor Author

I remove feature flag and change other LCD using default trait.

WIP: Add rotation feature

@darkautism
Copy link
Contributor Author

Finished.

I Add new trait to resolve different size of madctl.

@darkautism
Copy link
Contributor Author

I also notice .orientation(Orientation::new().rotate(Rotation::Deg270)) not been called when we using Builder. Is this the expected behavior?

I must rerun

    display
        .set_orientation(Orientation::default().rotate(config::ROTATION))
        .unwrap();
        ```

@rfuest
Copy link
Collaborator
rfuest commented Apr 14, 2025

I also notice .orientation(Orientation::new().rotate(Rotation::Deg270)) not been called when we using Builder. Is this the expected behavior?

The builder doesn't use set_orientation and instead relies on the Model::init implementation to set the orientation based on the passed options. You'll need to add something like this to the init method:

di.write_command(madctl)?;

Copy link
Collaborator
@rfuest rfuest left a comment

Choose a reason for hiding this comment

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

Looks much better than the previous version.

src/lib.rs Outdated
@@ -153,7 +154,7 @@ where
// Model Options, includes current orientation
options: options::ModelOptions,
// Current MADCTL value copy for runtime updates
madctl: dcs::SetAddressMode,
madctl: MODEL::AddressMode,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of the associated Model::AddressMode type, I would suggest to store the ModelOptions in this struct and add a update_options(&mut DI, options: &ModelOptions) -> Result<(), DI::Error> method to the Model trait.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will fix it tomorrow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems that madctl will never be used again.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I hadn't noticed that we already store the ModelOptions in Display.

It seems that madctl will never be used again.

Good catch, if that is the case you could just remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some test case use this field so i not remove it.

@rfuest
Copy link
Collaborator
rfuest commented Apr 15, 2025

I noticed one more issue while I looked at the application note for the ILI9225. The reference code drives the reset pin low for 10 ms and contains a comment that this delay is necessary. According to the datasheet the reset pulse needs to be at least 1 ms long.

This timing requirement is violated by our reset implementation, which only drives reset low for 10 µs:

mipidsi/src/builder.rs

Lines 179 to 181 in 4e5610d

rst.set_low().map_err(InitError::ResetPin)?;
delay_source.delay_us(10);
rst.set_high().map_err(InitError::ResetPin)?;

I would suggest to add a constant for the minimum reset time to the Model trait to allow Model impls to specify a longer reset duration.

Copy link
Collaborator
@rfuest rfuest left a comment

Choose a reason for hiding this comment

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

Some more things I notices:

  • set_tearing_effect, set_vertical_scroll_region, and set_vertical_scroll_offset are still hardcoded to use DCS commands and would fail on the ILI9225. They should either be supported or disabled.
  • The init routine doesn't use the color_inversion setting.

@darkautism darkautism requested a review from rfuest April 16, 2025 18:26
Copy link
Collaborator
@rfuest rfuest left a comment

Choose a reason for hiding this comment

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

I think you missed this previous comment I made: #181 (review)

Please also add an entry to CHANGELOG.md and add the ILI9225 to the supported model lists in src/lib.rs and README.md.

@almindor: Would you like to take another look at this?

Copy link
Owner
@almindor almindor left a comment

Choose a reason for hiding this comment

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

I think I'll be happy with this once CHANGELOG entry is added.

Pushing some of the lib commands into Model is a bit questionable but it gives us support for these custom displays and I think that's acceptable (I just won't be happy about it :D)

@rfuest
Copy link
Collaborator
rfuest commented Apr 17, 2025

Pushing some of the lib commands into Model is a bit questionable but it gives us support for these custom displays and I think that's acceptable (I just won't be happy about it :D)

What exactly makes you unhappy about this? Would you prefer that the additional methods wouldn't exist at all or do you dislike having default implementations in Model?

@darkautism
Copy link
Contributor Author

color_inversion not used in TFT_eSPI:
https://github.com/Bodmer/TFT_eSPI/blob/5793878d24161c1ed23ccb136f8564f332506d53/TFT_Drivers/ILI9225_Defines.h#L38
But it seems in datasheet. Try to impl by myself.

@darkautism
Copy link
Contributor Author
darkautism commented Apr 18, 2025

20250418_162210
20250418_162135
Is this behavior correct?

@rfuest
Copy link
Collaborator
rfuest commented Apr 18, 2025

Is this behavior correct?

That looks correct. You can also use the TestImage to check and refer to this chart: https://github.com/almindor/mipidsi/blob/v0.9.0/docs/TROUBLESHOOTING.md#incorrect-colors

Your code does currently set the REV bit in the "Display Control 1" register when the color inversion is set to Normal, which is the wrong way around. This might be counter intuitive, because it works correct on your display, but we decided to make this consistent across all models and require users of IPS panels to add .with_invert_colors(ColorInversion::Inverted) to their builder call.

@darkautism darkautism requested review from almindor and rfuest April 18, 2025 19:52
Copy link
Owner
@almindor almindor left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator
@rfuest rfuest left a comment

Choose a reason for hiding this comment

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

Some of the docs could be improved a bit, but I'll create a follow up PR for that. The rest looks good and can be merged. Thanks for the PR.

@rfuest rfuest merged commit d85192a into almindor:master Apr 22, 2025
15 checks passed
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.

3 participants
0