stm32: add ADC f3_v1_1

This commit is contained in:
Sam Mason 2023-12-04 14:03:31 +11:00
parent e5fdd35bd6
commit 35f16c6003
4 changed files with 466 additions and 14 deletions

View File

@ -0,0 +1,408 @@
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_futures::yield_now;
use embassy_hal_internal::into_ref;
use embassy_time::Instant;
use super::Resolution;
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
use crate::interrupt::typelevel::Interrupt;
use crate::time::Hertz;
use crate::{interrupt, Peripheral};
const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ;
pub const VDDA_CALIB_MV: u32 = 3300;
pub const ADC_MAX: u32 = (1 << 12) - 1;
pub const VREF_INT: u32 = 1230;
pub enum AdcPowerMode {
AlwaysOn,
DelayOff,
IdleOff,
DelayIdleOff,
}
pub enum Prescaler {
Div1,
Div2,
Div3,
Div4,
}
/// 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().sr().read().eoc() {
T::regs().cr1().modify(|w| w.set_eocie(false));
} else {
return;
}
T::state().waker.wake();
}
}
fn update_vref<T: Instance>(op: i8) {
static VREF_STATUS: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(0);
if op > 0 {
if VREF_STATUS.fetch_add(1, core::sync::atomic::Ordering::SeqCst) == 0 {
T::regs().ccr().modify(|w| w.set_tsvrefe(true));
}
} else {
if VREF_STATUS.fetch_sub(1, core::sync::atomic::Ordering::SeqCst) == 1 {
T::regs().ccr().modify(|w| w.set_tsvrefe(false));
}
}
}
pub struct Vref<T: Instance>(core::marker::PhantomData<T>);
impl<T: Instance> AdcPin<T> for Vref<T> {}
impl<T: Instance> super::sealed::AdcPin<T> for Vref<T> {
fn channel(&self) -> u8 {
17
}
}
impl<T: Instance> Vref<T> {
/// The value that vref would be if vdda was at 3000mv
pub fn calibrated_value(&self) -> u16 {
crate::pac::VREFINTCAL.data().read().value()
}
pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration {
let vref_val = adc.read(self).await;
Calibration {
vref_cal: self.calibrated_value(),
vref_val,
}
}
}
pub struct Calibration {
vref_cal: u16,
vref_val: u16,
}
impl Calibration {
/// The millivolts that the calibration value was measured at
pub const CALIBRATION_UV: u32 = 3_000_000;
/// Returns the measured VddA in microvolts (uV)
pub fn vdda_uv(&self) -> u32 {
(Self::CALIBRATION_UV * self.vref_cal as u32) / self.vref_val as u32
}
/// Returns the measured VddA as an f32
pub fn vdda_f32(&self) -> f32 {
(Self::CALIBRATION_UV as f32 / 1_000.0) * (self.vref_cal as f32 / self.vref_val as f32)
}
/// Returns a calibrated voltage value as in microvolts (uV)
pub fn cal_uv(&self, raw: u16, resolution: super::Resolution) -> u32 {
(self.vdda_uv() / resolution.to_max_count()) * raw as u32
}
/// Returns a calibrated voltage value as an f32
pub fn cal_f32(&self, raw: u16, resolution: super::Resolution) -> f32 {
raw as f32 * self.vdda_f32() / resolution.to_max_count() as f32
}
}
impl<T: Instance> Drop for Vref<T> {
fn drop(&mut self) {
update_vref::<T>(-1)
}
}
pub struct Temperature<T: Instance>(core::marker::PhantomData<T>);
impl<T: Instance> AdcPin<T> for Temperature<T> {}
impl<T: Instance> super::sealed::AdcPin<T> for Temperature<T> {
fn channel(&self) -> u8 {
16
}
}
impl<T: Instance> Drop for Temperature<T> {
fn drop(&mut self) {
update_vref::<T>(-1)
}
}
impl<'d, T: Instance> Adc<'d, T> {
pub fn new(
adc: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
into_ref!(adc);
T::enable_and_reset();
//let r = T::regs();
//r.cr2().write(|w| w.set_align(true));
T::Interrupt::unpend();
unsafe {
T::Interrupt::enable();
}
Self { adc }
}
fn freq() -> Hertz {
let div = T::regs().ccr().read().adcpre() + 1;
ADC_FREQ / div as u32
}
pub async fn set_resolution(&mut self, res: Resolution) {
let was_on = Self::is_on();
if was_on {
self.stop_adc().await;
}
T::regs().cr1().modify(|w| w.set_res(res.into()));
if was_on {
self.start_adc().await;
}
}
pub fn resolution(&self) -> Resolution {
T::regs().cr1().read().res().into()
}
pub fn enable_vref(&self) -> Vref<T> {
update_vref::<T>(1);
Vref(core::marker::PhantomData)
}
pub fn enable_temperature(&self) -> Temperature<T> {
T::regs().ccr().modify(|w| w.set_tsvrefe(true));
Temperature::<T>(core::marker::PhantomData)
}
/// Perform a single conversion.
async fn convert(&mut self) -> u16 {
let was_on = Self::is_on();
if !was_on {
self.start_adc().await;
}
self.wait_sample_ready().await;
T::regs().sr().write(|_| {});
T::regs().cr1().modify(|w| {
w.set_eocie(true);
w.set_scan(false);
});
T::regs().cr2().modify(|w| {
w.set_swstart(true);
w.set_cont(false);
}); // swstart cleared by HW
let res = poll_fn(|cx| {
T::state().waker.register(cx.waker());
if T::regs().sr().read().eoc() {
let res = T::regs().dr().read().rdata();
Poll::Ready(res)
} else {
Poll::Pending
}
})
.await;
if !was_on {
self.stop_adc().await;
}
res
}
#[inline(always)]
fn is_on() -> bool {
T::regs().sr().read().adons() || T::regs().cr2().read().adon()
}
pub async fn start_adc(&self) {
//defmt::trace!("Turn ADC on");
T::regs().cr2().modify(|w| w.set_adon(true));
//defmt::trace!("Waiting for ADC to turn on");
let mut t = Instant::now();
while !T::regs().sr().read().adons() {
yield_now().await;
if t.elapsed() > embassy_time::Duration::from_millis(1000) {
t = Instant::now();
//defmt::trace!("ADC still not on");
}
}
//defmt::trace!("ADC on");
}
pub async fn stop_adc(&self) {
if T::regs().cr2().read().adon() {
//defmt::trace!("ADC should be on, wait for it to start");
while !T::regs().csr().read().adons1() {
yield_now().await;
}
}
//defmt::trace!("Turn ADC off");
T::regs().cr2().modify(|w| w.set_adon(false));
//defmt::trace!("Waiting for ADC to turn off");
while T::regs().csr().read().adons1() {
yield_now().await;
}
}
pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
self.set_sample_sequence(&[pin.channel()]).await;
self.convert().await
}
async fn wait_sample_ready(&self) {
//trace!("Waiting for sample channel to be ready");
while T::regs().sr().read().rcnr() {
yield_now().await;
}
}
pub async fn set_sample_time(&mut self, pin: &mut impl AdcPin<T>, sample_time: SampleTime) {
if Self::get_channel_sample_time(pin.channel()) != sample_time {
self.stop_adc().await;
unsafe {
Self::set_channel_sample_time(pin.channel(), sample_time);
}
self.start_adc().await;
}
}
pub fn get_sample_time(&self, pin: &impl AdcPin<T>) -> SampleTime {
Self::get_channel_sample_time(pin.channel())
}
/// Sets the channel sample time
///
/// ## SAFETY:
/// - ADON == 0 i.e ADC must not be enabled when this is called.
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
let sample_time = sample_time.into();
match ch {
0..=9 => T::regs().smpr3().modify(|reg| reg.set_smp(ch as _, sample_time)),
10..=19 => T::regs()
.smpr2()
.modify(|reg| reg.set_smp(ch as usize - 10, sample_time)),
20..=29 => T::regs()
.smpr1()
.modify(|reg| reg.set_smp(ch as usize - 20, sample_time)),
30..=31 => T::regs()
.smpr0()
.modify(|reg| reg.set_smp(ch as usize - 30, sample_time)),
_ => panic!("Invalid channel to sample"),
}
}
fn get_channel_sample_time(ch: u8) -> SampleTime {
match ch {
0..=9 => T::regs().smpr3().read().smp(ch as _),
10..=19 => T::regs().smpr2().read().smp(ch as usize - 10),
20..=29 => T::regs().smpr1().read().smp(ch as usize - 20),
30..=31 => T::regs().smpr0().read().smp(ch as usize - 30),
_ => panic!("Invalid channel to sample"),
}
.into()
}
/// Sets the sequence to sample the ADC. Must be less than 28 elements.
async fn set_sample_sequence(&self, sequence: &[u8]) {
assert!(sequence.len() <= 28);
let mut iter = sequence.iter();
T::regs().sqr1().modify(|w| w.set_l((sequence.len() - 1) as _));
for (idx, ch) in iter.by_ref().take(6).enumerate() {
T::regs().sqr5().modify(|w| w.set_sq(idx, *ch));
}
for (idx, ch) in iter.by_ref().take(6).enumerate() {
T::regs().sqr4().modify(|w| w.set_sq(idx, *ch));
}
for (idx, ch) in iter.by_ref().take(6).enumerate() {
T::regs().sqr3().modify(|w| w.set_sq(idx, *ch));
}
for (idx, ch) in iter.by_ref().take(6).enumerate() {
T::regs().sqr2().modify(|w| w.set_sq(idx, *ch));
}
for (idx, ch) in iter.by_ref().take(4).enumerate() {
T::regs().sqr1().modify(|w| w.set_sq(idx, *ch));
}
}
fn get_res_clks(res: Resolution) -> u32 {
match res {
Resolution::TwelveBit => 12,
Resolution::TenBit => 11,
Resolution::EightBit => 9,
Resolution::SixBit => 7,
}
}
fn get_sample_time_clks(sample_time: SampleTime) -> u32 {
match sample_time {
SampleTime::Cycles4 => 4,
SampleTime::Cycles9 => 9,
SampleTime::Cycles16 => 16,
SampleTime::Cycles24 => 24,
SampleTime::Cycles48 => 48,
SampleTime::Cycles96 => 96,
SampleTime::Cycles192 => 192,
SampleTime::Cycles384 => 384,
}
}
pub fn sample_time_for_us(&self, us: u32) -> SampleTime {
let res_clks = Self::get_res_clks(self.resolution());
let us_clks = us * Self::freq().0 / 1_000_000;
let clks = us_clks.saturating_sub(res_clks);
match clks {
0..=4 => SampleTime::Cycles4,
5..=9 => SampleTime::Cycles9,
10..=16 => SampleTime::Cycles16,
17..=24 => SampleTime::Cycles24,
25..=48 => SampleTime::Cycles48,
49..=96 => SampleTime::Cycles96,
97..=192 => SampleTime::Cycles192,
193.. => SampleTime::Cycles384,
}
}
pub fn us_for_cfg(&self, res: Resolution, sample_time: SampleTime) -> u32 {
let res_clks = Self::get_res_clks(res);
let sample_clks = Self::get_sample_time_clks(sample_time);
(res_clks + sample_clks) * 1_000_000 / Self::freq().0
}
}
impl<'d, T: Instance> Drop for Adc<'d, T> {
fn drop(&mut self) {
while !T::regs().sr().read().adons() {}
T::regs().cr2().modify(|w| w.set_adon(false));
T::disable();
}
}

