diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cdfcc4..aad6f26 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,9 @@ jobs: logger: - log - defmt + eh-02: + - eh-02 + - env: # Peripheral Feature flags FLAGS: rt @@ -45,6 +48,6 @@ jobs: - name: Install thumbv8m rust target run: rustup target add thumbv8m.main-none-eabihf - name: Build - run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ env.FLAGS }} + run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.eh-02 }},${{ env.FLAGS }} - name: Test - run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ env.FLAGS }} + run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.eh-02 }},${{ env.FLAGS }} diff --git a/Cargo.toml b/Cargo.toml index 92f18eb..4252e51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,17 +67,22 @@ defmt = [ "stm32h5/defmt", ] +eh-02 = ["dep:embedded-hal-02", "dep:nb"] + [dependencies] cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] } stm32h5 = { package = "stm32h5", version = "0.16.0" } fugit = "0.3.7" embedded-dma = "0.2" embedded-hal = "1.0.0" +embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"], optional = true } + defmt = { version = "1.0.0", optional = true } paste = "1.0.15" log = { version = "0.4.20", optional = true} futures-util = { version = "0.3", default-features = false, features = ["async-await-macro"], optional = true} stm32-usbd = "0.8.0" +nb = { version = "1.1.0", optional = true } [dev-dependencies] log = { version = "0.4.20"} @@ -116,3 +121,8 @@ required-features = ["stm32h503"] [[example]] name = "i2c_target_manual_ack" required-features = ["stm32h503"] + +[[example]] +name = "adc12" +required-features = ["rm0481"] + diff --git a/examples/adc.rs b/examples/adc.rs new file mode 100644 index 0000000..3ff03de --- /dev/null +++ b/examples/adc.rs @@ -0,0 +1,99 @@ +//! Example of reading a voltage with ADC1 +//! +//! For an example of using ADC1 and ADC2 together, see examples/adc12.rs + +#![no_main] +#![no_std] + +use cortex_m_rt::entry; + +use stm32h5xx_hal::{ + adc::{self, AdcCommonExt, AdcSampleTime}, + delay::Delay, + pac, + prelude::*, + rcc::rec::AdcDacClkSel, +}; +use utilities::logger::info; + +#[macro_use] +mod utilities; + +#[entry] +fn main() -> ! { + utilities::logger::init(); + let cp = cortex_m::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); + + // Constrain and Freeze power + info!("Setup PWR... "); + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.freeze(); + + // Constrain and Freeze clock + info!("Setup RCC... "); + let rcc = dp.RCC.constrain(); + + // We need to configure a clock for adc_ker_ck_input. The default + // adc_ker_ck_input is pll2_p_ck, but we will use per_ck. per_ck is sourced + // from the 64MHz HSI + // + // adc_ker_ck_input is then divided by the ADC prescaler to give f_adc. The + // maximum f_adc is 50MHz + let mut ccdr = rcc + .sys_ck(192.MHz()) + .pll1_q_ck(64.MHz()) + .freeze(&pwrcfg, &dp.SBS); + + // Switch adc_ker_ck_input multiplexer to per_ck + ccdr.peripheral.kernel_adcdac_clk_mux(AdcDacClkSel::HsiKer); + + info!(""); + info!("stm32h5xx-hal example - ADC"); + info!(""); + + let mut delay = Delay::new(cp.SYST, &ccdr.clocks); + + #[cfg(feature = "rm0481")] + let mut adcc = + dp.ADCC + .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg); + + #[cfg(feature = "rm0492")] + let mut adcc = + dp.ADC1 + .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg); + + let mut temp = adc::Temperature::new(); + temp.enable(&mut adcc); + + #[cfg(feature = "rm0481")] + let mut adc = adcc + .claim_and_configure(dp.ADC1, &mut delay, adc::Resolution::TwelveBit) + .enable(); + + #[cfg(feature = "rm0492")] + let mut adc = adcc + .claim_and_configure(&mut delay, adc::Resolution::TwelveBit) + .enable(); + + // We can't use ADC2 here because ccdr.peripheral.ADC12 has been + // consumed. See examples/adc12.rs + + // Setup GPIOC + let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); + + // Configure pc0 as an analog input + let pin = gpioc.pc0.into_analog(); + + loop { + let data = adc.convert(&pin, AdcSampleTime::default()); + // voltage = reading * (vref/resolution) + info!( + "ADC reading: {}, voltage for nucleo: {}V. Temp reading: {}", + data, + data as f32 * (3.3 / adc.slope() as f32), + adc.convert(&temp, AdcSampleTime::default()) + ); + } +} diff --git a/examples/adc12.rs b/examples/adc12.rs new file mode 100644 index 0000000..460a740 --- /dev/null +++ b/examples/adc12.rs @@ -0,0 +1,91 @@ +//! Example of using ADC1 and ADC2 together +//! +//! This is not available for H503 since it only has ADC1 +//! +//! For an example of using ADC1 alone, see examples/adc.rs + +#![no_main] +#![no_std] + +use cortex_m_rt::entry; + +use stm32h5xx_hal::{ + adc::{self, AdcCommonExt, AdcSampleTime}, + delay::Delay, + pac, + prelude::*, + rcc::rec::AdcDacClkSel, +}; +use utilities::logger::info; + +#[macro_use] +mod utilities; + +#[entry] +fn main() -> ! { + utilities::logger::init(); + let cp = cortex_m::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); + + // Constrain and Freeze power + info!("Setup PWR... "); + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.freeze(); + + // Constrain and Freeze clock + info!("Setup RCC... "); + let rcc = dp.RCC.constrain(); + + // We need to configure a clock for adc_ker_ck_input. The default + // adc_ker_ck_input is pll2_p_ck, but we will use per_ck. per_ck is sourced + // from the 64MHz HSI + // + // adc_ker_ck_input is then divided by the ADC prescaler to give f_adc. The + // maximum f_adc is 50MHz + let mut ccdr = rcc + .sys_ck(192.MHz()) + .pll1_q_ck(64.MHz()) + .freeze(&pwrcfg, &dp.SBS); + + // Switch adc_ker_ck_input multiplexer to per_ck + ccdr.peripheral.kernel_adcdac_clk_mux(AdcDacClkSel::HsiKer); + + info!(""); + info!("stm32h5xx-hal example - ADC1 and ADC2"); + info!(""); + + let mut delay = Delay::new(cp.SYST, &ccdr.clocks); + + // Setup adc common + let adcc = + dp.ADCC + .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg); + + // Set up individual adc's + let adc1 = adcc.claim_and_configure( + dp.ADC1, + &mut delay, + adc::Resolution::TwelveBit, + ); + let adc2 = adcc.claim_and_configure( + dp.ADC2, + &mut delay, + adc::Resolution::TwelveBit, + ); + + let mut adc1 = adc1.enable(); + let mut adc2 = adc2.enable(); + + // Setup GPIOC + // NOTE: PC2 and PC3 are only pinned out on TFBGA packages!! + let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); + let pc2 = gpioc.pc2.into_analog(); // AIN 12 + let pc3 = gpioc.pc3.into_analog(); // AIN 13 + + loop { + let data_pc2 = adc1.convert(&pc2, AdcSampleTime::default()); + let data_pc3 = adc2.convert(&pc3, AdcSampleTime::default()); + // voltage = reading * (vref/resolution) + info!("ADC readings: {} {}", data_pc2, data_pc3); + } +} diff --git a/examples/blinky.rs b/examples/blinky.rs index c1cab03..38bc6ce 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -22,7 +22,7 @@ fn main() -> ! { // Constrain and Freeze clock let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); let mut led = gpioa.pa5.into_push_pull_output(); diff --git a/examples/dma.rs b/examples/dma.rs index 5562e52..7843725 100644 --- a/examples/dma.rs +++ b/examples/dma.rs @@ -24,7 +24,7 @@ fn main() -> ! { // Constrain and Freeze clock let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); let channels = dp.GPDMA1.channels(ccdr.peripheral.GPDMA1); diff --git a/examples/dwt-blinky.rs b/examples/dwt-blinky.rs index 4dbadde..c72fc0c 100644 --- a/examples/dwt-blinky.rs +++ b/examples/dwt-blinky.rs @@ -28,7 +28,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); let mut led = gpioa.pa5.into_push_pull_output(); diff --git a/examples/fractional-pll.rs b/examples/fractional-pll.rs index 1b767ec..e7371e5 100644 --- a/examples/fractional-pll.rs +++ b/examples/fractional-pll.rs @@ -31,7 +31,7 @@ fn main() -> ! { .pll2_r_ck(3_024_000.Hz()) // pll2_p / 2 --> mco2 .mco2_from_pll2_p_ck(7.MHz()) - .freeze(pwrcfg, &dp.SBS); + .freeze(&pwrcfg, &dp.SBS); // // Enable MCO2 output pin // let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); diff --git a/examples/i2c.rs b/examples/i2c.rs index 3f201eb..dce0b51 100644 --- a/examples/i2c.rs +++ b/examples/i2c.rs @@ -27,7 +27,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS); let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); diff --git a/examples/i2c_target.rs b/examples/i2c_target.rs index 60d4498..7390e98 100644 --- a/examples/i2c_target.rs +++ b/examples/i2c_target.rs @@ -28,7 +28,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS); let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); diff --git a/examples/i2c_target_manual_ack.rs b/examples/i2c_target_manual_ack.rs index 9085df5..a7136cd 100644 --- a/examples/i2c_target_manual_ack.rs +++ b/examples/i2c_target_manual_ack.rs @@ -28,7 +28,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS); let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); diff --git a/examples/rcc.rs b/examples/rcc.rs index 1195de9..f110f9b 100644 --- a/examples/rcc.rs +++ b/examples/rcc.rs @@ -24,7 +24,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); info!(""); info!("stm32h5xx-hal example - RCC"); diff --git a/examples/spi.rs b/examples/spi.rs index e13b98f..9a486ce 100644 --- a/examples/spi.rs +++ b/examples/spi.rs @@ -28,7 +28,7 @@ fn main() -> ! { let ccdr = rcc .sys_ck(192.MHz()) .pll1_q_ck(64.MHz()) - .freeze(pwrcfg, &dp.SBS); + .freeze(&pwrcfg, &dp.SBS); // Acquire the GPIOB peripheral. This also enables the clock for // GPIOB in the RCC register. diff --git a/examples/spi_send_frames.rs b/examples/spi_send_frames.rs index 016c4ed..c8047c0 100644 --- a/examples/spi_send_frames.rs +++ b/examples/spi_send_frames.rs @@ -37,7 +37,7 @@ fn main() -> ! { let ccdr = rcc .sys_ck(192.MHz()) .pll1_q_ck(64.MHz()) - .freeze(pwrcfg, &dp.SBS); + .freeze(&pwrcfg, &dp.SBS); // Acquire the GPIOB peripheral. This also enables the clock for // GPIOB in the RCC register. diff --git a/examples/spi_slave.rs b/examples/spi_slave.rs index def4c47..1eceaf9 100644 --- a/examples/spi_slave.rs +++ b/examples/spi_slave.rs @@ -24,7 +24,7 @@ fn main() -> ! { let ccdr = rcc .sys_ck(100.MHz()) .pll1_q_ck(50.MHz()) - .freeze(pwrcfg, &dp.SBS); + .freeze(&pwrcfg, &dp.SBS); // Acquire the GPIOB peripheral. This also enables the clock for // GPIOB in the RCC register. diff --git a/examples/usb_serial.rs b/examples/usb_serial.rs index dc6e04e..d38af24 100644 --- a/examples/usb_serial.rs +++ b/examples/usb_serial.rs @@ -31,7 +31,7 @@ fn main() -> ! { let pwrcfg = pwr.vos0().freeze(); // Constrain and Freeze clock let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); diff --git a/src/adc/h5.rs b/src/adc/h5.rs new file mode 100644 index 0000000..a481982 --- /dev/null +++ b/src/adc/h5.rs @@ -0,0 +1,160 @@ +use super::{AdcCommon, Temperature, Vbat, Vddcore, Vrefint}; +use crate::gpio::{self, Analog}; + +#[cfg(feature = "rm0492")] +use crate::stm32::{ADC1, ADC1 as ADCC}; + +#[cfg(feature = "rm0481")] +use crate::stm32::{ADC1, ADC2, ADCC}; + +macro_rules! adc_pins { + ($ADC:ident, $($input:ty => $chan:expr),+ $(,)*) => { + $( + #[cfg(feature = "eh-02")] + impl embedded_hal_02::adc::Channel<$ADC> for $input { + type ID = u8; + + fn channel() -> u8 { + $chan + } + } + + impl super::AdcChannel<$ADC> for $input { + const CH: u8 = $chan; + } + )+ + }; +} + +macro_rules! adc_internal { + ([$INT_ADC:ident, $INT_ADC_COMMON:ident]; $($input:ty => ($chan:expr, $en:ident)),+ $(,)*) => { + $( + impl $input { + pub fn new() -> Self { + Self {} + } + + /// Enables the internal voltage/sensor + /// ADC must be disabled. + pub fn enable(&mut self, _adc: &mut AdcCommon) { + // TODO: This is not safe since we do not hold both adcs + let common = unsafe { ADCC::steal() }; + + common.ccr().modify(|_, w| w.$en().bit(true)); + } + /// Disables the internal voltage/sdissor + /// ADC must be disabled. + pub fn disable(&mut self, _adc: &mut AdcCommon) { + // TODO: This is not safe since we do not hold both adcs + let common = unsafe { ADCC::steal() }; + + common.ccr().modify(|_, w| w.$en().bit(false)); + } + } + + adc_pins!($INT_ADC, $input => $chan); + )+ + }; +} + +#[cfg(feature = "rm0492")] +impl Vddcore { + pub fn enable(_adc: &mut AdcCommon) { + let adc = unsafe { ADC1::steal() }; + + adc.or().modify(|_, w| w.op1().set_bit()); + } + + pub fn disable(_adc: &mut AdcCommon) { + let adc = unsafe { ADC1::steal() }; + + adc.or().modify(|_, w| w.op1().clear_bit()); + } +} +#[cfg(feature = "rm0492")] +adc_pins!(ADC1, Vddcore => 16); + +#[cfg(feature = "rm0481")] +impl Vddcore { + pub fn enable(_adc: &mut super::Adc) { + let adc2 = unsafe { ADC2::steal() }; + + adc2.or().modify(|_, w| w.op0().bit(true)); + } + + pub fn disable(_adc: &mut super::Adc) { + let adc2 = unsafe { ADC2::steal() }; + + adc2.or().modify(|_, w| w.op0().bit(false)); + } +} +#[cfg(feature = "rm0481")] +adc_pins!(ADC2, Vddcore => 17); + +#[cfg(feature = "rm0492")] +adc_internal!( + [ADC1, ADCC]; + + Temperature => (16, tsen), + Vrefint => (17, vrefen), + Vbat => (2, vbaten), +); + +#[cfg(feature = "rm0481")] +adc_internal!( + [ADC1, ADCC]; + + Temperature => (16, tsen), + Vrefint => (17, vrefen), +); + +#[cfg(feature = "rm0481")] +adc_internal!( + [ADC2, ADCC]; + + Vbat => (16, vbaten), +); + +macro_rules! adc_pins_common { + ($($input:ty => $chan:expr),+ $(,)*) => {$( + adc_pins!(ADC1, $input => $chan); + + #[cfg(feature = "rm0481")] + adc_pins!(ADC2, $input => $chan); + )*}; +} + +adc_pins_common!( + gpio::PC0 => 10, + gpio::PC1 => 11, + gpio::PC2 => 12, + gpio::PC3 => 13, + + gpio::PA0 => 0, + gpio::PA1 => 1, + gpio::PA2 => 14, + gpio::PA3 => 15, + gpio::PA4 => 18, + gpio::PA5 => 19, + gpio::PA6 => 3, + gpio::PA7 => 7, + + gpio::PC4 => 4, + gpio::PC5 => 8, + gpio::PB0 => 9, + gpio::PB1 => 5, +); + +#[cfg(feature = "rm0481")] +adc_pins!( + ADC1, + gpio::PF11 => 2, + gpio::PF12 => 6, +); + +#[cfg(feature = "rm0481")] +adc_pins!( + ADC2, + gpio::PF13 => 2, + gpio::PF14 => 6, +); diff --git a/src/adc/mod.rs b/src/adc/mod.rs new file mode 100644 index 0000000..874ba34 --- /dev/null +++ b/src/adc/mod.rs @@ -0,0 +1,853 @@ +//! Analog to Digital Converter (ADC) +//! +//! ADC1 and ADC2 share a reset line. To initialise both of them, use the +//! [`adc12`] method. +//! +//! # Examples +//! +//! - [Reading a voltage using ADC1](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/adc.rs) +//! - [Reading a temperature using ADC2](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/temperature.rs) +//! +//! Originally from https://github.com/stm32-rs/stm32h7xx-hal +mod h5; + +#[cfg(feature = "eh-02")] +use core::convert::Infallible; +use core::marker::PhantomData; +use core::ops::Deref; + +use embedded_hal::delay::DelayNs; + +use crate::rcc::rec::AdcDacClkSelGetter; + +#[cfg(feature = "rm0492")] +use crate::stm32::{ADC1, ADC1 as ADCC}; + +#[cfg(feature = "rm0481")] +use crate::stm32::{ADC1, ADC2, ADCC}; + +use crate::pwr::{self, VoltageScale}; +//use crate::rcc::rec::AdcClkSelGetter; +use crate::rcc::{rec, CoreClocks, ResetEnable}; +use crate::time::Hertz; + +/// Marker trait for all ADC peripherals +pub trait Instance: + crate::Sealed + Deref +{ +} + +impl crate::Sealed for ADC1 {} + +#[cfg(feature = "rm0481")] +impl crate::Sealed for ADC2 {} + +impl Instance for ADC1 {} + +#[cfg(feature = "rm0481")] +impl Instance for ADC2 {} + +pub trait AdcCommonExt { + fn claim( + self, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> AdcCommon; +} + +impl AdcCommonExt for ADCC { + fn claim( + self, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> AdcCommon { + // Check adc_ker_ck_input + kernel_clk_unwrap(&prec, clocks); + + // Enable AHB clock + let prec = prec.enable(); + + // Reset peripheral + let prec = prec.reset(); + + let _f_adc = + AdcCommon::configure_clock(&self, f_adc, prec, clocks, pwrcfg); // ADC12_COMMON + #[cfg(feature = "defmt")] + defmt::trace!("Set f_adc to: {}", _f_adc); + #[cfg(feature = "log")] + log::trace!("Set f_adc to: {}", _f_adc); + + AdcCommon {} + } +} + +/// Type for initialized `ADC12_COMMON` or `ADC345_COMMON` +/// +/// See [`AdcCommon::claim`] +#[non_exhaustive] +pub struct AdcCommon {} + +impl AdcCommon { + /// Sets the clock configuration for this ADC. This is common + /// between ADC1 and ADC2, so the prec block is used to ensure + /// this method can only be called on one of the ADCs (or both, + /// using the [adc12](#method.adc12) method). + /// + /// Only `CKMODE[1:0]` = 0 is supported + fn configure_clock( + adcc: &ADCC, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> Hertz { + let ker_ck_input = kernel_clk_unwrap(&prec, clocks); + + let max_adc_ker_ck_analog = 75_000_000; + let (max_ker_ck, max_ker_ck_input) = match pwrcfg.vos { + VoltageScale::Scale0 => (125_000_000, 250_000_000), + VoltageScale::Scale1 => (100_000_000, 200_000_000), + VoltageScale::Scale2 => (75_000_000, 150_000_000), + VoltageScale::Scale3 => (50_000_000, 100_000_000), + }; + assert!(ker_ck_input.raw() <= max_ker_ck_input, + "Kernel clock violates maximum frequency defined in Reference Manual. \ + Can result in erroneous ADC readings"); + + let f_adc = Self::configure_clock_unchecked(adcc, f_adc, prec, clocks); + + // Maximum ADC clock speed. With BOOST = 0 there is a no + // minimum frequency given in part datasheets + assert!(f_adc.raw() <= max_ker_ck); + assert!(f_adc.raw() <= max_adc_ker_ck_analog); + + f_adc + } + + /// No clock checks + fn configure_clock_unchecked( + adcc: &ADCC, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + ) -> Hertz { + let ker_ck = kernel_clk_unwrap(&prec, clocks); + + let f_target = f_adc.raw(); + + let (divider, presc) = match ker_ck.raw().div_ceil(f_target) { + 1 => (1, 0b0000), + 2 => (2, 0b0001), + 3..=4 => (4, 0b0010), + 5..=6 => (6, 0b0011), + 7..=8 => (8, 0b0100), + 9..=10 => (10, 0b0101), + 11..=12 => (12, 0b0110), + 13..=16 => (16, 0b0111), + 17..=32 => (32, 0b1000), + 33..=64 => (64, 0b1001), + 65..=128 => (128, 0b1010), + 129..=256 => (256, 0b1011), + _ => panic!("Selecting the ADC clock required a prescaler > 256, \ + which is not possible in hardware. Either increase the ADC \ + clock frequency or decrease the kernel clock frequency"), + }; + adcc.ccr().modify(|_, w| unsafe { w.presc().bits(presc) }); + + Hertz::from_raw(ker_ck.raw() / divider) + } + + fn setup_adc( + adc: ADC, + delay: &mut impl DelayNs, + resolution: Resolution, + ) -> Adc { + // Consume ADC register block, produce ADC1/2 with default settings + let adc = Adc::::default_from_rb(adc); + + // Power Up, Preconfigure and Calibrate + let mut adc = adc.power_up_and_calibrate(delay); + + // From RM0481: + // This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected + adc.rb.or().modify(|_, w| w.op0().set_bit()); + + // Set resolution + adc.rb + .cfgr() + .modify(|_, w| unsafe { w.res().bits(resolution as _) }); + + adc.set_resolution(resolution); + adc + } + + /// Initialise ADC + /// + /// Sets all configurable parameters to one-shot defaults, + /// performs a boot-time calibration. + #[cfg(feature = "rm0481")] + pub fn claim_and_configure( + &self, + adc: ADC, + delay: &mut impl DelayNs, + resolution: Resolution, + ) -> Adc { + Self::setup_adc(adc, delay, resolution) + } + + /// Initialise ADC + /// + /// Sets all configurable parameters to one-shot defaults, + /// performs a boot-time calibration. + #[cfg(feature = "rm0492")] + pub fn claim_and_configure( + self, + delay: &mut impl DelayNs, + resolution: Resolution, + ) -> Adc { + let adcc = unsafe { ADC1::steal() }; + Self::setup_adc::(adcc, delay, resolution) + } +} + +#[cfg(feature = "defmt")] +use defmt::{assert, panic}; + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Resolution { + TwelveBit = 0b00, + TenBit = 0b01, + EightBit = 0b10, + SixBit = 0b11, +} + +trait NumberOfBits { + fn number_of_bits(&self) -> u32; +} + +impl NumberOfBits for Resolution { + fn number_of_bits(&self) -> u32 { + match *self { + Resolution::SixBit => 6, + Resolution::EightBit => 8, + Resolution::TenBit => 10, + Resolution::TwelveBit => 12, + } + } +} + +/// Enabled ADC (type state) +pub struct Enabled; +/// Disabled ADC (type state) +/// +/// Disabled but powered on +pub struct Disabled; + +/// Powered down ADC (type state) +pub struct PoweredDown; + +pub trait ED {} +impl ED for Enabled {} +impl ED for Disabled {} + +pub struct Adc { + rb: ADC, + sample_time: AdcSampleTime, + resolution: Resolution, + current_channel: Option, + _enabled: PhantomData, +} + +/// ADC DMA modes +/// +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AdcDmaMode { + OneShot, + Circular, +} + +/// ADC sampling time +/// +/// Options for the sampling time, each is T + 0.5 ADC clock cycles. +// +// Refer to RM0433 Rev 7 - Chapter 25.4.13 +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(non_camel_case_types)] +pub enum AdcSampleTime { + /// 2.5 cycles sampling time + T_2_5, + /// 6.5 cycles sampling time + T_6_5, + /// 12.5 cycles sampling time + T_12_5, + /// 24.5 cycles sampling time + T_24_5, + /// 47.5 cycles sampling time + T_47_5, + /// 92.5 cycles sampling time + T_92_5, + /// 247.5 cycles sampling time + T_247_5, + /// 640.5 cycles sampling time + #[default] + T_640_5, +} + +/// The place in the sequence a given channel should be captured +#[derive(Debug, PartialEq, PartialOrd, Copy, Clone)] +pub enum Sequence { + /// 1 + One, + /// 2 + Two, + /// 3 + Three, + /// 4 + Four, + /// 5 + Five, + /// 6 + Six, + /// 7 + Seven, + /// 8 + Eight, + /// 9 + Nine, + /// 10 + Ten, + /// 11 + Eleven, + /// 12 + Twelve, + /// 13 + Thirteen, + /// 14 + Fourteen, + /// 15 + Fifteen, + /// 16 + Sixteen, +} + +impl From for u8 { + fn from(s: Sequence) -> u8 { + match s { + Sequence::One => 0, + Sequence::Two => 1, + Sequence::Three => 2, + Sequence::Four => 3, + Sequence::Five => 4, + Sequence::Six => 5, + Sequence::Seven => 6, + Sequence::Eight => 7, + Sequence::Nine => 8, + Sequence::Ten => 9, + Sequence::Eleven => 10, + Sequence::Twelve => 11, + Sequence::Thirteen => 12, + Sequence::Fourteen => 13, + Sequence::Fifteen => 14, + Sequence::Sixteen => 15, + } + } +} + +impl From for Sequence { + fn from(bits: u8) -> Self { + match bits { + 0 => Sequence::One, + 1 => Sequence::Two, + 2 => Sequence::Three, + 3 => Sequence::Four, + 4 => Sequence::Five, + 5 => Sequence::Six, + 6 => Sequence::Seven, + 7 => Sequence::Eight, + 8 => Sequence::Nine, + 9 => Sequence::Ten, + 10 => Sequence::Eleven, + 11 => Sequence::Twelve, + 12 => Sequence::Thirteen, + 13 => Sequence::Fourteen, + 14 => Sequence::Fifteen, + 15 => Sequence::Sixteen, + _ => unimplemented!(), + } + } +} + +// Refer to RM0433 Rev 7 - Chapter 25.4.13 +impl From for u8 { + fn from(val: AdcSampleTime) -> u8 { + match val { + AdcSampleTime::T_2_5 => 0b000, + AdcSampleTime::T_6_5 => 0b001, + AdcSampleTime::T_12_5 => 0b010, + AdcSampleTime::T_24_5 => 0b011, + AdcSampleTime::T_47_5 => 0b100, + AdcSampleTime::T_92_5 => 0b101, + AdcSampleTime::T_247_5 => 0b110, + AdcSampleTime::T_640_5 => 0b111, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AdcCalOffset(u8); + +impl AdcCalOffset { + pub fn value(self) -> u8 { + self.0 + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AdcCalLinear([u32; 6]); + +impl AdcCalLinear { + pub fn value(self) -> [u32; 6] { + self.0 + } +} + +/// Vref internal signal +#[derive(Default)] +pub struct Vrefint; +/// Vbat/4 (Vbat pin input voltage divided by 4) internal signal +#[derive(Default)] +pub struct Vbat; +/// Internal temperature sensor +#[derive(Default)] +pub struct Temperature; +/// Internal digital core voltage +#[derive(Default)] +pub struct Vddcore; + +/// Stored ADC config can be restored using the `Adc::restore_cfg` method +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct StoredConfig(AdcSampleTime, Resolution); + +#[cfg(feature = "defmt")] +impl defmt::Format for StoredConfig { + fn format(&self, fmt: defmt::Formatter) { + let StoredConfig(sample_time, res) = &self; + defmt::write!(fmt, "StoredConfig({:?}, {:?})", sample_time, res) + } +} + +pub trait AdcChannel { + const CH: u8; +} + +/// Returns the frequency of the current adc_ker_ck +/// +/// # Panics +/// +/// Panics if the kernel clock is not running +fn kernel_clk_unwrap( + prec: &impl AdcDacClkSelGetter, + clocks: &CoreClocks, +) -> Hertz { + match prec.get_kernel_clk_mux() { + Some(rec::AdcDacClkSel::Hclk) => clocks.hclk(), + Some(rec::AdcDacClkSel::Sys) => clocks.sys_ck(), + Some(rec::AdcDacClkSel::Pll2R) => clocks + .pll2() + .r_ck() + .expect("ADC: PLL2R clock must be enabled"), + Some(rec::AdcDacClkSel::Hse) => { + clocks.hse_ck().expect("ADC: HSE clock must be enabled") + } + Some(rec::AdcDacClkSel::HsiKer) => { + clocks.hsi_ck().expect("ADC: HSI clock must be enabled") + } + Some(rec::AdcDacClkSel::CsiKer) => { + clocks.csi_ck().expect("ADC: CSI clock must be enabled") + } + None => unreachable!(), + } +} + +impl Adc { + /// Creates ADC with default settings + fn default_from_rb(rb: ADC) -> Self { + Self { + rb, + sample_time: AdcSampleTime::default(), + resolution: Resolution::TwelveBit, + current_channel: None, + _enabled: PhantomData, + } + } + + /// Disables Deeppowerdown-mode and enables voltage regulator + /// + /// Note: After power-up, a [`calibration`](#method.calibrate) shall be run + fn power_up(&mut self, delay: &mut impl DelayNs) { + // Refer to RM0433 Rev 7 - Chapter 25.4.6 + self.rb + .cr() + .modify(|_, w| w.deeppwd().clear_bit().advregen().set_bit()); + delay.delay_us(10); + } + + pub fn power_up_and_calibrate( + mut self, + delay: &mut impl DelayNs, + ) -> Adc { + self.power_up(delay); + + let mut adc = Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + current_channel: self.current_channel, + _enabled: PhantomData, + }; + adc.calibrate(); + adc + } +} + +impl Adc { + /// Enables Deeppowerdown-mode and disables voltage regulator + /// + /// Note: This resets the [`calibration`](#method.calibrate) of the ADC + pub fn power_down(self) -> Adc { + // Refer to RM0433 Rev 7 - Chapter 25.4.6 + self.rb + .cr() + .modify(|_, w| w.deeppwd().set_bit().advregen().clear_bit()); + + Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + current_channel: self.current_channel, + _enabled: PhantomData, + } + } + + /// Calibrates the ADC in single channel mode + /// + /// Note: The ADC must be disabled + fn calibrate(&mut self) { + // Refer to RM0433 Rev 7 - Chapter 25.4.8 + self.check_calibration_conditions(); + + let cal = |is_diff_mode| { + // single channel (INNx equals to V_ref-) + self.rb.cr().modify(|_, w| w.adcaldif().bit(is_diff_mode)); + // calibrate + self.rb.cr().modify(|_, w| w.adcal().set_bit()); + while self.rb.cr().read().adcal().bit_is_set() {} + }; + + // Calibrate for single ended + cal(false); + + // Calibrate for diff mode + cal(true); + } + + fn check_calibration_conditions(&self) { + let cr = self.rb.cr().read(); + if cr.aden().bit_is_set() { + panic!("Cannot start calibration when the ADC is enabled"); + } + if cr.deeppwd().bit_is_set() { + panic!("Cannot start calibration when the ADC is in deeppowerdown-mode"); + } + if cr.advregen().bit_is_clear() { + panic!("Cannot start calibration when the ADC voltage regulator is disabled"); + } + } + + /// Configuration process immediately after enabling the ADC + fn configure(&mut self) { + // Single conversion mode, Software trigger + // Refer to RM0433 Rev 7 - Chapters 25.4.15, 25.4.19 + self.rb.cfgr().modify(|_, w| { + w.cont().clear_bit().exten().disabled().discen().set_bit() + }); + + // TODO: Enable boost mode in PWR_PMCR for highest possible clock frequency when low vdd + } + + /// Enable ADC + pub fn enable(mut self) -> Adc { + // Refer to RM0433 Rev 7 - Chapter 25.4.9 + self.rb.isr().modify(|_, w| w.adrdy().clear()); + self.rb.cr().modify(|_, w| w.aden().set_bit()); + while self.rb.isr().read().adrdy().bit_is_clear() {} + self.rb.isr().modify(|_, w| w.adrdy().clear()); + + self.configure(); + + Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + current_channel: None, + _enabled: PhantomData, + } + } +} + +impl Adc { + fn stop_regular_conversion(&mut self) { + self.rb.cr().modify(|_, w| w.adstp().set_bit()); + while self.rb.cr().read().adstp().bit_is_set() {} + } + + fn stop_injected_conversion(&mut self) { + self.rb.cr().modify(|_, w| w.jadstp().set_bit()); + while self.rb.cr().read().jadstp().bit_is_set() {} + } + + pub fn configure_ch( + &mut self, + ch: u8, + sequence: Sequence, + sample_time: AdcSampleTime, + ) { + //Check the sequence is long enough + self.rb.sqr1().modify(|r, w| unsafe { + let prev: Sequence = r.l().bits().into(); + if prev < sequence { + w.l().bits(sequence.into()) + } else { + w + } + }); + let reg_i = u8::from(sequence) / 4; + let i = u8::from(sequence) % 4; + + //Set the channel in the right sequence field + match reg_i { + 0 => self.rb.sqr1().modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 1 => self.rb.sqr2().modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 2 => self.rb.sqr3().modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 3 => self.rb.sqr4().modify(|_, w| unsafe { w.sq(i).bits(ch) }), + _ => unreachable!(), + }; + + //Set the sample time for the channel + let st = u8::from(sample_time); + unsafe { + match ch { + 0..=9 => self.rb.smpr1().modify(|_, w| w.smp(ch).bits(st)), + 10.. => self.rb.smpr2().modify(|_, w| w.smp(ch - 10).bits(st)), + }; + } + } + + /// Configure a channel for sampling. + /// It will make sure the sequence is at least as long as the `sequence` provided. + /// # Arguments + /// * `channel` - channel to configure + /// * `sequence` - where in the sequence to sample the channel. Also called rank in some STM docs/code + /// * `sample_time` - how long to sample for. See datasheet and ref manual to work out how long you need\ + /// to sample for at a given ADC clock frequency + pub fn configure_channel( + &mut self, + _channel: &CHANNEL, + sequence: Sequence, + sample_time: AdcSampleTime, + ) where + CHANNEL: AdcChannel, + { + self.configure_ch(CHANNEL::CH, sequence, sample_time); + } + + /// Start conversion + /// + /// This method starts a conversion sequence + #[inline(always)] + fn start_conversion(&mut self) { + //Start conversion + self.rb.cr().modify(|_, w| w.adstart().set_bit()); + } + + /// Block until the conversion is completed and return to configured + pub fn wait_for_conversion_sequence(&mut self) { + while !self.rb.isr().read().eoc().bit_is_set() {} + } + + pub fn convert>( + &mut self, + pin: &PIN, + sample_time: AdcSampleTime, + ) -> u16 { + self.configure_channel(pin, Sequence::One, sample_time); + self.start_conversion(); + //Wait for the sequence to complete + self.wait_for_conversion_sequence(); + self.current_sample() + } + + /// Read sample + /// + /// `nb::Error::WouldBlock` in case the conversion is still + /// progressing. + // Refer to RM0433 Rev 7 - Chapter 25.4.16 + #[cfg(feature = "eh-02")] + pub fn read_sample(&mut self) -> nb::Result { + self.current_channel + .expect("No channel was selected, use start_conversion first"); + + // Wait for the conversion to finished + if self.rb.isr().read().eoc().is_not_complete() { + return Err(nb::Error::WouldBlock); + } + + self.current_channel = None; + + // Retrieve result + let result = self.current_sample(); + Ok(result) + } + + /// Read the current value in the data register + /// + /// This simply returns whatever value is in the data register without blocking. + /// Use [Self::read_sample] if you want to wait for any ongoing conversion to finish. + /// + /// NOTE: Depending on OVRMOD the data register acts like a FIFO queue with three stages + pub fn current_sample(&mut self) -> u16 { + self.rb.dr().read().rdata().bits() + } + + /// Disable ADC + pub fn disable(mut self) -> Adc { + let cr = self.rb.cr().read(); + // Refer to RM0433 Rev 7 - Chapter 25.4.9 + if cr.adstart().bit_is_set() { + self.stop_regular_conversion(); + } + if cr.jadstart().bit_is_set() { + self.stop_injected_conversion(); + } + + self.rb.cr().modify(|_, w| w.addis().set_bit()); + while self.rb.cr().read().aden().bit_is_set() {} + + Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + current_channel: None, + _enabled: PhantomData, + } + } +} + +impl Adc { + /// Save current ADC config + pub fn save_cfg(&mut self) -> StoredConfig { + StoredConfig(self.get_sample_time(), self.get_resolution()) + } + + /// Restore saved ADC config + pub fn restore_cfg(&mut self, cfg: StoredConfig) { + self.set_sample_time(cfg.0); + self.set_resolution(cfg.1); + } + + /// Reset the ADC config to default, return existing config + pub fn default_cfg(&mut self) -> StoredConfig { + let cfg = self.save_cfg(); + self.set_sample_time(AdcSampleTime::default()); + self.set_resolution(Resolution::TwelveBit); + cfg + } + + /// Get ADC samping time + pub fn get_sample_time(&self) -> AdcSampleTime { + self.sample_time + } + + /// Get ADC sampling resolution + pub fn get_resolution(&self) -> Resolution { + self.resolution + } + + /// Set ADC sampling time + /// + /// Options can be found in [AdcSampleTime]. + pub fn set_sample_time(&mut self, t_samp: AdcSampleTime) { + self.sample_time = t_samp; + } + + /// Set ADC sampling resolution + pub fn set_resolution(&mut self, res: Resolution) { + self.resolution = res; + } + + /// Returns the largest possible sample value for the current ADC configuration + /// + /// Using this value as the denominator when calculating + /// transfer functions results in a gain error, and thus should + /// be avoided. Use the [slope](#method.slope) method instead. + #[deprecated(since = "0.12.0", note = "See the slope() method instead")] + pub fn max_sample(&self) -> u32 { + (1 << self.get_resolution().number_of_bits()) - 1 + } + + /// Returns the slope for the current ADC configuration. 1 LSB = Vref / slope + /// + /// This value can be used in calcuations involving the transfer function of + /// the ADC. For example, to calculate an estimate for the + /// applied voltage of an ADC channel referenced to voltage + /// `vref` + /// + /// ``` + /// let v = adc.read(&ch).unwrap() as f32 * vref / adc.slope() as f32; + /// ``` + pub fn slope(&self) -> u32 { + 1 << self.get_resolution().number_of_bits() + } + + /// Returns the offset calibration value for single ended channel + pub fn read_offset_calibration_value(&self) -> AdcCalOffset { + AdcCalOffset(self.rb.calfact().read().calfact_s().bits()) + } + + /// Returns a reference to the inner peripheral + pub fn inner(&self) -> &ADC { + &self.rb + } + + /// Returns a mutable reference to the inner peripheral + pub fn inner_mut(&mut self) -> &mut ADC { + &mut self.rb + } +} + +#[cfg(feature = "eh-02")] +impl embedded_hal_02::adc::OneShot + for Adc +where + PIN: embedded_hal_02::adc::Channel, +{ + type Error = Infallible; + + // TODO: We are not really non-blocking + fn read(&mut self, _pin: &mut PIN) -> nb::Result { + self.configure_ch(PIN::channel(), Sequence::One, self.sample_time); + self.start_conversion(); + //Wait for the sequence to complete + self.wait_for_conversion_sequence(); + Ok(self.current_sample()) + } +} diff --git a/src/lib.rs b/src/lib.rs index cc3c317..ed4356c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,9 @@ pub mod prelude; #[macro_use] mod macros; +#[cfg(feature = "device-selected")] +pub mod adc; + #[cfg(feature = "device-selected")] pub mod pwr; diff --git a/src/rcc.rs b/src/rcc.rs index 6c0a432..dff42e3 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -558,7 +558,7 @@ impl Rcc { /// function may also panic if a clock specification can be /// achieved, but the mechanism for doing so is not yet /// implemented here. - pub fn freeze(mut self, pwrcfg: PowerConfiguration, sbs: &SBS) -> Ccdr { + pub fn freeze(mut self, pwrcfg: &PowerConfiguration, sbs: &SBS) -> Ccdr { // We do not reset RCC here. This routine must assert when // the previous state of the RCC peripheral is unacceptable.