Skip to content

Add SDMMC support. #315

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 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 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 .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ jobs:
- { id: stm32l462, additional-features: ",stm32-usbd" }
- { id: stm32l471, additional-features: "" }
- { id: stm32l475, additional-features: "" } # USB_OTG not supported by PAC
- { id: stm32l476, additional-features: ",otg_fs" }
- { id: stm32l486, additional-features: ",otg_fs" }
- { id: stm32l496, additional-features: ",otg_fs" }
- { id: stm32l4a6, additional-features: ",otg_fs" }
- { id: stm32l476, additional-features: ",otg_fs,sdmmc,embedded-sdmmc,fatfs" }
- { id: stm32l486, additional-features: ",otg_fs,sdmmc,embedded-sdmmc,fatfs" }
- { id: stm32l496, additional-features: ",otg_fs,sdmmc,embedded-sdmmc,fatfs" }
- { id: stm32l4a6, additional-features: ",otg_fs,sdmmc,embedded-sdmmc,fatfs" }

steps:
- uses: actions/checkout@v2
Expand Down
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ embedded-dma = "0.1"
bxcan = ">=0.4, <0.7"
fugit = "0.3.5"
bitfield = "0.13.2"
sdio-host = { version = "0.7.0", optional = true }
embedded-sdmmc = { version = "0.3.0", optional = true }
fatfs = { version = "0.4.0", default-features = false, optional = true }

[dependencies.rand_core]
version = "0.6.2"
Expand Down Expand Up @@ -69,6 +72,9 @@ features = ["rt", "stm32l432", "stm32-usbd"]
rt = ["stm32l4/rt"]
unproven = ["embedded-hal/unproven"]
otg_fs = ["synopsys-usb-otg"]
sdmmc = ["dep:sdio-host"]
embedded-sdmmc = ["dep:embedded-sdmmc", "sdmmc"]
fatfs = ["dep:fatfs", "sdmmc"]

# L4x1
stm32l431 = [ "stm32l4/stm32l4x1" ]
Expand Down Expand Up @@ -203,3 +209,6 @@ required-features = ["rt"]
[[example]]
name = "adc_dma"
required-features = ["rt"]

[patch.crates-io]
fatfs = { git = "https://github.com/rafalh/rust-fatfs" }
24 changes: 23 additions & 1 deletion examples/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::hal::prelude::*;
use crate::hal::serial::{Config, Serial};
use crate::hal::stm32;
use hal::hal::blocking::rng::Read;
use hal::rcc::Clk48Source;

macro_rules! uprint {
($serial:expr, $($arg:tt)*) => {
Expand Down Expand Up @@ -39,7 +40,28 @@ fn main() -> ! {

let clocks = rcc
.cfgr
.hsi48(true) // needed for RNG
// Needed for RNG.
.clk48_source({
#[cfg(any(
feature = "stm32l476",
feature = "stm32l486",
feature = "stm32l496",
feature = "stm32l4a6"
))]
{
Clk48Source::Hsi48
}

#[cfg(not(any(
feature = "stm32l476",
feature = "stm32l486",
feature = "stm32l496",
feature = "stm32l4a6"
)))]
{
Clk48Source::Msi
}
})
.sysclk(64.MHz())
.pclk1(32.MHz())
.freeze(&mut flash.acr, &mut pwr);
Expand Down
24 changes: 23 additions & 1 deletion examples/usb_serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
extern crate panic_semihosting;

use cortex_m_rt::entry;
use stm32l4xx_hal::rcc::Clk48Source;
use stm32l4xx_hal::usb::{Peripheral, UsbBus};
use stm32l4xx_hal::{prelude::*, stm32};
use usb_device::prelude::*;
Expand Down Expand Up @@ -43,7 +44,28 @@ fn main() -> ! {

let _clocks = rcc
.cfgr
.hsi48(true)
// Needed for USB.
.clk48_source({
#[cfg(any(
feature = "stm32l476",
feature = "stm32l486",
feature = "stm32l496",
feature = "stm32l4a6"
))]
{
Clk48Source::Hsi48
}

