From b6934fecb7f5f2ce1e7e12959ca4ce8ec44ca7d2 Mon Sep 17 00:00:00 2001 From: bboger Date: Fri, 22 Nov 2019 16:42:32 +0100 Subject: [PATCH 01/32] working example for 303xB/C/D/E --- src/adc.rs | 399 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 2 files changed, 401 insertions(+) create mode 100644 src/adc.rs diff --git a/src/adc.rs b/src/adc.rs new file mode 100644 index 000000000..3395b56d7 --- /dev/null +++ b/src/adc.rs @@ -0,0 +1,399 @@ +//! # Work in Progress +//! API for the ADC1 and ADC2 (Analog to Digital Converter) on the 303xB/C/D/E +//! +//! IMPORTANT - only valid for the 303xB/C/D/E variant. +//! NOT for the 303x6/8: +//! +//! The availability of ADC channels varies +//! for different sub-variants of stm32f3xx controllers. +//! This means that e.g. channel ADC1_IN11 +//! is connected to PB0 on a f303x6 chip +//! while there is no such channel on a F303xB/C. +//! However the PB0 on that variant is connected to channel ADC3_IN12. +//! +//! See → 15.3.4 +//! +//! +//! # Examples +//! check `adc.rs` in the examples folder +//! +use cortex_m::asm; +use embedded_hal::adc::{Channel, OneShot}; + +use crate::{ + gpio::{gpioa, gpiob, gpioc, gpiof, Analog}, + rcc::{Clocks, AHB}, + stm32::{ADC1, ADC1_2, ADC2}, +}; + +/// ADC configuration +pub struct Adc { + rb: ADC, + clocks: Clocks, + prescale: Prescale, + operation_mode: Option, +} + +#[derive(Clone, Copy, Debug)] +/// ADC sampling time +/// +/// each channel can be sampled with a different sample time. +/// the total conversion time is +/// 12.5 ADC clock cycles + sample time (T_x + .5) +/// +/// TODO: there are boundaries on how this can be set depending on the hardware. +pub enum SampleTime { + T_1, + T_2, + T_4, + T_7, + T_19, + T_61, + T_181, + T_601, +} + +impl SampleTime { + /// Get the default timer + pub fn default() -> Self { + SampleTime::T_19 + } + + /// Conversion to bits for SMP + fn bitcode(&self) -> u8 { + match self { + SampleTime::T_1 => 0b000, + SampleTime::T_2 => 0b001, + SampleTime::T_4 => 0b010, + SampleTime::T_7 => 0b011, + SampleTime::T_19 => 0b100, + SampleTime::T_61 => 0b101, + SampleTime::T_181 => 0b110, + SampleTime::T_601 => 0b111, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq)] +/// ADC operation mode +pub enum OperationMode { + OneShot, + // TODO all the other modes +} + +#[derive(Clone, Copy, Debug)] +/// ADC prescale +pub enum Prescale { + // HCLK_1 needs some requirements to be met + HCLK_2 = 2, + HCLK_4 = 4, +} + +impl Prescale { + /// Get default prescaler + fn default() -> Self { + Prescale::HCLK_2 + } + + /// Conversion to bits for CKMODE in ADCx_CCR + fn bitcode(&self) -> u8 { + match self { + Prescale::HCLK_2 => 0b10, + Prescale::HCLK_4 => 0b11, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +/// ADC data register alignment +pub enum Align { + /// Right alignment of output data + Right, + /// Left alignment of output data + Left, +} + +impl Align { + /// Default: right alignment + pub fn default() -> Self { + Align::Right + } +} + +impl Align { + /// Conversion to bits for ALIGN in ADCx_CFGR + fn bitvalue(&self) -> bool { + match self { + Align::Right => false, + Align::Left => true, + } + } +} + +macro_rules! adc_pins { + ($ADC:ident, $($pin:ty => $chan:expr),+ $(,)*) => { + $( + impl Channel<$ADC> for $pin { + type ID = u8; + + fn channel() -> u8 { $chan } + } + )+ + }; +} + +#[cfg(any(feature = "stm32f303",))] +adc_pins!(ADC1, + gpioa::PA0 => 1_u8, + gpioa::PA1 => 2_u8, + gpioa::PA2 => 3_u8, + gpioa::PA3 => 4_u8, + gpiof::PF4 => 5_u8, + // Channels 6 to 10 are shared channels (i.e. ADC12_INx) + gpioc::PC0 => 6_u8, + gpioc::PC1 => 7_u8, + gpioc::PC2 => 8_u8, + gpioc::PC3 => 9_u8, + gpiof::PF2 => 10_u8, +); + +#[cfg(any(feature = "stm32f303",))] +adc_pins!(ADC2, + gpioa::PA4 => 1_u8, + gpioa::PA5 => 2_u8, + gpioa::PA6 => 3_u8, + gpioa::PA7 => 4_u8, + gpioc::PC4 => 5_u8, + // Channels 6 to 10 are shared channels (i.e. ADC12_INx) + gpioc::PC0 => 6_u8, + gpioc::PC1 => 7_u8, + gpioc::PC2 => 8_u8, + gpioc::PC3 => 9_u8, + gpiof::PF2 => 10_u8, + // exclusive ADC2 channels again + gpioc::PC5 => 11_u8, + gpiob::PB2 => 12_u8, +); + +macro_rules! adc_hal { + ($( + $ADC:ident: ($init:ident, $ADC_COMMON:ident), + )+) => { + $( + impl Adc<$ADC> { + /// Init a new ADC + /// + /// Enables the clock, performs a calibration and enables the ADC + + pub fn $init( + rb: $ADC, + adc_common : &mut $ADC_COMMON, + ahb: &mut AHB, + clocks: Clocks, + ) -> Self { + let mut this_adc = Self { + rb, + clocks, + prescale : Prescale::default(), + operation_mode: None, + }; + this_adc.enable_clock(ahb, adc_common); + this_adc.set_align(Align::default()); + this_adc.calibrate(); + // ADEN bit cannot be set during ADCAL=1 and 4 ADC clock cycle after the ADCAL + // bit is cleared by hardware + this_adc.wait_adc_clk_cycles(4); + this_adc.enable(); + return this_adc; + } + + /// sets up adc in one shot mode for a single channel + pub fn setup_oneshot(&mut self) { + // stop and clear overrun events + self.rb.cr.modify(|_, w| w.adstp().set_bit()); + self.rb.isr.modify(|_, w| w.ovr().clear_bit()); + + self.rb.cfgr.modify(|_, w| w + .cont() .clear_bit() + .ovrmod() .clear_bit() + ); + + self.rb.sqr1.modify(|_, w| + // NOTE(unsafe): set the sequence length to 1 + unsafe { w.l3().bits(0) } + ); + + self.operation_mode = Some(OperationMode::OneShot); + } + + fn set_align(&self, align: Align) { + self.rb.cfgr.modify(|_, w| w.align().bit(align.bitvalue())); + } + + fn enable(&mut self) { + self.rb.cr.modify(|_, w| w.aden().set_bit()); + while self.rb.isr.read().adrdy().bit_is_clear() {} + } + + fn disable(&mut self) { + self.rb.cr.modify(|_, w| w.aden().clear_bit()); + } + + fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut $ADC_COMMON) { + ahb.enr().modify(|_, w| w.adc12en().enabled()); + unsafe { + adc_common.ccr.modify(|_, w| w + .ckmode().bits(self.prescale.bitcode()) + ); + } + } + + /// Calibrate according to 15.3.8 in the Reference Manual + fn calibrate(&mut self) { + if !self.advregen_enabled() { + self.advregen_enable(); + self.wait_advregen_startup(); + } + + self.disable(); + + self.rb.cr.modify(|_, w| w + // NOTE: needs to be adopted if implementing differential input + .adcaldif().clear_bit() + .adcal() .set_bit()); + + while self.rb.cr.read().adcal().bit_is_set() {} + } + + + fn wait_adc_clk_cycles(&self, cycles: u32) { + let adc_clk_cycle = self.clocks.hclk().0 / (self.prescale as u32); + asm::delay(adc_clk_cycle * cycles); + } + + + fn advregen_enabled(&self) -> bool { + return self._get_new_advregen() == 0b01; + } + + fn advregen_enable(&mut self){ + // need to go though 00 first + self._set_new_advregen(0b00); + self._set_new_advregen(0b01); + } + + /// returns ADVREGEN[1:0] + /// (deeppwd got merged as high bit in advregen - see ref manual) + fn _get_new_advregen(&self) -> u8 { + return + (self.rb.cr.read().deeppwd().bit() as u8) << 1 | + (self.rb.cr.read().advregen().bit() as u8); + } + + /// sets ADVREGEN[1:0] + /// (deeppwd got merged as high bit in advregen - see ref manual) + fn _set_new_advregen(&mut self, val: u8) { + self.rb.cr.modify(|_, w| { w + .deeppwd().bit((val & 0x02) != 0) + .advregen().bit((val & 0x01) != 0) + }); + } + + #[cfg(any(feature = "stm32f303",))] + fn wait_advregen_startup(&self) { + const MAX_STARTUP_TIME_US: u32 = 10; + asm::delay(MAX_STARTUP_TIME_US / (self.clocks.sysclk().0 /1_000_000)); + } + + fn convert_one(&mut self, chan: u8) -> u16 { + self.ensure_oneshot(); + self.set_chan_smps(chan, SampleTime::default()); + self.select_single_chan(chan); + + self.rb.cr.modify(|_, w| w.adstart().set_bit()); + while self.rb.isr.read().eos().bit_is_clear() {} + return self.rb.dr.read().regular_data().bits(); + } + + fn ensure_oneshot(&mut self) { + match self.operation_mode { + Some(mode) => + { + if mode != OperationMode::OneShot { + self.setup_oneshot(); + } + }, + _ => self.setup_oneshot(), + }; + } + + fn select_single_chan(&self, chan: u8) { + self.rb.sqr1.modify(|_, w| + // NOTE(unsafe): set the ADC_INx + unsafe { w.sq1().bits(chan) } + ); + } + + // Note: only allowed when ADSTART = 0 + fn set_chan_smps(&self, chan: u8, smp: SampleTime) { + match chan { + 1 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp1().bits(smp.bitcode())}), + 2 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp2().bits(smp.bitcode())}), + 3 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp3().bits(smp.bitcode())}), + 4 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp4().bits(smp.bitcode())}), + 5 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp5().bits(smp.bitcode())}), + 6 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp6().bits(smp.bitcode())}), + 7 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp7().bits(smp.bitcode())}), + 8 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp8().bits(smp.bitcode())}), + 9 => self.rb.smpr1.modify(|_, w| + unsafe {w.smp9().bits(smp.bitcode())}), + 11 => self.rb.smpr2.modify(|_, w| + unsafe {w.smp10().bits(smp.bitcode())}), + 12 => self.rb.smpr2.modify(|_, w| + unsafe {w.smp12().bits(smp.bitcode())}), + 13 => self.rb.smpr2.modify(|_, w| + unsafe {w.smp13().bits(smp.bitcode())}), + 14 => self.rb.smpr2.modify(|_, w| + unsafe {w.smp14().bits(smp.bitcode())}), + 15 => self.rb.smpr2.modify(|_, w| + unsafe {w.smp15().bits(smp.bitcode())}), + 16 => self.rb.smpr2.modify(|_, w| + unsafe {w.smp16().bits(smp.bitcode())}), + 17 => self.rb.smpr2.modify(|_, w| + unsafe {w.smp17().bits(smp.bitcode())}), + 18 => self.rb.smpr2.modify(|_, w| + unsafe {w.smp18().bits(smp.bitcode())}), + _ => unreachable!(), + }; + } + + } + + impl OneShot<$ADC, WORD, PIN> for Adc<$ADC> + where + WORD: From, + PIN: Channel<$ADC, ID = u8>, + { + type Error = (); + + fn read(&mut self, _pin: &mut PIN) -> nb::Result { + let res = self.convert_one(PIN::channel()); + return Ok(res.into()); + } + } + )+ + } +} + +#[cfg(any(feature = "stm32f303",))] +adc_hal! { + ADC1: (adc1, ADC1_2), + ADC2: (adc2, ADC1_2), +} diff --git a/src/lib.rs b/src/lib.rs index edd67042a..484ec2d32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,6 +120,8 @@ pub use crate::pac as stm32; #[cfg(feature = "rt")] pub use crate::pac::interrupt; +#[cfg(feature = "device-selected")] +pub mod adc; #[cfg(feature = "device-selected")] pub mod delay; #[cfg(feature = "stm32f303")] From 40a3a2c3b80cc11bef165a71214468ff91b648ef Mon Sep 17 00:00:00 2001 From: bboger Date: Fri, 22 Nov 2019 16:47:58 +0100 Subject: [PATCH 02/32] add example for adc --- examples/adc.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 examples/adc.rs diff --git a/examples/adc.rs b/examples/adc.rs new file mode 100644 index 000000000..72550fafb --- /dev/null +++ b/examples/adc.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] + +//! VCU - Vehicle Control Unit on the stm32f3discovery (STM32 F303 VCT6) + +extern crate panic_halt; + +use cortex_m_rt::entry; +use cortex_m_semihosting::hprintln; + +use stm32f3xx_hal::{adc, prelude::*, stm32}; + +#[entry] +/// Main Thread +fn main() -> ! { + let mut peripherals = stm32::Peripherals::take().unwrap(); + + let mut rcc = peripherals.RCC.constrain(); + let clocks = rcc.cfgr.freeze(&mut peripherals.FLASH.constrain().acr); + let mut gpio_a = peripherals.GPIOA.split(&mut rcc.ahb); + let mut adc1 = adc::Adc::adc1( + peripherals.ADC1, + &mut peripherals.ADC1_2, + &mut rcc.ahb, + clocks, + ); + + // set up pin pa0, pa1 as analog pin + let mut adc1_in1_pin = gpio_a.pa0.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr); + + loop { + let data: u16 = adc1.read(&mut adc1_in1_pin).unwrap(); + hprintln!("pa0 reads {}", data).expect("error in read"); + } +} From 2d20228d491bfc11c77ad82518a11b3d38dabfa9 Mon Sep 17 00:00:00 2001 From: bboger Date: Fri, 22 Nov 2019 17:13:00 +0100 Subject: [PATCH 03/32] add dev-dependencies for examples --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index acf993ae2..919209c12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,11 @@ panic-semihosting = "0.5" usb-device = "0.2" usbd-serial = "0.1" +[dev-dependencies] +cortex-m-semihosting = "0.3" +panic-halt = "0.2" + + [features] default = ["unproven"] device-selected = [] From 611f2199ca4b57f58979df4ba47b51251cdd0ba4 Mon Sep 17 00:00:00 2001 From: bboger Date: Thu, 28 Nov 2019 13:13:46 +0100 Subject: [PATCH 04/32] add sub versions stm32f303xx to adc --- src/adc.rs | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 3395b56d7..9a3433bc4 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,19 +1,6 @@ //! # Work in Progress //! API for the ADC1 and ADC2 (Analog to Digital Converter) on the 303xB/C/D/E //! -//! IMPORTANT - only valid for the 303xB/C/D/E variant. -//! NOT for the 303x6/8: -//! -//! The availability of ADC channels varies -//! for different sub-variants of stm32f3xx controllers. -//! This means that e.g. channel ADC1_IN11 -//! is connected to PB0 on a f303x6 chip -//! while there is no such channel on a F303xB/C. -//! However the PB0 on that variant is connected to channel ADC3_IN12. -//! -//! See → 15.3.4 -//! -//! //! # Examples //! check `adc.rs` in the examples folder //! @@ -141,7 +128,12 @@ macro_rules! adc_pins { }; } -#[cfg(any(feature = "stm32f303",))] +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] adc_pins!(ADC1, gpioa::PA0 => 1_u8, gpioa::PA1 => 2_u8, @@ -156,7 +148,12 @@ adc_pins!(ADC1, gpiof::PF2 => 10_u8, ); -#[cfg(any(feature = "stm32f303",))] +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] adc_pins!(ADC2, gpioa::PA4 => 1_u8, gpioa::PA5 => 2_u8, @@ -298,7 +295,12 @@ macro_rules! adc_hal { }); } - #[cfg(any(feature = "stm32f303",))] + #[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", + ))] fn wait_advregen_startup(&self) { const MAX_STARTUP_TIME_US: u32 = 10; asm::delay(MAX_STARTUP_TIME_US / (self.clocks.sysclk().0 /1_000_000)); @@ -392,7 +394,12 @@ macro_rules! adc_hal { } } -#[cfg(any(feature = "stm32f303",))] +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] adc_hal! { ADC1: (adc1, ADC1_2), ADC2: (adc2, ADC1_2), From 62716c3f9a457a59c0c8fea070828749cf48aced Mon Sep 17 00:00:00 2001 From: bboger Date: Thu, 28 Nov 2019 19:10:18 +0100 Subject: [PATCH 05/32] Add ADC34 and accessibility to ADC register --- src/adc.rs | 145 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 124 insertions(+), 21 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 9a3433bc4..cca4035aa 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -7,15 +7,27 @@ use cortex_m::asm; use embedded_hal::adc::{Channel, OneShot}; -use crate::{ - gpio::{gpioa, gpiob, gpioc, gpiof, Analog}, - rcc::{Clocks, AHB}, - stm32::{ADC1, ADC1_2, ADC2}, -}; +use crate::rcc::{Clocks, AHB}; + +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] +use crate::gpio::{gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, Analog}; + +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] +use crate::stm32::{ADC1, ADC1_2, ADC2, ADC3, ADC3_4, ADC4}; /// ADC configuration pub struct Adc { - rb: ADC, + pub rb: ADC, clocks: Clocks, prescale: Prescale, operation_mode: Option, @@ -160,15 +172,62 @@ adc_pins!(ADC2, gpioa::PA6 => 3_u8, gpioa::PA7 => 4_u8, gpioc::PC4 => 5_u8, + gpioc::PC5 => 11_u8, + gpiob::PB2 => 12_u8, // Channels 6 to 10 are shared channels (i.e. ADC12_INx) gpioc::PC0 => 6_u8, gpioc::PC1 => 7_u8, gpioc::PC2 => 8_u8, gpioc::PC3 => 9_u8, gpiof::PF2 => 10_u8, - // exclusive ADC2 channels again - gpioc::PC5 => 11_u8, - gpiob::PB2 => 12_u8, +); + +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] +adc_pins!(ADC3, + gpiob::PB1 => 1_u8, + gpioe::PE9 => 2_u8, + gpioe::PE13 => 3_u8, + gpiob::PB13 => 5_u8, + gpiob::PB0 => 12_u8, + gpioe::PE7 => 13_u8, + gpioe::PE10 => 14_u8, + gpioe::PE11 => 15_u8, + gpioe::PE12 => 16_u8, + // Shared channels (i.e. ADC34_INx) + gpioe::PE8 => 6_u8, + gpiod::PD10 => 7_u8, + gpiod::PD11 => 8_u8, + gpiod::PD12 => 9_u8, + gpiod::PD13 => 10_u8, + gpiod::PD14 => 11_u8, +); + +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] +adc_pins!(ADC4, + gpioe::PE14 => 1_u8, + gpioe::PE15 => 2_u8, + gpiob::PB12 => 3_u8, + gpiob::PB14 => 4_u8, + gpiob::PB15 => 5_u8, + gpiob::PB8 => 12_u8, + gpiob::PB9 => 13_u8, + // Shared channels (i.e. ADC34_INx) + gpioe::PE8 => 6_u8, + gpiod::PD10 => 7_u8, + gpiod::PD11 => 8_u8, + gpiod::PD12 => 9_u8, + gpiod::PD13 => 10_u8, + gpiod::PD14 => 11_u8, ); macro_rules! adc_hal { @@ -177,10 +236,10 @@ macro_rules! adc_hal { )+) => { $( impl Adc<$ADC> { + /// Init a new ADC /// /// Enables the clock, performs a calibration and enables the ADC - pub fn $init( rb: $ADC, adc_common : &mut $ADC_COMMON, @@ -235,14 +294,6 @@ macro_rules! adc_hal { self.rb.cr.modify(|_, w| w.aden().clear_bit()); } - fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut $ADC_COMMON) { - ahb.enr().modify(|_, w| w.adc12en().enabled()); - unsafe { - adc_common.ccr.modify(|_, w| w - .ckmode().bits(self.prescale.bitcode()) - ); - } - } /// Calibrate according to 15.3.8 in the Reference Manual fn calibrate(&mut self) { @@ -394,13 +445,65 @@ macro_rules! adc_hal { } } +macro_rules! adc12_hal { + ($( + $ADC:ident: ($init:ident), + )+) => { + $( + impl Adc<$ADC> { + fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC1_2) { + ahb.enr().modify(|_, w| w.adc12en().enabled()); + unsafe { + adc_common.ccr.modify(|_, w| w + .ckmode().bits(self.prescale.bitcode()) + ); + } + } + } + )+ + adc_hal! { + $ADC: ($init, ADC1_2) + } +} + +macro_rules! adc34_hal { + ($( + $ADC:ident: ($init:ident), + )+) => { + $( + impl Adc<$ADC> { + fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC3_4) { + ahb.enr().modify(|_, w| w.adc34en().enabled()); + unsafe { + adc_common.ccr.modify(|_, w| w + .ckmode().bits(self.prescale.bitcode()) + ); + } + } + } + )+ + adc_hal! { + $ADC: ($init, ADC3_4) + } +} + +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] +adc12_hal! { + ADC1: (adc1), + ADC2: (adc2), +} #[cfg(any( feature = "stm32f303xb", feature = "stm32f303xc", feature = "stm32f303xd", feature = "stm32f303xe", ))] -adc_hal! { - ADC1: (adc1, ADC1_2), - ADC2: (adc2, ADC1_2), +adc34_hal! { + ADC3: (adc3), + ADC4: (adc4), } From 13857e36d5db906749f6ac30a9444eea1badfdd0 Mon Sep 17 00:00:00 2001 From: bboger Date: Thu, 28 Nov 2019 19:38:11 +0100 Subject: [PATCH 06/32] Extend example to adc3 --- Cargo.toml | 2 -- examples/adc.rs | 18 +++++++++++++++--- src/adc.rs | 13 +++++++------ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 919209c12..9f3915fde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,8 +49,6 @@ default-features = false panic-semihosting = "0.5" usb-device = "0.2" usbd-serial = "0.1" - -[dev-dependencies] cortex-m-semihosting = "0.3" panic-halt = "0.2" diff --git a/examples/adc.rs b/examples/adc.rs index 72550fafb..db24bd146 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -3,7 +3,7 @@ //! VCU - Vehicle Control Unit on the stm32f3discovery (STM32 F303 VCT6) -extern crate panic_halt; +extern crate panic_semihosting; use cortex_m_rt::entry; use cortex_m_semihosting::hprintln; @@ -18,18 +18,30 @@ fn main() -> ! { let mut rcc = peripherals.RCC.constrain(); let clocks = rcc.cfgr.freeze(&mut peripherals.FLASH.constrain().acr); let mut gpio_a = peripherals.GPIOA.split(&mut rcc.ahb); + let mut gpio_b = peripherals.GPIOB.split(&mut rcc.ahb); + let mut adc1 = adc::Adc::adc1( peripherals.ADC1, &mut peripherals.ADC1_2, &mut rcc.ahb, clocks, ); + let mut adc3 = adc::Adc::adc3( + peripherals.ADC3, + &mut peripherals.ADC3_4, + &mut rcc.ahb, + clocks, + ); // set up pin pa0, pa1 as analog pin let mut adc1_in1_pin = gpio_a.pa0.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr); + let mut adc3_in1_pin = gpio_b.pb1.into_analog(&mut gpio_b.moder, &mut gpio_b.pupdr); + loop { - let data: u16 = adc1.read(&mut adc1_in1_pin).unwrap(); - hprintln!("pa0 reads {}", data).expect("error in read"); + let adc1_in1_data: u16 = adc1.read(&mut adc1_in1_pin).unwrap(); + let adc3_in1_data: u16 = adc3.read(&mut adc3_in1_pin).unwrap(); + hprintln!("pa0 reads {}, pb1 reads {}", adc1_in1_data, adc3_in1_data) + .expect("error in read"); } } diff --git a/src/adc.rs b/src/adc.rs index cca4035aa..50d5cf971 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,5 +1,5 @@ //! # Work in Progress -//! API for the ADC1 and ADC2 (Analog to Digital Converter) on the 303xB/C/D/E +//! API for the ADC (Analog to Digital Converter) on the 303xB/C/D/E //! //! # Examples //! check `adc.rs` in the examples folder @@ -460,12 +460,12 @@ macro_rules! adc12_hal { } } } + adc_hal! { + $ADC: ($init, ADC1_2), + } )+ - adc_hal! { - $ADC: ($init, ADC1_2) } } - macro_rules! adc34_hal { ($( $ADC:ident: ($init:ident), @@ -481,9 +481,10 @@ macro_rules! adc34_hal { } } } + adc_hal! { + $ADC: ($init, ADC3_4), + } )+ - adc_hal! { - $ADC: ($init, ADC3_4) } } From dc7f63a44f12b48640b7b7da8ceb7fe7fab45f60 Mon Sep 17 00:00:00 2001 From: bboger Date: Sun, 29 Dec 2019 19:59:48 +0100 Subject: [PATCH 07/32] Add ADC support for all f303 sub variants. Also... * Reworked the Doc comments, added some TODO notes * moved MAX_ADVREGEN_STARTUP_US out of the corresponding function. * Removed some derived Traits that are not needed. * Refactored `wait_advregen_startup` function to avoid division by 0. * Added NOTEs for unsafe usage. --- src/adc.rs | 148 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 42 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 50d5cf971..edc9ee786 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,21 +1,34 @@ //! # Work in Progress -//! API for the ADC (Analog to Digital Converter) on the 303xB/C/D/E +//! API for the ADC (Analog to Digital Converter) //! -//! # Examples -//! check `adc.rs` in the examples folder +//! Note that the more specific your hardware selection is +//! (e.g. stm32f303**xc** instead of just stm32f303) +//! the more functionality is accessible +//! (in this case: ADC3 and ADC4 additionaly to ADC1 and ADC2). //! +//! # Examples +//! For a simple, working example check `adc.rs` in the examples folder. use cortex_m::asm; use embedded_hal::adc::{Channel, OneShot}; use crate::rcc::{Clocks, AHB}; +#[cfg(feature = "stm32f303")] +const MAX_ADVREGEN_STARTUP_US: u32 = 10; + +#[cfg(feature = "stm32f303")] +use crate::gpio::{gpioa, gpiob, gpioc, Analog}; + #[cfg(any( feature = "stm32f303xb", feature = "stm32f303xc", feature = "stm32f303xd", feature = "stm32f303xe", ))] -use crate::gpio::{gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, Analog}; +use crate::gpio::{gpiod, gpioe, gpiof}; + +#[cfg(feature = "stm32f303")] +use crate::stm32::{ADC1, ADC1_2, ADC2}; #[cfg(any( feature = "stm32f303xb", @@ -23,9 +36,12 @@ use crate::gpio::{gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, Analog}; feature = "stm32f303xd", feature = "stm32f303xe", ))] -use crate::stm32::{ADC1, ADC1_2, ADC2, ADC3, ADC3_4, ADC4}; +use crate::stm32::{ADC3, ADC3_4, ADC4}; /// ADC configuration +/// +/// TODO: Remove `pub` from the register block once all functionalities are implemented. +/// Leave it here until then as it allows easy access to the registers. pub struct Adc { pub rb: ADC, clocks: Clocks, @@ -33,14 +49,14 @@ pub struct Adc { operation_mode: Option, } -#[derive(Clone, Copy, Debug)] /// ADC sampling time /// -/// each channel can be sampled with a different sample time. -/// the total conversion time is -/// 12.5 ADC clock cycles + sample time (T_x + .5) +/// Each channel can be sampled with a different sample time. +/// For Sampletime T_n the total conversion time (in ADC clock cycles) is +/// 12.5 + (n + .5) /// /// TODO: there are boundaries on how this can be set depending on the hardware. +/// Check them and implement a sample time setting mechanism. pub enum SampleTime { T_1, T_2, @@ -72,17 +88,20 @@ impl SampleTime { } } } -#[derive(Clone, Copy, Debug, PartialEq)] + +#[derive(Clone, Copy, PartialEq)] /// ADC operation mode +/// +/// TODO: Implement other modes (DMA, Differential,…) pub enum OperationMode { OneShot, - // TODO all the other modes } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy)] /// ADC prescale +/// +/// TODO: Implement a setter that checks requirements for `HCLK_1` and add it. pub enum Prescale { - // HCLK_1 needs some requirements to be met HCLK_2 = 2, HCLK_4 = 4, } @@ -102,7 +121,6 @@ impl Prescale { } } -#[derive(Clone, Copy, Debug, PartialEq)] /// ADC data register alignment pub enum Align { /// Right alignment of output data @@ -128,6 +146,7 @@ impl Align { } } +/// Maps pins to ADC Channels. macro_rules! adc_pins { ($ADC:ident, $($pin:ty => $chan:expr),+ $(,)*) => { $( @@ -140,24 +159,26 @@ macro_rules! adc_pins { }; } -#[cfg(any( - feature = "stm32f303xb", - feature = "stm32f303xc", - feature = "stm32f303xd", - feature = "stm32f303xe", -))] +// # ADC1 Pin/Channel mapping +// ## f303 + +#[cfg(feature = "stm32f303")] adc_pins!(ADC1, gpioa::PA0 => 1_u8, gpioa::PA1 => 2_u8, gpioa::PA2 => 3_u8, gpioa::PA3 => 4_u8, - gpiof::PF4 => 5_u8, - // Channels 6 to 10 are shared channels (i.e. ADC12_INx) gpioc::PC0 => 6_u8, gpioc::PC1 => 7_u8, gpioc::PC2 => 8_u8, gpioc::PC3 => 9_u8, - gpiof::PF2 => 10_u8, +); + +#[cfg(any(feature = "stm32f303x6", feature = "stm32f303x8"))] +adc_pins!(ADC1, + gpiob::PB0 => 11_u8, + gpiob::PB1 => 12_u8, + gpiob::PB13 => 13_u8, ); #[cfg(any( @@ -166,6 +187,15 @@ adc_pins!(ADC1, feature = "stm32f303xd", feature = "stm32f303xe", ))] +adc_pins!(ADC1, + gpiof::PF4 => 5_u8, + gpiof::PF2 => 10_u8, +); + +// # ADC2 Pin/Channel mapping +// ## f303 + +#[cfg(feature = "stm32f303")] adc_pins!(ADC2, gpioa::PA4 => 1_u8, gpioa::PA5 => 2_u8, @@ -174,14 +204,32 @@ adc_pins!(ADC2, gpioc::PC4 => 5_u8, gpioc::PC5 => 11_u8, gpiob::PB2 => 12_u8, - // Channels 6 to 10 are shared channels (i.e. ADC12_INx) gpioc::PC0 => 6_u8, gpioc::PC1 => 7_u8, gpioc::PC2 => 8_u8, gpioc::PC3 => 9_u8, +); + +#[cfg(any(feature = "stm32f303x6", feature = "stm32f303x8"))] +adc_pins!(ADC2, + gpiob::PB12 => 13_u8, + gpiob::PB14 => 14_u8, + gpiob::PB15 => 15_u8, +); + +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] +adc_pins!(ADC2, gpiof::PF2 => 10_u8, ); +// # ADC3 Pin/Channel mapping +// ## f303 + #[cfg(any( feature = "stm32f303xb", feature = "stm32f303xc", @@ -207,6 +255,9 @@ adc_pins!(ADC3, gpiod::PD14 => 11_u8, ); +// # ADC4 Pin/Channel mapping +// ## f303 + #[cfg(any( feature = "stm32f303xb", feature = "stm32f303xc", @@ -230,6 +281,10 @@ adc_pins!(ADC4, gpiod::PD14 => 11_u8, ); +/// Abstract implementation of ADC functionality +/// +/// Do not use directly. See adc12_hal for a applicable Macro. +/// TODO: Extend/generalize beyond f303 macro_rules! adc_hal { ($( $ADC:ident: ($init:ident, $ADC_COMMON:ident), @@ -255,7 +310,8 @@ macro_rules! adc_hal { this_adc.enable_clock(ahb, adc_common); this_adc.set_align(Align::default()); this_adc.calibrate(); - // ADEN bit cannot be set during ADCAL=1 and 4 ADC clock cycle after the ADCAL + // ADEN bit cannot be set during ADCAL=1 + // and 4 ADC clock cycle after the ADCAL // bit is cleared by hardware this_adc.wait_adc_clk_cycles(4); this_adc.enable(); @@ -269,8 +325,8 @@ macro_rules! adc_hal { self.rb.isr.modify(|_, w| w.ovr().clear_bit()); self.rb.cfgr.modify(|_, w| w - .cont() .clear_bit() - .ovrmod() .clear_bit() + .cont().clear_bit() + .ovrmod().clear_bit() ); self.rb.sqr1.modify(|_, w| @@ -346,17 +402,14 @@ macro_rules! adc_hal { }); } - #[cfg(any( - feature = "stm32f303xb", - feature = "stm32f303xc", - feature = "stm32f303xd", - feature = "stm32f303xe", - ))] + /// wait for the advregen to startup. + /// + /// This is based on the MAX_ADVREGEN_STARTUP_US of the device. fn wait_advregen_startup(&self) { - const MAX_STARTUP_TIME_US: u32 = 10; - asm::delay(MAX_STARTUP_TIME_US / (self.clocks.sysclk().0 /1_000_000)); + asm::delay((MAX_ADVREGEN_STARTUP_US * 1_000_000) / self.clocks.sysclk().0); } + /// busy ADC read fn convert_one(&mut self, chan: u8) -> u16 { self.ensure_oneshot(); self.set_chan_smps(chan, SampleTime::default()); @@ -386,8 +439,9 @@ macro_rules! adc_hal { ); } - // Note: only allowed when ADSTART = 0 + /// Note: only allowed when ADSTART = 0 fn set_chan_smps(&self, chan: u8, smp: SampleTime) { + // NOTE(unsafe): Use only predefined, valid values. match chan { 1 => self.rb.smpr1.modify(|_, w| unsafe {w.smp1().bits(smp.bitcode())}), @@ -445,6 +499,9 @@ macro_rules! adc_hal { } } +/// Macro to implement ADC functionallity for ADC1 and ADC2 +/// +/// TODO: Extend/differentiate beyond f303. macro_rules! adc12_hal { ($( $ADC:ident: ($init:ident), @@ -453,6 +510,7 @@ macro_rules! adc12_hal { impl Adc<$ADC> { fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC1_2) { ahb.enr().modify(|_, w| w.adc12en().enabled()); + // NOTE(unsafe): Use only predefined, valid values. unsafe { adc_common.ccr.modify(|_, w| w .ckmode().bits(self.prescale.bitcode()) @@ -466,6 +524,16 @@ macro_rules! adc12_hal { )+ } } + +/// Macro to implement ADC functionallity for ADC3 and ADC4 +/// +/// TODO: Extend/differentiate beyond f303. +#[cfg(any( + feature = "stm32f303xb", + feature = "stm32f303xc", + feature = "stm32f303xd", + feature = "stm32f303xe", +))] macro_rules! adc34_hal { ($( $ADC:ident: ($init:ident), @@ -474,6 +542,7 @@ macro_rules! adc34_hal { impl Adc<$ADC> { fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC3_4) { ahb.enr().modify(|_, w| w.adc34en().enabled()); + // NOTE(unsafe): Use only predefined, valid values. unsafe { adc_common.ccr.modify(|_, w| w .ckmode().bits(self.prescale.bitcode()) @@ -488,12 +557,7 @@ macro_rules! adc34_hal { } } -#[cfg(any( - feature = "stm32f303xb", - feature = "stm32f303xc", - feature = "stm32f303xd", - feature = "stm32f303xe", -))] +#[cfg(feature = "stm32f303")] adc12_hal! { ADC1: (adc1), ADC2: (adc2), From 3a57d6ba945f98ff9ed9d937b367c7e68a5b39c2 Mon Sep 17 00:00:00 2001 From: bboger Date: Sun, 29 Dec 2019 20:37:19 +0100 Subject: [PATCH 08/32] Update ADC Code to stm32f3 bump This means: * Renaming of some register accesses. * Dropping some helper functions for advregen that are not needed anymore. * Dropping some (now) unnecessary `unsafe` blocks. Also added ADC availability to the CHANGELOG.md --- CHANGELOG.md | 1 + src/adc.rs | 92 +++++++++++++++------------------------------------- 2 files changed, 27 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df1a158a..6d43942aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Implement `InputPin` for `Output` pins ([#114](https://github.com/stm32-rs/stm32f3xx-hal/pull/114)) - Support for safe one-shot DMA transfers ([#86](https://github.com/stm32-rs/stm32f3xx-hal/pull/86)) - DMA support for serial reception and transmission ([#86](https://github.com/stm32-rs/stm32f3xx-hal/pull/86)) +- ADC support for `stm32f303` devices. ### Fixed diff --git a/src/adc.rs b/src/adc.rs index edc9ee786..1a1c5e2a1 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -329,10 +329,7 @@ macro_rules! adc_hal { .ovrmod().clear_bit() ); - self.rb.sqr1.modify(|_, w| - // NOTE(unsafe): set the sequence length to 1 - unsafe { w.l3().bits(0) } - ); + self.rb.sqr1.modify(|_, w| w.l().bits(0)); self.operation_mode = Some(OperationMode::OneShot); } @@ -353,7 +350,7 @@ macro_rules! adc_hal { /// Calibrate according to 15.3.8 in the Reference Manual fn calibrate(&mut self) { - if !self.advregen_enabled() { + if !self.rb.cr.read().advregen().is_enabled() { self.advregen_enable(); self.wait_advregen_startup(); } @@ -374,32 +371,11 @@ macro_rules! adc_hal { asm::delay(adc_clk_cycle * cycles); } - - fn advregen_enabled(&self) -> bool { - return self._get_new_advregen() == 0b01; - } - fn advregen_enable(&mut self){ - // need to go though 00 first - self._set_new_advregen(0b00); - self._set_new_advregen(0b01); - } - - /// returns ADVREGEN[1:0] - /// (deeppwd got merged as high bit in advregen - see ref manual) - fn _get_new_advregen(&self) -> u8 { - return - (self.rb.cr.read().deeppwd().bit() as u8) << 1 | - (self.rb.cr.read().advregen().bit() as u8); - } + // need to go through intermediate first + self.rb.cr.modify(|_, w| w.advregen().intermediate()); + self.rb.cr.modify(|_, w| w.advregen().enabled()); - /// sets ADVREGEN[1:0] - /// (deeppwd got merged as high bit in advregen - see ref manual) - fn _set_new_advregen(&mut self, val: u8) { - self.rb.cr.modify(|_, w| { w - .deeppwd().bit((val & 0x02) != 0) - .advregen().bit((val & 0x01) != 0) - }); } /// wait for the advregen to startup. @@ -417,7 +393,7 @@ macro_rules! adc_hal { self.rb.cr.modify(|_, w| w.adstart().set_bit()); while self.rb.isr.read().eos().bit_is_clear() {} - return self.rb.dr.read().regular_data().bits(); + return self.rb.dr.read().rdata().bits(); } fn ensure_oneshot(&mut self) { @@ -432,51 +408,35 @@ macro_rules! adc_hal { }; } + /// This should only be invoked with the defined channels for the particular + /// device. (See Pin/Channel mapping above) fn select_single_chan(&self, chan: u8) { self.rb.sqr1.modify(|_, w| - // NOTE(unsafe): set the ADC_INx + // NOTE(unsafe): chan is the x in ADCn_INx unsafe { w.sq1().bits(chan) } ); } /// Note: only allowed when ADSTART = 0 fn set_chan_smps(&self, chan: u8, smp: SampleTime) { - // NOTE(unsafe): Use only predefined, valid values. match chan { - 1 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp1().bits(smp.bitcode())}), - 2 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp2().bits(smp.bitcode())}), - 3 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp3().bits(smp.bitcode())}), - 4 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp4().bits(smp.bitcode())}), - 5 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp5().bits(smp.bitcode())}), - 6 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp6().bits(smp.bitcode())}), - 7 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp7().bits(smp.bitcode())}), - 8 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp8().bits(smp.bitcode())}), - 9 => self.rb.smpr1.modify(|_, w| - unsafe {w.smp9().bits(smp.bitcode())}), - 11 => self.rb.smpr2.modify(|_, w| - unsafe {w.smp10().bits(smp.bitcode())}), - 12 => self.rb.smpr2.modify(|_, w| - unsafe {w.smp12().bits(smp.bitcode())}), - 13 => self.rb.smpr2.modify(|_, w| - unsafe {w.smp13().bits(smp.bitcode())}), - 14 => self.rb.smpr2.modify(|_, w| - unsafe {w.smp14().bits(smp.bitcode())}), - 15 => self.rb.smpr2.modify(|_, w| - unsafe {w.smp15().bits(smp.bitcode())}), - 16 => self.rb.smpr2.modify(|_, w| - unsafe {w.smp16().bits(smp.bitcode())}), - 17 => self.rb.smpr2.modify(|_, w| - unsafe {w.smp17().bits(smp.bitcode())}), - 18 => self.rb.smpr2.modify(|_, w| - unsafe {w.smp18().bits(smp.bitcode())}), + 1 => self.rb.smpr1.modify(|_, w| w.smp1().bits(smp.bitcode())), + 2 => self.rb.smpr1.modify(|_, w| w.smp2().bits(smp.bitcode())), + 3 => self.rb.smpr1.modify(|_, w| w.smp3().bits(smp.bitcode())), + 4 => self.rb.smpr1.modify(|_, w| w.smp4().bits(smp.bitcode())), + 5 => self.rb.smpr1.modify(|_, w| w.smp5().bits(smp.bitcode())), + 6 => self.rb.smpr1.modify(|_, w| w.smp6().bits(smp.bitcode())), + 7 => self.rb.smpr1.modify(|_, w| w.smp7().bits(smp.bitcode())), + 8 => self.rb.smpr1.modify(|_, w| w.smp8().bits(smp.bitcode())), + 9 => self.rb.smpr1.modify(|_, w| w.smp9().bits(smp.bitcode())), + 11 => self.rb.smpr2.modify(|_, w| w.smp10().bits(smp.bitcode())), + 12 => self.rb.smpr2.modify(|_, w| w.smp12().bits(smp.bitcode())), + 13 => self.rb.smpr2.modify(|_, w| w.smp13().bits(smp.bitcode())), + 14 => self.rb.smpr2.modify(|_, w| w.smp14().bits(smp.bitcode())), + 15 => self.rb.smpr2.modify(|_, w| w.smp15().bits(smp.bitcode())), + 16 => self.rb.smpr2.modify(|_, w| w.smp16().bits(smp.bitcode())), + 17 => self.rb.smpr2.modify(|_, w| w.smp17().bits(smp.bitcode())), + 18 => self.rb.smpr2.modify(|_, w| w.smp18().bits(smp.bitcode())), _ => unreachable!(), }; } From 49d19dbf9da00add9ea52e95a9c41caf22ad00ee Mon Sep 17 00:00:00 2001 From: bboger Date: Sun, 29 Dec 2019 20:54:07 +0100 Subject: [PATCH 09/32] Reworked ADC example to work with all f303 devices. --- Cargo.toml | 5 +++++ examples/adc.rs | 22 ++++++---------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9f3915fde..d75221d3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,3 +113,8 @@ required-features = ["stm32f303"] [[example]] name = "serial_dma" required-features = ["stm32f303"] +||||||| parent of f894222... Reworked ADC example to work with all f303 devices. + +[[example]] +name = "adc" +required-features = ["stm32f303"] diff --git a/examples/adc.rs b/examples/adc.rs index db24bd146..39938bb58 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -//! VCU - Vehicle Control Unit on the stm32f3discovery (STM32 F303 VCT6) +//! Example usage for ADC. extern crate panic_semihosting; @@ -13,35 +13,25 @@ use stm32f3xx_hal::{adc, prelude::*, stm32}; #[entry] /// Main Thread fn main() -> ! { + // get peripherals, clocks and freeze them let mut peripherals = stm32::Peripherals::take().unwrap(); - let mut rcc = peripherals.RCC.constrain(); let clocks = rcc.cfgr.freeze(&mut peripherals.FLASH.constrain().acr); - let mut gpio_a = peripherals.GPIOA.split(&mut rcc.ahb); - let mut gpio_b = peripherals.GPIOB.split(&mut rcc.ahb); + // set up adc1 let mut adc1 = adc::Adc::adc1( peripherals.ADC1, &mut peripherals.ADC1_2, &mut rcc.ahb, clocks, ); - let mut adc3 = adc::Adc::adc3( - peripherals.ADC3, - &mut peripherals.ADC3_4, - &mut rcc.ahb, - clocks, - ); - // set up pin pa0, pa1 as analog pin + // set up pin pa0 as analog pin + let mut gpio_a = peripherals.GPIOA.split(&mut rcc.ahb); let mut adc1_in1_pin = gpio_a.pa0.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr); - let mut adc3_in1_pin = gpio_b.pb1.into_analog(&mut gpio_b.moder, &mut gpio_b.pupdr); - loop { let adc1_in1_data: u16 = adc1.read(&mut adc1_in1_pin).unwrap(); - let adc3_in1_data: u16 = adc3.read(&mut adc3_in1_pin).unwrap(); - hprintln!("pa0 reads {}, pb1 reads {}", adc1_in1_data, adc3_in1_data) - .expect("error in read"); + hprintln!("pa0 reads {}", adc1_in1_data).expect("error in read"); } } From f1ee27c7114718a62fdc668480654012c8f3c587 Mon Sep 17 00:00:00 2001 From: strom-und-spiele <53340143+strom-und-spiele@users.noreply.github.com> Date: Mon, 6 Jan 2020 16:09:46 +0100 Subject: [PATCH 10/32] Apply consistent namings from code review Co-Authored-By: Sh3Rm4n --- examples/adc.rs | 2 +- src/adc.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index 39938bb58..c2788d4eb 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -14,7 +14,7 @@ use stm32f3xx_hal::{adc, prelude::*, stm32}; /// Main Thread fn main() -> ! { // get peripherals, clocks and freeze them - let mut peripherals = stm32::Peripherals::take().unwrap(); + let mut dp = stm32::Peripherals::take().unwrap(); let mut rcc = peripherals.RCC.constrain(); let clocks = rcc.cfgr.freeze(&mut peripherals.FLASH.constrain().acr); diff --git a/src/adc.rs b/src/adc.rs index 1a1c5e2a1..941e408dc 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -287,7 +287,7 @@ adc_pins!(ADC4, /// TODO: Extend/generalize beyond f303 macro_rules! adc_hal { ($( - $ADC:ident: ($init:ident, $ADC_COMMON:ident), + $ADC:ident: ($adcx:ident, $ADC_COMMON:ident), )+) => { $( impl Adc<$ADC> { @@ -315,7 +315,7 @@ macro_rules! adc_hal { // bit is cleared by hardware this_adc.wait_adc_clk_cycles(4); this_adc.enable(); - return this_adc; + this_adc } /// sets up adc in one shot mode for a single channel From e2d37c869fa604ccee4d5f3cd48fb9530055bf08 Mon Sep 17 00:00:00 2001 From: ba_bo Date: Mon, 6 Jan 2020 16:17:24 +0100 Subject: [PATCH 11/32] Update consistency 2 --- src/adc.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 941e408dc..a1b23319c 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -295,7 +295,7 @@ macro_rules! adc_hal { /// Init a new ADC /// /// Enables the clock, performs a calibration and enables the ADC - pub fn $init( + pub fn $adcx( rb: $ADC, adc_common : &mut $ADC_COMMON, ahb: &mut AHB, @@ -464,7 +464,7 @@ macro_rules! adc_hal { /// TODO: Extend/differentiate beyond f303. macro_rules! adc12_hal { ($( - $ADC:ident: ($init:ident), + $ADC:ident: ($adcx:ident), )+) => { $( impl Adc<$ADC> { @@ -479,7 +479,7 @@ macro_rules! adc12_hal { } } adc_hal! { - $ADC: ($init, ADC1_2), + $ADC: ($adcx, ADC1_2), } )+ } @@ -496,7 +496,7 @@ macro_rules! adc12_hal { ))] macro_rules! adc34_hal { ($( - $ADC:ident: ($init:ident), + $ADC:ident: ($adcx:ident), )+) => { $( impl Adc<$ADC> { @@ -511,7 +511,7 @@ macro_rules! adc34_hal { } } adc_hal! { - $ADC: ($init, ADC3_4), + $ADC: ($adcx, ADC3_4), } )+ } From 3e29640bec8659eda9bf0ef7e9309de310cee6e1 Mon Sep 17 00:00:00 2001 From: ba_bo Date: Tue, 7 Jan 2020 00:02:49 +0100 Subject: [PATCH 12/32] Implement Default as Trait. --- src/adc.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index a1b23319c..ee28f91d0 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -68,12 +68,13 @@ pub enum SampleTime { T_601, } -impl SampleTime { - /// Get the default timer - pub fn default() -> Self { +impl Default for SampleTime { + fn default() -> Self { SampleTime::T_19 } +} +impl SampleTime { /// Conversion to bits for SMP fn bitcode(&self) -> u8 { match self { @@ -106,12 +107,13 @@ pub enum Prescale { HCLK_4 = 4, } -impl Prescale { - /// Get default prescaler +impl Default for Prescale { fn default() -> Self { Prescale::HCLK_2 } +} +impl Prescale { /// Conversion to bits for CKMODE in ADCx_CCR fn bitcode(&self) -> u8 { match self { @@ -129,9 +131,8 @@ pub enum Align { Left, } -impl Align { - /// Default: right alignment - pub fn default() -> Self { +impl Default for Align { + fn default() -> Self { Align::Right } } From 5b8b96a88f804c05bceb1521ac61dfcdeed2916a Mon Sep 17 00:00:00 2001 From: ba_bo Date: Tue, 7 Jan 2020 00:04:27 +0100 Subject: [PATCH 13/32] Update Example Skip rustfmt in the example when building the adc The reader should check each line to make sure they understand what they are passing. --- examples/adc.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index c2788d4eb..ce4ab59c3 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -//! Example usage for ADC. +//! Example usage for ADC on STM32F303 extern crate panic_semihosting; @@ -15,19 +15,20 @@ use stm32f3xx_hal::{adc, prelude::*, stm32}; fn main() -> ! { // get peripherals, clocks and freeze them let mut dp = stm32::Peripherals::take().unwrap(); - let mut rcc = peripherals.RCC.constrain(); - let clocks = rcc.cfgr.freeze(&mut peripherals.FLASH.constrain().acr); + let mut rcc = dp.RCC.constrain(); + let clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr); // set up adc1 + #[rustfmt::skip] let mut adc1 = adc::Adc::adc1( - peripherals.ADC1, - &mut peripherals.ADC1_2, + dp.ADC1, + &mut dp.ADC1_2, &mut rcc.ahb, clocks, ); // set up pin pa0 as analog pin - let mut gpio_a = peripherals.GPIOA.split(&mut rcc.ahb); + let mut gpio_a = dp.GPIOA.split(&mut rcc.ahb); let mut adc1_in1_pin = gpio_a.pa0.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr); loop { From 3e84d059d69e4563be707657afc58cbbb2b7a610 Mon Sep 17 00:00:00 2001 From: bboger Date: Tue, 7 Apr 2020 17:30:32 +0200 Subject: [PATCH 14/32] removing deprecated unsafe blocks from adc --- src/adc.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index ee28f91d0..c4441e2b4 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -471,12 +471,9 @@ macro_rules! adc12_hal { impl Adc<$ADC> { fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC1_2) { ahb.enr().modify(|_, w| w.adc12en().enabled()); - // NOTE(unsafe): Use only predefined, valid values. - unsafe { - adc_common.ccr.modify(|_, w| w - .ckmode().bits(self.prescale.bitcode()) - ); - } + adc_common.ccr.modify(|_, w| w + .ckmode().bits(self.prescale.bitcode()) + ); } } adc_hal! { @@ -503,12 +500,9 @@ macro_rules! adc34_hal { impl Adc<$ADC> { fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC3_4) { ahb.enr().modify(|_, w| w.adc34en().enabled()); - // NOTE(unsafe): Use only predefined, valid values. - unsafe { - adc_common.ccr.modify(|_, w| w - .ckmode().bits(self.prescale.bitcode()) - ); - } + adc_common.ccr.modify(|_, w| w + .ckmode().bits(self.prescale.bitcode()) + ); } } adc_hal! { From 1988a0d29bcf702336369b7bcfe19c2ce17abceb Mon Sep 17 00:00:00 2001 From: bboger Date: Tue, 7 Apr 2020 18:33:35 +0200 Subject: [PATCH 15/32] Tread EOS flag better i.e. - use context sensitive read/write functions - clear flag after usage --- src/adc.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/adc.rs b/src/adc.rs index c4441e2b4..a323e4423 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -393,7 +393,9 @@ macro_rules! adc_hal { self.select_single_chan(chan); self.rb.cr.modify(|_, w| w.adstart().set_bit()); - while self.rb.isr.read().eos().bit_is_clear() {} + while self.rb.isr.read().eos().is_not_complete() + {} + self.rb.isr.modify(|_, w| w.eos().clear()); return self.rb.dr.read().rdata().bits(); } From 204fa84bde727912e1936b11cddc9534e8beb5d1 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 11 Apr 2020 18:26:39 +0200 Subject: [PATCH 16/32] Make use of CKMODE_A --- src/adc.rs | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index a323e4423..3066f4b45 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -10,6 +10,8 @@ //! For a simple, working example check `adc.rs` in the examples folder. use cortex_m::asm; use embedded_hal::adc::{Channel, OneShot}; +#[cfg(feature = "stm32f303")] +use stm32f3::stm32f303::adc1_2::ccr::CKMODE_A; use crate::rcc::{Clocks, AHB}; @@ -45,7 +47,7 @@ use crate::stm32::{ADC3, ADC3_4, ADC4}; pub struct Adc { pub rb: ADC, clocks: Clocks, - prescale: Prescale, + ckmode: CKMODE, operation_mode: Option, } @@ -99,26 +101,34 @@ pub enum OperationMode { } #[derive(Clone, Copy)] -/// ADC prescale +/// ADC CKMODE /// -/// TODO: Implement a setter that checks requirements for `HCLK_1` and add it. -pub enum Prescale { - HCLK_2 = 2, - HCLK_4 = 4, +/// TODO: Add ASYNCHRONOUS mode +/// TODO: ADD SYNCDIV1 mode with a check if the AHB prescaler of the +/// RCC is set to 1 (the duty cycle of the AHB clock must be 50% in this configuration). +/// see RM0316 15.3.3 +pub enum CKMODE { + // ASYNCHRONOUS = 0, + // SYNCDIV1 = 1 + SYNCDIV2 = 2, + SYNCDIV4 = 4, } -impl Default for Prescale { +impl Default for CKMODE { fn default() -> Self { - Prescale::HCLK_2 + CKMODE::SYNCDIV2 } } -impl Prescale { - /// Conversion to bits for CKMODE in ADCx_CCR - fn bitcode(&self) -> u8 { - match self { - Prescale::HCLK_2 => 0b10, - Prescale::HCLK_4 => 0b11, +// ADC3_2 returns a pointer to a adc1_2 type, so this from is ok for both. +#[cfg(feature = "stm32f303")] +impl From for stm32f3::stm32f303::adc1_2::ccr::CKMODE_A { + fn from(ckmode: CKMODE) -> Self { + match ckmode { + //CKMODE::ASYNCHRONOUS => CKMODE_A::ASYNCHRONOUS, + //CKMODE::SYNCDIV1 => CKMODE_A::SYNCDIV1, + CKMODE::SYNCDIV2 => CKMODE_A::SYNCDIV2, + CKMODE::SYNCDIV4 => CKMODE_A::SYNCDIV4, } } } @@ -305,7 +315,7 @@ macro_rules! adc_hal { let mut this_adc = Self { rb, clocks, - prescale : Prescale::default(), + ckmode : CKMODE::default(), operation_mode: None, }; this_adc.enable_clock(ahb, adc_common); @@ -368,7 +378,7 @@ macro_rules! adc_hal { fn wait_adc_clk_cycles(&self, cycles: u32) { - let adc_clk_cycle = self.clocks.hclk().0 / (self.prescale as u32); + let adc_clk_cycle = self.clocks.hclk().0 / (self.ckmode as u32); asm::delay(adc_clk_cycle * cycles); } @@ -474,7 +484,7 @@ macro_rules! adc12_hal { fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC1_2) { ahb.enr().modify(|_, w| w.adc12en().enabled()); adc_common.ccr.modify(|_, w| w - .ckmode().bits(self.prescale.bitcode()) + .ckmode().variant(self.ckmode.into()) ); } } @@ -503,7 +513,7 @@ macro_rules! adc34_hal { fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC3_4) { ahb.enr().modify(|_, w| w.adc34en().enabled()); adc_common.ccr.modify(|_, w| w - .ckmode().bits(self.prescale.bitcode()) + .ckmode().variant(self.ckmode.into()) ); } } From a09800bafcf8d0df39e03ebc3e2df5cac00dec62 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 11 Apr 2020 19:23:03 +0200 Subject: [PATCH 17/32] Allows CKMODE to be set when initalizing the adc Clocksettings are checked during setup and as the setup can fail, Option is returned now. --- examples/adc.rs | 3 ++- src/adc.rs | 45 +++++++++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index ce4ab59c3..c653b9645 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -24,8 +24,9 @@ fn main() -> ! { dp.ADC1, &mut dp.ADC1_2, &mut rcc.ahb, + adc::CKMODE::default(), clocks, - ); + ).unwrap(); // set up pin pa0 as analog pin let mut gpio_a = dp.GPIOA.split(&mut rcc.ahb); diff --git a/src/adc.rs b/src/adc.rs index 3066f4b45..33a209dee 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -100,16 +100,13 @@ pub enum OperationMode { OneShot, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] /// ADC CKMODE /// /// TODO: Add ASYNCHRONOUS mode -/// TODO: ADD SYNCDIV1 mode with a check if the AHB prescaler of the -/// RCC is set to 1 (the duty cycle of the AHB clock must be 50% in this configuration). -/// see RM0316 15.3.3 pub enum CKMODE { // ASYNCHRONOUS = 0, - // SYNCDIV1 = 1 + SYNCDIV1 = 1, SYNCDIV2 = 2, SYNCDIV4 = 4, } @@ -126,7 +123,7 @@ impl From for stm32f3::stm32f303::adc1_2::ccr::CKMODE_A { fn from(ckmode: CKMODE) -> Self { match ckmode { //CKMODE::ASYNCHRONOUS => CKMODE_A::ASYNCHRONOUS, - //CKMODE::SYNCDIV1 => CKMODE_A::SYNCDIV1, + CKMODE::SYNCDIV1 => CKMODE_A::SYNCDIV1, CKMODE::SYNCDIV2 => CKMODE_A::SYNCDIV2, CKMODE::SYNCDIV4 => CKMODE_A::SYNCDIV4, } @@ -310,15 +307,21 @@ macro_rules! adc_hal { rb: $ADC, adc_common : &mut $ADC_COMMON, ahb: &mut AHB, + ckmode: CKMODE, clocks: Clocks, - ) -> Self { + ) -> Option { let mut this_adc = Self { rb, clocks, - ckmode : CKMODE::default(), + ckmode, operation_mode: None, }; - this_adc.enable_clock(ahb, adc_common); + if !(this_adc.clocks_welldefined(clocks)) { + return None; + } + if !(this_adc.enable_clock(ahb, adc_common)){ + return None; + } this_adc.set_align(Align::default()); this_adc.calibrate(); // ADEN bit cannot be set during ADCAL=1 @@ -326,7 +329,13 @@ macro_rules! adc_hal { // bit is cleared by hardware this_adc.wait_adc_clk_cycles(4); this_adc.enable(); - this_adc + Some(this_adc) + } + + /// Software can use CKMODE::SYNCDIV1 only if + /// hclk and sysclk are the same. (see reference manual 15.3.3) + fn clocks_welldefined(&self, clocks: Clocks) -> bool { + !(self.ckmode == CKMODE::SYNCDIV1 && !(clocks.hclk().0 == clocks.sysclk().0)) } /// sets up adc in one shot mode for a single channel @@ -481,11 +490,16 @@ macro_rules! adc12_hal { )+) => { $( impl Adc<$ADC> { - fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC1_2) { + fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC1_2) -> bool { + if ahb.enr().read().adc34en().is_enabled() && + (adc_common.ccr.read().ckmode().variant() != self.ckmode.into()) { + return false; + } ahb.enr().modify(|_, w| w.adc12en().enabled()); adc_common.ccr.modify(|_, w| w .ckmode().variant(self.ckmode.into()) ); + true } } adc_hal! { @@ -510,11 +524,18 @@ macro_rules! adc34_hal { )+) => { $( impl Adc<$ADC> { - fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC3_4) { + /// Returns true iff + /// the clock was enabled + /// or the clock was already enabled with the same settings + fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC3_4) -> bool { + if ahb.enr().read().adc34en().is_enabled() { + return (adc_common.ccr.read().ckmode().variant() == self.ckmode.into()) + } ahb.enr().modify(|_, w| w.adc34en().enabled()); adc_common.ccr.modify(|_, w| w .ckmode().variant(self.ckmode.into()) ); + true } } adc_hal! { From 86c049b3f7fdd0be7df76fd0a7d1860f06653004 Mon Sep 17 00:00:00 2001 From: ba_bo Date: Sun, 12 Apr 2020 18:23:14 +0200 Subject: [PATCH 18/32] Update adc example with more context --- examples/adc.rs | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index c653b9645..3da4ebd14 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -13,7 +13,7 @@ use stm32f3xx_hal::{adc, prelude::*, stm32}; #[entry] /// Main Thread fn main() -> ! { - // get peripherals, clocks and freeze them + // Get peripherals, clocks and freeze them let mut dp = stm32::Peripherals::take().unwrap(); let mut rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr); @@ -21,19 +21,56 @@ fn main() -> ! { // set up adc1 #[rustfmt::skip] let mut adc1 = adc::Adc::adc1( - dp.ADC1, + dp.ADC1, // The ADC we are going to control + // The following is only needed to make sure the clock signal for the ADC is set up + // correctly. &mut dp.ADC1_2, &mut rcc.ahb, adc::CKMODE::default(), clocks, + // If everything is set up correctly, we'll get `Some(adc1)`. to access it, we need to `unwrap` + // it. If there was an error, we'll get `None` and this unwrap will `panic!`. ).unwrap(); - // set up pin pa0 as analog pin + // Set up pin PA0 as analog pin. + // This pin is connected to the user button on the stm32f3discovery board. let mut gpio_a = dp.GPIOA.split(&mut rcc.ahb); let mut adc1_in1_pin = gpio_a.pa0.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr); + // Be aware that the values in the table below depend on the input of VREF + // Also know that and that the ADC hardware unit always rounds down. + // + // To have a stable VREF input, put a condensator and a volt limiting diode in front of it. + // + // To compensate for the unit and integer math rounding down, use the following formula: + // ```rust + // // [setup as above] + // let mut adc: u3216 = adc1.read(&mut adc1_in1_pin).expect('Error reading adc1.'); + // // add '0.5' to the adc value, compensating the ADC hardware rounding down + // adc <<= 1; + // adc += 1; + // // multiply it by '300 mV', converting to your voltage level + // let mut volt_mv = adc * 300; + // // make up for the coming integer division + // volt_mv += 1 << 12; + // // undo the first bitshift above and the 12bit ADC size + // volt_mv >>= 13; + // println!("Was reading {} mV", volt_mv); + // ``` + hprintln!(" + The ADC has a 12 bit resolution, i.e. if your reference Value is 3V: + approx. ADC value | approx. volt value + ==================+=================== + 0 | 0 mV + 2048 | 150 mV + 4095 | 300 mV + + If you are using a STM32F3Discovery, PA0 is connected to the User Button. + Pressing it should connect the user Button to to HIGH and the value should change from 0 to 4095. + ").expect("Error using hprintln."); + loop { - let adc1_in1_data: u16 = adc1.read(&mut adc1_in1_pin).unwrap(); - hprintln!("pa0 reads {}", adc1_in1_data).expect("error in read"); + let adc1_in1_data: u16 = adc1.read(&mut adc1_in1_pin).expect("Error reading adc1."); + hprintln!("PA0 reads {}", adc1_in1_data).unwrap_or(()); } } From c130f7af2b62ce4cc43989612f4677b1ab6188c4 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 10:24:43 +0200 Subject: [PATCH 19/32] drop panic-halt dependency --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d75221d3e..306a6928c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,8 +50,6 @@ panic-semihosting = "0.5" usb-device = "0.2" usbd-serial = "0.1" cortex-m-semihosting = "0.3" -panic-halt = "0.2" - [features] default = ["unproven"] From 54cf1ed84dd4eadec6cae0eca80640a0123da81e Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 11:13:17 +0200 Subject: [PATCH 20/32] replace stm32 by pac --- examples/adc.rs | 4 ++-- src/adc.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index 3da4ebd14..afb054de6 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -8,13 +8,13 @@ extern crate panic_semihosting; use cortex_m_rt::entry; use cortex_m_semihosting::hprintln; -use stm32f3xx_hal::{adc, prelude::*, stm32}; +use stm32f3xx_hal::{adc, pac, prelude::*}; #[entry] /// Main Thread fn main() -> ! { // Get peripherals, clocks and freeze them - let mut dp = stm32::Peripherals::take().unwrap(); + let mut dp = pac::Peripherals::take().unwrap(); let mut rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr); diff --git a/src/adc.rs b/src/adc.rs index 33a209dee..84a431b44 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -30,7 +30,7 @@ use crate::gpio::{gpioa, gpiob, gpioc, Analog}; use crate::gpio::{gpiod, gpioe, gpiof}; #[cfg(feature = "stm32f303")] -use crate::stm32::{ADC1, ADC1_2, ADC2}; +use crate::pac::{ADC1, ADC1_2, ADC2}; #[cfg(any( feature = "stm32f303xb", @@ -38,7 +38,7 @@ use crate::stm32::{ADC1, ADC1_2, ADC2}; feature = "stm32f303xd", feature = "stm32f303xe", ))] -use crate::stm32::{ADC3, ADC3_4, ADC4}; +use crate::pac::{ADC3, ADC3_4, ADC4}; /// ADC configuration /// From d8a007f51f7efbd2cde80b516c152951caa4866a Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 11:32:08 +0200 Subject: [PATCH 21/32] update example comments --- examples/adc.rs | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index afb054de6..ada419d59 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -19,7 +19,6 @@ fn main() -> ! { let clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr); // set up adc1 - #[rustfmt::skip] let mut adc1 = adc::Adc::adc1( dp.ADC1, // The ADC we are going to control // The following is only needed to make sure the clock signal for the ADC is set up @@ -30,40 +29,27 @@ fn main() -> ! { clocks, // If everything is set up correctly, we'll get `Some(adc1)`. to access it, we need to `unwrap` // it. If there was an error, we'll get `None` and this unwrap will `panic!`. - ).unwrap(); + ) + .unwrap(); // Set up pin PA0 as analog pin. // This pin is connected to the user button on the stm32f3discovery board. let mut gpio_a = dp.GPIOA.split(&mut rcc.ahb); let mut adc1_in1_pin = gpio_a.pa0.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr); - // Be aware that the values in the table below depend on the input of VREF - // Also know that and that the ADC hardware unit always rounds down. - // + // Be aware that the values in the table below depend on the input of VREF. // To have a stable VREF input, put a condensator and a volt limiting diode in front of it. // - // To compensate for the unit and integer math rounding down, use the following formula: - // ```rust - // // [setup as above] - // let mut adc: u3216 = adc1.read(&mut adc1_in1_pin).expect('Error reading adc1.'); - // // add '0.5' to the adc value, compensating the ADC hardware rounding down - // adc <<= 1; - // adc += 1; - // // multiply it by '300 mV', converting to your voltage level - // let mut volt_mv = adc * 300; - // // make up for the coming integer division - // volt_mv += 1 << 12; - // // undo the first bitshift above and the 12bit ADC size - // volt_mv >>= 13; - // println!("Was reading {} mV", volt_mv); - // ``` + // Also know that integer division and the ADC hardware unit always round down. + // To make up for those errors, see this forum entry: + // [https://forum.allaboutcircuits.com/threads/why-adc-1024-is-correct-and-adc-1023-is-just-plain-wrong.80018/] hprintln!(" The ADC has a 12 bit resolution, i.e. if your reference Value is 3V: approx. ADC value | approx. volt value ==================+=================== - 0 | 0 mV - 2048 | 150 mV - 4095 | 300 mV + 0 | 0 mV + 2048 | 1500 mV + 4095 | 3000 mV If you are using a STM32F3Discovery, PA0 is connected to the User Button. Pressing it should connect the user Button to to HIGH and the value should change from 0 to 4095. @@ -71,6 +57,6 @@ fn main() -> ! { loop { let adc1_in1_data: u16 = adc1.read(&mut adc1_in1_pin).expect("Error reading adc1."); - hprintln!("PA0 reads {}", adc1_in1_data).unwrap_or(()); + hprintln!("PA0 reads {}", adc1_in1_data).ok(); } } From 09af232ecc44c5258f9bf99ac99bdf98461c7594 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 12:53:20 +0200 Subject: [PATCH 22/32] fixup! update example comments --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 306a6928c..bf5f44766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,7 +111,6 @@ required-features = ["stm32f303"] [[example]] name = "serial_dma" required-features = ["stm32f303"] -||||||| parent of f894222... Reworked ADC example to work with all f303 devices. [[example]] name = "adc" From 9ea3dcf8660daa2ea7e5427ab4c195c1b28b6d5d Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 13:09:44 +0200 Subject: [PATCH 23/32] refactoring based on review --- src/adc.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 84a431b44..297606453 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -412,22 +412,15 @@ macro_rules! adc_hal { self.select_single_chan(chan); self.rb.cr.modify(|_, w| w.adstart().set_bit()); - while self.rb.isr.read().eos().is_not_complete() - {} + while self.rb.isr.read().eos().is_not_complete() {} self.rb.isr.modify(|_, w| w.eos().clear()); return self.rb.dr.read().rdata().bits(); } fn ensure_oneshot(&mut self) { - match self.operation_mode { - Some(mode) => - { - if mode != OperationMode::OneShot { - self.setup_oneshot(); - } - }, - _ => self.setup_oneshot(), - }; + if self.operation_mode != Some(OperationMode::OneShot) { + self.setup_oneshot(); + } } /// This should only be invoked with the defined channels for the particular @@ -490,10 +483,12 @@ macro_rules! adc12_hal { )+) => { $( impl Adc<$ADC> { + /// Returns true iff + /// the clock can be enabled with the given settings + /// or the clock was already enabled with the same settings fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC1_2) -> bool { - if ahb.enr().read().adc34en().is_enabled() && - (adc_common.ccr.read().ckmode().variant() != self.ckmode.into()) { - return false; + if ahb.enr().read().adc12en().is_enabled() { + return (adc_common.ccr.read().ckmode().variant() == self.ckmode.into()); } ahb.enr().modify(|_, w| w.adc12en().enabled()); adc_common.ccr.modify(|_, w| w @@ -525,11 +520,11 @@ macro_rules! adc34_hal { $( impl Adc<$ADC> { /// Returns true iff - /// the clock was enabled + /// the clock can be enabled with the given settings /// or the clock was already enabled with the same settings fn enable_clock(&self, ahb: &mut AHB, adc_common: &mut ADC3_4) -> bool { if ahb.enr().read().adc34en().is_enabled() { - return (adc_common.ccr.read().ckmode().variant() == self.ckmode.into()) + return (adc_common.ccr.read().ckmode().variant() == self.ckmode.into()); } ahb.enr().modify(|_, w| w.adc34en().enabled()); adc_common.ccr.modify(|_, w| w From 1d812ae95d8527818def9e95a133deb4f9bb0864 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 14:12:41 +0200 Subject: [PATCH 24/32] update feature gates --- src/adc.rs | 83 ++++++++++++++++++++++-------------------------------- src/lib.rs | 2 +- 2 files changed, 34 insertions(+), 51 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 297606453..43bbc7505 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,49 +1,37 @@ -//! # Work in Progress //! API for the ADC (Analog to Digital Converter) //! -//! Note that the more specific your hardware selection is -//! (e.g. stm32f303**xc** instead of just stm32f303) -//! the more functionality is accessible -//! (in this case: ADC3 and ADC4 additionaly to ADC1 and ADC2). -//! //! # Examples -//! For a simple, working example check `adc.rs` in the examples folder. +//! Check `adc.rs` in the examples folder. +//! It can be built for the STM32F3Discovery running +//! `cargo build --example adc --features=stm32f303xc` +use crate::{ + gpio::Analog, + rcc::{Clocks, AHB}, +}; use cortex_m::asm; use embedded_hal::adc::{Channel, OneShot}; -#[cfg(feature = "stm32f303")] -use stm32f3::stm32f303::adc1_2::ccr::CKMODE_A; -use crate::rcc::{Clocks, AHB}; - -#[cfg(feature = "stm32f303")] +use crate::{ + gpio::{gpioa, gpiob, gpioc}, + pac::{ADC1, ADC1_2, ADC2}, +}; +use stm32f3::stm32f303::adc1_2::ccr::CKMODE_A; const MAX_ADVREGEN_STARTUP_US: u32 = 10; -#[cfg(feature = "stm32f303")] -use crate::gpio::{gpioa, gpiob, gpioc, Analog}; - -#[cfg(any( - feature = "stm32f303xb", - feature = "stm32f303xc", - feature = "stm32f303xd", - feature = "stm32f303xe", -))] -use crate::gpio::{gpiod, gpioe, gpiof}; - -#[cfg(feature = "stm32f303")] -use crate::pac::{ADC1, ADC1_2, ADC2}; - #[cfg(any( feature = "stm32f303xb", feature = "stm32f303xc", feature = "stm32f303xd", feature = "stm32f303xe", ))] -use crate::pac::{ADC3, ADC3_4, ADC4}; +use crate::{ + gpio::{gpiod, gpioe, gpiof}, + pac::{ADC3, ADC3_4, ADC4}, +}; /// ADC configuration -/// -/// TODO: Remove `pub` from the register block once all functionalities are implemented. -/// Leave it here until then as it allows easy access to the registers. +// TODO: Remove `pub` from the register block once all functionalities are implemented. +// Leave it here until then as it allows easy access to the registers. pub struct Adc { pub rb: ADC, clocks: Clocks, @@ -54,11 +42,11 @@ pub struct Adc { /// ADC sampling time /// /// Each channel can be sampled with a different sample time. -/// For Sampletime T_n the total conversion time (in ADC clock cycles) is -/// 12.5 + (n + .5) -/// -/// TODO: there are boundaries on how this can be set depending on the hardware. -/// Check them and implement a sample time setting mechanism. +/// There is always an overhead of 13 ADC clock cycles. +/// E.g. For Sampletime T_19 the total conversion time (in ADC clock cycles) is +/// 13 + 19 = 32 ADC Clock Cycles +// TODO: there are boundaries on how this can be set depending on the hardware. +// Check them and implement a sample time setting mechanism. pub enum SampleTime { T_1, T_2, @@ -94,16 +82,14 @@ impl SampleTime { #[derive(Clone, Copy, PartialEq)] /// ADC operation mode -/// -/// TODO: Implement other modes (DMA, Differential,…) +// TODO: Implement other modes (DMA, Differential,…) pub enum OperationMode { OneShot, } #[derive(Clone, Copy, PartialEq)] /// ADC CKMODE -/// -/// TODO: Add ASYNCHRONOUS mode +// TODO: Add ASYNCHRONOUS mode pub enum CKMODE { // ASYNCHRONOUS = 0, SYNCDIV1 = 1, @@ -119,7 +105,7 @@ impl Default for CKMODE { // ADC3_2 returns a pointer to a adc1_2 type, so this from is ok for both. #[cfg(feature = "stm32f303")] -impl From for stm32f3::stm32f303::adc1_2::ccr::CKMODE_A { +impl From for CKMODE_A { fn from(ckmode: CKMODE) -> Self { match ckmode { //CKMODE::ASYNCHRONOUS => CKMODE_A::ASYNCHRONOUS, @@ -289,10 +275,9 @@ adc_pins!(ADC4, gpiod::PD14 => 11_u8, ); -/// Abstract implementation of ADC functionality -/// -/// Do not use directly. See adc12_hal for a applicable Macro. -/// TODO: Extend/generalize beyond f303 +// Abstract implementation of ADC functionality +// Do not use directly. See adc12_hal for a applicable Macro. +// TODO: Extend/generalize beyond f303 macro_rules! adc_hal { ($( $ADC:ident: ($adcx:ident, $ADC_COMMON:ident), @@ -474,9 +459,8 @@ macro_rules! adc_hal { } } -/// Macro to implement ADC functionallity for ADC1 and ADC2 -/// -/// TODO: Extend/differentiate beyond f303. +// Macro to implement ADC functionallity for ADC1 and ADC2 +// TODO: Extend/differentiate beyond f303. macro_rules! adc12_hal { ($( $ADC:ident: ($adcx:ident), @@ -504,9 +488,8 @@ macro_rules! adc12_hal { } } -/// Macro to implement ADC functionallity for ADC3 and ADC4 -/// -/// TODO: Extend/differentiate beyond f303. +// Macro to implement ADC functionallity for ADC3 and ADC4 +// TODO: Extend/differentiate beyond f303. #[cfg(any( feature = "stm32f303xb", feature = "stm32f303xc", diff --git a/src/lib.rs b/src/lib.rs index 484ec2d32..6c5841662 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,7 +120,7 @@ pub use crate::pac as stm32; #[cfg(feature = "rt")] pub use crate::pac::interrupt; -#[cfg(feature = "device-selected")] +#[cfg(feature = "stm32f303")] pub mod adc; #[cfg(feature = "device-selected")] pub mod delay; From 5e86d4847cdc6c83c7f50eb17f90226352e9160d Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 14:50:45 +0200 Subject: [PATCH 25/32] remove u8 annotations --- src/adc.rs | 112 ++++++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 43bbc7505..fb6aecddb 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -158,21 +158,21 @@ macro_rules! adc_pins { #[cfg(feature = "stm32f303")] adc_pins!(ADC1, - gpioa::PA0 => 1_u8, - gpioa::PA1 => 2_u8, - gpioa::PA2 => 3_u8, - gpioa::PA3 => 4_u8, - gpioc::PC0 => 6_u8, - gpioc::PC1 => 7_u8, - gpioc::PC2 => 8_u8, - gpioc::PC3 => 9_u8, + gpioa::PA0 => 1, + gpioa::PA1 => 2, + gpioa::PA2 => 3, + gpioa::PA3 => 4, + gpioc::PC0 => 6, + gpioc::PC1 => 7, + gpioc::PC2 => 8, + gpioc::PC3 => 9, ); #[cfg(any(feature = "stm32f303x6", feature = "stm32f303x8"))] adc_pins!(ADC1, - gpiob::PB0 => 11_u8, - gpiob::PB1 => 12_u8, - gpiob::PB13 => 13_u8, + gpiob::PB0 => 11, + gpiob::PB1 => 12, + gpiob::PB13 => 13, ); #[cfg(any( @@ -182,8 +182,8 @@ adc_pins!(ADC1, feature = "stm32f303xe", ))] adc_pins!(ADC1, - gpiof::PF4 => 5_u8, - gpiof::PF2 => 10_u8, + gpiof::PF4 => 5, + gpiof::PF2 => 10, ); // # ADC2 Pin/Channel mapping @@ -191,24 +191,24 @@ adc_pins!(ADC1, #[cfg(feature = "stm32f303")] adc_pins!(ADC2, - gpioa::PA4 => 1_u8, - gpioa::PA5 => 2_u8, - gpioa::PA6 => 3_u8, - gpioa::PA7 => 4_u8, - gpioc::PC4 => 5_u8, - gpioc::PC5 => 11_u8, - gpiob::PB2 => 12_u8, - gpioc::PC0 => 6_u8, - gpioc::PC1 => 7_u8, - gpioc::PC2 => 8_u8, - gpioc::PC3 => 9_u8, + gpioa::PA4 => 1, + gpioa::PA5 => 2, + gpioa::PA6 => 3, + gpioa::PA7 => 4, + gpioc::PC4 => 5, + gpioc::PC5 => 11, + gpiob::PB2 => 12, + gpioc::PC0 => 6, + gpioc::PC1 => 7, + gpioc::PC2 => 8, + gpioc::PC3 => 9, ); #[cfg(any(feature = "stm32f303x6", feature = "stm32f303x8"))] adc_pins!(ADC2, - gpiob::PB12 => 13_u8, - gpiob::PB14 => 14_u8, - gpiob::PB15 => 15_u8, + gpiob::PB12 => 13, + gpiob::PB14 => 14, + gpiob::PB15 => 15, ); #[cfg(any( @@ -218,7 +218,7 @@ adc_pins!(ADC2, feature = "stm32f303xe", ))] adc_pins!(ADC2, - gpiof::PF2 => 10_u8, + gpiof::PF2 => 10, ); // # ADC3 Pin/Channel mapping @@ -231,22 +231,22 @@ adc_pins!(ADC2, feature = "stm32f303xe", ))] adc_pins!(ADC3, - gpiob::PB1 => 1_u8, - gpioe::PE9 => 2_u8, - gpioe::PE13 => 3_u8, - gpiob::PB13 => 5_u8, - gpiob::PB0 => 12_u8, - gpioe::PE7 => 13_u8, - gpioe::PE10 => 14_u8, - gpioe::PE11 => 15_u8, - gpioe::PE12 => 16_u8, + gpiob::PB1 => 1, + gpioe::PE9 => 2, + gpioe::PE13 => 3, + gpiob::PB13 => 5, + gpiob::PB0 => 12, + gpioe::PE7 => 13, + gpioe::PE10 => 14, + gpioe::PE11 => 15, + gpioe::PE12 => 16, // Shared channels (i.e. ADC34_INx) - gpioe::PE8 => 6_u8, - gpiod::PD10 => 7_u8, - gpiod::PD11 => 8_u8, - gpiod::PD12 => 9_u8, - gpiod::PD13 => 10_u8, - gpiod::PD14 => 11_u8, + gpioe::PE8 => 6, + gpiod::PD10 => 7, + gpiod::PD11 => 8, + gpiod::PD12 => 9, + gpiod::PD13 => 10, + gpiod::PD14 => 11, ); // # ADC4 Pin/Channel mapping @@ -259,20 +259,20 @@ adc_pins!(ADC3, feature = "stm32f303xe", ))] adc_pins!(ADC4, - gpioe::PE14 => 1_u8, - gpioe::PE15 => 2_u8, - gpiob::PB12 => 3_u8, - gpiob::PB14 => 4_u8, - gpiob::PB15 => 5_u8, - gpiob::PB8 => 12_u8, - gpiob::PB9 => 13_u8, + gpioe::PE14 => 1, + gpioe::PE15 => 2, + gpiob::PB12 => 3, + gpiob::PB14 => 4, + gpiob::PB15 => 5, + gpiob::PB8 => 12, + gpiob::PB9 => 13, // Shared channels (i.e. ADC34_INx) - gpioe::PE8 => 6_u8, - gpiod::PD10 => 7_u8, - gpiod::PD11 => 8_u8, - gpiod::PD12 => 9_u8, - gpiod::PD13 => 10_u8, - gpiod::PD14 => 11_u8, + gpioe::PE8 => 6, + gpiod::PD10 => 7, + gpiod::PD11 => 8, + gpiod::PD12 => 9, + gpiod::PD13 => 10, + gpiod::PD14 => 11, ); // Abstract implementation of ADC functionality From 3258be62932d3a6a696f62d86f8c11ad810cd5c5 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 14:58:09 +0200 Subject: [PATCH 26/32] resorted pin => channel mapping --- src/adc.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index fb6aecddb..7d77c5f8a 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -196,12 +196,12 @@ adc_pins!(ADC2, gpioa::PA6 => 3, gpioa::PA7 => 4, gpioc::PC4 => 5, - gpioc::PC5 => 11, - gpiob::PB2 => 12, gpioc::PC0 => 6, gpioc::PC1 => 7, gpioc::PC2 => 8, gpioc::PC3 => 9, + gpioc::PC5 => 11, + gpiob::PB2 => 12, ); #[cfg(any(feature = "stm32f303x6", feature = "stm32f303x8"))] @@ -234,19 +234,19 @@ adc_pins!(ADC3, gpiob::PB1 => 1, gpioe::PE9 => 2, gpioe::PE13 => 3, + // There is no ADC3 Channel #4 gpiob::PB13 => 5, - gpiob::PB0 => 12, - gpioe::PE7 => 13, - gpioe::PE10 => 14, - gpioe::PE11 => 15, - gpioe::PE12 => 16, - // Shared channels (i.e. ADC34_INx) gpioe::PE8 => 6, gpiod::PD10 => 7, gpiod::PD11 => 8, gpiod::PD12 => 9, gpiod::PD13 => 10, gpiod::PD14 => 11, + gpiob::PB0 => 12, + gpioe::PE7 => 13, + gpioe::PE10 => 14, + gpioe::PE11 => 15, + gpioe::PE12 => 16, ); // # ADC4 Pin/Channel mapping @@ -264,15 +264,14 @@ adc_pins!(ADC4, gpiob::PB12 => 3, gpiob::PB14 => 4, gpiob::PB15 => 5, - gpiob::PB8 => 12, - gpiob::PB9 => 13, - // Shared channels (i.e. ADC34_INx) gpioe::PE8 => 6, gpiod::PD10 => 7, gpiod::PD11 => 8, gpiod::PD12 => 9, gpiod::PD13 => 10, gpiod::PD14 => 11, + gpiob::PD8 => 12, + gpiob::PD9 => 13, ); // Abstract implementation of ADC functionality From a9832b98a8794424eae0fad84e3c1af930f6d5a2 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 15:12:56 +0200 Subject: [PATCH 27/32] panic on wrong settings instead of returning None obj --- examples/adc.rs | 5 +---- src/adc.rs | 26 +++++++++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index ada419d59..f1d118324 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -27,10 +27,7 @@ fn main() -> ! { &mut rcc.ahb, adc::CKMODE::default(), clocks, - // If everything is set up correctly, we'll get `Some(adc1)`. to access it, we need to `unwrap` - // it. If there was an error, we'll get `None` and this unwrap will `panic!`. - ) - .unwrap(); + ); // Set up pin PA0 as analog pin. // This pin is connected to the user button on the stm32f3discovery board. diff --git a/src/adc.rs b/src/adc.rs index 7d77c5f8a..726204e0a 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -270,8 +270,8 @@ adc_pins!(ADC4, gpiod::PD12 => 9, gpiod::PD13 => 10, gpiod::PD14 => 11, - gpiob::PD8 => 12, - gpiob::PD9 => 13, + gpiod::PD8 => 12, + gpiod::PD9 => 13, ); // Abstract implementation of ADC functionality @@ -287,13 +287,19 @@ macro_rules! adc_hal { /// Init a new ADC /// /// Enables the clock, performs a calibration and enables the ADC + /// + /// # Panics + /// If one of the following occurs: + /// * the clocksetting is not well defined. + /// * the clock was already enabled with a different setting + /// pub fn $adcx( rb: $ADC, adc_common : &mut $ADC_COMMON, ahb: &mut AHB, ckmode: CKMODE, clocks: Clocks, - ) -> Option { + ) -> Self { let mut this_adc = Self { rb, clocks, @@ -301,10 +307,10 @@ macro_rules! adc_hal { operation_mode: None, }; if !(this_adc.clocks_welldefined(clocks)) { - return None; + panic!("Clock settings not well defined"); } if !(this_adc.enable_clock(ahb, adc_common)){ - return None; + panic!("Clock already enabled with a different setting"); } this_adc.set_align(Align::default()); this_adc.calibrate(); @@ -313,13 +319,19 @@ macro_rules! adc_hal { // bit is cleared by hardware this_adc.wait_adc_clk_cycles(4); this_adc.enable(); - Some(this_adc) + + this_adc } /// Software can use CKMODE::SYNCDIV1 only if /// hclk and sysclk are the same. (see reference manual 15.3.3) fn clocks_welldefined(&self, clocks: Clocks) -> bool { - !(self.ckmode == CKMODE::SYNCDIV1 && !(clocks.hclk().0 == clocks.sysclk().0)) + if (self.ckmode == CKMODE::SYNCDIV1) + { + clocks.hclk().0 == clocks.sysclk().0 + } else { + true + } } /// sets up adc in one shot mode for a single channel From 153b51a1979701badf409c3b286de0359e1b3d86 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 15:38:53 +0200 Subject: [PATCH 28/32] Use descriptive methods on register calls Note: In fn calibrate, `while self.rb.cr.read().adcal().is_not_complete() {}` would be better than `while !self.rb.cr.read().adcal().is_complete() {}` However this seems to lack in our version of the PAC In fn disable, `self.rb.cr.modify(|_, w| w.aden().disable());` would be better than `self.rb.cr.modify(|_, w| w.aden().clear_bit());` However this seems to lack in our version of the PAC --- src/adc.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 726204e0a..f302bc2ad 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -336,27 +336,31 @@ macro_rules! adc_hal { /// sets up adc in one shot mode for a single channel pub fn setup_oneshot(&mut self) { - // stop and clear overrun events - self.rb.cr.modify(|_, w| w.adstp().set_bit()); - self.rb.isr.modify(|_, w| w.ovr().clear_bit()); + self.rb.cr.modify(|_, w| w.adstp().stop()); + self.rb.isr.modify(|_, w| w.ovr().clear()); self.rb.cfgr.modify(|_, w| w - .cont().clear_bit() - .ovrmod().clear_bit() + .cont().single() + .ovrmod().preserve() ); - self.rb.sqr1.modify(|_, w| w.l().bits(0)); + self.set_sequence_len(1); self.operation_mode = Some(OperationMode::OneShot); } + fn set_sequence_len(&mut self, len: u8) { + debug_assert!(len <= 16); + self.rb.sqr1.modify(|_, w| w.l().bits(len - 1)); + } + fn set_align(&self, align: Align) { self.rb.cfgr.modify(|_, w| w.align().bit(align.bitvalue())); } fn enable(&mut self) { - self.rb.cr.modify(|_, w| w.aden().set_bit()); - while self.rb.isr.read().adrdy().bit_is_clear() {} + self.rb.cr.modify(|_, w| w.aden().enable()); + while self.rb.isr.read().adrdy().is_not_ready() {} } fn disable(&mut self) { @@ -374,11 +378,10 @@ macro_rules! adc_hal { self.disable(); self.rb.cr.modify(|_, w| w - // NOTE: needs to be adopted if implementing differential input - .adcaldif().clear_bit() - .adcal() .set_bit()); + .adcaldif().single_ended() + .adcal() .calibration()); - while self.rb.cr.read().adcal().bit_is_set() {} + while !self.rb.cr.read().adcal().is_complete() {} } @@ -407,7 +410,7 @@ macro_rules! adc_hal { self.set_chan_smps(chan, SampleTime::default()); self.select_single_chan(chan); - self.rb.cr.modify(|_, w| w.adstart().set_bit()); + self.rb.cr.modify(|_, w| w.adstart().start()); while self.rb.isr.read().eos().is_not_complete() {} self.rb.isr.modify(|_, w| w.eos().clear()); return self.rb.dr.read().rdata().bits(); From 776c5fde933d507a614a82401c2dca486f53e4e3 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 15:52:12 +0200 Subject: [PATCH 29/32] refactor CkMode and Align --- examples/adc.rs | 2 +- src/adc.rs | 46 +++++++++++++++++++++------------------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index f1d118324..8edcc65a8 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -25,7 +25,7 @@ fn main() -> ! { // correctly. &mut dp.ADC1_2, &mut rcc.ahb, - adc::CKMODE::default(), + adc::CkMode::default(), clocks, ); diff --git a/src/adc.rs b/src/adc.rs index f302bc2ad..354656484 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -15,7 +15,7 @@ use crate::{ gpio::{gpioa, gpiob, gpioc}, pac::{ADC1, ADC1_2, ADC2}, }; -use stm32f3::stm32f303::adc1_2::ccr::CKMODE_A; +use stm32f3::stm32f303::{adc1::cfgr::ALIGN_A, adc1_2::ccr::CKMODE_A}; const MAX_ADVREGEN_STARTUP_US: u32 = 10; #[cfg(any( @@ -35,7 +35,7 @@ use crate::{ pub struct Adc { pub rb: ADC, clocks: Clocks, - ckmode: CKMODE, + ckmode: CkMode, operation_mode: Option, } @@ -88,30 +88,29 @@ pub enum OperationMode { } #[derive(Clone, Copy, PartialEq)] -/// ADC CKMODE +/// ADC CkMode // TODO: Add ASYNCHRONOUS mode -pub enum CKMODE { +pub enum CkMode { // ASYNCHRONOUS = 0, SYNCDIV1 = 1, SYNCDIV2 = 2, SYNCDIV4 = 4, } -impl Default for CKMODE { +impl Default for CkMode { fn default() -> Self { - CKMODE::SYNCDIV2 + CkMode::SYNCDIV2 } } // ADC3_2 returns a pointer to a adc1_2 type, so this from is ok for both. -#[cfg(feature = "stm32f303")] -impl From for CKMODE_A { - fn from(ckmode: CKMODE) -> Self { +impl From for CKMODE_A { + fn from(ckmode: CkMode) -> Self { match ckmode { - //CKMODE::ASYNCHRONOUS => CKMODE_A::ASYNCHRONOUS, - CKMODE::SYNCDIV1 => CKMODE_A::SYNCDIV1, - CKMODE::SYNCDIV2 => CKMODE_A::SYNCDIV2, - CKMODE::SYNCDIV4 => CKMODE_A::SYNCDIV4, + //CkMode::ASYNCHRONOUS => CKMODE_A::ASYNCHRONOUS, + CkMode::SYNCDIV1 => CKMODE_A::SYNCDIV1, + CkMode::SYNCDIV2 => CKMODE_A::SYNCDIV2, + CkMode::SYNCDIV4 => CKMODE_A::SYNCDIV4, } } } @@ -130,12 +129,11 @@ impl Default for Align { } } -impl Align { - /// Conversion to bits for ALIGN in ADCx_CFGR - fn bitvalue(&self) -> bool { - match self { - Align::Right => false, - Align::Left => true, +impl From for ALIGN_A { + fn from(align: Align) -> ALIGN_A { + match align { + Align::Right => ALIGN_A::RIGHT, + Align::Left => ALIGN_A::LEFT, } } } @@ -297,7 +295,7 @@ macro_rules! adc_hal { rb: $ADC, adc_common : &mut $ADC_COMMON, ahb: &mut AHB, - ckmode: CKMODE, + ckmode: CkMode, clocks: Clocks, ) -> Self { let mut this_adc = Self { @@ -323,10 +321,10 @@ macro_rules! adc_hal { this_adc } - /// Software can use CKMODE::SYNCDIV1 only if + /// Software can use CkMode::SYNCDIV1 only if /// hclk and sysclk are the same. (see reference manual 15.3.3) fn clocks_welldefined(&self, clocks: Clocks) -> bool { - if (self.ckmode == CKMODE::SYNCDIV1) + if (self.ckmode == CkMode::SYNCDIV1) { clocks.hclk().0 == clocks.sysclk().0 } else { @@ -355,7 +353,7 @@ macro_rules! adc_hal { } fn set_align(&self, align: Align) { - self.rb.cfgr.modify(|_, w| w.align().bit(align.bitvalue())); + self.rb.cfgr.modify(|_, w| w.align().variant(align.into())); } fn enable(&mut self) { @@ -367,7 +365,6 @@ macro_rules! adc_hal { self.rb.cr.modify(|_, w| w.aden().clear_bit()); } - /// Calibrate according to 15.3.8 in the Reference Manual fn calibrate(&mut self) { if !self.rb.cr.read().advregen().is_enabled() { @@ -384,7 +381,6 @@ macro_rules! adc_hal { while !self.rb.cr.read().adcal().is_complete() {} } - fn wait_adc_clk_cycles(&self, cycles: u32) { let adc_clk_cycle = self.clocks.hclk().0 / (self.ckmode as u32); asm::delay(adc_clk_cycle * cycles); From 0c472c72b43fd05595bf1258832a75b27042a054 Mon Sep 17 00:00:00 2001 From: bboger Date: Sat, 18 Jul 2020 15:53:32 +0200 Subject: [PATCH 30/32] -newlines and indents --- src/adc.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 354656484..fdeabf9dd 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -390,14 +390,13 @@ macro_rules! adc_hal { // need to go through intermediate first self.rb.cr.modify(|_, w| w.advregen().intermediate()); self.rb.cr.modify(|_, w| w.advregen().enabled()); - } /// wait for the advregen to startup. /// /// This is based on the MAX_ADVREGEN_STARTUP_US of the device. fn wait_advregen_startup(&self) { - asm::delay((MAX_ADVREGEN_STARTUP_US * 1_000_000) / self.clocks.sysclk().0); + asm::delay((MAX_ADVREGEN_STARTUP_US * 1_000_000) / self.clocks.sysclk().0); } /// busy ADC read From ff7a430d9261cad741c4c49b2e75731053b30546 Mon Sep 17 00:00:00 2001 From: bboger Date: Sun, 19 Jul 2020 12:57:18 +0200 Subject: [PATCH 31/32] fix some errors from the review --- src/adc.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index fdeabf9dd..c66263fb8 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -324,8 +324,7 @@ macro_rules! adc_hal { /// Software can use CkMode::SYNCDIV1 only if /// hclk and sysclk are the same. (see reference manual 15.3.3) fn clocks_welldefined(&self, clocks: Clocks) -> bool { - if (self.ckmode == CkMode::SYNCDIV1) - { + if (self.ckmode == CkMode::SYNCDIV1) { clocks.hclk().0 == clocks.sysclk().0 } else { true @@ -348,7 +347,7 @@ macro_rules! adc_hal { } fn set_sequence_len(&mut self, len: u8) { - debug_assert!(len <= 16); + assert!(len - 1 < 16, "ADC sequence length must be in 1..=16"); self.rb.sqr1.modify(|_, w| w.l().bits(len - 1)); } @@ -362,7 +361,7 @@ macro_rules! adc_hal { } fn disable(&mut self) { - self.rb.cr.modify(|_, w| w.aden().clear_bit()); + self.rb.cr.modify(|_, w| w.addis().disable()); } /// Calibrate according to 15.3.8 in the Reference Manual @@ -378,7 +377,7 @@ macro_rules! adc_hal { .adcaldif().single_ended() .adcal() .calibration()); - while !self.rb.cr.read().adcal().is_complete() {} + while self.rb.cr.read().adcal().is_calibration() {} } fn wait_adc_clk_cycles(&self, cycles: u32) { From 26610c643ad5710f12ecd8b17269160cd84280a2 Mon Sep 17 00:00:00 2001 From: bboger Date: Sun, 19 Jul 2020 13:04:30 +0200 Subject: [PATCH 32/32] Change default Sampletime to T_1 This is the reset value of the ADC. Now when running the example it is possible to get values other than 0 and 4095 by pressing the user button shortly. --- src/adc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index c66263fb8..93013970d 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -45,8 +45,6 @@ pub struct Adc { /// There is always an overhead of 13 ADC clock cycles. /// E.g. For Sampletime T_19 the total conversion time (in ADC clock cycles) is /// 13 + 19 = 32 ADC Clock Cycles -// TODO: there are boundaries on how this can be set depending on the hardware. -// Check them and implement a sample time setting mechanism. pub enum SampleTime { T_1, T_2, @@ -59,8 +57,9 @@ pub enum SampleTime { } impl Default for SampleTime { + /// T_1 is also the reset value. fn default() -> Self { - SampleTime::T_19 + SampleTime::T_1 } } @@ -426,6 +425,7 @@ macro_rules! adc_hal { } /// Note: only allowed when ADSTART = 0 + // TODO: there are boundaries on how this can be set depending on the hardware. fn set_chan_smps(&self, chan: u8, smp: SampleTime) { match chan { 1 => self.rb.smpr1.modify(|_, w| w.smp1().bits(smp.bitcode())),