|
| 1 | +//! This example initializes the STM32F469I-DISCO LCD and displays a test pattern |
| 2 | +//! Run as: |
| 3 | +//! cargo run --release --example f469disco-lcd-test --features="stm32f469,defmt" |
| 4 | +
|
| 5 | +#![deny(warnings)] |
| 6 | +#![no_main] |
| 7 | +#![no_std] |
| 8 | + |
| 9 | +extern crate cortex_m; |
| 10 | +extern crate cortex_m_rt as rt; |
| 11 | + |
| 12 | +use cortex_m_rt::entry; |
| 13 | + |
| 14 | +use defmt_rtt as _; |
| 15 | +use panic_probe as _; |
| 16 | + |
| 17 | +use stm32f4xx_hal as hal; |
| 18 | + |
| 19 | +use crate::hal::{ |
| 20 | + dsi::{ |
| 21 | + ColorCoding, DsiChannel, DsiCmdModeTransmissionKind, DsiConfig, DsiHost, DsiInterrupts, |
| 22 | + DsiMode, DsiPhyTimers, DsiPllConfig, DsiVideoMode, LaneCount, |
| 23 | + }, |
| 24 | + ltdc::{DisplayConfig, DisplayController, PixelFormat}, |
| 25 | + pac::{CorePeripherals, Peripherals}, |
| 26 | + prelude::*, |
| 27 | +}; |
| 28 | + |
| 29 | +use otm8009a::{Otm8009A, Otm8009AConfig}; |
| 30 | + |
| 31 | +pub const WIDTH: usize = 480; |
| 32 | +pub const HEIGHT: usize = 800; |
| 33 | + |
| 34 | +pub const DISPLAY_CONFIGURATION: DisplayConfig = DisplayConfig { |
| 35 | + active_width: WIDTH as _, |
| 36 | + active_height: HEIGHT as _, |
| 37 | + h_back_porch: 34, |
| 38 | + h_front_porch: 34, |
| 39 | + v_back_porch: 15, |
| 40 | + v_front_porch: 16, |
| 41 | + h_sync: 2, |
| 42 | + v_sync: 1, |
| 43 | + frame_rate: 60, |
| 44 | + h_sync_pol: true, |
| 45 | + v_sync_pol: true, |
| 46 | + no_data_enable_pol: false, |
| 47 | + pixel_clock_pol: true, |
| 48 | +}; |
| 49 | + |
| 50 | +#[entry] |
| 51 | +fn main() -> ! { |
| 52 | + let dp = Peripherals::take().unwrap(); |
| 53 | + let cp = CorePeripherals::take().unwrap(); |
| 54 | + |
| 55 | + let rcc = dp.RCC.constrain(); |
| 56 | + |
| 57 | + let hse_freq = 8.MHz(); |
| 58 | + let clocks = rcc |
| 59 | + .cfgr |
| 60 | + .use_hse(hse_freq) |
| 61 | + .pclk2(32.MHz()) |
| 62 | + .sysclk(180.MHz()) |
| 63 | + .freeze(); |
| 64 | + let mut delay = cp.SYST.delay(&clocks); |
| 65 | + |
| 66 | + let gpioh = dp.GPIOH.split(); |
| 67 | + |
| 68 | + // Reset display |
| 69 | + let mut lcd_reset = gpioh.ph7.into_push_pull_output(); |
| 70 | + lcd_reset.set_low(); |
| 71 | + delay.delay_ms(20u32); |
| 72 | + lcd_reset.set_high(); |
| 73 | + delay.delay_ms(10u32); |
| 74 | + |
| 75 | + // Initialize LTDC, needed to provide pixel clock to DSI |
| 76 | + defmt::info!("Initializing LTDC"); |
| 77 | + let ltdc_freq = 27_429.kHz(); |
| 78 | + let _display = DisplayController::<u32>::new( |
| 79 | + dp.LTDC, |
| 80 | + dp.DMA2D, |
| 81 | + None, |
| 82 | + PixelFormat::ARGB8888, |
| 83 | + DISPLAY_CONFIGURATION, |
| 84 | + Some(hse_freq), |
| 85 | + ); |
| 86 | + |
| 87 | + // Initialize DSI Host |
| 88 | + // VCO = (8MHz HSE / 2 IDF) * 2 * 125 = 1000MHz |
| 89 | + // 1000MHz VCO / (2 * 1 ODF * 8) = 62.5MHz |
| 90 | + let dsi_pll_config = unsafe { |
| 91 | + DsiPllConfig::manual(125, 2, 0 /*div1*/, 4) |
| 92 | + }; |
| 93 | + |
| 94 | + let dsi_config = DsiConfig { |
| 95 | + mode: DsiMode::Video { |
| 96 | + mode: DsiVideoMode::Burst, |
| 97 | + }, |
| 98 | + lane_count: LaneCount::DoubleLane, |
| 99 | + channel: DsiChannel::Ch0, |
| 100 | + hse_freq, |
| 101 | + ltdc_freq, |
| 102 | + interrupts: DsiInterrupts::None, |
| 103 | + color_coding_host: ColorCoding::TwentyFourBits, |
| 104 | + color_coding_wrapper: ColorCoding::TwentyFourBits, |
| 105 | + lp_size: 4, |
| 106 | + vlp_size: 4, |
| 107 | + }; |
| 108 | + |
| 109 | + defmt::info!("Initializing DSI {:?} {:?}", dsi_config, dsi_pll_config); |
| 110 | + let mut dsi_host = DsiHost::init( |
| 111 | + dsi_pll_config, |
| 112 | + DISPLAY_CONFIGURATION, |
| 113 | + dsi_config, |
| 114 | + dp.DSI, |
| 115 | + &clocks, |
| 116 | + ) |
| 117 | + .unwrap(); |
| 118 | + |
| 119 | + dsi_host.configure_phy_timers(DsiPhyTimers { |
| 120 | + dataline_hs2lp: 35, |
| 121 | + dataline_lp2hs: 35, |
| 122 | + clock_hs2lp: 35, |
| 123 | + clock_lp2hs: 35, |
| 124 | + dataline_max_read_time: 0, |
| 125 | + stop_wait_time: 10, |
| 126 | + }); |
| 127 | + |
| 128 | + dsi_host.set_command_mode_transmission_kind(DsiCmdModeTransmissionKind::AllInLowPower); |
| 129 | + dsi_host.start(); |
| 130 | + dsi_host.enable_bus_turn_around(); // Must be before read attempts |
| 131 | + dsi_host.set_command_mode_transmission_kind(DsiCmdModeTransmissionKind::AllInHighSpeed); |
| 132 | + dsi_host.force_rx_low_power(true); |
| 133 | + dsi_host.enable_color_test(); // Must enable before display initialized |
| 134 | + |
| 135 | + defmt::info!("Initializing OTM8009A"); |
| 136 | + let otm8009a_config = Otm8009AConfig { |
| 137 | + frame_rate: otm8009a::FrameRate::_60Hz, |
| 138 | + mode: otm8009a::Mode::Portrait, |
| 139 | + color_map: otm8009a::ColorMap::Rgb, |
| 140 | + cols: WIDTH as u16, |
| 141 | + rows: HEIGHT as u16, |
| 142 | + }; |
| 143 | + let mut otm8009a = Otm8009A::new(); |
| 144 | + otm8009a |
| 145 | + .init(&mut dsi_host, otm8009a_config, &mut delay) |
| 146 | + .unwrap(); |
| 147 | + |
| 148 | + defmt::info!("Outputting Color/BER test patterns..."); |
| 149 | + let delay_ms = 5000u32; |
| 150 | + loop { |
| 151 | + dsi_host.enable_color_test(); |
| 152 | + delay.delay_ms(delay_ms); |
| 153 | + dsi_host.enable_ber_test(); |
| 154 | + delay.delay_ms(delay_ms); |
| 155 | + } |
| 156 | +} |
0 commit comments