almindor / mipidsi

MIPI Display Serial Interface unified driver
MIT License
108 stars 46 forks source link

ST7789 display is black with mipidsi crate, until program execution is interrupted- working fine with st7789 crate #56

Closed tingox closed 1 year ago

tingox commented 1 year ago

This example

// nrf52840dk-mipidsi_examples - xtext
//
#![no_std]
#![no_main]

extern crate cortex_m_rt as rt;

extern crate nrf52840_hal;
extern crate panic_halt;

use cortex_m_rt::entry;
use defmt_rtt as _; // global logger
use display_interface_spi::SPIInterfaceNoCS;
use embedded_graphics::{
    mono_font::{ascii::{FONT_6X10, FONT_10X20}, MonoTextStyle},
    pixelcolor::Rgb565,
    prelude::*,
    text::Text,
};

use nrf52840_hal::gpio::p0::Parts;
use nrf52840_hal::gpio::Level;
use nrf52840_hal::prelude::*;
use nrf52840_hal::spim;
use nrf52840_hal::Delay;

use mipidsi::{Builder, Orientation};

#[entry]
fn main() -> ! {
    let core = nrf52840_hal::pac::CorePeripherals::take().unwrap();
    let mut delay = Delay::new(core.SYST);

    let p = nrf52840_hal::pac::Peripherals::take().unwrap();
    let port0 = Parts::new(p.P0);

    let _backlight = port0.p0_31.into_push_pull_output(Level::Low); // set medium backlight on
    // let rst = port0.p0_29.into_push_pull_output(Level::Low); // reset pin
    let rst = port0.p0_29.into_push_pull_output(Level::High);   // reset pin
    let _cs = port0.p0_28.into_push_pull_output(Level::Low);    // keep low while driving display
    let dc = port0.p0_30.into_push_pull_output(Level::Low);     // data/clock switch

    let spiclk = port0.p0_03.into_push_pull_output(Level::Low).degrade(); // SPI clock to LCD
    let spimosi = port0.p0_04.into_push_pull_output(Level::Low).degrade(); // SPI MOSI to LCD

    let pins = spim::Pins {
        sck: spiclk,
        miso: None,
        mosi: Some(spimosi),
    };

    // create SPI interface
    let spi = spim::Spim::new(p.SPIM0, pins, spim::Frequency::M8, spim::MODE_3, 122);

    // display interface abstraction from SPI and DC
    let di = SPIInterfaceNoCS::new(spi, dc);

    // create driver
    let mut display = Builder::st7789(di)          // known model
            .with_display_size(240, 240)           // set options
            .init(&mut delay, Some(rst)).unwrap(); // with reset pin

    display.set_orientation(Orientation::Portrait(false)).unwrap();

    // color constants:BLACK, WHITE, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW

    // make text style
    // Create a new character style
    let style_white = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
    let style_blue = MonoTextStyle::new(&FONT_10X20, Rgb565::BLUE);
    let style_red = MonoTextStyle::new(&FONT_10X20, Rgb565::RED);
    let style_yellow = MonoTextStyle::new(&FONT_10X20, Rgb565::YELLOW);

    // Create a text at position (20, 30) with the previously defined style
    let text1 = Text::new("Hello Rust!", Point::new(20, 30), style_white);

    // draw text on black background
    display.clear(Rgb565::BLACK).unwrap();
    text1.draw(&mut display).unwrap();

    delay.delay_ms(1000u16);
    // clear display to a white background
    display.clear(Rgb565::WHITE).unwrap();
    // write text in different colors
    Text::new("Different", Point::new(20,30), style_blue).draw(&mut display).unwrap();
    Text::new("colors", Point::new(20,50), style_red).draw(&mut display).unwrap();

    defmt::info!("Rendering done");

    loop {
        Text::new("test", Point::new(20,70), style_blue).draw(&mut display).unwrap();
        delay.delay_ms(250u8);
        Text::new("test", Point::new(20,70), style_yellow).draw(&mut display).unwrap();
        delay.delay_ms(250u8);
    }
}

