Differentiate between read and read_internal for STM32F0 ADC

The internal channels (vbat, vref, and temperature) are not real pins and do
not have the `set_as_analog` method. They must be read using the
`read_internal` method.
This commit is contained in:
Matthew W. Samsonoff 2022-10-03 14:18:37 -04:00 committed by Grant Miller
parent a0b6096610
commit 511a951246

View File

@ -48,25 +48,33 @@ impl Resolution {
} }
} }
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
mod sealed {
pub trait InternalChannel<T> {
fn channel(&self) -> u8;
}
}
pub struct Vbat; pub struct Vbat;
impl<T: Instance> AdcPin<T> for Vbat {} impl<T: Instance> InternalChannel<T> for Vbat {}
impl<T: Instance> super::sealed::AdcPin<T> for Vbat { impl<T: Instance> sealed::InternalChannel<T> for Vbat {
fn channel(&self) -> u8 { fn channel(&self) -> u8 {
18 18
} }
} }
pub struct Vref; pub struct Vref;
impl<T: Instance> AdcPin<T> for Vref {} impl<T: Instance> InternalChannel<T> for Vref {}
impl<T: Instance> super::sealed::AdcPin<T> for Vref { impl<T: Instance> sealed::InternalChannel<T> for Vref {
fn channel(&self) -> u8 { fn channel(&self) -> u8 {
17 17
} }
} }
pub struct Temperature; pub struct Temperature;
impl<T: Instance> AdcPin<T> for Temperature {} impl<T: Instance> InternalChannel<T> for Temperature {}
impl<T: Instance> super::sealed::AdcPin<T> for Temperature { impl<T: Instance> sealed::InternalChannel<T> for Temperature {
fn channel(&self) -> u8 { fn channel(&self) -> u8 {
16 16
} }
@ -219,64 +227,64 @@ impl<'d, T: Instance> Adc<'d, T> {
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
} }
fn convert(&mut self) -> u16 {
unsafe {
T::regs().isr().modify(|reg| {
reg.set_eoc(true);
reg.set_eosmp(true);
});
// A.7.5 Single conversion sequence code example - Software trigger
T::regs().cr().modify(|reg| reg.set_adstart(true));
while !T::regs().isr().read().eoc() {
// spin
}
T::regs().dr().read().0 as u16
}
}
pub fn read<P>(&mut self, pin: &mut P) -> u16 pub fn read<P>(&mut self, pin: &mut P) -> u16
where where
P: AdcPin<T> + crate::gpio::sealed::Pin, P: AdcPin<T> + crate::gpio::sealed::Pin,
{ {
let channel = pin.channel();
unsafe { unsafe {
// A.7.2 ADC enable sequence code example
if T::regs().isr().read().adrdy() {
T::regs().isr().modify(|reg| reg.set_adrdy(true));
}
T::regs().cr().modify(|reg| reg.set_aden(true));
while !T::regs().isr().read().adrdy() {
// ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration
// Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the
// ADEN bit until the ADRDY flag goes high.
T::regs().cr().modify(|reg| reg.set_aden(true));
}
T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res()));
Self::set_channel_sample_time(pin.channel(), self.sample_time);
pin.set_as_analog(); pin.set_as_analog();
T::regs() self.read_channel(channel)
.chselr()
.write(|reg| reg.set_chselx(pin.channel() as usize, true));
let value = self.convert();
// A.7.3 ADC disable code example
T::regs().cr().modify(|reg| reg.set_adstp(true));
while T::regs().cr().read().adstp() {
// spin
}
T::regs().cr().modify(|reg| reg.set_addis(true));
while T::regs().cr().read().aden() {
// spin
}
value
} }
} }
unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); let channel = channel.channel();
unsafe {
self.read_channel(channel)
}
}
unsafe fn read_channel(&mut self, channel: u8) -> u16 {
// A.7.2 ADC enable sequence code example
if T::regs().isr().read().adrdy() {
T::regs().isr().modify(|reg| reg.set_adrdy(true));
}
T::regs().cr().modify(|reg| reg.set_aden(true));
while !T::regs().isr().read().adrdy() {
// ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration
// Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the
// ADEN bit until the ADRDY flag goes high.
T::regs().cr().modify(|reg| reg.set_aden(true));
}
T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res()));
T::regs().isr().modify(|reg| {
reg.set_eoc(true);
reg.set_eosmp(true);
});
// A.7.5 Single conversion sequence code example - Software trigger
T::regs()
.chselr()
.write(|reg| reg.set_chselx(channel as usize, true));
T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.sample_time()));
T::regs().cr().modify(|reg| reg.set_adstart(true));
while !T::regs().isr().read().eoc() {
// spin
}
let value = T::regs().dr().read().0 as u16;
// A.7.3 ADC disable code example
T::regs().cr().modify(|reg| reg.set_adstp(true));
while T::regs().cr().read().adstp() {
// spin
}
T::regs().cr().modify(|reg| reg.set_addis(true));
while T::regs().cr().read().aden() {
// spin
}
value
} }
} }