diff --git a/examples/lcd.rs b/examples/lcd.rs index bf089343..2260e6b8 100644 --- a/examples/lcd.rs +++ b/examples/lcd.rs @@ -55,13 +55,13 @@ fn main() -> ! { gpio_a, gpio_b, gpio_c, gpio_d, gpio_e, gpio_f, gpio_g, gpio_h, gpio_i, gpio_j, gpio_k, ); - init::init_sdram(&mut rcc, &mut fmc); - let mut lcd = init::init_lcd(&mut ltdc, &mut rcc); + let mut sdram = init::init_sdram(&mut rcc, &mut fmc); + let mut lcd = lcd::init(&mut ltdc, &mut rcc, &mut sdram); pins.display_enable.set(true); pins.backlight.set(true); - let mut layer_1 = lcd.layer_1().unwrap(); - let mut layer_2 = lcd.layer_2().unwrap(); + let mut layer_1 = lcd.layer_1.take().unwrap(); + let mut layer_2 = lcd.layer_2.take().unwrap(); layer_1.clear(); layer_2.clear(); diff --git a/src/bin/async-await.rs b/src/bin/async-await.rs index 6a6f7df0..979f41d8 100644 --- a/src/bin/async-await.rs +++ b/src/bin/async-await.rs @@ -102,8 +102,8 @@ fn run() -> ! { init::init_systick(Hz(100), &mut systick, &rcc); systick.enable_interrupt(); - init::init_sdram(&mut rcc, &mut fmc); - let mut lcd = init::init_lcd(&mut ltdc, &mut rcc); + let mut sdram = init::init_sdram(&mut rcc, &mut fmc); + let mut lcd = lcd::init(&mut ltdc, &mut rcc, &mut sdram); pins.display_enable.set(true); pins.backlight.set(true); @@ -111,8 +111,8 @@ fn run() -> ! { unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } lcd.set_background_color(Color::from_hex(0x006600)); - let layer_1 = lcd.layer_1().unwrap(); - let mut layer_2 = lcd.layer_2().unwrap(); + let layer_1 = lcd.layer_1.take().unwrap(); + let mut layer_2 = lcd.layer_2.take().unwrap(); layer_2.clear(); diff --git a/src/bin/polling.rs b/src/bin/polling.rs index 4a957937..5e6dffb7 100644 --- a/src/bin/polling.rs +++ b/src/bin/polling.rs @@ -87,13 +87,13 @@ fn main() -> ! { init::init_systick(Hz(100), &mut systick, &rcc); systick.enable_interrupt(); - init::init_sdram(&mut rcc, &mut fmc); - let mut lcd = init::init_lcd(&mut ltdc, &mut rcc); + let mut sdram = init::init_sdram(&mut rcc, &mut fmc); + let mut lcd = lcd::init(&mut ltdc, &mut rcc, &mut sdram); pins.display_enable.set(true); pins.backlight.set(true); - let mut layer_1 = lcd.layer_1().unwrap(); - let mut layer_2 = lcd.layer_2().unwrap(); + let mut layer_1 = lcd.layer_1.take().unwrap(); + let mut layer_2 = lcd.layer_2.take().unwrap(); layer_1.clear(); layer_2.clear(); diff --git a/src/init/mod.rs b/src/init/mod.rs index 99ce0d64..d22aef37 100644 --- a/src/init/mod.rs +++ b/src/init/mod.rs @@ -1,12 +1,12 @@ //! Provides various hardware initialization functions. use crate::i2c::{self, I2C}; -use crate::lcd::{self, Lcd}; use crate::system_clock; -use stm32f7::stm32f7x6::{self as device, FLASH, FMC, LTDC, PWR, RCC, SAI2, SYST}; +use stm32f7::stm32f7x6::{self as device, FLASH, FMC, PWR, RCC, SAI2, SYST}; pub use self::pins::init as pins; pub use self::pins::Pins; +use core::mem; mod pins; @@ -146,10 +146,39 @@ pub fn enable_syscfg(rcc: &mut RCC) { let _unused = rcc.apb2enr.read(); } +static mut SDRAM_INITIALIZED: bool = false; + +/// SdRam allocator helper with some convenience methods +pub struct SdRam(&'static mut [volatile::Volatile]); + +impl SdRam { + /// Allocates `size` bytes or panics if not enough memory available + /// + /// Note: it is not possible to free any memory + pub fn allocate(&mut self, size: usize) -> &'static mut [volatile::Volatile] { + let memory = mem::replace(&mut self.0, &mut []); + let (ret, rest) = memory.split_at_mut(size); + mem::replace(&mut self.0, rest); + ret + } + + /// Yields the rest of the available memory + pub fn all(self) -> &'static mut [volatile::Volatile] { + self.0 + } +} + /// Initializes the SDRAM, which makes more memory accessible. /// /// This is a prerequisite for using the LCD. -pub fn init_sdram(rcc: &mut RCC, fmc: &mut FMC) { +pub fn init_sdram(rcc: &mut RCC, fmc: &mut FMC) -> SdRam { + + // ensures that we don't do this twice and end up with two `&'static mut` to the same memory + unsafe { + assert!(!SDRAM_INITIALIZED); + SDRAM_INITIALIZED = true; + } + #[allow(dead_code)] #[derive(Debug, Clone, Copy)] enum Bank { @@ -221,7 +250,7 @@ pub fn init_sdram(rcc: &mut RCC, fmc: &mut FMC) { rcc.ahb3rstr.modify(|_, w| w.fmcrst().reset()); rcc.ahb3rstr.modify(|_, w| w.fmcrst().clear_bit()); - // SDRAM contol register + // SDRAM control register fmc.sdcr1.modify(|_, w| unsafe { w.nc().bits(8 - 8); // number_of_column_address_bits w.nr().bits(12 - 11); // number_of_row_address_bits @@ -272,30 +301,32 @@ pub fn init_sdram(rcc: &mut RCC, fmc: &mut FMC) { w }); - // test sdram - use core::ptr; + let sdram_start: usize = 0xC000_0000; + let sdram_len: usize = 64 / 8 * 1024 * 1024; // 64 Mbit according to packaging + + { + // test sdram + use core::ptr; - let ptr1 = 0xC000_0000 as *mut u32; - let ptr2 = 0xC053_6170 as *mut u32; - let ptr3 = 0xC07F_FFFC as *mut u32; + let ptr1 = sdram_start as *mut u32; + let ptr2 = (sdram_start + 0x0053_6170) as *mut u32; + let ptr3 = (sdram_start + sdram_len - 4) as *mut u32; + unsafe { + ptr::write_volatile(ptr1, 0xcafe_babe); + ptr::write_volatile(ptr2, 0xdead_beaf); + ptr::write_volatile(ptr3, 0x0dea_fbee); + assert_eq!(ptr::read_volatile(ptr1), 0xcafe_babe); + assert_eq!(ptr::read_volatile(ptr2), 0xdead_beaf); + assert_eq!(ptr::read_volatile(ptr3), 0x0dea_fbee); + } + } + // this block's safety is guaranteed by SDRAM_INITIALIZED check at the start of this function unsafe { - ptr::write_volatile(ptr1, 0xcafe_babe); - ptr::write_volatile(ptr2, 0xdead_beaf); - ptr::write_volatile(ptr3, 0x0dea_fbee); - assert_eq!(ptr::read_volatile(ptr1), 0xcafe_babe); - assert_eq!(ptr::read_volatile(ptr2), 0xdead_beaf); - assert_eq!(ptr::read_volatile(ptr3), 0x0dea_fbee); + SdRam(core::slice::from_raw_parts_mut(sdram_start as *mut volatile::Volatile, sdram_len)) } } -/// Initializes the LCD. -/// -/// This function is equivalent to [`lcd::init`](crate::lcd::init::init). -pub fn init_lcd<'a>(ltdc: &'a mut LTDC, rcc: &mut RCC) -> Lcd<'a> { - lcd::init(ltdc, rcc) -} - /// Initializes the I2C3 bus. /// /// This function is equivalent to [`i2c::init`](crate::i2c::init). diff --git a/src/lcd/init.rs b/src/lcd/init.rs index 24a5b354..4cf24948 100644 --- a/src/lcd/init.rs +++ b/src/lcd/init.rs @@ -1,14 +1,19 @@ +use crate::init::SdRam; use super::Lcd; use stm32f7::stm32f7x6::{LTDC, RCC}; /// Initializes the LCD controller. /// -/// The SDRAM must be initialized before this function is called. See the -/// [`init_sdram`] function for more information. +/// Needs memory for the screen buffers. On stm32f7 this is usually done via SDRAM. +/// The SDRAM is initialized via the [`init_sdram`] function. /// /// [`init_sdram`]: crate::init::init_sdram -pub fn init<'a>(ltdc: &'a mut LTDC, rcc: &mut RCC) -> Lcd<'a> { - use crate::lcd::{self, LAYER_1_START, LAYER_2_START}; +pub fn init<'a>( + ltdc: &'a mut LTDC, + rcc: &mut RCC, + mem: &mut SdRam, +) -> Lcd<'a> { + use crate::lcd; const HEIGHT: u16 = lcd::HEIGHT as u16; const WIDTH: u16 = lcd::WIDTH as u16; const LAYER_1_OCTETS_PER_PIXEL: u16 = lcd::LAYER_1_OCTETS_PER_PIXEL as u16; @@ -152,11 +157,14 @@ pub fn init<'a>(ltdc: &'a mut LTDC, rcc: &mut RCC) -> Lcd<'a> { w }); + let layer1 = mem.allocate(lcd::LAYER_1_LENGTH); + let layer2 = mem.allocate(lcd::LAYER_2_LENGTH); + // configure color frame buffer start address ltdc.l1cfbar - .modify(|_, w| unsafe { w.cfbadd().bits(LAYER_1_START as u32) }); + .modify(|_, w| unsafe { w.cfbadd().bits(layer1.as_mut_ptr() as usize as u32) }); ltdc.l2cfbar - .modify(|_, w| unsafe { w.cfbadd().bits(LAYER_2_START as u32) }); + .modify(|_, w| unsafe { w.cfbadd().bits(layer2.as_mut_ptr() as usize as u32) }); // configure color frame buffer line length and pitch ltdc.l1cfblr.modify(|_, w| unsafe { @@ -180,10 +188,11 @@ pub fn init<'a>(ltdc: &'a mut LTDC, rcc: &mut RCC) -> Lcd<'a> { ltdc.l1cr.modify(|_, w| w.len().set_bit()); ltdc.l2cr.modify(|_, w| w.len().set_bit().cluten().set_bit()); - // reload shadow registers - ltdc.srcr.modify(|_, w| w.imr().set_bit()); // IMMEDIATE_RELOAD + let mut lcd = Lcd::new(ltdc, layer1, layer2); - let mut lcd = Lcd::new(ltdc); lcd.set_color_lookup_table(255, super::Color::rgb(255, 255, 255)); + + lcd.reload_shadow_registers(); // IMMEDIATE_RELOAD + lcd } diff --git a/src/lcd/mod.rs b/src/lcd/mod.rs index 06a44960..d90077cd 100644 --- a/src/lcd/mod.rs +++ b/src/lcd/mod.rs @@ -7,7 +7,7 @@ pub use self::color::Color; pub use self::init::init; pub use self::stdout::init as init_stdout; -use core::{fmt, ptr}; +use core::fmt; use stm32f7::stm32f7x6::LTDC; #[macro_use] @@ -29,26 +29,31 @@ pub const LAYER_2_OCTETS_PER_PIXEL: usize = 2; /// The length of the layer 1 buffer in bytes. pub const LAYER_2_LENGTH: usize = HEIGHT * WIDTH * LAYER_2_OCTETS_PER_PIXEL; -/// Start address of the SDRAM where the framebuffers live. -pub const SDRAM_START: usize = 0xC000_0000; -/// Start address of the layer 1 framebuffer. -pub const LAYER_1_START: usize = SDRAM_START; -/// Start address of the layer 2 framebuffer. -pub const LAYER_2_START: usize = SDRAM_START + LAYER_1_LENGTH; - /// Represents the LCD and provides methods to access both layers. pub struct Lcd<'a> { controller: &'a mut LTDC, - layer_1_in_use: bool, - layer_2_in_use: bool, + + /// A layer with RGB + alpha value + /// + /// Use `.take()` to get an owned version of this layer. + pub layer_1: Option>, + + /// A layer with alpha + color lookup table index + /// + /// Use `.take()` to get an owned version of this layer. + pub layer_2: Option>, } impl<'a> Lcd<'a> { - fn new(ltdc: &'a mut LTDC) -> Self { + fn new( + ltdc: &'a mut LTDC, + layer_1: &'static mut [volatile::Volatile], + layer_2: &'static mut [volatile::Volatile], + ) -> Self { Self { controller: ltdc, - layer_1_in_use: false, - layer_2_in_use: false, + layer_1: Some(Layer { framebuffer: FramebufferArgb8888::new(layer_1) } ), + layer_2: Some(Layer { framebuffer: FramebufferAl88::new(layer_2) } ), } } @@ -71,26 +76,8 @@ impl<'a> Lcd<'a> { }); } - /// Returns a reference to layer 1. - pub fn layer_1(&mut self) -> Option> { - if self.layer_1_in_use { - None - } else { - Some(Layer { - framebuffer: FramebufferArgb8888::new(LAYER_1_START), - }) - } - } - - /// Returns a reference to layer 2. - pub fn layer_2(&mut self) -> Option> { - if self.layer_2_in_use { - None - } else { - Some(Layer { - framebuffer: FramebufferAl88::new(LAYER_2_START), - }) - } + fn reload_shadow_registers(&mut self) { + self.controller.srcr.modify(|_, w| w.imr().set_bit()); // IMMEDIATE_RELOAD } } @@ -104,20 +91,23 @@ pub trait Framebuffer { /// /// It uses 8bits for alpha, red, green, and black respectively, totaling in 32bits per pixel. pub struct FramebufferArgb8888 { - base_addr: usize, + mem: &'static mut [volatile::Volatile], } impl FramebufferArgb8888 { - const fn new(base_addr: usize) -> Self { - Self { base_addr } + fn new(mem: &'static mut [volatile::Volatile]) -> Self { + Self { mem } } } impl Framebuffer for FramebufferArgb8888 { fn set_pixel(&mut self, x: usize, y: usize, color: Color) { let pixel = y * WIDTH + x; - let pixel_ptr = (self.base_addr + pixel * LAYER_1_OCTETS_PER_PIXEL) as *mut u32; - unsafe { ptr::write_volatile(pixel_ptr, color.to_argb8888()) }; + let pixel_idx = pixel * LAYER_1_OCTETS_PER_PIXEL; + self.mem[pixel_idx].write(color.alpha); + self.mem[pixel_idx + 1].write(color.red); + self.mem[pixel_idx + 2].write(color.green); + self.mem[pixel_idx + 3].write(color.blue); } } @@ -126,20 +116,21 @@ impl Framebuffer for FramebufferArgb8888 { /// There are 8bits for the alpha channel and 8 bits for specifying a color using a /// lookup table. Thus, each pixel is represented by 16bits. pub struct FramebufferAl88 { - base_addr: usize, + mem: &'static mut [volatile::Volatile], } impl FramebufferAl88 { - const fn new(base_addr: usize) -> Self { - Self { base_addr } + fn new(mem: &'static mut [volatile::Volatile]) -> Self { + Self { mem } } } impl Framebuffer for FramebufferAl88 { fn set_pixel(&mut self, x: usize, y: usize, color: Color) { let pixel = y * WIDTH + x; - let pixel_ptr = (self.base_addr + pixel * LAYER_2_OCTETS_PER_PIXEL) as *mut u16; - unsafe { ptr::write_volatile(pixel_ptr, u16::from(color.alpha) << 8 | u16::from(color.red)) }; + let pixel_idx = pixel * LAYER_2_OCTETS_PER_PIXEL; + self.mem[pixel_idx].write(color.alpha); + self.mem[pixel_idx + 1].write(color.red); } }