Skip to content

Commit

Permalink
dsi: add color pattern test for STM32F469I-DISC board
Browse files Browse the repository at this point in the history
Initializes the LTDC, DSIHOST and OTM8009A to output alternating
color / BER patterns.  This does not use a framebuffer so can
run without SDRAM.
  • Loading branch information
tegimeki committed Aug 3, 2024
1 parent 2e8f1ba commit e815b4d
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
- Add `f469disc-lcd-test` with color/BER test pattern LCD output [#789]

### Added

Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ synopsys-usb-otg = { version = "0.4.0", features = [
sdio-host = { version = "0.9.0", optional = true }
embedded-dma = "0.2.0"
embedded-display-controller = { version = "^0.2.0", optional = true }
otm8009a = "0.1"
bare-metal = { version = "1" }
void = { default-features = false, version = "1.0.2" }
display-interface = { version = "0.5.0", optional = true }
Expand Down Expand Up @@ -605,6 +606,10 @@ required-features = [
"fsmc_lcd",
] # stm32f413

[[example]]
name = "f469disco-lcd-test"
required-features = ["stm32f469", "defmt"]

[[example]]
name = "hd44780"
required-features = []
Expand Down
156 changes: 156 additions & 0 deletions examples/f469disco-lcd-test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//! This example initializes the STM32F469I-DISCO LCD and displays a test pattern
//! Run as:
//! cargo run --release --example f469disco-lcd-test --features="stm32f469,defmt"

#![deny(warnings)]
#![no_main]
#![no_std]

extern crate cortex_m;
extern crate cortex_m_rt as rt;

use cortex_m_rt::entry;

use defmt_rtt as _;
use panic_probe as _;

use stm32f4xx_hal as hal;

use crate::hal::{
dsi::{
ColorCoding, DsiChannel, DsiCmdModeTransmissionKind, DsiConfig, DsiHost, DsiInterrupts,
DsiMode, DsiPhyTimers, DsiPllConfig, DsiVideoMode, LaneCount,
},
ltdc::{DisplayConfig, DisplayController, PixelFormat},
pac::{CorePeripherals, Peripherals},
prelude::*,
};

use otm8009a::{Otm8009A, Otm8009AConfig};

pub const WIDTH: usize = 480;
pub const HEIGHT: usize = 800;

pub const DISPLAY_CONFIGURATION: DisplayConfig = DisplayConfig {
active_width: WIDTH as _,
active_height: HEIGHT as _,
h_back_porch: 34,
h_front_porch: 34,
v_back_porch: 15,
v_front_porch: 16,
h_sync: 2,
v_sync: 1,
frame_rate: 60,
h_sync_pol: true,
v_sync_pol: true,
no_data_enable_pol: false,
pixel_clock_pol: true,
};

#[entry]
fn main() -> ! {
let dp = Peripherals::take().unwrap();
let cp = CorePeripherals::take().unwrap();

let rcc = dp.RCC.constrain();

let hse_freq = 8.MHz();
let clocks = rcc
.cfgr
.use_hse(hse_freq)
.pclk2(32.MHz())
.sysclk(180.MHz())
.freeze();
let mut delay = cp.SYST.delay(&clocks);

let gpioh = dp.GPIOH.split();

// Reset display
let mut lcd_reset = gpioh.ph7.into_push_pull_output();
lcd_reset.set_low();
delay.delay_ms(20u32);
lcd_reset.set_high();
delay.delay_ms(10u32);

// Initialize LTDC, needed to provide pixel clock to DSI
defmt::info!("Initializing LTDC");
let ltdc_freq = 27_429.kHz();
let _display = DisplayController::<u32>::new(
dp.LTDC,
dp.DMA2D,
None,
PixelFormat::ARGB8888,
DISPLAY_CONFIGURATION,
Some(hse_freq),
);

// Initialize DSI Host
// VCO = (8MHz HSE / 2 IDF) * 2 * 125 = 1000MHz
// 1000MHz VCO / (2 * 1 ODF * 8) = 62.5MHz
let dsi_pll_config = unsafe {
DsiPllConfig::manual(125, 2, 0 /*div1*/, 4)
};

let dsi_config = DsiConfig {
mode: DsiMode::Video {
mode: DsiVideoMode::Burst,
},
lane_count: LaneCount::DoubleLane,
channel: DsiChannel::Ch0,
hse_freq,
ltdc_freq,
interrupts: DsiInterrupts::None,
color_coding_host: ColorCoding::TwentyFourBits,
color_coding_wrapper: ColorCoding::TwentyFourBits,
lp_size: 4,
vlp_size: 4,
};

defmt::info!("Initializing DSI {:?} {:?}", dsi_config, dsi_pll_config);
let mut dsi_host = DsiHost::init(
dsi_pll_config,
DISPLAY_CONFIGURATION,
dsi_config,
dp.DSI,
&clocks,
)
.unwrap();

dsi_host.configure_phy_timers(DsiPhyTimers {
dataline_hs2lp: 35,
dataline_lp2hs: 35,
clock_hs2lp: 35,
clock_lp2hs: 35,
dataline_max_read_time: 0,
stop_wait_time: 10,
});

dsi_host.set_command_mode_transmission_kind(DsiCmdModeTransmissionKind::AllInLowPower);
dsi_host.start();
dsi_host.enable_bus_turn_around(); // Must be before read attempts
dsi_host.set_command_mode_transmission_kind(DsiCmdModeTransmissionKind::AllInHighSpeed);
dsi_host.force_rx_low_power(true);
dsi_host.enable_color_test(); // Must enable before display initialized

defmt::info!("Initializing OTM8009A");
let otm8009a_config = Otm8009AConfig {
frame_rate: otm8009a::FrameRate::_60Hz,
mode: otm8009a::Mode::Portrait,
color_map: otm8009a::ColorMap::Rgb,
cols: WIDTH as u16,
rows: HEIGHT as u16,
};
let mut otm8009a = Otm8009A::new();
otm8009a
.init(&mut dsi_host, otm8009a_config, &mut delay)
.unwrap();

defmt::info!("Outputting Color/BER test patterns...");
let delay_ms = 5000u32;
loop {
dsi_host.enable_color_test();
delay.delay_ms(delay_ms);
dsi_host.enable_ber_test();
delay.delay_ms(delay_ms);
}
}
12 changes: 12 additions & 0 deletions src/dsi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,18 @@ impl DsiHost {
pub fn enable_bus_turn_around(&mut self) {
self.dsi.pcr().modify(|_, w| w.btae().set_bit()); // Enable bus turn around
}

pub fn enable_color_test(&mut self) {
self.dsi
.vmcr()
.modify(|_, w| w.pge().set_bit().pgm().clear_bit());
}

pub fn enable_ber_test(&mut self) {
self.dsi
.vmcr()
.modify(|_, w| w.pge().set_bit().pgm().set_bit());
}
}

impl DsiRefreshHandle {
Expand Down

0 comments on commit e815b4d

Please sign in to comment.