Merge pull request #380 from bgamari/stm32g0-adc

Fix STM32G0 ADC
This commit is contained in:
Dario Nieuwenhuis 2021-09-29 06:58:33 +02:00 committed by GitHub
commit d9e2d17625
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 251 additions and 77 deletions

View File

@ -1 +0,0 @@

View File

@ -1,7 +1,7 @@
#![macro_use] #![macro_use]
#[cfg_attr(adc_v3, path = "v3.rs")] #[cfg_attr(adc_v3, path = "v3.rs")]
#[cfg_attr(adc_g0, path = "g0.rs")] #[cfg_attr(adc_g0, path = "v3.rs")]
mod _version; mod _version;
#[allow(unused)] #[allow(unused)]
@ -72,6 +72,9 @@ macro_rules! impl_pin {
} }
crate::pac::peripheral_pins!( crate::pac::peripheral_pins!(
($inst:ident, adc, ADC, $pin:ident, IN0) => {
impl_pin!($inst, $pin, 0);
};
($inst:ident, adc, ADC, $pin:ident, IN1) => { ($inst:ident, adc, ADC, $pin:ident, IN1) => {
impl_pin!($inst, $pin, 1); impl_pin!($inst, $pin, 1);
}; };

View File

@ -6,6 +6,17 @@ use embedded_hal::blocking::delay::DelayUs;
pub const VDDA_CALIB_MV: u32 = 3000; pub const VDDA_CALIB_MV: u32 = 3000;
/// Sadly we cannot use `RccPeripheral::enable` since devices are quite inconsistent ADC clock
/// configuration.
unsafe fn enable() {
#[cfg(rcc_h7)]
crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true));
#[cfg(rcc_g0)]
crate::pac::RCC.apbenr2().modify(|w| w.set_adcen(true));
#[cfg(rcc_l4)]
crate::pac::RCC.ahb2enr().modify(|w| w.set_adcen(true));
}
pub enum Resolution { pub enum Resolution {
TwelveBit, TwelveBit,
TenBit, TenBit,
@ -43,7 +54,11 @@ pub struct Vref;
impl<T: Instance> AdcPin<T> for Vref {} impl<T: Instance> AdcPin<T> for Vref {}
impl<T: Instance> super::sealed::AdcPin<T> for Vref { impl<T: Instance> super::sealed::AdcPin<T> for Vref {
fn channel(&self) -> u8 { fn channel(&self) -> u8 {
0 #[cfg(not(rcc_g0))]
let val = 0;
#[cfg(rcc_g0)]
let val = 13;
val
} }
} }
@ -51,7 +66,11 @@ pub struct Temperature;
impl<T: Instance> AdcPin<T> for Temperature {} impl<T: Instance> AdcPin<T> for Temperature {}
impl<T: Instance> super::sealed::AdcPin<T> for Temperature { impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
fn channel(&self) -> u8 { fn channel(&self) -> u8 {
17 #[cfg(not(rcc_g0))]
let val = 17;
#[cfg(rcc_g0)]
let val = 12;
val
} }
} }
@ -59,10 +78,16 @@ pub struct Vbat;
impl<T: Instance> AdcPin<T> for Vbat {} impl<T: Instance> AdcPin<T> for Vbat {}
impl<T: Instance> super::sealed::AdcPin<T> for Vbat { impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
fn channel(&self) -> u8 { fn channel(&self) -> u8 {
18 #[cfg(not(rcc_g0))]
let val = 18;
#[cfg(rcc_g0)]
let val = 14;
val
} }
} }
#[cfg(not(adc_g0))]
mod sample_time {
/// ADC sample time /// ADC sample time
/// ///
/// The default setting is 2.5 ADC clock cycles. /// The default setting is 2.5 ADC clock cycles.
@ -94,7 +119,7 @@ pub enum SampleTime {
} }
impl SampleTime { impl SampleTime {
fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime {
match self { match self {
SampleTime::Cycles2_5 => crate::pac::adc::vals::SampleTime::CYCLES2_5, SampleTime::Cycles2_5 => crate::pac::adc::vals::SampleTime::CYCLES2_5,
SampleTime::Cycles6_5 => crate::pac::adc::vals::SampleTime::CYCLES6_5, SampleTime::Cycles6_5 => crate::pac::adc::vals::SampleTime::CYCLES6_5,
@ -113,6 +138,63 @@ impl Default for SampleTime {
Self::Cycles2_5 Self::Cycles2_5
} }
} }
}
#[cfg(adc_g0)]
mod sample_time {
/// ADC sample time
///
/// The default setting is 1.5 ADC clock cycles.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum SampleTime {
/// 1.5 ADC clock cycles
Cycles1_5 = 0b000,
/// 3.5 ADC clock cycles
Cycles3_5 = 0b001,
/// 7.5 ADC clock cycles
Cycles7_5 = 0b010,
/// 12.5 ADC clock cycles
Cycles12_5 = 0b011,
/// 19.5 ADC clock cycles
Cycles19_5 = 0b100,
/// 39.5 ADC clock cycles
Cycles39_5 = 0b101,
/// 79.5 ADC clock cycles
Cycles79_5 = 0b110,
/// 160.5 ADC clock cycles
Cycles160_5 = 0b111,
}
impl SampleTime {
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime {
match self {
SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5,
SampleTime::Cycles3_5 => crate::pac::adc::vals::SampleTime::CYCLES3_5,
SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5,
SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5,
SampleTime::Cycles19_5 => crate::pac::adc::vals::SampleTime::CYCLES19_5,
SampleTime::Cycles39_5 => crate::pac::adc::vals::SampleTime::CYCLES39_5,
SampleTime::Cycles79_5 => crate::pac::adc::vals::SampleTime::CYCLES79_5,
SampleTime::Cycles160_5 => crate::pac::adc::vals::SampleTime::CYCLES160_5,
}
}
}
impl Default for SampleTime {
fn default() -> Self {
Self::Cycles1_5
}
}
}
pub use sample_time::SampleTime;
pub struct Adc<'d, T: Instance> { pub struct Adc<'d, T: Instance> {
sample_time: SampleTime, sample_time: SampleTime,
@ -125,15 +207,26 @@ impl<'d, T: Instance> Adc<'d, T> {
pub fn new(_peri: impl Unborrow<Target = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { pub fn new(_peri: impl Unborrow<Target = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
unborrow!(_peri); unborrow!(_peri);
unsafe { unsafe {
enable();
T::regs().cr().modify(|reg| { T::regs().cr().modify(|reg| {
#[cfg(not(adc_g0))]
reg.set_deeppwd(false); reg.set_deeppwd(false);
reg.set_advregen(true); reg.set_advregen(true);
}); });
#[cfg(adc_g0)]
T::regs().cfgr1().modify(|reg| {
reg.set_chselrmod(true);
});
} }
delay.delay_us(20); delay.delay_us(20);
unsafe { unsafe {
T::regs().cr().modify(|reg| {
reg.set_adcal(true);
});
while T::regs().cr().read().adcal() { while T::regs().cr().read().adcal() {
// spin // spin
} }
@ -229,6 +322,27 @@ impl<'d, T: Instance> Adc<'d, T> {
} }
*/ */
/// Perform a single conversion.
fn convert(&mut self) -> u16 {
unsafe {
T::regs().isr().modify(|reg| {
reg.set_eos(true);
reg.set_eoc(true);
});
// Start conversion
T::regs().cr().modify(|reg| {
reg.set_adstart(true);
});
while !T::regs().isr().read().eos() {
// spin
}
T::regs().dr().read().0 as u16
}
}
pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 { pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
unsafe { unsafe {
// Make sure bits are off // Make sure bits are off
@ -249,48 +363,36 @@ impl<'d, T: Instance> Adc<'d, T> {
} }
// Configure ADC // Configure ADC
#[cfg(not(rcc_g0))]
T::regs() T::regs()
.cfgr() .cfgr()
.modify(|reg| reg.set_res(self.resolution.res())); .modify(|reg| reg.set_res(self.resolution.res()));
#[cfg(rcc_g0)]
T::regs()
.cfgr1()
.modify(|reg| reg.set_res(self.resolution.res()));
// Configure channel // Configure channel
Self::set_channel_sample_time(pin.channel(), self.sample_time); Self::set_channel_sample_time(pin.channel(), self.sample_time);
// Select channel // Select channel
#[cfg(not(rcc_g0))]
T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
#[cfg(rcc_g0)]
T::regs()
.chselr()
.write(|reg| reg.set_chsel(pin.channel() as u32));
// Start conversion // Some models are affected by an erratum:
T::regs().isr().modify(|reg| { // If we perform conversions slower than 1 kHz, the first read ADC value can be
reg.set_eos(true); // corrupted, so we discard it and measure again.
reg.set_eoc(true); //
}); // STM32L471xx: Section 2.7.3
T::regs().cr().modify(|reg| { // STM32G4: Section 2.7.3
reg.set_adstart(true); #[cfg(any(rcc_l4, rcc_g4))]
}); let _ = self.convert();
while !T::regs().isr().read().eos() { let val = self.convert();
// spin
}
// Read ADC value first time and discard it, as per errata sheet.
// The errata states that if we do conversions slower than 1 kHz, the
// first read ADC value can be corrupted, so we discard it and measure again.
let _ = T::regs().dr().read();
T::regs().isr().modify(|reg| {
reg.set_eos(true);
reg.set_eoc(true);
});
T::regs().cr().modify(|reg| {
reg.set_adstart(true);
});
while !T::regs().isr().read().eos() {
// spin
}
let val = T::regs().dr().read().0 as u16;
T::regs().cr().modify(|reg| reg.set_addis(true)); T::regs().cr().modify(|reg| reg.set_addis(true));
@ -298,6 +400,14 @@ impl<'d, T: Instance> Adc<'d, T> {
} }
} }
#[cfg(rcc_g0)]
unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
T::regs()
.smpr()
.modify(|reg| reg.set_smp1(sample_time.sample_time()));
}
#[cfg(not(rcc_g0))]
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
if ch <= 9 { if ch <= 9 {
T::regs() T::regs()

View File

@ -5,6 +5,17 @@ use core::marker::PhantomData;
use embassy::util::Unborrow; use embassy::util::Unborrow;
use embassy_hal_common::unborrow; use embassy_hal_common::unborrow;
/// Sadly we cannot use `RccPeripheral::enable` since devices are quite inconsistent DAC clock
/// configuration.
unsafe fn enable() {
#[cfg(rcc_h7)]
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
#[cfg(rcc_g0)]
crate::pac::RCC.apbenr1().modify(|w| w.set_dac1en(true));
#[cfg(rcc_l4)]
crate::pac::RCC.apb1enr1().modify(|w| w.set_dac1en(true));
}
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error { pub enum Error {
@ -91,6 +102,10 @@ impl<'d, T: Instance> Dac<'d, T> {
) -> Self { ) -> Self {
unborrow!(ch1, ch2); unborrow!(ch1, ch2);
unsafe {
enable();
}
let ch1 = ch1.degrade_optional(); let ch1 = ch1.degrade_optional();
if ch1.is_some() { if ch1.is_some() {
unsafe { unsafe {

View File

@ -18,10 +18,37 @@ pub const LSI_FREQ: u32 = 32_000;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum ClockSrc { pub enum ClockSrc {
HSE(Hertz), HSE(Hertz),
HSI16, HSI16(HSI16Prescaler),
LSI, LSI,
} }
#[derive(Clone, Copy)]
pub enum HSI16Prescaler {
NotDivided,
Div2,
Div4,
Div8,
Div16,
Div32,
Div64,
Div128,
}
impl Into<u8> for HSI16Prescaler {
fn into(self) -> u8 {
match self {
HSI16Prescaler::NotDivided => 0x00,
HSI16Prescaler::Div2 => 0x01,
HSI16Prescaler::Div4 => 0x02,
HSI16Prescaler::Div8 => 0x03,
HSI16Prescaler::Div16 => 0x04,
HSI16Prescaler::Div32 => 0x05,
HSI16Prescaler::Div64 => 0x06,
HSI16Prescaler::Div128 => 0x07,
}
}
}
impl Into<u8> for APBPrescaler { impl Into<u8> for APBPrescaler {
fn into(self) -> u8 { fn into(self) -> u8 {
match self { match self {
@ -55,15 +82,17 @@ pub struct Config {
mux: ClockSrc, mux: ClockSrc,
ahb_pre: AHBPrescaler, ahb_pre: AHBPrescaler,
apb_pre: APBPrescaler, apb_pre: APBPrescaler,
low_power_run: bool,
} }
impl Default for Config { impl Default for Config {
#[inline] #[inline]
fn default() -> Config { fn default() -> Config {
Config { Config {
mux: ClockSrc::HSI16, mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided),
ahb_pre: AHBPrescaler::NotDivided, ahb_pre: AHBPrescaler::NotDivided,
apb_pre: APBPrescaler::NotDivided, apb_pre: APBPrescaler::NotDivided,
low_power_run: false,
} }
} }
} }
@ -86,6 +115,12 @@ impl Config {
self.apb_pre = pre; self.apb_pre = pre;
self self
} }
#[inline]
pub fn low_power_run(mut self, on: bool) -> Self {
self.low_power_run = on;
self
}
} }
/// RCC peripheral /// RCC peripheral
@ -119,14 +154,18 @@ impl RccExt for RCC {
fn freeze(self, cfgr: Config) -> Clocks { fn freeze(self, cfgr: Config) -> Clocks {
let rcc = pac::RCC; let rcc = pac::RCC;
let (sys_clk, sw) = match cfgr.mux { let (sys_clk, sw) = match cfgr.mux {
ClockSrc::HSI16 => { ClockSrc::HSI16(div) => {
// Enable HSI16 // Enable HSI16
let div: u8 = div.into();
unsafe { unsafe {
rcc.cr().write(|w| w.set_hsion(true)); rcc.cr().write(|w| {
w.set_hsidiv(div);
w.set_hsion(true)
});
while !rcc.cr().read().hsirdy() {} while !rcc.cr().read().hsirdy() {}
} }
(HSI_FREQ, 0x00) (HSI_FREQ >> div, 0x00)
} }
ClockSrc::HSE(freq) => { ClockSrc::HSE(freq) => {
// Enable HSE // Enable HSE
@ -174,6 +213,14 @@ impl RccExt for RCC {
} }
}; };
let pwr = pac::PWR;
if cfgr.low_power_run {
assert!(sys_clk.hz() <= 2_000_000.hz());
unsafe {
pwr.cr1().modify(|w| w.set_lpr(true));
}
}
Clocks { Clocks {
sys: sys_clk.hz(), sys: sys_clk.hz(),
ahb: ahb_freq.hz(), ahb: ahb_freq.hz(),