#[cfg(not(any(
feature = "stm32l476",
feature = "stm32l486",
feature = "stm32l496",
feature = "stm32l4a6"
)))]
{
Clk48Source::Msi
}
})
.sysclk(80.MHz())
.freeze(&mut flash.acr, &mut pwr);

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ pub mod qspi;
pub mod rcc;
pub mod rng;
pub mod rtc;
pub mod sdmmc;
pub mod serial;
pub mod signature;
pub mod spi;
Expand Down
121 changes: 97 additions & 24 deletions src/rcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl RccExt for RCC {
hse: None,
lse: None,
msi: None,
hsi48: false,
clk48_source: None,
lsi: false,
hclk: None,
pclk1: None,
Expand Down Expand Up @@ -148,11 +148,13 @@ impl CRRCR {
}

/// Checks if the 48 MHz HSI is enabled
#[inline]
pub fn is_hsi48_on(&mut self) -> bool {
self.crrcr().read().hsi48on().bit()
}

/// Checks if the 48 MHz HSI is ready
#[inline]
pub fn is_hsi48_ready(&mut self) -> bool {
self.crrcr().read().hsi48rdy().bit()
}
Expand Down Expand Up @@ -347,14 +349,14 @@ pub struct CFGR {
hse: Option<HseConfig>,
lse: Option<LseConfig>,
msi: Option<MsiFreq>,
hsi48: bool,
lsi: bool,
hclk: Option<u32>,
pclk1: Option<u32>,
pclk2: Option<u32>,
sysclk: Option<u32>,
pll_source: Option<PllSource>,
pll_config: Option<PllConfig>,
clk48_source: Option<Clk48Source>,
}

impl CFGR {
Expand Down Expand Up @@ -382,12 +384,6 @@ impl CFGR {
self
}

/// Enable the 48 MHz USB, RNG, SDMMC HSI clock source. Not available on all stm32l4x6 series
pub fn hsi48(mut self, on: bool) -> Self {
self.hsi48 = on;
self
}

/// Enables the MSI with the specified speed
pub fn msi(mut self, range: MsiFreq) -> Self {
self.msi = Some(range);
Expand Down Expand Up @@ -431,6 +427,11 @@ impl CFGR {
self
}

pub fn clk48_source(mut self, source: Clk48Source) -> Self {
self.clk48_source = Some(source);
self
}

/// Freezes the clock configuration, making it effective
pub fn freeze(&self, acr: &mut ACR, pwr: &mut Pwr) -> Clocks {
let rcc = unsafe { &*RCC::ptr() };
Expand Down Expand Up @@ -553,18 +554,40 @@ impl CFGR {
while rcc.cr.read().msirdy().bit_is_clear() {}
}

// Turn on USB, RNG Clock using the HSI48 CLK source
if self.hsi48 {
// p. 180 in ref-manual
rcc.crrcr.modify(|_, w| w.hsi48on().set_bit());

// Wait until HSI48 is running
while rcc.crrcr.read().hsi48rdy().bit_is_clear() {}
}
// Select 48 MHz clock source for SDMMC, USB, RNG, etc.
match self.clk48_source {
None => {
unsafe { rcc.ccipr.modify(|_, w| w.clk48sel().bits(0b00)) };
}
#[cfg(any(
feature = "stm32l476",
feature = "stm32l486",
feature = "stm32l496",
feature = "stm32l4a6"
))]
Some(Clk48Source::Hsi48) => {
// Turn on USB, RNG Clock using the HSI48 CLK source.

// p. 180 in ref-manual
rcc.crrcr.modify(|_, w| w.hsi48on().set_bit());

// Wait until HSI48 is running.
while rcc.crrcr.read().hsi48rdy().bit_is_clear() {}

unsafe { rcc.ccipr.modify(|_, w| w.clk48sel().bits(0b00)) };
}
Some(Clk48Source::PllSai1) => {
unsafe { rcc.ccipr.modify(|_, w| w.clk48sel().bits(0b01)) };

// Select MSI as clock source for usb48, rng ...
if let Some(MsiFreq::RANGE48M) = self.msi {
unsafe { rcc.ccipr.modify(|_, w| w.clk48sel().bits(0b11)) };
unimplemented!()
}
Some(Clk48Source::Pll) => {
unsafe { rcc.ccipr.modify(|_, w| w.clk48sel().bits(0b10)) };
}
Some(Clk48Source::Msi) => {
assert_eq!(Some(MsiFreq::RANGE48M), self.msi);
unsafe { rcc.ccipr.modify(|_, w| w.clk48sel().bits(0b11)) };
}
}

//
Expand Down Expand Up @@ -716,14 +739,23 @@ impl CFGR {
})
}

