diff options
| author | xoviat <[email protected]> | 2023-09-05 16:50:33 -0500 |
|---|---|---|
| committer | xoviat <[email protected]> | 2023-09-05 16:50:33 -0500 |
| commit | 7e18e5e0c123027f15dd7a75706b9950da9aed0c (patch) | |
| tree | a4c42d0b7197056d9e9e1797f120784fbdf3be75 | |
| parent | e2f8bf19ea55341f274c6cb9c3650a96f4a09fe4 (diff) | |
| parent | 49ba9c3da2b6929c5ec1fb17d8c43c271a70eb34 (diff) | |
Merge branch 'stm32g4_adc' of https://github.com/daehyeok/embassy into adc-g4
| -rw-r--r-- | embassy-stm32/Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v4.rs | 193 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/g4.rs | 54 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 6 | ||||
| -rw-r--r-- | examples/stm32g4/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc.rs | 41 |
6 files changed, 192 insertions, 109 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d169107df..712996f23 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -58,7 +58,7 @@ sdio-host = "0.5.0" | |||
| 58 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } | 58 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } |
| 59 | critical-section = "1.1" | 59 | critical-section = "1.1" |
| 60 | atomic-polyfill = "1.0.1" | 60 | atomic-polyfill = "1.0.1" |
| 61 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3" } | 61 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7e2310f49fa123fbc3225b91be73522b212703f0" } |
| 62 | vcell = "0.1.3" | 62 | vcell = "0.1.3" |
| 63 | bxcan = "0.7.0" | 63 | bxcan = "0.7.0" |
| 64 | nb = "1.0.0" | 64 | nb = "1.0.0" |
| @@ -70,6 +70,7 @@ embedded-io-async = { version = "0.5.0", optional = true } | |||
| 70 | chrono = { version = "^0.4", default-features = false, optional = true} | 70 | chrono = { version = "^0.4", default-features = false, optional = true} |
| 71 | bit_field = "0.10.2" | 71 | bit_field = "0.10.2" |
| 72 | document-features = "0.2.7" | 72 | document-features = "0.2.7" |
| 73 | paste = "1.0" | ||
| 73 | 74 | ||
| 74 | [dev-dependencies] | 75 | [dev-dependencies] |
| 75 | critical-section = { version = "1.1", features = ["std"] } | 76 | critical-section = { version = "1.1", features = ["std"] } |
| @@ -77,7 +78,7 @@ critical-section = { version = "1.1", features = ["std"] } | |||
| 77 | [build-dependencies] | 78 | [build-dependencies] |
| 78 | proc-macro2 = "1.0.36" | 79 | proc-macro2 = "1.0.36" |
| 79 | quote = "1.0.15" | 80 | quote = "1.0.15" |
| 80 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3", default-features = false, features = ["metadata"]} | 81 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7e2310f49fa123fbc3225b91be73522b212703f0", default-features = false, features = ["metadata"]} |
| 81 | 82 | ||
| 82 | [features] | 83 | [features] |
| 83 | default = ["rt"] | 84 | default = ["rt"] |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 64d0f0c75..d03f2550d 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | use core::sync::atomic::{AtomicU8, Ordering}; | 1 | use core::sync::atomic::{AtomicU8, Ordering}; |
| 2 | 2 | ||
| 3 | use embedded_hal_02::blocking::delay::DelayUs; | 3 | use embedded_hal_02::blocking::delay::DelayUs; |
| 4 | #[allow(unused)] | ||
| 4 | use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; | 5 | use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; |
| 5 | use pac::adccommon::vals::Presc; | 6 | use pac::adccommon::vals::Presc; |
| 7 | use paste::paste; | ||
| 6 | 8 | ||
| 7 | use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; | 9 | use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; |
| 8 | use crate::time::Hertz; | 10 | use crate::time::Hertz; |
| @@ -13,12 +15,31 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 13 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 15 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 14 | pub const VREF_CALIB_MV: u32 = 3300; | 16 | pub const VREF_CALIB_MV: u32 = 3300; |
| 15 | 17 | ||
| 16 | // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | 18 | /// Max single ADC operation clock frequency |
| 19 | #[cfg(stm32g4)] | ||
| 20 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | ||
| 21 | #[cfg(stm32h7)] | ||
| 22 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | ||
| 23 | |||
| 24 | #[cfg(stm32g4)] | ||
| 25 | const VREF_CHANNEL: u8 = 18; | ||
| 26 | #[cfg(stm32g4)] | ||
| 27 | const TEMP_CHANNEL: u8 = 16; | ||
| 28 | |||
| 29 | #[cfg(stm32h7)] | ||
| 30 | const VREF_CHANNEL: u8 = 19; | ||
| 31 | #[cfg(stm32h7)] | ||
| 32 | const TEMP_CHANNEL: u8 = 18; | ||
| 33 | |||
| 34 | // TODO this should be 14 for H7a/b/35 | ||
| 35 | const VBAT_CHANNEL: u8 = 17; | ||
| 36 | |||
| 37 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | ||
| 17 | pub struct VrefInt; | 38 | pub struct VrefInt; |
| 18 | impl<T: Instance> InternalChannel<T> for VrefInt {} | 39 | impl<T: Instance> InternalChannel<T> for VrefInt {} |
| 19 | impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt { | 40 | impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt { |
| 20 | fn channel(&self) -> u8 { | 41 | fn channel(&self) -> u8 { |
| 21 | 19 | 42 | VREF_CHANNEL |
| 22 | } | 43 | } |
| 23 | } | 44 | } |
| 24 | 45 | ||
| @@ -26,7 +47,7 @@ pub struct Temperature; | |||
| 26 | impl<T: Instance> InternalChannel<T> for Temperature {} | 47 | impl<T: Instance> InternalChannel<T> for Temperature {} |
| 27 | impl<T: Instance> super::sealed::InternalChannel<T> for Temperature { | 48 | impl<T: Instance> super::sealed::InternalChannel<T> for Temperature { |
| 28 | fn channel(&self) -> u8 { | 49 | fn channel(&self) -> u8 { |
| 29 | 18 | 50 | TEMP_CHANNEL |
| 30 | } | 51 | } |
| 31 | } | 52 | } |
| 32 | 53 | ||
| @@ -34,59 +55,20 @@ pub struct Vbat; | |||
| 34 | impl<T: Instance> InternalChannel<T> for Vbat {} | 55 | impl<T: Instance> InternalChannel<T> for Vbat {} |
| 35 | impl<T: Instance> super::sealed::InternalChannel<T> for Vbat { | 56 | impl<T: Instance> super::sealed::InternalChannel<T> for Vbat { |
| 36 | fn channel(&self) -> u8 { | 57 | fn channel(&self) -> u8 { |
| 37 | // TODO this should be 14 for H7a/b/35 | 58 | VBAT_CHANNEL |
| 38 | 17 | ||
| 39 | } | 59 | } |
| 40 | } | 60 | } |
| 41 | 61 | ||
| 42 | static ADC12_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0); | 62 | static ADC12_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0); |
| 63 | #[cfg(any(stm32g4x3, stm32g4x4))] | ||
| 64 | static ADC345_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0); | ||
| 43 | 65 | ||
| 44 | #[cfg(stm32h7)] | 66 | macro_rules! rcc_peripheral { |
| 45 | foreach_peripheral!( | 67 | ($adc_name:ident, $freqs:ident, $ahb:ident, $reg:ident $(, $counter:ident )? ) => { |
| 46 | (adc, ADC1) => { | 68 | impl crate::rcc::sealed::RccPeripheral for crate::peripherals::$adc_name { |
| 47 | impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC1 { | ||
| 48 | fn frequency() -> crate::time::Hertz { | ||
| 49 | critical_section::with(|_| { | ||
| 50 | match unsafe { crate::rcc::get_freqs() }.adc { | ||
| 51 | Some(ck) => ck, | ||
| 52 | None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") | ||
| 53 | } | ||
| 54 | }) | ||
| 55 | } | ||
| 56 | |||
| 57 | fn enable() { | ||
| 58 | critical_section::with(|_| { | ||
| 59 | crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) | ||
| 60 | }); | ||
| 61 | ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); | ||
| 62 | } | ||
| 63 | |||
| 64 | fn disable() { | ||
| 65 | if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { | ||
| 66 | critical_section::with(|_| { | ||
| 67 | crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); | ||
| 68 | }) | ||
| 69 | } | ||
| 70 | ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst); | ||
| 71 | } | ||
| 72 | |||
| 73 | fn reset() { | ||
| 74 | if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { | ||
| 75 | critical_section::with(|_| { | ||
| 76 | crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); | ||
| 77 | crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); | ||
| 78 | }); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | impl crate::rcc::RccPeripheral for crate::peripherals::ADC1 {} | ||
| 84 | }; | ||
| 85 | (adc, ADC2) => { | ||
| 86 | impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC2 { | ||
| 87 | fn frequency() -> crate::time::Hertz { | 69 | fn frequency() -> crate::time::Hertz { |
| 88 | critical_section::with(|_| { | 70 | critical_section::with(|_| { |
| 89 | match unsafe { crate::rcc::get_freqs() }.adc { | 71 | match unsafe { crate::rcc::get_freqs() }.$freqs { |
| 90 | Some(ck) => ck, | 72 | Some(ck) => ck, |
| 91 | None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") | 73 | None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") |
| 92 | } | 74 | } |
| @@ -95,65 +77,57 @@ foreach_peripheral!( | |||
| 95 | 77 | ||
| 96 | fn enable() { | 78 | fn enable() { |
| 97 | critical_section::with(|_| { | 79 | critical_section::with(|_| { |
| 98 | crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) | 80 | paste!{crate::pac::RCC.[< $ahb enr >]().modify(|w| w.[< set_ $reg en >](true))} |
| 99 | }); | 81 | }); |
| 100 | ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); | 82 | $ ( $counter.fetch_add(1, Ordering::SeqCst); )? |
| 101 | } | 83 | } |
| 102 | 84 | ||
| 103 | fn disable() { | 85 | fn disable() { |
| 104 | if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { | 86 | $ ( if $counter.load(Ordering::SeqCst) == 1 )? { |
| 105 | critical_section::with(|_| { | 87 | critical_section::with(|_| { |
| 106 | crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); | 88 | paste!{crate::pac::RCC.[< $ahb enr >]().modify(|w| w.[< set_ $reg en >](false))} |
| 107 | }) | 89 | }) |
| 108 | } | 90 | } |
| 109 | ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst); | 91 | $ ( $counter.fetch_sub(1, Ordering::SeqCst); )? |
| 110 | } | 92 | } |
| 111 | 93 | ||
| 112 | fn reset() { | 94 | fn reset() { |
| 113 | if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { | 95 | $ ( if $counter.load(Ordering::SeqCst) == 1 )? { |
| 114 | critical_section::with(|_| { | 96 | critical_section::with(|_| { |
| 115 | crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); | 97 | paste!{crate::pac::RCC.[< $ahb rstr >]().modify(|w| w.[< set_ $reg rst >](true))} |
| 116 | crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); | 98 | paste!{crate::pac::RCC.[< $ahb rstr >]().modify(|w| w.[< set_ $reg rst >](false))} |
| 117 | }); | 99 | }); |
| 118 | } | 100 | } |
| 119 | } | 101 | } |
| 120 | } | 102 | } |
| 121 | 103 | ||
| 122 | impl crate::rcc::RccPeripheral for crate::peripherals::ADC2 {} | 104 | impl crate::rcc::RccPeripheral for crate::peripherals::$adc_name {} |
| 123 | }; | 105 | }; |
| 124 | (adc, ADC3) => { | 106 | } |
| 125 | impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC3 { | ||
| 126 | fn frequency() -> crate::time::Hertz { | ||
| 127 | critical_section::with(|_| { | ||
| 128 | match unsafe { crate::rcc::get_freqs() }.adc { | ||
| 129 | Some(ck) => ck, | ||
| 130 | None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") | ||
| 131 | } | ||
| 132 | }) | ||
| 133 | } | ||
| 134 | 107 | ||
| 135 | fn enable() { | 108 | #[cfg(stm32g4)] |
| 136 | critical_section::with(|_| { | 109 | foreach_peripheral!( |
| 137 | crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true)) | 110 | (adc, ADC1) => { rcc_peripheral!(ADC1, adc12, ahb2, adc12, ADC12_ENABLE_COUNTER); }; |
| 138 | }); | 111 | (adc, ADC2) => { rcc_peripheral!(ADC2, adc12, ahb2, adc12, ADC12_ENABLE_COUNTER); }; |
| 139 | } | 112 | ); |
| 140 | 113 | ||
| 141 | fn disable() { | 114 | #[cfg(stm32g4x1)] |
| 142 | critical_section::with(|_| { | 115 | foreach_peripheral!( |
| 143 | crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false)); | 116 | (adc, ADC3) => { rcc_peripheral!(ADC3, adc345, ahb2, adc345); }; |
| 144 | }) | 117 | ); |
| 145 | } | ||
| 146 | 118 | ||
| 147 | fn reset() { | 119 | #[cfg(any(stm32g4x3, stm32g4x4))] |
| 148 | critical_section::with(|_| { | 120 | foreach_peripheral!( |
| 149 | crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true)); | 121 | (adc, ADC3) => { rcc_peripheral!(ADC3, adc345, ahb2, adc345, ADC345_ENABLE_COUNTER); }; |
| 150 | crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false)); | 122 | (adc, ADC4) => { rcc_peripheral!(ADC4, adc345, ahb2, adc345, ADC345_ENABLE_COUNTER); }; |
| 151 | }); | 123 | (adc, ADC5) => { rcc_peripheral!(ADC5, adc345, ahb2, adc345, ADC345_ENABLE_COUNTER); }; |
| 152 | } | 124 | ); |
| 153 | } | ||
| 154 | 125 | ||
| 155 | impl crate::rcc::RccPeripheral for crate::peripherals::ADC3 {} | 126 | #[cfg(stm32h7)] |
| 156 | }; | 127 | foreach_peripheral!( |
| 128 | (adc, ADC1) => { rcc_peripheral!(ADC1, adc, ahb1, adc12, ADC12_ENABLE_COUNTER); }; | ||
| 129 | (adc, ADC2) => { rcc_peripheral!(ADC2, adc, ahb1, adc12, ADC12_ENABLE_COUNTER); }; | ||
| 130 | (adc, ADC3) => { rcc_peripheral!(ADC3, adc, ahb4, adc3); }; | ||
| 157 | ); | 131 | ); |
| 158 | 132 | ||
| 159 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 133 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, |
| @@ -176,7 +150,7 @@ enum Prescaler { | |||
| 176 | 150 | ||
| 177 | impl Prescaler { | 151 | impl Prescaler { |
| 178 | fn from_ker_ck(frequency: Hertz) -> Self { | 152 | fn from_ker_ck(frequency: Hertz) -> Self { |
| 179 | let raw_prescaler = frequency.0 / 50_000_000; | 153 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; |
| 180 | match raw_prescaler { | 154 | match raw_prescaler { |
| 181 | 0 => Self::NotDivided, | 155 | 0 => Self::NotDivided, |
| 182 | 1 => Self::DividedBy2, | 156 | 1 => Self::DividedBy2, |
| @@ -237,20 +211,23 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 237 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 211 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); |
| 238 | info!("ADC frequency set to {} Hz", frequency.0); | 212 | info!("ADC frequency set to {} Hz", frequency.0); |
| 239 | 213 | ||
| 240 | if frequency > Hertz::mhz(50) { | 214 | if frequency > MAX_ADC_CLK_FREQ { |
| 241 | panic!("Maximal allowed frequency for the ADC is 50 MHz and it varies with different packages, refer to ST docs for more information."); | 215 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); |
| 242 | } | 216 | } |
| 243 | let boost = if frequency < Hertz::khz(6_250) { | ||
| 244 | Boost::LT6_25 | ||
| 245 | } else if frequency < Hertz::khz(12_500) { | ||
| 246 | Boost::LT12_5 | ||
| 247 | } else if frequency < Hertz::mhz(25) { | ||
| 248 | Boost::LT25 | ||
| 249 | } else { | ||
| 250 | Boost::LT50 | ||
| 251 | }; | ||
| 252 | T::regs().cr().modify(|w| w.set_boost(boost)); | ||
| 253 | 217 | ||
| 218 | #[cfg(stm32h7)] | ||
| 219 | { | ||
| 220 | let boost = if frequency < Hertz::khz(6_250) { | ||
| 221 | Boost::LT6_25 | ||
| 222 | } else if frequency < Hertz::khz(12_500) { | ||
| 223 | Boost::LT12_5 | ||
| 224 | } else if frequency < Hertz::mhz(25) { | ||
| 225 | Boost::LT25 | ||
| 226 | } else { | ||
| 227 | Boost::LT50 | ||
| 228 | }; | ||
| 229 | T::regs().cr().modify(|w| w.set_boost(boost)); | ||
| 230 | } | ||
| 254 | let mut s = Self { | 231 | let mut s = Self { |
| 255 | adc, | 232 | adc, |
| 256 | sample_time: Default::default(), | 233 | sample_time: Default::default(), |
| @@ -379,10 +356,14 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 379 | // Configure channel | 356 | // Configure channel |
| 380 | Self::set_channel_sample_time(channel, self.sample_time); | 357 | Self::set_channel_sample_time(channel, self.sample_time); |
| 381 | 358 | ||
| 382 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | 359 | #[cfg(stm32h7)] |
| 383 | T::regs() | 360 | { |
| 384 | .pcsel() | 361 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); |
| 385 | .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | 362 | T::regs() |
| 363 | .pcsel() | ||
| 364 | .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 365 | } | ||
| 366 | |||
| 386 | T::regs().sqr1().write(|reg| { | 367 | T::regs().sqr1().write(|reg| { |
| 387 | reg.set_sq(0, channel); | 368 | reg.set_sq(0, channel); |
| 388 | reg.set_l(0); | 369 | reg.set_l(0); |
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 3b044cd11..4c95bb154 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use stm32_metapac::flash::vals::Latency; | 1 | use stm32_metapac::flash::vals::Latency; |
| 2 | use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; | 2 | use stm32_metapac::rcc::vals::{Adcsel, Hpre, Pllsrc, Ppre, Sw}; |
| 3 | use stm32_metapac::FLASH; | 3 | use stm32_metapac::FLASH; |
| 4 | 4 | ||
| 5 | pub use super::bus::{AHBPrescaler, APBPrescaler}; | 5 | pub use super::bus::{AHBPrescaler, APBPrescaler}; |
| @@ -14,6 +14,29 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); | |||
| 14 | /// LSI speed | 14 | /// LSI speed |
| 15 | pub const LSI_FREQ: Hertz = Hertz(32_000); | 15 | pub const LSI_FREQ: Hertz = Hertz(32_000); |
| 16 | 16 | ||
| 17 | #[derive(Clone, Copy)] | ||
| 18 | pub enum AdcClockSource { | ||
| 19 | NoClk, | ||
| 20 | SysClk, | ||
| 21 | PllP, | ||
| 22 | } | ||
| 23 | |||
| 24 | impl AdcClockSource { | ||
| 25 | pub fn adcsel(&self) -> Adcsel { | ||
| 26 | match self { | ||
| 27 | AdcClockSource::NoClk => Adcsel::NOCLK, | ||
| 28 | AdcClockSource::SysClk => Adcsel::SYSCLK, | ||
| 29 | AdcClockSource::PllP => Adcsel::PLLP, | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | impl Default for AdcClockSource { | ||
| 35 | fn default() -> Self { | ||
| 36 | Self::NoClk | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 17 | /// System clock mux source | 40 | /// System clock mux source |
| 18 | #[derive(Clone, Copy)] | 41 | #[derive(Clone, Copy)] |
| 19 | pub enum ClockSrc { | 42 | pub enum ClockSrc { |
| @@ -327,6 +350,8 @@ pub struct Config { | |||
| 327 | pub pll: Option<Pll>, | 350 | pub pll: Option<Pll>, |
| 328 | /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. | 351 | /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. |
| 329 | pub clock_48mhz_src: Option<Clock48MhzSrc>, | 352 | pub clock_48mhz_src: Option<Clock48MhzSrc>, |
| 353 | pub adc12_clock_source: AdcClockSource, | ||
| 354 | pub adc345_clock_source: AdcClockSource, | ||
| 330 | } | 355 | } |
| 331 | 356 | ||
| 332 | /// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator. | 357 | /// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator. |
| @@ -346,6 +371,8 @@ impl Default for Config { | |||
| 346 | low_power_run: false, | 371 | low_power_run: false, |
| 347 | pll: None, | 372 | pll: None, |
| 348 | clock_48mhz_src: None, | 373 | clock_48mhz_src: None, |
| 374 | adc12_clock_source: Default::default(), | ||
| 375 | adc345_clock_source: Default::default(), | ||
| 349 | } | 376 | } |
| 350 | } | 377 | } |
| 351 | } | 378 | } |
| @@ -549,6 +576,29 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 549 | RCC.ccipr().modify(|w| w.set_clk48sel(source)); | 576 | RCC.ccipr().modify(|w| w.set_clk48sel(source)); |
| 550 | } | 577 | } |
| 551 | 578 | ||
| 579 | RCC.ccipr() | ||
| 580 | .modify(|w| w.set_adc12sel(config.adc12_clock_source.adcsel())); | ||
| 581 | RCC.ccipr() | ||
| 582 | .modify(|w| w.set_adc345sel(config.adc345_clock_source.adcsel())); | ||
| 583 | |||
| 584 | let adc12_ck = match config.adc12_clock_source { | ||
| 585 | AdcClockSource::NoClk => None, | ||
| 586 | AdcClockSource::PllP => match &pll_freq { | ||
| 587 | Some(pll) => pll.pll_p, | ||
| 588 | None => None, | ||
| 589 | }, | ||
| 590 | AdcClockSource::SysClk => Some(Hertz(sys_clk)), | ||
| 591 | }; | ||
| 592 | |||
| 593 | let adc345_ck = match config.adc345_clock_source { | ||
| 594 | AdcClockSource::NoClk => None, | ||
| 595 | AdcClockSource::PllP => match &pll_freq { | ||
| 596 | Some(pll) => pll.pll_p, | ||
| 597 | None => None, | ||
| 598 | }, | ||
| 599 | AdcClockSource::SysClk => Some(Hertz(sys_clk)), | ||
| 600 | }; | ||
| 601 | |||
| 552 | if config.low_power_run { | 602 | if config.low_power_run { |
| 553 | assert!(sys_clk <= 2_000_000); | 603 | assert!(sys_clk <= 2_000_000); |
| 554 | PWR.cr1().modify(|w| w.set_lpr(true)); | 604 | PWR.cr1().modify(|w| w.set_lpr(true)); |
| @@ -562,5 +612,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 562 | apb1_tim: Hertz(apb1_tim_freq), | 612 | apb1_tim: Hertz(apb1_tim_freq), |
| 563 | apb2: Hertz(apb2_freq), | 613 | apb2: Hertz(apb2_freq), |
| 564 | apb2_tim: Hertz(apb2_tim_freq), | 614 | apb2_tim: Hertz(apb2_tim_freq), |
| 615 | adc12: adc12_ck, | ||
| 616 | adc345: adc345_ck, | ||
| 565 | }); | 617 | }); |
| 566 | } | 618 | } |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 0430e4a74..ecabe23ae 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -77,6 +77,12 @@ pub struct Clocks { | |||
| 77 | #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] | 77 | #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] |
| 78 | pub adc: Option<Hertz>, | 78 | pub adc: Option<Hertz>, |
| 79 | 79 | ||
| 80 | #[cfg(any(rcc_g4))] | ||
| 81 | pub adc12: Option<Hertz>, | ||
| 82 | |||
| 83 | #[cfg(any(rcc_g4))] | ||
| 84 | pub adc345: Option<Hertz>, | ||
| 85 | |||
| 80 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | 86 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] |
| 81 | /// Set only if the lsi or lse is configured, indicates stop is supported | 87 | /// Set only if the lsi or lse is configured, indicates stop is supported |
| 82 | pub rtc: Option<Hertz>, | 88 | pub rtc: Option<Hertz>, |
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 0c1cdd67c..2e81d2060 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml | |||
| @@ -11,6 +11,8 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de | |||
| 11 | embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 12 | embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 15 | usbd-hid = "0.6.0" | ||
| 14 | 16 | ||
| 15 | defmt = "0.3" | 17 | defmt = "0.3" |
| 16 | defmt-rtt = "0.4" | 18 | defmt-rtt = "0.4" |
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs new file mode 100644 index 000000000..a792748bc --- /dev/null +++ b/examples/stm32g4/src/bin/adc.rs | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 8 | use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSrc}; | ||
| 9 | use embassy_stm32::Config; | ||
| 10 | use embassy_time::{Delay, Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let mut config = Config::default(); | ||
| 16 | |||
| 17 | config.rcc.pll = Some(Pll { | ||
| 18 | source: PllSrc::HSI16, | ||
| 19 | prediv_m: PllM::Div4, | ||
| 20 | mul_n: PllN::Mul85, | ||
| 21 | div_p: None, | ||
| 22 | div_q: None, | ||
| 23 | // Main system clock at 170 MHz | ||
| 24 | div_r: Some(PllR::Div2), | ||
| 25 | }); | ||
| 26 | |||
| 27 | config.rcc.adc12_clock_source = AdcClockSource::SysClk; | ||
| 28 | config.rcc.mux = ClockSrc::PLL; | ||
| 29 | |||
| 30 | let mut p = embassy_stm32::init(config); | ||
| 31 | info!("Hello World!"); | ||
| 32 | |||
| 33 | let mut adc = Adc::new(p.ADC2, &mut Delay); | ||
| 34 | adc.set_sample_time(SampleTime::Cycles32_5); | ||
| 35 | |||
| 36 | loop { | ||
| 37 | let measured = adc.read(&mut p.PA7); | ||
| 38 | info!("measured: {}", measured); | ||
| 39 | Timer::after(Duration::from_millis(500)).await; | ||
| 40 | } | ||
| 41 | } | ||