View File

@ -3,6 +3,7 @@
#[cfg(not(adc_f3_v2))]
#[cfg_attr(adc_f1, path = "f1.rs")]
#[cfg_attr(adc_f3, path = "f3.rs")]
#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
#[cfg_attr(adc_v1, path = "v1.rs")]
#[cfg_attr(adc_v2, path = "v2.rs")]
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
@ -26,20 +27,20 @@ use crate::peripherals;
pub struct Adc<'d, T: Instance> {
#[allow(unused)]
adc: crate::PeripheralRef<'d, T>,
#[cfg(not(adc_f3_v2))]
#[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))]
sample_time: SampleTime,
}
pub(crate) mod sealed {
#[cfg(any(adc_f1, adc_f3, adc_v1))]
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(any(adc_f1, adc_f3, adc_v1))]
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
pub struct State {
pub waker: AtomicWaker,
}
#[cfg(any(adc_f1, adc_f3, adc_v1))]
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
impl State {
pub const fn new() -> Self {
Self {
@ -54,11 +55,11 @@ pub(crate) mod sealed {
pub trait Instance: InterruptableInstance {
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_f3_v1_1, adc_g0)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon;
#[cfg(adc_f3)]
fn frequency() -> crate::time::Hertz;
#[cfg(any(adc_f1, adc_f3, adc_v1))]
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
fn state() -> &'static State;
}
@ -74,9 +75,9 @@ pub(crate) mod sealed {
}
}
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_g0)))]
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))]
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_g0))]
#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))]
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
@ -89,7 +90,7 @@ foreach_adc!(
crate::pac::$inst
}
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon {
return crate::pac::$common_inst
}
@ -99,7 +100,7 @@ foreach_adc!(
unsafe { crate::rcc::get_freqs() }.$clock.unwrap()
}
#[cfg(any(adc_f1, adc_f3, adc_v1))]
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
fn state() -> &'static sealed::State {
static STATE: sealed::State = sealed::State::new();
&STATE

