Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

- Improve STM32F469 Disco display/touch examples and expose the `dma` module.

## [v0.23.0] - 2025-09-22

- Implement `embedded_hal::i2c::I2c` for `I2cMasterDma` [#838]
Expand Down
34 changes: 32 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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 = { version = "0.1", optional = true }
bare-metal = { version = "1" }
void = { default-features = false, version = "1.0.2" }
display-interface = { version = "0.5.0", optional = true }
Expand Down Expand Up @@ -92,6 +93,10 @@ version = "0.6.1"
version = "0.5.0"
optional = true

[dependencies.embedded-graphics-core]
version = "0.4"
optional = true

[dev-dependencies]
static_cell = "2.1.1"
defmt = "1.0.1"
Expand Down Expand Up @@ -517,7 +522,7 @@ dcmi = []
dfsdm1 = ["dfsdm"]
dfsdm2 = ["dfsdm"]
dma2d = []
dsihost = ["embedded-display-controller"]
dsihost = ["embedded-display-controller", "dep:otm8009a"]
eth = []
fmc = []
fsmc = []
Expand All @@ -532,6 +537,7 @@ gpiok = []
i2c3 = []
lptim1 = []
ltdc = ["dep:micromath"]
framebuffer = ["dep:embedded-graphics-core"]
quadspi = []
otg-fs = []
otg-hs = []
Expand Down Expand Up @@ -620,9 +626,33 @@ required-features = [
"fsmc_lcd",
] # stm32f413


[[example]]
name = "f469disco-hello-eg"
required-features = ["stm32f469", "stm32-fmc", "framebuffer", "dsihost", "defmt"]

[[example]]
name = "f469disco-image-slider"
required-features = ["stm32f469", "stm32-fmc", "framebuffer", "dsihost", "defmt"]

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

[[example]]
name = "f469disco-paint"
required-features = ["stm32f469", "stm32-fmc", "framebuffer", "dsihost", "defmt"]

[[example]]
name = "f469disco-touch-debug"
required-features = ["stm32f469", "stm32-fmc", "defmt"]
[[example]]
name = "f469disco-paint"
required-features = ["stm32f469", "stm32-fmc", "framebuffer", "dsihost", "defmt"]

[[example]]
name = "f469disco-slideshow"
required-features = ["stm32f469", "stm32-fmc", "framebuffer", "dsihost", "defmt"]

[[example]]
name = "hd44780"
Expand Down
154 changes: 154 additions & 0 deletions examples/f469disco-hello-eg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//! STM32F469I-DISCO embedded-graphics hello world.
//!
//! Renders text and colored shapes on the DSI display using the HAL
//! `LtdcFramebuffer` `DrawTarget` implementation.
//!
//! Build:
//! ```bash
//! cargo build --release --example f469disco-hello-eg --features="stm32f469,stm32-fmc,framebuffer,defmt"
//! ```

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

use cortex_m_rt::entry;
use defmt_rtt as _;
use panic_probe as _;

use hal::{
fmc::FmcExt,
gpio::alt::fmc as fmc_alt,
ltdc::{Layer, LtdcFramebuffer, PixelFormat},
pac::{CorePeripherals, Peripherals},
prelude::*,
};
use stm32f4xx_hal::{self as hal, rcc::Config};

use embedded_graphics::{
mono_font::{ascii::FONT_10X20, MonoTextStyle},
pixelcolor::Rgb565,
prelude::*,
primitives::{Circle, PrimitiveStyle, Rectangle, Triangle},
text::Text,
};

use stm32f4xx_hal::display::f469disco as display_init;

use display_init::{FB_SIZE, HEIGHT, WIDTH};
use stm32_fmc::devices::is42s32400f_6;

macro_rules! fmc_pins {
($($alt:ident: $pin:expr,)*) => {
($(fmc_alt::$alt::from($pin.internal_pull_up(true))),*)
};
}

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

let mut rcc = dp
.RCC
.freeze(Config::hse(8.MHz()).pclk2(32.MHz()).sysclk(180.MHz()));
let mut delay = cp.SYST.delay(&rcc.clocks);

let _gpiob = dp.GPIOB.split(&mut rcc);
let gpioc = dp.GPIOC.split(&mut rcc);
let gpiod = dp.GPIOD.split(&mut rcc);
let gpioe = dp.GPIOE.split(&mut rcc);
let gpiof = dp.GPIOF.split(&mut rcc);
let gpiog = dp.GPIOG.split(&mut rcc);
let gpioh = dp.GPIOH.split(&mut rcc);
let gpioi = dp.GPIOI.split(&mut rcc);

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);

