summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/src/gpio.rs32
-rw-r--r--embassy-stm32/src/adc/mod.rs9
-rw-r--r--embassy-stm32/src/adc/v2.rs161
-rw-r--r--embassy-stm32/src/adc/v4.rs16
-rw-r--r--examples/stm32f4/src/bin/adc.rs25
5 files changed, 160 insertions, 83 deletions
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index a28bae96..f79f592b 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -599,12 +599,12 @@ pub(crate) mod sealed {
fn pin_bank(&self) -> u8;
#[inline]
- fn pin(&self) -> u8 {
+ fn _pin(&self) -> u8 {
self.pin_bank() & 0x1f
}
#[inline]
- fn bank(&self) -> Bank {
+ fn _bank(&self) -> Bank {
if self.pin_bank() & 0x20 == 0 {
Bank::Bank0
} else {
@@ -613,35 +613,35 @@ pub(crate) mod sealed {
}
fn io(&self) -> pac::io::Gpio {
- let block = match self.bank() {
+ let block = match self._bank() {
Bank::Bank0 => crate::pac::IO_BANK0,
Bank::Qspi => crate::pac::IO_QSPI,
};
- block.gpio(self.pin() as _)
+ block.gpio(self._pin() as _)
}
fn pad_ctrl(&self) -> Reg<pac::pads::regs::GpioCtrl, RW> {
- let block = match self.bank() {
+ let block = match self._bank() {
Bank::Bank0 => crate::pac::PADS_BANK0,
Bank::Qspi => crate::pac::PADS_QSPI,
};
- block.gpio(self.pin() as _)
+ block.gpio(self._pin() as _)
}
fn sio_out(&self) -> pac::sio::Gpio {
- SIO.gpio_out(self.bank() as _)
+ SIO.gpio_out(self._bank() as _)
}
fn sio_oe(&self) -> pac::sio::Gpio {
- SIO.gpio_oe(self.bank() as _)
+ SIO.gpio_oe(self._bank() as _)
}
fn sio_in(&self) -> Reg<u32, RW> {
- SIO.gpio_in(self.bank() as _)
+ SIO.gpio_in(self._bank() as _)
}
fn int_proc(&self) -> pac::io::Int {
- let io_block = match self.bank() {
+ let io_block = match self._bank() {
Bank::Bank0 => crate::pac::IO_BANK0,
Bank::Qspi => crate::pac::IO_QSPI,
};
@@ -658,6 +658,18 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
pin_bank: self.pin_bank(),
}
}
+
+ /// Returns the pin number within a bank
+ #[inline]
+ fn pin(&self) -> u8 {
+ self._pin()
+ }
+
+ /// Returns the bank of this pin
+ #[inline]
+ fn bank(&self) -> Bank {
+ self._bank()
+ }
}
pub struct AnyPin {
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 8da13073..0eb4eba7 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -28,15 +28,20 @@ pub(crate) mod sealed {
pub trait AdcPin<T: Instance> {
fn channel(&self) -> u8;
}
+
+ pub trait InternalChannel<T> {
+ fn channel(&self) -> u8;
+ }
}
-#[cfg(not(adc_f1))]
+#[cfg(not(any(adc_f1, adc_v2)))]
pub trait Instance: sealed::Instance + 'static {}
-#[cfg(adc_f1)]
+#[cfg(any(adc_f1, adc_v2))]
pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {}
#[cfg(all(not(adc_f1), not(adc_v1)))]
pub trait Common: sealed::Common + 'static {}
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
+pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
#[cfg(not(stm32h7))]
foreach_peripheral!(
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index 25b7ba96..4fe4ad1f 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -3,7 +3,9 @@ use core::marker::PhantomData;
use embassy_hal_common::into_ref;
use embedded_hal_02::blocking::delay::DelayUs;
+use super::InternalChannel;
use crate::adc::{AdcPin, Instance};
+use crate::peripherals::ADC1;
use crate::time::Hertz;
use crate::Peripheral;
@@ -12,20 +14,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
/// VREF voltage used for factory calibration of VREFINTCAL register.
pub const VREF_CALIB_MV: u32 = 3300;
-#[cfg(not(any(rcc_f4, rcc_f7)))]
-fn enable() {
- todo!()
-}
-
-#[cfg(any(rcc_f4, rcc_f7))]
-fn enable() {
- critical_section::with(|_| unsafe {
- // TODO do not enable all adc clocks if not needed
- crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true));
- crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true));
- crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true));
- });
-}
+/// ADC turn-on time
+pub const ADC_POWERUP_TIME_US: u32 = 3;
pub enum Resolution {
TwelveBit,
@@ -61,24 +51,53 @@ impl Resolution {
}
pub struct VrefInt;
-impl<T: Instance> AdcPin<T> for VrefInt {}
-impl<T: Instance> super::sealed::AdcPin<T> for VrefInt {
+impl InternalChannel<ADC1> for VrefInt {}
+impl super::sealed::InternalChannel<ADC1> for VrefInt {
fn channel(&self) -> u8 {
17
}
}
+impl VrefInt {
+ /// Time needed for internal voltage reference to stabilize
+ pub fn start_time_us() -> u32 {
+ 10
+ }
+}
+
pub struct Temperature;
-impl<T: Instance> AdcPin<T> for Temperature {}
-impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
+impl InternalChannel<ADC1> for Temperature {}
+impl super::sealed::InternalChannel<ADC1> for Temperature {
fn channel(&self) -> u8 {
- 16
+ cfg_if::cfg_if! {
+ if #[cfg(any(stm32f40, stm32f41))] {
+ 16
+ } else {
+ 18
+ }
+ }
+ }
+}
+
+impl Temperature {
+ /// Converts temperature sensor reading in millivolts to degrees celcius
+ pub fn to_celcius(sample_mv: u16) -> f32 {
+ // From 6.3.22 Temperature sensor characteristics
+ const V25: i32 = 760; // mV
+ const AVG_SLOPE: f32 = 2.5; // mV/C
+
+ (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0
+ }
+
+ /// Time needed for temperature sensor readings to stabilize
+ pub fn start_time_us() -> u32 {
+ 10
}
}
pub struct Vbat;
-impl<T: Instance> AdcPin<T> for Vbat {}
-impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
+impl InternalChannel<ADC1> for Vbat {}
+impl super::sealed::InternalChannel<ADC1> for Vbat {
fn channel(&self) -> u8 {
18
}
@@ -164,21 +183,19 @@ where
{
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
into_ref!(_peri);
- enable();
+ T::enable();
+ T::reset();
- let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) };
+ let presc = Prescaler::from_pclk2(T::frequency());
unsafe {
T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre()));
- }
- unsafe {
- // disable before config is set
T::regs().cr2().modify(|reg| {
- reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
+ reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
});
}
- delay.delay_us(20); // TODO?
+ delay.delay_us(ADC_POWERUP_TIME_US);
Self {
sample_time: Default::default(),
@@ -208,6 +225,45 @@ where
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
}
+ /// Enables internal voltage reference and returns [VrefInt], which can be used in
+ /// [Adc::read_internal()] to perform conversion.
+ pub fn enable_vrefint(&self) -> VrefInt {
+ unsafe {
+ T::common_regs().ccr().modify(|reg| {
+ reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
+ });
+ }
+
+ VrefInt {}
+ }
+
+ /// Enables internal temperature sensor and returns [Temperature], which can be used in
+ /// [Adc::read_internal()] to perform conversion.
+ ///
+ /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled,
+ /// temperature sensor will return vbat value.
+ pub fn enable_temperature(&self) -> Temperature {
+ unsafe {
+ T::common_regs().ccr().modify(|reg| {
+ reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
+ });
+ }
+
+ Temperature {}
+ }
+
+ /// Enables vbat input and returns [Vbat], which can be used in
+ /// [Adc::read_internal()] to perform conversion.
+ pub fn enable_vbat(&self) -> Vbat {
+ unsafe {
+ T::common_regs().ccr().modify(|reg| {
+ reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED);
+ });
+ }
+
+ Vbat {}
+ }
+
/// Perform a single conversion.
fn convert(&mut self) -> u16 {
unsafe {
@@ -238,42 +294,29 @@ where
P: crate::gpio::sealed::Pin,
{
unsafe {
- // dissable ADC
- T::regs().cr2().modify(|reg| {
- reg.set_swstart(false);
- });
- T::regs().cr2().modify(|reg| {
- reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
- });
-
pin.set_as_analog();
- // Configure ADC
- T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
+ self.read_channel(pin.channel())
+ }
+ }
- // Select channel
- T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel()));
+ pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
+ unsafe { self.read_channel(channel.channel()) }
+ }
- // Configure channel
- Self::set_channel_sample_time(pin.channel(), self.sample_time);
+ unsafe fn read_channel(&mut self, channel: u8) -> u16 {
+ // Configure ADC
+ T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
- // enable adc
- T::regs().cr2().modify(|reg| {
- reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
- });
+ // Select channel
+ T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
- let val = self.convert();
+ // Configure channel
+ Self::set_channel_sample_time(channel, self.sample_time);
- // dissable ADC
- T::regs().cr2().modify(|reg| {
- reg.set_swstart(false);
- });
- T::regs().cr2().modify(|reg| {
- reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
- });
+ let val = self.convert();
- val
- }
+ val
}
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
@@ -288,3 +331,9 @@ where
}
}
}
+
+impl<'d, T: Instance> Drop for Adc<'d, T> {
+ fn drop(&mut self) {
+ T::disable();
+ }
+}
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index d356d7b6..eda2b2a7 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs;
use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
use pac::adccommon::vals::Presc;
-use super::{AdcPin, Instance};
+use super::{AdcPin, Instance, InternalChannel};
use crate::time::Hertz;
use crate::{pac, Peripheral};
@@ -50,18 +50,10 @@ impl Resolution {
}
}
-pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
-
-mod sealed {
- pub trait InternalChannel<T> {
- fn channel(&self) -> u8;
- }
-}
-
// 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
pub struct VrefInt;
impl<T: Instance> InternalChannel<T> for VrefInt {}
-impl<T: Instance> sealed::InternalChannel<T> for VrefInt {
+impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
fn channel(&self) -> u8 {
19
}
@@ -69,7 +61,7 @@ impl<T: Instance> sealed::InternalChannel<T> for VrefInt {
pub struct Temperature;
impl<T: Instance> InternalChannel<T> for Temperature {}
-impl<T: Instance> sealed::InternalChannel<T> for Temperature {
+impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
fn channel(&self) -> u8 {
18
}
@@ -77,7 +69,7 @@ impl<T: Instance> sealed::InternalChannel<T> for Temperature {
pub struct Vbat;
impl<T: Instance> InternalChannel<T> for Vbat {}
-impl<T: Instance> sealed::InternalChannel<T> for Vbat {
+impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
fn channel(&self) -> u8 {
// TODO this should be 14 for H7a/b/35
17
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs
index 87118507..1d030f7d 100644
--- a/examples/stm32f4/src/bin/adc.rs
+++ b/examples/stm32f4/src/bin/adc.rs
@@ -2,9 +2,10 @@
#![no_main]
#![feature(type_alias_impl_trait)]
+use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
use defmt::*;
use embassy_executor::Spawner;
-use embassy_stm32::adc::Adc;
+use embassy_stm32::adc::{Adc, Temperature, VrefInt};
use embassy_time::{Delay, Duration, Timer};
use {defmt_rtt as _, panic_probe as _};
@@ -13,12 +14,30 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
- let mut adc = Adc::new(p.ADC1, &mut Delay);
+ let mut delay = Delay;
+ let mut adc = Adc::new(p.ADC1, &mut delay);
let mut pin = p.PC1;
+ let mut vrefint = adc.enable_vrefint();
+ let mut temp = adc.enable_temperature();
+
+ // Startup delay can be combined to the maximum of either
+ delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us()));
+
loop {
+ // Read pin
let v = adc.read(&mut pin);
- info!("--> {} - {} mV", v, adc.to_millivolts(v));
+ info!("PC1: {} ({} mV)", v, adc.to_millivolts(v));
+
+ // Read internal temperature
+ let v = adc.read_internal(&mut temp);
+ let celcius = Temperature::to_celcius(adc.to_millivolts(v));
+ info!("Internal temp: {} ({} C)", v, celcius);
+
+ // Read internal voltage reference
+ let v = adc.read_internal(&mut vrefint);
+ info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v));
+
Timer::after(Duration::from_millis(100)).await;
}
}