stm32/adc: add async conversion
This commit is contained in:
parent
daeb497045
commit
e640933e2f
@ -1,14 +1,36 @@
|
|||||||
|
use core::future::poll_fn;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_internal::into_ref;
|
use embassy_hal_internal::into_ref;
|
||||||
use embedded_hal_02::blocking::delay::DelayUs;
|
use embedded_hal_02::blocking::delay::DelayUs;
|
||||||
|
|
||||||
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
|
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
|
||||||
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::Peripheral;
|
use crate::{interrupt, Peripheral};
|
||||||
|
|
||||||
pub const VDDA_CALIB_MV: u32 = 3300;
|
pub const VDDA_CALIB_MV: u32 = 3300;
|
||||||
pub const ADC_MAX: u32 = (1 << 12) - 1;
|
pub const ADC_MAX: u32 = (1 << 12) - 1;
|
||||||
pub const VREF_INT: u32 = 1230;
|
pub const VREF_INT: u32 = 1230;
|
||||||
|
|
||||||
|
/// Interrupt handler.
|
||||||
|
pub struct InterruptHandler<T: Instance> {
|
||||||
|
_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||||
|
unsafe fn on_interrupt() {
|
||||||
|
if T::regs().isr().read().eoc() {
|
||||||
|
T::regs().ier().modify(|w| w.set_eocie(false));
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
T::state().waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Vref;
|
pub struct Vref;
|
||||||
impl<T: Instance> AdcPin<T> for Vref {}
|
impl<T: Instance> AdcPin<T> for Vref {}
|
||||||
impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
||||||
@ -17,6 +39,13 @@ impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Vref {
|
||||||
|
/// The value that vref would be if vdda was at 3300mv
|
||||||
|
pub fn value(&self) -> u16 {
|
||||||
|
crate::pac::VREFINTCAL.data().read().value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Temperature;
|
pub struct Temperature;
|
||||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||||
@ -26,7 +55,11 @@ impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Adc<'d, T> {
|
impl<'d, T: Instance> Adc<'d, T> {
|
||||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
pub fn new(
|
||||||
|
adc: impl Peripheral<P = T> + 'd,
|
||||||
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
|
delay: &mut impl DelayUs<u32>,
|
||||||
|
) -> Self {
|
||||||
use crate::pac::adc::vals;
|
use crate::pac::adc::vals;
|
||||||
|
|
||||||
into_ref!(adc);
|
into_ref!(adc);
|
||||||
@ -58,6 +91,11 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
// Wait until the adc is ready
|
// Wait until the adc is ready
|
||||||
while !T::regs().isr().read().adrdy() {}
|
while !T::regs().isr().read().adrdy() {}
|
||||||
|
|
||||||
|
T::Interrupt::unpend();
|
||||||
|
unsafe {
|
||||||
|
T::Interrupt::enable();
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
adc,
|
adc,
|
||||||
sample_time: Default::default(),
|
sample_time: Default::default(),
|
||||||
@ -97,30 +135,41 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a single conversion.
|
/// Perform a single conversion.
|
||||||
fn convert(&mut self) -> u16 {
|
async fn convert(&mut self) -> u16 {
|
||||||
T::regs().isr().write(|_| {});
|
T::regs().isr().write(|_| {});
|
||||||
|
T::regs().ier().modify(|w| w.set_eocie(true));
|
||||||
T::regs().cr().modify(|w| w.set_adstart(true));
|
T::regs().cr().modify(|w| w.set_adstart(true));
|
||||||
|
|
||||||
while !T::regs().isr().read().eoc() && !T::regs().isr().read().eos() {}
|
poll_fn(|cx| {
|
||||||
|
T::state().waker.register(cx.waker());
|
||||||
|
|
||||||
|
if T::regs().isr().read().eoc() {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
T::regs().isr().write(|_| {});
|
T::regs().isr().write(|_| {});
|
||||||
|
|
||||||
T::regs().dr().read().rdata()
|
T::regs().dr().read().rdata()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||||
|
|
||||||
// Configure the channel to sample
|
// Configure the channel to sample
|
||||||
T::regs().sqr1().write(|w| w.set_sq(0, pin.channel()));
|
T::regs().sqr1().write(|w| w.set_sq(0, pin.channel()));
|
||||||
self.convert()
|
self.convert().await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||||
let sample_time = sample_time.into();
|
let sample_time = sample_time.into();
|
||||||
if ch <= 9 {
|
if ch <= 9 {
|
||||||
T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
|
T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||||
} else {
|
} else {
|
||||||
T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,35 @@ pub struct Adc<'d, T: Instance> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
pub trait Instance {
|
#[cfg(adc_f3)]
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
|
#[cfg(adc_f3)]
|
||||||
|
pub struct State {
|
||||||
|
pub waker: AtomicWaker,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(adc_f3)]
|
||||||
|
impl State {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
waker: AtomicWaker::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InterruptableInstance {
|
||||||
|
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance: InterruptableInstance {
|
||||||
fn regs() -> crate::pac::adc::Adc;
|
fn regs() -> crate::pac::adc::Adc;
|
||||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
|
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
|
||||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||||
#[cfg(adc_f3)]
|
#[cfg(adc_f3)]
|
||||||
fn frequency() -> crate::time::Hertz;
|
fn frequency() -> crate::time::Hertz;
|
||||||
|
#[cfg(adc_f3)]
|
||||||
|
fn state() -> &'static State;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AdcPin<T: Instance> {
|
pub trait AdcPin<T: Instance> {
|
||||||
@ -72,8 +95,22 @@ foreach_adc!(
|
|||||||
fn frequency() -> crate::time::Hertz {
|
fn frequency() -> crate::time::Hertz {
|
||||||
unsafe { crate::rcc::get_freqs() }.$clock.unwrap()
|
unsafe { crate::rcc::get_freqs() }.$clock.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(adc_f3)]
|
||||||
|
fn state() -> &'static sealed::State {
|
||||||
|
static STATE: sealed::State = sealed::State::new();
|
||||||
|
&STATE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach_interrupt!(
|
||||||
|
($inst,adc,ADC,GLOBAL,$irq:ident) => {
|
||||||
|
impl sealed::InterruptableInstance for peripherals::$inst {
|
||||||
|
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
impl crate::adc::Instance for peripherals::$inst {}
|
impl crate::adc::Instance for peripherals::$inst {}
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
@ -4,13 +4,18 @@
|
|||||||
|
|
||||||
use defmt::info;
|
use defmt::info;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::adc::{Adc, SampleTime, VREF_INT};
|
use embassy_stm32::adc::{Adc, SampleTime};
|
||||||
|
use embassy_stm32::peripherals::ADC1;
|
||||||
use embassy_stm32::rcc::AdcClockSource;
|
use embassy_stm32::rcc::AdcClockSource;
|
||||||
use embassy_stm32::time::mhz;
|
use embassy_stm32::time::mhz;
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::{adc, bind_interrupts, Config};
|
||||||
use embassy_time::{Delay, Duration, Timer};
|
use embassy_time::{Delay, Duration, Timer};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
ADC1_2 => adc::InterruptHandler<ADC1>;
|
||||||
|
});
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) -> ! {
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
@ -24,7 +29,7 @@ async fn main(_spawner: Spawner) -> ! {
|
|||||||
|
|
||||||
info!("create adc...");
|
info!("create adc...");
|
||||||
|
|
||||||
let mut adc = Adc::new(p.ADC1, &mut Delay);
|
let mut adc = Adc::new(p.ADC1, Irqs, &mut Delay);
|
||||||
|
|
||||||
adc.set_sample_time(SampleTime::Cycles601_5);
|
adc.set_sample_time(SampleTime::Cycles601_5);
|
||||||
|
|
||||||
@ -34,18 +39,18 @@ async fn main(_spawner: Spawner) -> ! {
|
|||||||
let mut temperature = adc.enable_temperature();
|
let mut temperature = adc.enable_temperature();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let vref = adc.read(&mut vrefint);
|
let vref = adc.read(&mut vrefint).await;
|
||||||
info!("read vref: {}", vref);
|
info!("read vref: {} (should be {})", vref, vrefint.value());
|
||||||
|
|
||||||
let temp = adc.read(&mut temperature);
|
let temp = adc.read(&mut temperature).await;
|
||||||
info!("read temperature: {}", temp);
|
info!("read temperature: {}", temp);
|
||||||
|
|
||||||
let pin = adc.read(&mut p.PA0);
|
let pin = adc.read(&mut p.PA0).await;
|
||||||
info!("read pin: {}", pin);
|
info!("read pin: {}", pin);
|
||||||
|
|
||||||
let pin_mv = pin as u32 * VREF_INT as u32 / vref as u32;
|
let pin_mv = (pin as u32 * vrefint.value() as u32 / vref as u32) * 3300 / 4095;
|
||||||
info!("computed pin mv: {}", pin_mv);
|
info!("computed pin mv: {}", pin_mv);
|
||||||
|
|
||||||
Timer::after(Duration::from_secs(1)).await;
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user