Skip to content

Ownership based SD-RAM #95

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 4 additions & 4 deletions examples/lcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
8 changes: 4 additions & 4 deletions src/bin/async-await.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,17 @@ 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);

// Initialize the allocator BEFORE you use it
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();

Expand Down
8 changes: 4 additions & 4 deletions src/bin/polling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
75 changes: 53 additions & 22 deletions src/init/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<u8>]);

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<u8>] {
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<u8>] {
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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<u8>, 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).
Expand Down
27 changes: 18 additions & 9 deletions src/lcd/init.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
77 changes: 34 additions & 43 deletions src/lcd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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<Layer<FramebufferArgb8888>>,

/// A layer with alpha + color lookup table index
///
/// Use `.take()` to get an owned version of this layer.
pub layer_2: Option<Layer<FramebufferAl88>>,
}

impl<'a> Lcd<'a> {
fn new(ltdc: &'a mut LTDC) -> Self {
fn new(
ltdc: &'a mut LTDC,
layer_1: &'static mut [volatile::Volatile<u8>],
layer_2: &'static mut [volatile::Volatile<u8>],
) -> 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) } ),
}
}

Expand All @@ -71,26 +76,8 @@ impl<'a> Lcd<'a> {
});
}

/// Returns a reference to layer 1.
pub fn layer_1(&mut self) -> Option<Layer<FramebufferArgb8888>> {
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<Layer<FramebufferAl88>> {
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
}
}

Expand All @@ -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<u8>],
}

impl FramebufferArgb8888 {
const fn new(base_addr: usize) -> Self {
Self { base_addr }
fn new(mem: &'static mut [volatile::Volatile<u8>]) -> 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);
}
}

Expand All @@ -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<u8>],
}

impl FramebufferAl88 {
const fn new(base_addr: usize) -> Self {
Self { base_addr }
fn new(mem: &'static mut [volatile::Volatile<u8>]) -> 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);
}
}

Expand Down