does not produce output on the display until the program is interrupted. If I use the st7789 crate instead, it works without problems. Is there a way to turn off the batch feature (I couldn't figure out a way from the documentation) so I can test if that has anything to do with the problem?

tingox commented 1 year ago

The program is running

$ cargo run --example xtext
   Compiling nrf52840dk-mipidsi_examples v0.1.0 (C:\Users\torfinn.ingolfsen\OneDrive - Inventas AS\Documents\Inventas\3_intern\2023\AN_Artifical_Nose\display\nrf52840dk-mipidsi_examples)
    Finished dev [unoptimized + debuginfo] target(s) in 0.74s
     Running `probe-run --chip nRF52840_xxAA target\thumbv7em-none-eabihf\debug\examples\xtext`
(HOST) INFO  flashing program (24 pages / 96.00 KiB)
(HOST) INFO  success!
────────────────────────────────────────────────────────────────────────────────
INFO  Rendering done
└─ xtext::__cortex_m_rt_main @ examples\xtext.rs:88

and the display stays black mipidsi_1_IMG_20230509_142559629

tingox commented 1 year ago

I interrupt program execution (using Ctrl-C) and now the output shows on the display mipidsi_2_IMG_20230509_142629951

tingox commented 1 year ago

The same example using the st7789 crate

// nrf52840dk-st7789examples - text
//
#![no_std]
#![no_main]

extern crate cortex_m_rt as rt;

extern crate nrf52840_hal;
extern crate panic_halt;

use cortex_m_rt::entry;
use defmt_rtt as _;             // global logger
use display_interface_spi::SPIInterfaceNoCS;
// use embedded_graphics::image::*;
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::*;
use embedded_graphics::mono_font::{ascii::{FONT_6X10, FONT_10X20}, MonoTextStyle};
use embedded_graphics::text::Text;
use nrf52840_hal::gpio::p0::Parts;
use nrf52840_hal::gpio::Level;
use nrf52840_hal::prelude::*;
use nrf52840_hal::spim;
use nrf52840_hal::Delay;
use st7789::{Orientation, ST7789};

#[entry]
fn main() -> ! {
    let core = nrf52840_hal::pac::CorePeripherals::take().unwrap();
    let mut delay = Delay::new(core.SYST);

    let p = nrf52840_hal::pac::Peripherals::take().unwrap();
    let port0 = Parts::new(p.P0);

    let backlight = port0.p0_31.into_push_pull_output(Level::Low); // set medium backlight on
    let rst = port0.p0_29.into_push_pull_output(Level::Low); // reset pin
    let _cs = port0.p0_28.into_push_pull_output(Level::Low); // keep low while driving display
    let dc = port0.p0_30.into_push_pull_output(Level::Low);  // data/clock switch

    let spiclk = port0.p0_03.into_push_pull_output(Level::Low).degrade(); // SPI clock to LCD
    let spimosi = port0.p0_04.into_push_pull_output(Level::Low).degrade(); // SPI MOSI to LCD

    let pins = spim::Pins {
        sck: spiclk,
        miso: None,
        mosi: Some(spimosi),
    };

    // create SPI interface
    let spi = spim::Spim::new(p.SPIM0, pins, spim::Frequency::M8, spim::MODE_3, 122);
    //let spi = spim::Spim::new(p.SPIM0, pins, spim::Frequency::M4, spim::MODE_3, 122);

    // display interface abstraction from SPI and DC
    let di = SPIInterfaceNoCS::new(spi, dc);

    // create driver
    //let mut display = ST7789::new(di, rst, backlight, 240, 240);
    let mut display = ST7789::new(di, Some(rst), Some(backlight), 240, 240);

    // initialize
    display.init(&mut delay).unwrap();
    // set default orientation
    //display.set_orientation(Orientation::Landscape).unwrap();
    display.set_orientation(Orientation::Portrait).unwrap();

    // color constants:BLACK, WHITE, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW

    // make text style
    // Create a new character style
    let style_white = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
    let style_blue = MonoTextStyle::new(&FONT_10X20, Rgb565::BLUE);
    let style_red = MonoTextStyle::new(&FONT_10X20, Rgb565::RED);
    let style_yellow = MonoTextStyle::new(&FONT_10X20, Rgb565::YELLOW);

    // Create a text at position (20, 30) with the previously defined style
    let text1 = Text::new("Hello Rust!", Point::new(20, 30), style_white);

    // draw text on black background
    display.clear(Rgb565::BLACK).unwrap();
    text1.draw(&mut display).unwrap();

    delay.delay_ms(1000u16);
    // clear display to a white background
    display.clear(Rgb565::WHITE).unwrap();
    // write text in different colors
    Text::new("Different", Point::new(20,30), style_blue).draw(&mut display).unwrap();
    Text::new("colors", Point::new(20,50), style_red).draw(&mut display).unwrap();

    defmt::info!("Rendering done");

    loop {
        Text::new("test", Point::new(20,70), style_blue).draw(&mut display).unwrap();
        delay.delay_ms(250u8);
        Text::new("test", Point::new(20,70), style_yellow).draw(&mut display).unwrap();
        delay.delay_ms(250u8);
    }
}
almindor commented 1 year ago

Could you try compiling mipidsi with default-features=false and see if it works? That eliminates batching and heapless.

I'm a bit busy atm. but will try to replicate with my MCU tomorrow or so

tingox commented 1 year ago

I put

[dependencies.mipidsi]
version = "0.6"
default-features = false

in Cargo.toml and rebuilt the example. alas, no change - I still have to interrupt execution before anything shows on the display.

tingox commented 1 year ago

I found out something. If I change the backlight pin from let _backlight = port0.p0_31.into_push_pull_output(Level::Low); to let _backlight = port0.p0_31.into_push_pull_output(Level::High); it works. Strange that the st7789 crate works with this pin Low.

almindor commented 1 year ago

I found out something. If I change the backlight pin from let _backlight = port0.p0_31.into_push_pull_output(Level::Low); to let _backlight = port0.p0_31.into_push_pull_output(Level::High); it works. Strange that the st7789 crate works with this pin Low.

I suspect it's some sort of reset/init combo or possibly even randomness. EDIT: as @rfuest says I kinda forgot the old driver did explicit backlight control. Mipidsi does not, it complicated things (on the generics code level) a lot since we had to support both with and without backlight pin cases. It was decided early on to not put that into this driver.

OK to close the issue?

rfuest commented 1 year ago

The ST7789 crate supports backlight control, which mipidsi doesn't. If you compare the constructor calls you'll notice that one takes a backlight pin argument and the other doesn't.

With mipidsi the backlight state must be set manually, either by changing the initial state or by calling backlight.set_high.

almindor commented 1 year ago

The ST7789 crate supports backlight control, which mipidsi doesn't. If you compare the constructor calls you'll notice that one takes a backlight pin argument and the other doesn't.

With mipidsi the backlight state must be set manually, either by changing the initial state or by calling backlight.set_high.

Yes, and this is by design. Backlight is usually a binary state and forget thing and depends on wiring rather than model or even trim of the display device. Having it in the driver would complicate things for almost no reason.

I can add some documentation about needing to control backlight manualy to prevent this kind of confusion though.

rfuest commented 1 year ago

Yes, and this is by design. Backlight is usually a binary state and forget thing and depends on wiring rather than model or even trim of the display device. Having it in the driver would complicate things for almost no reason.

I did write my reply before noticing yours and just wanted to make sure that the difference between the creates is clear.

Abstracting backlight control was planned for the display-driver-hal crate, but that crate was never finished or adopted by any drivers.

I can add some documentation about needing to control backlight manualy to prevent this kind of confusion though.

That makes sense. I think we had previously discussed to add a troubleshooting section for common problems (like wrong colors due to inversion and RGB/BGR) to the docs and this would fit in there.

tingox commented 1 year ago

Yes, just close the issue. And thanks for all the answers, they were very helpful for my understanding.