View File

@ -1,5 +1,6 @@
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Resolution {
TwelveBit,
TenBit,
@ -9,6 +10,7 @@ pub enum Resolution {
#[cfg(adc_v4)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Resolution {
SixteenBit,
FourteenBit,
@ -19,7 +21,7 @@ pub enum Resolution {
impl Default for Resolution {
fn default() -> Self {
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
{
Self::TwelveBit
}
@ -40,12 +42,28 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
}
}
}
impl From<crate::pac::adc::vals::Res> for Resolution {
fn from(res: crate::pac::adc::vals::Res) -> Resolution {
match res {
#[cfg(adc_v4)]
crate::pac::adc::vals::Res::SIXTEENBIT => Resolution::SixteenBit,
#[cfg(adc_v4)]
crate::pac::adc::vals::Res::FOURTEENBITV => Resolution::FourteenBit,
crate::pac::adc::vals::Res::TWELVEBIT => Resolution::TwelveBit,
crate::pac::adc::vals::Res::TENBIT => Resolution::TenBit,
crate::pac::adc::vals::Res::EIGHTBIT => Resolution::EightBit,
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1, adc_f3_v3))]
crate::pac::adc::vals::Res::SIXBIT => Resolution::SixBit,
}
}
}
impl Resolution {
pub fn to_max_count(&self) -> u32 {
match self {
@ -56,7 +74,7 @@ impl Resolution {
Resolution::TwelveBit => (1 << 12) - 1,
Resolution::TenBit => (1 << 10) - 1,
Resolution::EightBit => (1 << 8) - 1,
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
Resolution::SixBit => (1 << 6) - 1,
}
}

View File

@ -3,6 +3,7 @@ macro_rules! impl_sample_time {
($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => {
#[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SampleTime {
$(
#[doc = concat!($doc, " ADC clock cycles.")]
@ -18,6 +19,14 @@ macro_rules! impl_sample_time {
}
}
impl From<crate::pac::adc::vals::SampleTime> for SampleTime {
fn from(sample_time: crate::pac::adc::vals::SampleTime) -> SampleTime {
match sample_time {
$(crate::pac::adc::vals::SampleTime::$pac_variant => SampleTime::$variant),*
}
}
}
impl Default for SampleTime {
fn default() -> Self {
Self::$default
@ -121,3 +130,19 @@ impl_sample_time!(
("601.5", Cycles601_5, CYCLES601_5)
)
);
#[cfg(any(adc_f3_v1_1))]
impl_sample_time!(
"4",
Cycles4,
(
("4", Cycles4, CYCLES4),
("9", Cycles9, CYCLES9),
("16", Cycles16, CYCLES16),
("24", Cycles24, CYCLES24),
("48", Cycles48, CYCLES48),
("96", Cycles96, CYCLES96),
("192", Cycles192, CYCLES192),
("384", Cycles384, CYCLES384)
)
);