let mut pll48m1clk = None;

let sysclk_src_bits;
let mut msi = self.msi;
if let Some(pllconf) = pllconf {
// Sanity-checks per RM0394, 6.4.4 PLL configuration register (RCC_PLLCFGR)
let r = pllconf.r.to_division_factor();
let clock_speed = clock_speed / (pllconf.m as u32 + 1);
let vco = clock_speed * pllconf.n as u32;
let output_clock = vco / r;
let pllclk = vco / r;

let q = (vco + 48_000_000 - 1) / 48_000_000;
pll48m1clk = Some((vco / q).Hz());

if self.clk48_source == Some(Clk48Source::Pll) {
assert_eq!(q, 48_000_000);
}

assert!(r <= 8); // Allowed max output divider
assert!(pllconf.n >= 8); // Allowed min multiplier
Expand All @@ -732,7 +764,7 @@ impl CFGR {
assert!(clock_speed <= 16_000_000); // VCO input clock max
assert!(vco >= 64_000_000); // VCO output min
assert!(vco <= 334_000_000); // VCO output max
assert!(output_clock <= 80_000_000); // Max output clock
assert!(pllclk <= 80_000_000); // Max output clock

// use PLL as source
sysclk_src_bits = 0b11;
Expand All @@ -750,6 +782,8 @@ impl CFGR {
.bits(pllconf.r.to_bits())
.plln()
.bits(pllconf.n)
.pllq()
.bits(q as u8)
});

rcc.cr.modify(|_, w| w.pllon().set_bit());
Expand Down Expand Up @@ -810,15 +844,16 @@ impl CFGR {
lsi: lsi_used,
lse: self.lse.is_some(),
msi,
hsi48: self.hsi48,
pclk1: pclk1.Hz(),
pclk2: pclk2.Hz(),
ppre1,
ppre2,
pll48m1clk,
sysclk: sysclk.Hz(),
timclk1: timclk1.Hz(),
timclk2: timclk2.Hz(),
pll_source: pllconf.map(|_| pll_source),
clk48_source: self.clk48_source,
}
}
}
Expand Down Expand Up @@ -900,24 +935,44 @@ impl PllSource {
}
}

#[derive(Clone, Copy, Debug, PartialEq)]
/// 48 MHz clock source used by USB OTG FS, RNG and SDMMC.
pub enum Clk48Source {
/// HSI48 clock
#[cfg(any(
feature = "stm32l476",
feature = "stm32l486",
feature = "stm32l496",
feature = "stm32l4a6"
))]
Hsi48,
/// PLLSAI1 “Q” clock (PLL48M2CLK)
PllSai1,
/// PLL “Q” clock (PLL48M1CLK)
Pll,
/// MSI clock
Msi,
}

/// Frozen clock frequencies
///
/// The existence of this value indicates that the clock configuration can no longer be changed
#[derive(Clone, Copy, Debug)]
pub struct Clocks {
hclk: Hertz,
hsi48: bool,
msi: Option<MsiFreq>,
lsi: bool,
lse: bool,
pclk1: Hertz,
pclk2: Hertz,
ppre1: u8,
ppre2: u8,
pll48m1clk: Option<Hertz>,
sysclk: Hertz,
timclk1: Hertz,
timclk2: Hertz,
pll_source: Option<PllSource>,
clk48_source: Option<Clk48Source>,
}

impl Clocks {
Expand All @@ -928,7 +983,25 @@ impl Clocks {

/// Returns status of HSI48
pub fn hsi48(&self) -> bool {
self.hsi48
#[cfg(any(
feature = "stm32l476",
feature = "stm32l486",
feature = "stm32l496",
feature = "stm32l4a6"
))]
{
self.clk48_source == Some(Clk48Source::Hsi48)
}

#[cfg(not(any(
feature = "stm32l476",
feature = "stm32l486",
feature = "stm32l496",
feature = "stm32l4a6"
)))]
{
false
}
}

// Returns the status of the MSI
Expand Down
Loading