#[rustfmt::skip]
let fmc_pins = fmc_pins! {
A0: gpiof.pf0, A1: gpiof.pf1, A2: gpiof.pf2, A3: gpiof.pf3,
A4: gpiof.pf4, A5: gpiof.pf5, A6: gpiof.pf12, A7: gpiof.pf13,
A8: gpiof.pf14, A9: gpiof.pf15, A10: gpiog.pg0, A11: gpiog.pg1,
Ba0: gpiog.pg4, Ba1: gpiog.pg5,
D0: gpiod.pd14, D1: gpiod.pd15, D2: gpiod.pd0, D3: gpiod.pd1,
D4: gpioe.pe7, D5: gpioe.pe8, D6: gpioe.pe9, D7: gpioe.pe10,
D8: gpioe.pe11, D9: gpioe.pe12, D10: gpioe.pe13, D11: gpioe.pe14,
D12: gpioe.pe15, D13: gpiod.pd8, D14: gpiod.pd9, D15: gpiod.pd10,
D16: gpioh.ph8, D17: gpioh.ph9, D18: gpioh.ph10, D19: gpioh.ph11,
D20: gpioh.ph12, D21: gpioh.ph13, D22: gpioh.ph14, D23: gpioh.ph15,
D24: gpioi.pi0, D25: gpioi.pi1, D26: gpioi.pi2, D27: gpioi.pi3,
D28: gpioi.pi6, D29: gpioi.pi7, D30: gpioi.pi9, D31: gpioi.pi10,
Nbl0: gpioe.pe0, Nbl1: gpioe.pe1, Nbl2: gpioi.pi4, Nbl3: gpioi.pi5,
Sdcke0: gpioh.ph2, Sdclk: gpiog.pg8,
Sdncas: gpiog.pg15, Sdne0: gpioh.ph3,
Sdnras: gpiof.pf11, Sdnwe: gpioc.pc0,
};

defmt::info!("Initializing SDRAM...");
let mut sdram = dp
.FMC
.sdram(fmc_pins, is42s32400f_6::Is42s32400f6 {}, &rcc.clocks);
let base_ptr = sdram.init(&mut delay) as *mut u16;
assert!(!base_ptr.is_null());

let buffer: &'static mut [u16] =
unsafe { &mut *core::ptr::slice_from_raw_parts_mut(base_ptr, FB_SIZE) };
let mut fb = LtdcFramebuffer::new(buffer, WIDTH, HEIGHT);

// Clear to black
fb.clear(Rgb565::BLACK).ok();

// Title text
let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
Text::new("Hello embedded-graphics!", Point::new(40, 60), text_style)
.draw(&mut fb)
.ok();

// Red rectangle
Rectangle::new(Point::new(50, 100), Size::new(200, 100))
.into_styled(PrimitiveStyle::with_fill(Rgb565::RED))
.draw(&mut fb)
.ok();

// Green circle
Circle::new(Point::new(300, 100), 80)
.into_styled(PrimitiveStyle::with_fill(Rgb565::GREEN))
.draw(&mut fb)
.ok();

// Blue triangle
Triangle::new(
Point::new(50, 400),
Point::new(200, 300),
Point::new(350, 400),
)
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLUE))
.draw(&mut fb)
.ok();

let buffer = fb.into_inner();

// Initialize DSI, detect panel, and configure LTDC in the proven order
let (mut display_ctrl, _controller) = display_init::init_display_full(
dp.DSI,
dp.LTDC,
dp.DMA2D,
&mut rcc,
&mut delay,
display_init::BoardHint::Unknown,
PixelFormat::RGB565,
);
display_ctrl.config_layer(Layer::L1, buffer, hal::ltdc::PixelFormat::RGB565);
display_ctrl.enable_layer(Layer::L1);
display_ctrl.reload();

defmt::info!("Hello embedded-graphics! Display ready.");
loop {
cortex_m::asm::wfi();
}
}
Loading