const VREFCAL: *const u16 = 0x1FFF_F7BA as *const u16;
const VTEMPCAL30: *const u16 = 0x1FFF_F7B8 as *const u16;
const VTEMPCAL110: *const u16 = 0x1FFF_F7C2 as *const u16;
const VDD_CALIB: u16 = 3300;
use core::ptr;
use embedded_hal::{
adc::{Channel, OneShot},
blocking::delay::DelayUs,
};
use crate::{
delay::Delay,
gpio::*,
pac::{
adc::{
cfgr1::{ALIGN_A, RES_A},
smpr::SMP_A,
},
ADC,
},
rcc::Rcc,
};
pub struct Adc {
rb: ADC,
sample_time: AdcSampleTime,
align: AdcAlign,
precision: AdcPrecision,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AdcSampleTime {
T_1,
T_7,
T_13,
T_28,
T_41,
T_55,
T_71,
T_239,
}
impl AdcSampleTime {
pub fn default() -> Self {
AdcSampleTime::T_239
}
}
impl From<AdcSampleTime> for SMP_A {
fn from(val: AdcSampleTime) -> Self {
match val {
AdcSampleTime::T_1 => SMP_A::CYCLES1_5,
AdcSampleTime::T_7 => SMP_A::CYCLES7_5,
AdcSampleTime::T_13 => SMP_A::CYCLES13_5,
AdcSampleTime::T_28 => SMP_A::CYCLES28_5,
AdcSampleTime::T_41 => SMP_A::CYCLES41_5,
AdcSampleTime::T_55 => SMP_A::CYCLES55_5,
AdcSampleTime::T_71 => SMP_A::CYCLES71_5,
AdcSampleTime::T_239 => SMP_A::CYCLES239_5,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AdcAlign {
Left,
Right,
LeftAsRM,
}
impl AdcAlign {
pub fn default() -> Self {
AdcAlign::Right
}
}
impl From<AdcAlign> for ALIGN_A {
fn from(val: AdcAlign) -> Self {
match val {
AdcAlign::Left => ALIGN_A::LEFT,
AdcAlign::Right => ALIGN_A::RIGHT,
AdcAlign::LeftAsRM => ALIGN_A::LEFT,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AdcPrecision {
B_12,
B_10,
B_8,
B_6,
}
impl AdcPrecision {
pub fn default() -> Self {
AdcPrecision::B_12
}
}
impl From<AdcPrecision> for RES_A {
fn from(val: AdcPrecision) -> Self {
match val {
AdcPrecision::B_12 => RES_A::TWELVEBIT,
AdcPrecision::B_10 => RES_A::TENBIT,
AdcPrecision::B_8 => RES_A::EIGHTBIT,
AdcPrecision::B_6 => RES_A::SIXBIT,
}
}
}
macro_rules! adc_pins {
($($pin:ty => $chan:expr),+ $(,)*) => {
$(
impl Channel<Adc> for $pin {
type ID = u8;
fn channel() -> u8 { $chan }
}
)+
};
}
adc_pins!(
gpioa::PA0<Analog> => 0_u8,
gpioa::PA1<Analog> => 1_u8,
gpioa::PA2<Analog> => 2_u8,
gpioa::PA3<Analog> => 3_u8,
gpioa::PA4<Analog> => 4_u8,
gpioa::PA5<Analog> => 5_u8,
gpioa::PA6<Analog> => 6_u8,
gpioa::PA7<Analog> => 7_u8,
gpiob::PB0<Analog> => 8_u8,
gpiob::PB1<Analog> => 9_u8,
);
#[cfg(any(
feature = "stm32f030",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f070",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
adc_pins!(
gpioc::PC0<Analog> => 10_u8,
gpioc::PC1<Analog> => 11_u8,
gpioc::PC2<Analog> => 12_u8,
gpioc::PC3<Analog> => 13_u8,
gpioc::PC4<Analog> => 14_u8,
gpioc::PC5<Analog> => 15_u8,
);
#[derive(Debug, Default)]
pub struct VTemp;
#[derive(Debug, Default)]
pub struct VRef;
adc_pins!(
VTemp => 16_u8,
VRef => 17_u8,
);
impl VTemp {
pub fn new() -> Self {
VTemp::default()
}
pub fn enable(&mut self, adc: &mut Adc) {
adc.rb.ccr.modify(|_, w| w.tsen().set_bit());
}
pub fn disable(&mut self, adc: &mut Adc) {
adc.rb.ccr.modify(|_, w| w.tsen().clear_bit());
}
pub fn is_enabled(&self, adc: &Adc) -> bool {
adc.rb.ccr.read().tsen().bit_is_set()
}
fn convert_temp(vtemp: u16, vdda: u16) -> i16 {
let vtemp30_cal = i32::from(unsafe { ptr::read(VTEMPCAL30) }) * 100;
let vtemp110_cal = i32::from(unsafe { ptr::read(VTEMPCAL110) }) * 100;
let mut temperature = i32::from(vtemp) * 100;
temperature = (temperature * (i32::from(vdda) / i32::from(VDD_CALIB))) - vtemp30_cal;
temperature *= (110 - 30) * 100;
temperature /= vtemp110_cal - vtemp30_cal;
temperature += 3000;
temperature as i16
}
pub fn read(adc: &mut Adc, delay: Option<&mut Delay>) -> i16 {
let mut vtemp = Self::new();
let vtemp_preenable = vtemp.is_enabled(&adc);
if !vtemp_preenable {
vtemp.enable(adc);
if let Some(dref) = delay {
dref.delay_us(2_u16);
} else {
VRef::read_vdda(adc);
}
}
let vdda = VRef::read_vdda(adc);
let prev_cfg = adc.default_cfg();
let vtemp_val = adc.read(&mut vtemp).unwrap();
if !vtemp_preenable {
vtemp.disable(adc);
}
adc.restore_cfg(prev_cfg);
Self::convert_temp(vtemp_val, vdda)
}
}
impl VRef {
pub fn new() -> Self {
VRef::default()
}
pub fn enable(&mut self, adc: &mut Adc) {
adc.rb.ccr.modify(|_, w| w.vrefen().set_bit());
}
pub fn disable(&mut self, adc: &mut Adc) {
adc.rb.ccr.modify(|_, w| w.vrefen().clear_bit());
}
pub fn is_enabled(&self, adc: &Adc) -> bool {
adc.rb.ccr.read().vrefen().bit_is_set()
}
pub fn read_vdda(adc: &mut Adc) -> u16 {
let vrefint_cal = u32::from(unsafe { ptr::read(VREFCAL) });
let mut vref = Self::new();
let prev_cfg = adc.default_cfg();
let vref_val: u32 = if vref.is_enabled(&adc) {
adc.read(&mut vref).unwrap()
} else {
vref.enable(adc);
let ret = adc.read(&mut vref).unwrap();
vref.disable(adc);
ret
};
adc.restore_cfg(prev_cfg);
(u32::from(VDD_CALIB) * vrefint_cal / vref_val) as u16
}
}
#[cfg(any(
feature = "stm32f031",
feature = "stm32f038",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
#[derive(Debug, Default)]
pub struct VBat;
#[cfg(any(
feature = "stm32f031",
feature = "stm32f038",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
adc_pins!(
VBat => 18_u8,
);
#[cfg(any(
feature = "stm32f031",
feature = "stm32f038",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
impl VBat {
pub fn new() -> Self {
VBat::default()
}
pub fn enable(&mut self, adc: &mut Adc) {
adc.rb.ccr.modify(|_, w| w.vbaten().set_bit());
}
pub fn disable(&mut self, adc: &mut Adc) {
adc.rb.ccr.modify(|_, w| w.vbaten().clear_bit());
}
pub fn is_enabled(&self, adc: &Adc) -> bool {
adc.rb.ccr.read().vbaten().bit_is_set()
}
pub fn read(adc: &mut Adc) -> u16 {
let mut vbat = Self::new();
let vbat_val: u16 = if vbat.is_enabled(&adc) {
adc.read_abs_mv(&mut vbat)
} else {
vbat.enable(adc);
let ret = adc.read_abs_mv(&mut vbat);
vbat.disable(adc);
ret
};
vbat_val * 2
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct StoredConfig(AdcSampleTime, AdcAlign, AdcPrecision);
impl Adc {
pub fn new(adc: ADC, rcc: &mut Rcc) -> Self {
let mut s = Self {
rb: adc,
sample_time: AdcSampleTime::default(),
align: AdcAlign::default(),
precision: AdcPrecision::default(),
};
s.select_clock(rcc);
s.calibrate();
s
}
pub fn save_cfg(&mut self) -> StoredConfig {
StoredConfig(self.sample_time, self.align, self.precision)
}
pub fn restore_cfg(&mut self, cfg: StoredConfig) {
self.sample_time = cfg.0;
self.align = cfg.1;
self.precision = cfg.2;
}
pub fn default_cfg(&mut self) -> StoredConfig {
let cfg = self.save_cfg();
self.sample_time = AdcSampleTime::default();
self.align = AdcAlign::default();
self.precision = AdcPrecision::default();
cfg
}
pub fn set_sample_time(&mut self, t_samp: AdcSampleTime) {
self.sample_time = t_samp;
}
pub fn set_align(&mut self, align: AdcAlign) {
self.align = align;
}
pub fn set_precision(&mut self, precision: AdcPrecision) {
self.precision = precision;
}
pub fn max_sample(&self) -> u16 {
match self.align {
AdcAlign::Left => u16::max_value(),
AdcAlign::LeftAsRM => match self.precision {
AdcPrecision::B_6 => u16::from(u8::max_value()),
_ => u16::max_value(),
},
AdcAlign::Right => match self.precision {
AdcPrecision::B_12 => (1 << 12) - 1,
AdcPrecision::B_10 => (1 << 10) - 1,
AdcPrecision::B_8 => (1 << 8) - 1,
AdcPrecision::B_6 => (1 << 6) - 1,
},
}
}
pub fn read_abs_mv<PIN: Channel<Adc, ID = u8>>(&mut self, pin: &mut PIN) -> u16 {
let vdda = u32::from(VRef::read_vdda(self));
let v: u32 = self.read(pin).unwrap();
let max_samp = u32::from(self.max_sample());
(v * vdda / max_samp) as u16
}
fn calibrate(&mut self) {
if self.rb.cr.read().aden().is_enabled() {
self.rb.cr.modify(|_, w| w.addis().disable());
}
while self.rb.cr.read().aden().is_enabled() {}
self.rb.cfgr1.modify(|_, w| w.dmaen().disabled());
self.rb.cr.modify(|_, w| w.adcal().start_calibration());
while self.rb.cr.read().adcal().is_calibrating() {}
}
fn select_clock(&mut self, rcc: &mut Rcc) {
rcc.regs.apb2enr.modify(|_, w| w.adcen().enabled());
rcc.regs.cr2.modify(|_, w| w.hsi14on().on());
while rcc.regs.cr2.read().hsi14rdy().is_not_ready() {}
}
fn power_up(&mut self) {
if self.rb.isr.read().adrdy().is_ready() {
self.rb.isr.modify(|_, w| w.adrdy().clear());
}
self.rb.cr.modify(|_, w| w.aden().enabled());
while self.rb.isr.read().adrdy().is_not_ready() {}
}
fn power_down(&mut self) {
self.rb.cr.modify(|_, w| w.adstp().stop_conversion());
while self.rb.cr.read().adstp().is_stopping() {}
self.rb.cr.modify(|_, w| w.addis().disable());
while self.rb.cr.read().aden().is_enabled() {}
}
fn convert(&mut self, chan: u8) -> u16 {
self.rb.chselr.write(|w| unsafe { w.bits(1_u32 << chan) });
self.rb
.smpr
.write(|w| w.smp().variant(self.sample_time.into()));
self.rb.cfgr1.modify(|_, w| {
w.res()
.variant(self.precision.into())
.align()
.variant(self.align.into())
});
self.rb.cr.modify(|_, w| w.adstart().start_conversion());
while self.rb.isr.read().eoc().is_not_complete() {}
let res = self.rb.dr.read().bits() as u16;
if self.align == AdcAlign::Left && self.precision == AdcPrecision::B_6 {
res << 8
} else {
res
}
}
}
impl<WORD, PIN> OneShot<Adc, WORD, PIN> for Adc
where
WORD: From<u16>,
PIN: Channel<Adc, ID = u8>,
{
type Error = ();
fn read(&mut self, _pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
self.power_up();
let res = self.convert(PIN::channel());
self.power_down();
Ok(res.into())
}
}