Closed almindor closed 2 years ago
@brianmay @KerryRJ happy to hear your thoughts on this
I am finding that switching between the portrait()/landscape() methods, and the enums(Inversion) a bit strange. Might just be me. I would prefer if @brianmay weighs in on this.
I am finding that switching between the portrait()/landscape() methods, and the enums(Inversion) a bit strange. Might just be me. I would prefer if @brianmay weighs in on this.
I agree it's not ideal. The problem is if we disconnect the inverts then set_orientation
becomes asymmetric with init
which is silly, basically preventing anyone from initializing the display inverted "just because".
I'm open to suggestions that'd make this more ergonomic given this constraint.
Taking a step back, and doing a complete rethink, I believe that there are two use cases for setting the inversion bits (actually MY and MX I think):
My screen is mirror image! I need to hold it to a mirror just to read the writing! Toggling one of the invert lines is required. Contrary to what I though previously, It doesn't actually matter which one, if you get the wrong one, the screen will appear upside-down. In which case the next use case will apply.
My screen is upside down! Toggle both of the invert lines from the values previously set.
So how about:
1, For the init function we have an argument mirror_image: bool
. This will set one of the invert bits. Either one. But not both.
pub enum Orientation {
Portrait(bool),
Landscape(bool),
}
Or maybe we just have 4 variations like before. The bool flag means "use this orientation but display upside-down".
If this bool flag is false, we do nothing. If the bool flag is true, then we toggle both of MY and MX. If the values were both 0, we set them to both 1. If one of the values was set previously in step 1, then we set that bit to 0, and the other bit to 1.
The theory is that once the mirror_image
bit is set correctly, you should be able to use all 4 possible combinations for Orientation
, and all of them will display correctly without being reversed.
Assumption: I believe that toggling the MV (rotate) bit should not cause the image to be reversed, on any display. At least this is the case for my display. Correct me if I am wrong. If I am wrong, might need to have two mirror_portrait
and mirror_landscape
parameters that only take affect for the respective orientation. But hoping this isn't required.
If memory serves me correct the st7735s needs both invert bits set to swap the image other wise the text ends up reversed on a single plane. I'd need to double check to confirm.
Confirming that single axis inversion is reversing text on the plane and I have to use XY to get it swapped.
Yes, this is as I said. You toggle one bit to mirror the display, or toggle both bits to turn the display upside down.
Taking a step back, and doing a complete rethink, I believe that there are two use cases for setting the inversion bits (actually MY and MX I think):
- My screen is mirror image! I need to hold it to a mirror just to read the writing! Toggling one of the invert lines is required. Contrary to what I though previously, It doesn't actually matter which one, if you get the wrong one, the screen will appear upside-down. In which case the next use case will apply.
- My screen is upside down! Toggle both of the invert lines from the values previously set.
So how about:
1, For the init function we have an argument
mirror_image: bool
. This will set one of the invert bits. Either one. But not both. 2. The orientations enum values have an invert boolean. i.e.:pub enum Orientation { Portrait(bool), Landscape(bool), }
Or maybe we just have 4 variations like before. The bool flag means "use this orientation but display upside-down".
If this bool flag is false, we do nothing. If the bool flag is true, then we toggle both of MY and MX. If the values were both 0, we set them to both 1. If one of the values was set previously in step 1, then we set that bit to 0, and the other bit to 1.
The theory is that once the
mirror_image
bit is set correctly, you should be able to use all 4 possible combinations forOrientation
, and all of them will display correctly without being reversed.Assumption: I believe that toggling the MV (rotate) bit should not cause the image to be reversed, on any display. At least this is the case for my display. Correct me if I am wrong. If I am wrong, might need to have two
mirror_portrait
andmirror_landscape
parameters that only take affect for the respective orientation. But hoping this isn't required.
Wait, if we wanted to do it as 4 enum values what would the MADCTL look like for each?
#[repr(u8)]
#[derive(Copy, Clone)]
pub enum Orientation {
Portrait = 0b0000_0000,
Landscape = 0b0110_0000,
PortraitInverted = ???,
LandscapeInverted = ???,
}
Wait, if we wanted to do it as 4 enum values what would the MADCTL look like for each?
Depends on if mirror was set to true or not in the init call.
So for the init call:
fn init(mirror: bool) {
let madctl = if mirror {0x0b1000_0000 } else { 0b0000_0000 }
}
And for the set_orientation call:
fn set_orientation(&mut self, orientation: Orientation) {
let madctl = match orientation {
Portrait => 0b0000_0000 | self.madctl & 0b1101_1111,
Landscape => 0b0010_0000 | self.madctl & 0b1101_1111,
PortraitInverted => 0b0000_0000 | (self.madctl ^ 0x0b1100_0000) & 0b1101_1111
LandscapeInverted => 0b0010_0000 | (self.madctl ^ 0x0b1100_0000) & 0b1101_1111
}
self.madctl = mactl;
// output new madctl value
}
Perhaps a truth table might help:
Mirror | Orientation | MX | MY | MV |
---|---|---|---|---|
FALSE | Portrait | FALSE | FALSE | FALSE |
FALSE | Landscape | FALSE | FALSE | TRUE |
FALSE | Portrait Inverse | TRUE | TRUE | FALSE |
FALSE | Landscape Inverse | TRUE | TRUE | TRUE |
TRUE | Portrait | TRUE | FALSE | FALSE |
TRUE | Landscape | TRUE | FALSE | TRUE |
TRUE | Portrait Inverse | FALSE | TRUE | FALSE |
TRUE | Landscape Inverse | FALSE | TRUE | TRUE |
Wait, if we wanted to do it as 4 enum values what would the MADCTL look like for each?
Depends on if mirror was set to true or not in the init call.
So for the init call:
fn init(mirror: bool) { let madctl = if mirror {0x0b1000_0000 } else { 0b0000_0000 } }
And for the set_orientation call:
fn set_orientation(&mut self, orientation: Orientation) { let madctl = match orientation { Portrait => 0b0000_0000 | self.madctl & 0b1101_1111, Landscape => 0b0010_0000 | self.madctl & 0b1101_1111, PortraitInverted => 0b0000_0000 | (self.madctl ^ 0x0b1100_0000) & 0b1101_1111 LandscapeInverted => 0b0010_0000 | (self.madctl ^ 0x0b1100_0000) & 0b1101_1111 } self.madctl = mactl; // output new madctl value }
Perhaps a truth table might help:
Mirror Orientation MX MY MV FALSE Portrait FALSE FALSE FALSE FALSE Landscape FALSE FALSE TRUE FALSE Portrait Inverse TRUE TRUE FALSE FALSE Landscape Inverse TRUE TRUE TRUE TRUE Portrait TRUE FALSE FALSE TRUE Landscape TRUE FALSE TRUE TRUE Portrait Inverse FALSE TRUE FALSE TRUE Landscape Inverse FALSE TRUE TRUE
This means that we cannot just have 4 values in the Orientation
enum then tho. We'd still need to have either a (bool)
or store the mirror
boolean somewhere else somehow.
This means that we cannot just have 4 values in the Orientation enum then tho. We'd still need to have either a (bool) or store the mirror boolean somewhere else somehow.
That is one way.
You can also store the values in the madctl and use XOR operation to toggle the bits, as I have shown in the sample (untested) code above.
Possibly a boolean might make the code easier to read, not sure.
This means that we cannot just have 4 values in the Orientation enum then tho. We'd still need to have either a (bool) or store the mirror boolean somewhere else somehow.
That is one way.
You can also store the values in the madctl and use XOR operation to toggle the bits, as I have shown in the sample (untested) code above.
Possibly a boolean might make the code easier to read, not sure.
Added the bool to Orientation
+ some more documentation to it.
Actually not sure what is going on with this example:
let options = DisplayOptions {
orientation: Orientation::Landscape(false),
invert_vertical_refresh: false,
color_order: ColorOrder::Bgr,
invert_horizontal_refresh: false,
};
display.init(&mut delay::Ets, options).unwrap();
display
.set_orientation(Orientation::Portrait(false))
.unwrap();
I get Portrait screen dimensions, but the screen appears in Landscape. Huh?
OH, wonder if the problem is this line on set_orientation
:
let value = self.madctl | (orientation.value_u8() & 0b1110_0000);
Probably need to use self.madctl & 0b0001_1111
here.
Contrary to what I said above (and I thought had tested too) Portrait(false)
is mirrored but Landscape(false)
isn't. Weird. Using Portrait(true)
fixes it, so not a problem. But thought I should correct what I said before.
Contrary to what I said above (and I thought had tested too)
Portrait(false)
is mirrored butLandscape(false)
isn't. Weird. UsingPortrait(true)
fixes it, so not a problem. But thought I should correct what I said before.
Wait, I'm confused now. When you say Portrait(false)
is mirrored, do you mean it's bugged and should be swapped with what Portrait(true)
gives instead?
Wait, I'm confused now. When you say Portrait(false) is mirrored, do you mean it's bugged and should be swapped with what Portrait(true) gives instead?
On my hardware maybe. This might be hardware specific, not sure, I only have one device to test. The current situation with this PR doesn't really bother me though,
Don't forget to get the set_orientation
function call as per https://github.com/almindor/mipidsi/pull/17#issuecomment-1094387568
Actually not sure what is going on with this example:
let options = DisplayOptions { orientation: Orientation::Landscape(false), invert_vertical_refresh: false, color_order: ColorOrder::Bgr, invert_horizontal_refresh: false, }; display.init(&mut delay::Ets, options).unwrap(); display .set_orientation(Orientation::Portrait(false)) .unwrap();
I get Portrait screen dimensions, but the screen appears in Landscape. Huh?
OH, wonder if the problem is this line on
set_orientation
:let value = self.madctl | (orientation.value_u8() & 0b1110_0000);
Probably need to use
self.madctl & 0b0001_1111
here.
Fixed
Introduces
DisplayOptions
with all possibleMADCTL
values abstracted into a more digestible struct.I've also refactored
Orientation
to include inversion logic so we don't haveinit
vsset_orientation
asymmetry.This should fix #10