Fix internal channel reading on adc_v2

This commit is contained in:
chemicstry 2022-10-07 14:31:55 +03:00
parent 9dca368c3d
commit df7174ecb0
4 changed files with 130 additions and 57 deletions

View File

@ -28,6 +28,10 @@ pub(crate) mod sealed {
pub trait AdcPin<T: Instance> {
fn channel(&self) -> u8;
}
pub trait InternalChannel<T> {
fn channel(&self) -> u8;
}
}
#[cfg(not(any(adc_f1, adc_v2)))]
@ -37,6 +41,7 @@ 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!(

View File

@ -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,6 +14,9 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
/// VREF voltage used for factory calibration of VREFINTCAL register.
pub const VREF_CALIB_MV: u32 = 3300;
/// ADC turn-on time
pub const ADC_POWERUP_TIME_US: u32 = 3;
pub enum Resolution {
TwelveBit,
TenBit,
@ -46,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
}
@ -152,19 +186,16 @@ where
T::enable();
T::reset();
let presc = unsafe { Prescaler::from_pclk2(T::frequency()) };
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(),
@ -194,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 {
@ -224,44 +294,31 @@ 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()));
// Select channel
T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel()));
// Configure channel
Self::set_channel_sample_time(pin.channel(), self.sample_time);
// enable adc
T::regs().cr2().modify(|reg| {
reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
});
let val = self.convert();
// 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);
});
val
self.read_channel(pin.channel())
}
}
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
unsafe { self.read_channel(channel.channel()) }
}
unsafe fn read_channel(&mut self, channel: u8) -> u16 {
// Configure ADC
T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
// Select channel
T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
// Configure channel
Self::set_channel_sample_time(channel, self.sample_time);
let val = self.convert();
val
}
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
if ch <= 9 {
T::regs()

View File

@ -50,14 +50,6 @@ 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 {}

View File

@ -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, SampleTime, 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;
}
}