Skip to content

Commit a84ac66

Browse files
grodinomvertescher
authored andcommitted
Added example for LTDC screen controller
1 parent 1174d6a commit a84ac66

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ version = "1.0.2"
4242
cortex-m-semihosting = "0.3.3"
4343
panic-halt = "0.2.0"
4444
panic-semihosting = "0.5.2"
45+
embedded-graphics = "0.6.1"
4546

4647
[features]
4748
device-selected = []
@@ -94,3 +95,7 @@ required-features = ["stm32f746", "rt"]
9495
[[example]]
9596
name = "timer"
9697
required-features = ["stm32f746", "rt"]
98+
99+
[[example]]
100+
name = "stm32f7disco-screen"
101+
required-features = ["stm32f746", "rt"]

examples/stm32f7disco-screen/main.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#![deny(warnings)]
2+
#![no_main]
3+
#![no_std]
4+
5+
// Required
6+
extern crate panic_semihosting;
7+
8+
use cortex_m_rt::entry;
9+
use embedded_graphics::{
10+
egcircle, egrectangle, egtext,
11+
fonts::Font6x8,
12+
pixelcolor::{Rgb565, RgbColor},
13+
prelude::*,
14+
primitive_style, text_style,
15+
};
16+
17+
use stm32f7xx_hal::{
18+
device,
19+
gpio::Speed,
20+
ltdc::{Layer, PixelFormat},
21+
prelude::*,
22+
rcc::{HSEClock, HSEClockMode, Rcc},
23+
};
24+
25+
mod screen;
26+
27+
// DIMENSIONS
28+
const WIDTH: u16 = 480;
29+
const HEIGHT: u16 = 272;
30+
31+
// Graphics framebuffer
32+
const FB_GRAPHICS_SIZE: usize = (WIDTH as usize) * (HEIGHT as usize);
33+
static mut FB_LAYER1: [u16; FB_GRAPHICS_SIZE] = [0; FB_GRAPHICS_SIZE];
34+
35+
#[entry]
36+
fn main() -> ! {
37+
let perif = device::Peripherals::take().unwrap();
38+
let _cp = cortex_m::Peripherals::take().unwrap();
39+
40+
let rcc_hal: Rcc = perif.RCC.constrain();
41+
42+
// Set up pins
43+
let _gpioa = perif.GPIOA.split();
44+
let _gpiob = perif.GPIOB.split();
45+
let gpioe = perif.GPIOE.split();
46+
let gpiog = perif.GPIOG.split();
47+
let gpioh = perif.GPIOH.split();
48+
let gpioi = perif.GPIOI.split();
49+
let gpioj = perif.GPIOJ.split();
50+
let gpiok = perif.GPIOK.split();
51+
52+
gpioe.pe4.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_B0
53+
54+
gpiog.pg12.into_alternate_af9().set_speed(Speed::VeryHigh); // LTCD_B4
55+
56+
gpioi.pi9.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_VSYNC
57+
gpioi.pi10.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_HSYNC
58+
gpioi.pi13.into_alternate_af14().set_speed(Speed::VeryHigh);
59+
gpioi.pi14.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_CLK
60+
gpioi.pi15.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_R0
61+
62+
gpioj.pj0.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_R1
63+
gpioj.pj1.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_R2
64+
gpioj.pj2.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_R3
65+
gpioj.pj3.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_R4
66+
gpioj.pj4.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_R5
67+
gpioj.pj5.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_R6
68+
gpioj.pj6.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_R7
69+
gpioj.pj7.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_G0
70+
gpioj.pj8.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_G1
71+
gpioj.pj9.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_G2
72+
gpioj.pj10.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_G3
73+
gpioj.pj11.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_G4
74+
gpioj.pj13.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_B1
75+
gpioj.pj14.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_B2
76+
gpioj.pj15.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_B3
77+
78+
gpiok.pk0.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_G5
79+
gpiok.pk1.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_G6
80+
gpiok.pk2.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_G7
81+
gpiok.pk4.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_B5
82+
gpiok.pk5.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_B6
83+
gpiok.pk6.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_D7
84+
gpiok.pk7.into_alternate_af14().set_speed(Speed::VeryHigh); // LTCD_E
85+
86+
// HSE osc out in High Z
87+
gpioh.ph1.into_floating_input();
88+
let _clocks = rcc_hal
89+
.cfgr
90+
.hse(HSEClock::new(25.mhz(), HSEClockMode::Bypass))
91+
.sysclk(216.mhz())
92+
.hclk(216.mhz())
93+
.freeze();
94+
95+
// LCD enable: set it low first to avoid LCD bleed while setting up timings
96+
let mut disp_on = gpioi.pi12.into_push_pull_output();
97+
disp_on.set_low().ok();
98+
99+
// LCD backlight enable
100+
let mut backlight = gpiok.pk3.into_push_pull_output();
101+
backlight.set_high().ok();
102+
103+
let mut display = screen::Stm32F7DiscoDisplay::new(perif.LTDC, perif.DMA2D);
104+
display
105+
.controller
106+
.config_layer(Layer::L1, unsafe { &mut FB_LAYER1 }, PixelFormat::RGB565);
107+
108+
display.controller.enable_layer(Layer::L1);
109+
display.controller.reload();
110+
111+
let display = &mut display;
112+
113+
// LCD enable: activate LCD !
114+
disp_on.set_high().ok();
115+
116+
let r = egrectangle!(
117+
top_left = (0, 0),
118+
bottom_right = (479, 271),
119+
style = primitive_style!(fill_color = Rgb565::new(0, 0b11110, 0b11011))
120+
);
121+
r.draw(display).ok();
122+
123+
let c1 = egcircle!(
124+
center = (20, 20),
125+
radius = 8,
126+
style = primitive_style!(fill_color = Rgb565::new(0, 63, 0))
127+
);
128+
129+
let c2 = egcircle!(
130+
center = (25, 20),
131+
radius = 8,
132+
style = primitive_style!(fill_color = Rgb565::new(31, 0, 0))
133+
);
134+
135+
let t = egtext!(
136+
text = "Hello Rust!",
137+
top_left = (100, 100),
138+
style = text_style!(font = Font6x8, text_color = RgbColor::WHITE)
139+
);
140+
141+
c1.draw(display).ok();
142+
c2.draw(display).ok();
143+
t.draw(display).ok();
144+
145+
for i in 0..300 {
146+
let c1 = egcircle!(
147+
center = (20 + i, 20),
148+
radius = 8,
149+
style = primitive_style!(fill_color = RgbColor::GREEN)
150+
);
151+
c1.draw(display).ok();
152+
}
153+
154+
loop {}
155+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use embedded_graphics::{
2+
drawable::Pixel,
3+
geometry::Size,
4+
pixelcolor::{Rgb565, RgbColor},
5+
style::{PrimitiveStyle, Styled},
6+
DrawTarget,
7+
primitives
8+
};
9+
10+
use stm32f7xx_hal::{
11+
device::{LTDC, DMA2D},
12+
ltdc::{DisplayConfig, Layer, DisplayController, SupportedWord, PixelFormat},
13+
rcc::{HSEClock, HSEClockMode},
14+
time::U32Ext,
15+
};
16+
17+
/// STM32F7-DISCO board display
18+
pub const DISCO_SCREEN_CONFIG: DisplayConfig = DisplayConfig {
19+
active_width: 480,
20+
active_height: 272,
21+
h_back_porch: 13,
22+
h_front_porch: 30,
23+
h_sync: 30,
24+
v_back_porch: 2,
25+
v_front_porch: 2,
26+
v_sync: 10,
27+
frame_rate: 60,
28+
h_sync_pol: false,
29+
v_sync_pol: false,
30+
no_data_enable_pol: true,
31+
pixel_clock_pol: false,
32+
};
33+
34+
pub struct Stm32F7DiscoDisplay<T: 'static + SupportedWord> {
35+
pub controller: DisplayController<T>,
36+
}
37+
38+
impl<T: 'static + SupportedWord> Stm32F7DiscoDisplay<T> {
39+
pub fn new(ltdc: LTDC, dma2d: DMA2D) -> Stm32F7DiscoDisplay<T> {
40+
let controller = DisplayController::new(
41+
ltdc,
42+
dma2d,
43+
PixelFormat::RGB565,
44+
DISCO_SCREEN_CONFIG,
45+
Some(&HSEClock::new(25.mhz(), HSEClockMode::Bypass)),
46+
);
47+
48+
Stm32F7DiscoDisplay {
49+
controller,
50+
}
51+
}
52+
}
53+
54+
impl DrawTarget<Rgb565> for Stm32F7DiscoDisplay<u16> {
55+
type Error = core::convert::Infallible;
56+
57+
/// Draw a `Pixel` that has a color defined
58+
fn draw_pixel(&mut self, pixel: Pixel<Rgb565>) -> Result<(), Self::Error> {
59+
let Pixel(coord, color) = pixel;
60+
let value: u16 = (color.b() as u16 & 0x1F)
61+
| ((color.g() as u16 & 0x3F) << 5)
62+
| ((color.r() as u16 & 0x1F) << 11);
63+
64+
// TODO : draw pixel
65+
self.controller
66+
.draw_pixel(Layer::L1, coord.x as usize, coord.y as usize, value);
67+
Ok(())
68+
}
69+
70+
/// Draw a hardware accelerated (by DMA2D) rectangle
71+
fn draw_rectangle(&mut self, item: &Styled<primitives::Rectangle, PrimitiveStyle<Rgb565>>) -> Result<(), Self::Error> {
72+
if item.style.stroke_color.is_none() {
73+
let top_left = (item.primitive.top_left.x as usize, item.primitive.top_left.y as usize);
74+
let bottom_right = (item.primitive.bottom_right.x as usize, item.primitive.bottom_right.y as usize);
75+
let color = match item.style.fill_color {
76+
Some(c) => {
77+
(c.b() as u32 & 0x1F) | ((c.g() as u32 & 0x3F) << 5) | ((c.r() as u32 & 0x1F) << 11)
78+
},
79+
None => 0u32
80+
};
81+
82+
// Note(unsafe) because transfert might not be before an other write
83+
// to the buffer occurs. However, such Register -> Buffer transfert
84+
// is so fast that such issue does not occur
85+
// TODO : use safer DMA api when the embedde-hal DMA traits will be stabilised
86+
unsafe {self.controller.draw_rectangle(Layer::L1, top_left, bottom_right, color);}
87+
} else {
88+
self.draw_iter(item).unwrap();
89+
}
90+
91+
Ok(())
92+
}
93+
94+
/// Return the sise of the screen
95+
fn size(&self) -> Size {
96+
Size::new(480, 272)
97+
}
98+
}

0 commit comments

Comments
 (0)