const VREFINT_VAL: u16 = 1200;
const VTEMPCAL_LOW: *const u16 = 0x1FFF_0F14 as *const u16;
const VTEMPCAL_HIGH: *const u16 = 0x1FFF_0F18 as *const u16;
const VTEMPVAL_LOW: u16 = 30;
const VTEMPVAL_HIGH: u16 = 85;
const VTEMPVAL_DELTA: u16 = VTEMPVAL_HIGH - VTEMPVAL_LOW;
use core::ptr;
use embedded_hal_02::adc::{Channel, OneShot};
use embedded_hal_02::blocking::delay::DelayUs;
use crate::{
gpio::*,
pac::{
adc::{
cfgr1::{ALIGN_A, RES_A},
cfgr2::CKMODE_A,
smpr::SMP_A,
},
ADC, RCC,
},
rcc::{Enable, Reset},
};
pub struct Adc {
rb: ADC,
sample_time: AdcSampleTime,
align: AdcAlign,
precision: AdcPrecision,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AdcClockMode {
Pclk,
PclkDiv2,
PclkDiv4,
PclkDiv8,
PclkDiv16,
PclkDiv32,
PclkDiv64,
Hsi,
HsiDiv2,
HsiDiv4,
HsiDiv8,
HsiDiv16,
HsiDiv32,
HsiDiv64,
}
impl AdcClockMode {
pub fn default() -> Self {
AdcClockMode::Pclk
}
}
impl From<AdcClockMode> for CKMODE_A {
fn from(value: AdcClockMode) -> Self {
match value {
AdcClockMode::Pclk => CKMODE_A::Pclk,
AdcClockMode::PclkDiv2 => CKMODE_A::PclkDiv2,
AdcClockMode::PclkDiv4 => CKMODE_A::PclkDiv4,
AdcClockMode::PclkDiv8 => CKMODE_A::PclkDiv8,
AdcClockMode::PclkDiv16 => CKMODE_A::PclkDiv16,
AdcClockMode::PclkDiv32 => CKMODE_A::PclkDiv32,
AdcClockMode::PclkDiv64 => CKMODE_A::PclkDiv64,
AdcClockMode::Hsi => CKMODE_A::Hsi,
AdcClockMode::HsiDiv2 => CKMODE_A::HsiDiv2,
AdcClockMode::HsiDiv4 => CKMODE_A::HsiDiv4,
AdcClockMode::HsiDiv8 => CKMODE_A::HsiDiv8,
AdcClockMode::HsiDiv16 => CKMODE_A::HsiDiv16,
AdcClockMode::HsiDiv32 => CKMODE_A::HsiDiv32,
AdcClockMode::HsiDiv64 => CKMODE_A::HsiDiv64,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AdcSampleTime {
T_3,
T_5,
T_7,
T_13,
T_28,
T_41,
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_3 => SMP_A::Cycles35,
AdcSampleTime::T_5 => SMP_A::Cycles55,
AdcSampleTime::T_7 => SMP_A::Cycles75,
AdcSampleTime::T_13 => SMP_A::Cycles135,
AdcSampleTime::T_28 => SMP_A::Cycles285,
AdcSampleTime::T_41 => SMP_A::Cycles415,
AdcSampleTime::T_71 => SMP_A::Cycles715,
AdcSampleTime::T_239 => SMP_A::Cycles2395,
}
}
}
#[derive(Copy, Clone, 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(Copy, Clone, 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,
);
#[derive(Debug, Default)]
pub struct VTemp;
#[derive(Debug, Default)]
pub struct VRef;
adc_pins!(
VTemp => 11_u8,
VRef => 12_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) -> i16 {
let vtempcal_low = unsafe { ptr::read(VTEMPCAL_LOW) } as i32;
let vtempcal_high = unsafe { ptr::read(VTEMPCAL_HIGH) } as i32;
((vtemp as i32 - vtempcal_low) * 100 * (VTEMPVAL_DELTA as i32)
/ (vtempcal_high - vtempcal_low)
+ 3000) as i16
}
pub fn read(adc: &mut Adc, delay: Option<&mut dyn DelayUs<u32>>) -> 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);
} else {
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)
}
}
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 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(VREFINT_VAL) * 4095) / vref_val) as u16
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct StoredConfig(AdcSampleTime, AdcAlign, AdcPrecision);
impl Adc {
pub fn new(adc: ADC, ckmode: AdcClockMode) -> Self {
let mut s = Self {
rb: adc,
sample_time: AdcSampleTime::default(),
align: AdcAlign::default(),
precision: AdcPrecision::default(),
};
s.select_clock(ckmode);
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 val: u32 = self.read(pin).unwrap();
let max_samp = u32::from(self.max_sample());
(val * vdda / max_samp) as u16
}
fn calibrate(&mut self) {
if self.rb.cr.read().aden().is_enabled() {
self.rb.cr.modify(|_, w| w.adstp().stop_conversion());
while self.rb.cr.read().adstp().is_stopping() {}
}
while self.rb.cr.read().aden().is_enabled() {}
#[cfg(feature = "with-dma")]
let (dmacfg, dmaen) = {
let dmacfg = self.rb.cfgr1.read().dmacfg().bit();
let dmaen = self.rb.cfgr1.read().dmaen().is_enabled();
(dmacfg, dmaen)
};
#[cfg(feature = "with-dma")]
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() {}
#[cfg(feature = "with-dma")]
self.rb
.cfgr1
.modify(|_, w| w.dmacfg().bit(dmacfg).dmaen().bit(dmaen));
}
fn select_clock(&mut self, ckmode: AdcClockMode) {
let rcc = unsafe { &(*RCC::ptr()) };
ADC::reset(rcc);
ADC::enable(rcc);
self.rb
.cfgr2
.modify(|_, w| w.ckmode().variant(ckmode.into()));
}
fn apply_cfg(&mut self) {
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())
.wait()
.disabled()
});
}
fn power_up(&mut self, chan: u8) {
self.apply_cfg();
self.rb.chselr.write(|w| unsafe { w.bits(1_u32 << chan) });
self.rb.cr.modify(|_, w| w.aden().enabled());
}
fn power_down(&mut self) {
if self.rb.cr.read().aden().is_enabled() {
self.rb.cr.modify(|_, w| w.adstp().stop_conversion());
while self.rb.cr.read().adstp().is_stopping() {}
}
}
fn convert(&mut self) -> u16 {
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(PIN::channel());
let res = self.convert();
self.power_down();
Ok(res.into())
}
}