use cfg_if::cfg_if;
#[cfg(adc_g0)]
use heapless::Vec;
#[cfg(adc_g0)]
use pac::adc::vals::Ckmode;
use pac::adc::vals::Dmacfg;
#[cfg(adc_v3)]
use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs};
#[cfg(adc_g0)]
pub use pac::adc::vals::{Ovsr, Ovss, Presc};
#[allow(unused_imports)]
use super::SealedAdcChannel;
use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us};
use crate::adc::ConversionMode;
use crate::{Peri, pac, rcc};
pub const VREF_DEFAULT_MV: u32 = 3300;
#[cfg(any(adc_v3, adc_g0, adc_u0))]
pub const VREF_CALIB_MV: u32 = 3000;
#[cfg(any(adc_h5, adc_h7rs))]
pub const VREF_CALIB_MV: u32 = 3300;
#[cfg(adc_g0)]
const SAMPLE_TIMES_CAPACITY: usize = 2;
#[cfg(adc_g0)]
impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {
const CHANNEL: u8 = 13;
}
#[cfg(any(adc_h5, adc_h7rs))]
impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {
const CHANNEL: u8 = 17;
}
#[cfg(adc_u0)]
impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {
const CHANNEL: u8 = 12;
}
#[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))]
impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {
const CHANNEL: u8 = 0;
}
#[cfg(adc_g0)]
impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {
const CHANNEL: u8 = 12;
}
#[cfg(any(adc_h5, adc_h7rs))]
impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {
const CHANNEL: u8 = 16;
}
#[cfg(adc_u0)]
impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {
const CHANNEL: u8 = 11;
}
#[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))]
impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {
const CHANNEL: u8 = 17;
}
#[cfg(adc_g0)]
impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
const CHANNEL: u8 = 14;
}
#[cfg(any(adc_h5, adc_h7rs))]
impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
const CHANNEL: u8 = 16;
}
#[cfg(adc_u0)]
impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
const CHANNEL: u8 = 13;
}
#[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))]
impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
const CHANNEL: u8 = 18;
}
cfg_if! {
if #[cfg(any(adc_h5, adc_h7rs))] {
pub struct VddCore;
impl<T: Instance> super::AdcChannel<T> for VddCore {}
impl<T: Instance> super::SealedAdcChannel<T> for VddCore {
fn channel(&self) -> u8 {
17
}
}
}
}
cfg_if! {
if #[cfg(adc_u0)] {
pub struct DacOut;
impl<T: Instance> super::AdcChannel<T> for DacOut {}
impl<T: Instance> super::SealedAdcChannel<T> for DacOut {
fn channel(&self) -> u8 {
19
}
}
}
}
cfg_if! { if #[cfg(adc_g0)] {
pub enum CkModePclk {
DIV1,
DIV2,
DIV4,
}
pub enum Clock {
Sync { div: CkModePclk },
Async { div: Presc },
}
}}
#[cfg(adc_u0)]
type Ovss = u8;
#[cfg(adc_u0)]
type Ovsr = u8;
#[cfg(adc_v3)]
type Ovss = OversamplingShift;
#[cfg(adc_v3)]
type Ovsr = OversamplingRatio;
#[derive(Default)]
pub struct AdcConfig {
#[cfg(any(adc_u0, adc_g0, adc_v3))]
pub oversampling_shift: Option<Ovss>,
#[cfg(any(adc_u0, adc_g0, adc_v3))]
pub oversampling_ratio: Option<Ovsr>,
#[cfg(any(adc_u0, adc_g0))]
pub oversampling_enable: Option<bool>,
#[cfg(adc_v3)]
pub oversampling_mode: Option<(Rovsm, Trovs, bool)>,
#[cfg(adc_g0)]
pub clock: Option<Clock>,
pub resolution: Option<Resolution>,
pub averaging: Option<Averaging>,
}
impl super::AdcRegs for crate::pac::adc::Adc {
fn data(&self) -> *mut u16 {
crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16
}
fn enable(&self) {
#[cfg(adc_u0)]
if self.cfgr1().read().autoff() {
return;
}
while self.cr().read().addis() {
}
if !self.cr().read().aden() {
self.isr().modify(|reg| {
reg.set_adrdy(true);
});
self.cr().modify(|reg| {
reg.set_aden(true);
});
while !self.isr().read().adrdy() {
}
}
}
fn start(&self) {
self.cr().modify(|reg| {
reg.set_adstart(true);
});
}
fn stop(&self) {
if self.cr().read().adstart() && !self.cr().read().addis() {
self.cr().modify(|reg| {
reg.set_adstp(true);
});
while self.cr().read().adstart() {}
}
#[cfg(not(any(adc_g0, adc_u0)))]
self.cfgr().modify(|reg| {
reg.set_cont(false);
reg.set_dmaen(false);
});
#[cfg(any(adc_g0, adc_u0))]
self.cfgr1().modify(|reg| {
reg.set_cont(false);
reg.set_dmaen(false);
});
}
fn convert(&self) {
#[cfg(any(rcc_l4, rcc_g4))]
let len = 2;
#[cfg(not(any(rcc_l4, rcc_g4)))]
let len = 1;
for _ in 0..len {
self.isr().modify(|reg| {
reg.set_eos(true);
reg.set_eoc(true);
});
self.cr().modify(|reg| {
reg.set_adstart(true);
});
while !self.isr().read().eos() {
}
}
}
fn configure_dma(&self, conversion_mode: ConversionMode) {
self.isr().modify(|reg| {
reg.set_ovr(true);
});
#[cfg(not(any(adc_g0, adc_u0)))]
let regs = self.cfgr();
#[cfg(any(adc_g0, adc_u0))]
let regs = self.cfgr1();
match conversion_mode {
ConversionMode::Singular => {
regs.modify(|reg| {
reg.set_discen(false);
reg.set_cont(true);
reg.set_dmacfg(Dmacfg::ONE_SHOT);
reg.set_dmaen(true);
});
}
#[cfg(any(adc_v3, adc_g0, adc_u0))]
ConversionMode::Repeated(trigger) => {
#[cfg(not(adc_g0))]
{
let _ = trigger; regs.modify(|reg| {
reg.set_discen(false);
reg.set_cont(true);
reg.set_dmacfg(Dmacfg::CIRCULAR);
reg.set_dmaen(true);
});
}
#[cfg(adc_g0)]
match trigger.signal {
u8::MAX => {
regs.modify(|reg| {
reg.set_discen(false);
reg.set_cont(true);
reg.set_dmacfg(Dmacfg::CIRCULAR);
reg.set_dmaen(true);
});
}
_ => {
regs.modify(|reg| {
reg.set_discen(false);
reg.set_cont(false); reg.set_dmacfg(Dmacfg::CIRCULAR);
reg.set_dmaen(true);
reg.set_exten(trigger.edge);
reg.set_extsel(trigger.signal.into());
});
self.ier().modify(|r| r.set_eosie(false));
}
}
}
}
}
fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
#[cfg(adc_h5)]
self.cr().modify(|w| w.set_aden(false));
#[cfg(not(any(adc_g0, adc_u0)))]
self.sqr1().modify(|w| {
w.set_l(sequence.len() as u8 - 1);
});
#[cfg(adc_g0)]
{
let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new();
self.chselr().write(|chselr| {
self.smpr().write(|smpr| {
for ((channel, _), sample_time) in sequence {
chselr.set_chsel(channel.into(), true);
if let Some(i) = sample_times.iter().position(|&t| t == sample_time) {
smpr.set_smpsel(channel.into(), (i as u8).into());
} else {
smpr.set_sample_time(sample_times.len(), sample_time);
if let Err(_) = sample_times.push(sample_time) {
panic!(
"Implementation is limited to {} unique sample times among all channels.",
SAMPLE_TIMES_CAPACITY
);
}
}
}
})
});
}
#[cfg(not(adc_g0))]
{
#[cfg(adc_u0)]
let mut channel_mask = 0;
#[cfg(adc_h5)]
let mut difsel = 0u32;
for (_i, ((channel, _is_differential), sample_time)) in sequence.enumerate() {
#[cfg(any(adc_h5, adc_h7rs))]
if channel == 0 {
self.or().modify(|reg| reg.set_op0(true));
}
cfg_if! {
if #[cfg(adc_u0)] {
self.smpr().modify(|reg| reg.set_smp1(sample_time.into()));
} else if #[cfg(any(adc_h5, adc_h7rs))] {
match channel {
0..=9 => self.smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())),
_ => self.smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())),
}
} else {
let sample_time = sample_time.into();
self
.smpr(channel as usize / 10)
.modify(|reg| reg.set_smp(channel as usize % 10, sample_time));
}
}
#[cfg(stm32h7)]
{
use crate::pac::adc::vals::Pcsel;
self.cfgr2().modify(|w| w.set_lshift(0));
self.pcsel()
.write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED));
}
#[cfg(not(any(adc_g0, adc_u0)))]
match _i {
0..=3 => {
self.sqr1().modify(|w| {
w.set_sq(_i, channel);
});
}
4..=8 => {
self.sqr2().modify(|w| {
w.set_sq(_i - 4, channel);
});
}
9..=13 => {
self.sqr3().modify(|w| {
w.set_sq(_i - 9, channel);
});
}
14..=15 => {
self.sqr4().modify(|w| {
w.set_sq(_i - 14, channel);
});
}
_ => unreachable!(),
}
#[cfg(adc_h5)]
{
difsel |= (_is_differential as u32) << channel;
}
#[cfg(adc_u0)]
{
channel_mask |= 1 << channel;
}
}
#[cfg(adc_h5)]
self.difsel().write(|w| w.set_difsel(difsel));
#[cfg(adc_u0)]
self.chselr().modify(|reg| {
reg.set_chsel(channel_mask);
});
}
}
}
impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {
fn init_regulator() {
rcc::enable_and_reset_without_stop::<T>();
T::regs().cr().modify(|reg| {
#[cfg(not(any(adc_g0, adc_u0)))]
reg.set_deeppwd(false);
reg.set_advregen(true);
});
#[cfg(any(adc_g0, adc_u0))]
T::regs().cfgr1().modify(|reg| {
reg.set_chselrmod(false);
});
blocking_delay_us(20);
}
fn init_calibrate() {
#[cfg(adc_u0)]
let auto_off = T::regs().cfgr1().read().autoff();
#[cfg(adc_u0)]
T::regs().cfgr1().modify(|reg| {
reg.set_autoff(false);
});
T::regs().cr().modify(|reg| {
reg.set_adcal(true);
});
while T::regs().cr().read().adcal() {
}
#[cfg(adc_u0)]
T::regs().cfgr1().modify(|reg| {
reg.set_autoff(auto_off);
});
blocking_delay_us(1);
}
pub fn new(adc: Peri<'d, T>) -> Self {
Self::init_regulator();
Self::init_calibrate();
Self { adc }
}
pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self {
#[cfg(not(adc_g0))]
let s = Self::new(adc);
#[cfg(adc_g0)]
let s = match config.clock {
Some(clock) => Self::new_with_clock(adc, clock),
None => Self::new(adc),
};
#[cfg(any(adc_g0, adc_u0, adc_v3))]
if let Some(shift) = config.oversampling_shift {
T::regs().cfgr2().modify(|reg| reg.set_ovss(shift));
}
#[cfg(any(adc_g0, adc_u0, adc_v3))]
if let Some(ratio) = config.oversampling_ratio {
T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio));
}
#[cfg(any(adc_g0, adc_u0))]
if let Some(enable) = config.oversampling_enable {
T::regs().cfgr2().modify(|reg| reg.set_ovse(enable));
}
#[cfg(adc_v3)]
if let Some((mode, trig_mode, enable)) = config.oversampling_mode {
T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode));
T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode));
T::regs().cfgr2().modify(|reg| reg.set_rovse(enable));
}
if let Some(resolution) = config.resolution {
#[cfg(not(any(adc_g0, adc_u0)))]
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
#[cfg(any(adc_g0, adc_u0))]
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
}
if let Some(averaging) = config.averaging {
let (enable, samples, right_shift) = match averaging {
Averaging::Disabled => (false, 0, 0),
Averaging::Samples2 => (true, 0, 1),
Averaging::Samples4 => (true, 1, 2),
Averaging::Samples8 => (true, 2, 3),
Averaging::Samples16 => (true, 3, 4),
Averaging::Samples32 => (true, 4, 5),
Averaging::Samples64 => (true, 5, 6),
Averaging::Samples128 => (true, 6, 7),
Averaging::Samples256 => (true, 7, 8),
};
T::regs().cfgr2().modify(|reg| {
#[cfg(not(any(adc_g0, adc_u0)))]
reg.set_rovse(enable);
#[cfg(any(adc_g0, adc_u0))]
reg.set_ovse(enable);
#[cfg(any(adc_h5, adc_h7rs))]
reg.set_ovsr(samples.into());
#[cfg(not(any(adc_h5, adc_h7rs)))]
reg.set_ovsr(samples.into());
reg.set_ovss(right_shift.into());
})
}
s
}
#[cfg(adc_g0)]
pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self {
Self::init_regulator();
#[cfg(any(stm32wl5x))]
{
let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0;
match clock {
Clock::Async { div: _ } => {
assert!(async_clock_available);
}
Clock::Sync { div: _ } => {
if async_clock_available {
warn!("Not using configured ADC clock");
}
}
}
}
match clock {
Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)),
Clock::Sync { div } => T::regs().cfgr2().modify(|reg| {
reg.set_ckmode(match div {
CkModePclk::DIV1 => Ckmode::PCLK,
CkModePclk::DIV2 => Ckmode::PCLK_DIV2,
CkModePclk::DIV4 => Ckmode::PCLK_DIV4,
})
}),
}
Self::init_calibrate();
Self { adc }
}
pub fn power_down(&mut self) {
super::AdcRegs::stop(&T::regs());
if T::regs().cr().read().aden() {
T::regs().cr().modify(|reg| {
reg.set_addis(true);
});
while T::regs().cr().read().aden() {}
}
}
#[cfg(adc_u0)]
pub fn enable_auto_off(&self) {
T::regs().cfgr1().modify(|reg| {
reg.set_autoff(true);
});
}
#[cfg(adc_u0)]
pub fn disable_auto_off(&self) {
T::regs().cfgr1().modify(|reg| {
reg.set_autoff(false);
});
}
pub fn enable_vrefint(&self) -> VrefInt {
#[cfg(not(any(adc_g0, adc_u0)))]
T::common_regs().ccr().modify(|reg| {
reg.set_vrefen(true);
});
#[cfg(any(adc_g0, adc_u0))]
T::regs().ccr().modify(|reg| {
reg.set_vrefen(true);
});
blocking_delay_us(15);
VrefInt {}
}
pub fn enable_temperature(&self) -> Temperature {
cfg_if! {
if #[cfg(any(adc_g0, adc_u0))] {
T::regs().ccr().modify(|reg| {
reg.set_tsen(true);
});
} else if #[cfg(any(adc_h5, adc_h7rs))] {
T::common_regs().ccr().modify(|reg| {
reg.set_tsen(true);
});
} else {
T::common_regs().ccr().modify(|reg| {
reg.set_ch17sel(true);
});
}
}
Temperature {}
}
pub fn enable_vbat(&self) -> Vbat {
cfg_if! {
if #[cfg(any(adc_g0, adc_u0))] {
T::regs().ccr().modify(|reg| {
reg.set_vbaten(true);
});
} else if #[cfg(any(adc_h5, adc_h7rs))] {
T::common_regs().ccr().modify(|reg| {
reg.set_vbaten(true);
});
} else {
T::common_regs().ccr().modify(|reg| {
reg.set_ch18sel(true);
});
}
}
Vbat {}
}
pub fn disable_vbat(&self) {
cfg_if! {
if #[cfg(any(adc_g0, adc_u0))] {
T::regs().ccr().modify(|reg| {
reg.set_vbaten(false);
});
} else if #[cfg(any(adc_h5, adc_h7rs))] {
T::common_regs().ccr().modify(|reg| {
reg.set_vbaten(false);
});
} else {
T::common_regs().ccr().modify(|reg| {
reg.set_ch18sel(false);
});
}
}
}
}
impl<'d, T: Instance> Drop for Adc<'d, T> {
fn drop(&mut self) {
super::AdcRegs::stop(&T::regs());
<T as crate::rcc::SealedRccPeripheral>::RCC_INFO.disable_without_stop();
}
}