Merge pull request #2246 from CaptainMaso/adc_f3_v1_1
stm32: add ADC f3_v1_1
This commit is contained in:
commit
a9ec623622
14 changed files with 572 additions and 32 deletions
|
@ -58,7 +58,7 @@ rand_core = "0.6.3"
|
|||
sdio-host = "0.5.0"
|
||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||
critical-section = "1.1"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a5da1c47c092c199bc39a7f84fb444f2adcdf" }
|
||||
vcell = "0.1.3"
|
||||
bxcan = "0.7.0"
|
||||
nb = "1.0.0"
|
||||
|
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||
[build-dependencies]
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a5da1c47c092c199bc39a7f84fb444f2adcdf", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
|
|
|
@ -930,6 +930,10 @@ fn main() {
|
|||
} else if pin.signal.starts_with("INN") {
|
||||
// TODO handle in the future when embassy supports differential measurements
|
||||
None
|
||||
} else if pin.signal.starts_with("IN") && pin.signal.ends_with("b") {
|
||||
// we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63
|
||||
let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix("b").unwrap();
|
||||
Some(32u8 + signal.parse::<u8>().unwrap())
|
||||
} else if pin.signal.starts_with("IN") {
|
||||
Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap())
|
||||
} else {
|
||||
|
|
|
@ -148,7 +148,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||
reg.set_cont(false);
|
||||
reg.set_exttrig(true);
|
||||
reg.set_swstart(false);
|
||||
reg.set_extsel(crate::pac::adc::vals::Extsel::SWSTART);
|
||||
reg.set_extsel(7); // SWSTART
|
||||
});
|
||||
|
||||
// Configure the channel to sample
|
||||
|
|
413
embassy-stm32/src/adc/f3_v1_1.rs
Normal file
413
embassy-stm32/src/adc/f3_v1_1.rs
Normal file
|
@ -0,0 +1,413 @@
|
|||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_futures::yield_now;
|
||||
use embassy_hal_internal::into_ref;
|
||||
use embassy_time::Instant;
|
||||
|
||||
use super::Resolution;
|
||||
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::time::Hertz;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ;
|
||||
|
||||
pub const VDDA_CALIB_MV: u32 = 3300;
|
||||
pub const ADC_MAX: u32 = (1 << 12) - 1;
|
||||
pub const VREF_INT: u32 = 1230;
|
||||
|
||||
pub enum AdcPowerMode {
|
||||
AlwaysOn,
|
||||
DelayOff,
|
||||
IdleOff,
|
||||
DelayIdleOff,
|
||||
}
|
||||
|
||||
pub enum Prescaler {
|
||||
Div1,
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
}
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
if T::regs().sr().read().eoc() {
|
||||
T::regs().cr1().modify(|w| w.set_eocie(false));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
T::state().waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_vref<T: Instance>(op: i8) {
|
||||
static VREF_STATUS: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(0);
|
||||
|
||||
if op > 0 {
|
||||
if VREF_STATUS.fetch_add(1, core::sync::atomic::Ordering::SeqCst) == 0 {
|
||||
T::regs().ccr().modify(|w| w.set_tsvrefe(true));
|
||||
}
|
||||
} else {
|
||||
if VREF_STATUS.fetch_sub(1, core::sync::atomic::Ordering::SeqCst) == 1 {
|
||||
T::regs().ccr().modify(|w| w.set_tsvrefe(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vref<T: Instance>(core::marker::PhantomData<T>);
|
||||
impl<T: Instance> AdcPin<T> for Vref<T> {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vref<T> {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> Vref<T> {
|
||||
/// The value that vref would be if vdda was at 3000mv
|
||||
pub fn calibrated_value(&self) -> u16 {
|
||||
crate::pac::VREFINTCAL.data().read().value()
|
||||
}
|
||||
|
||||
pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration {
|
||||
let vref_val = adc.read(self).await;
|
||||
Calibration {
|
||||
vref_cal: self.calibrated_value(),
|
||||
vref_val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Calibration {
|
||||
vref_cal: u16,
|
||||
vref_val: u16,
|
||||
}
|
||||
|
||||
impl Calibration {
|
||||
/// The millivolts that the calibration value was measured at
|
||||
pub const CALIBRATION_UV: u32 = 3_000_000;
|
||||
|
||||
/// Returns the measured VddA in microvolts (uV)
|
||||
pub fn vdda_uv(&self) -> u32 {
|
||||
(Self::CALIBRATION_UV * self.vref_cal as u32) / self.vref_val as u32
|
||||
}
|
||||
|
||||
/// Returns the measured VddA as an f32
|
||||
pub fn vdda_f32(&self) -> f32 {
|
||||
(Self::CALIBRATION_UV as f32 / 1_000.0) * (self.vref_cal as f32 / self.vref_val as f32)
|
||||
}
|
||||
|
||||
/// Returns a calibrated voltage value as in microvolts (uV)
|
||||
pub fn cal_uv(&self, raw: u16, resolution: super::Resolution) -> u32 {
|
||||
(self.vdda_uv() / resolution.to_max_count()) * raw as u32
|
||||
}
|
||||
|
||||
/// Returns a calibrated voltage value as an f32
|
||||
pub fn cal_f32(&self, raw: u16, resolution: super::Resolution) -> f32 {
|
||||
raw as f32 * self.vdda_f32() / resolution.to_max_count() as f32
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> Drop for Vref<T> {
|
||||
fn drop(&mut self) {
|
||||
update_vref::<T>(-1)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Temperature<T: Instance>(core::marker::PhantomData<T>);
|
||||
impl<T: Instance> AdcPin<T> for Temperature<T> {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature<T> {
|
||||
fn channel(&self) -> u8 {
|
||||
16
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> Drop for Temperature<T> {
|
||||
fn drop(&mut self) {
|
||||
update_vref::<T>(-1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(
|
||||
adc: impl Peripheral<P = T> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(adc);
|
||||
|
||||
T::enable_and_reset();
|
||||
|
||||
//let r = T::regs();
|
||||
//r.cr2().write(|w| w.set_align(true));
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe {
|
||||
T::Interrupt::enable();
|
||||
}
|
||||
|
||||
Self { adc }
|
||||
}
|
||||
|
||||
fn freq() -> Hertz {
|
||||
let div = T::regs().ccr().read().adcpre() + 1;
|
||||
ADC_FREQ / div as u32
|
||||
}
|
||||
|
||||
pub async fn set_resolution(&mut self, res: Resolution) {
|
||||
let was_on = Self::is_on();
|
||||
if was_on {
|
||||
self.stop_adc().await;
|
||||
}
|
||||
|
||||
T::regs().cr1().modify(|w| w.set_res(res.into()));
|
||||
|
||||
if was_on {
|
||||
self.start_adc().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolution(&self) -> Resolution {
|
||||
match T::regs().cr1().read().res() {
|
||||
crate::pac::adc::vals::Res::TWELVEBIT => Resolution::TwelveBit,
|
||||
crate::pac::adc::vals::Res::TENBIT => Resolution::TenBit,
|
||||
crate::pac::adc::vals::Res::EIGHTBIT => Resolution::EightBit,
|
||||
crate::pac::adc::vals::Res::SIXBIT => Resolution::SixBit,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_vref(&self) -> Vref<T> {
|
||||
update_vref::<T>(1);
|
||||
|
||||
Vref(core::marker::PhantomData)
|
||||
}
|
||||
|
||||
pub fn enable_temperature(&self) -> Temperature<T> {
|
||||
T::regs().ccr().modify(|w| w.set_tsvrefe(true));
|
||||
|
||||
Temperature::<T>(core::marker::PhantomData)
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
async fn convert(&mut self) -> u16 {
|
||||
let was_on = Self::is_on();
|
||||
|
||||
if !was_on {
|
||||
self.start_adc().await;
|
||||
}
|
||||
|
||||
self.wait_sample_ready().await;
|
||||
|
||||
T::regs().sr().write(|_| {});
|
||||
T::regs().cr1().modify(|w| {
|
||||
w.set_eocie(true);
|
||||
w.set_scan(false);
|
||||
});
|
||||
T::regs().cr2().modify(|w| {
|
||||
w.set_swstart(true);
|
||||
w.set_cont(false);
|
||||
}); // swstart cleared by HW
|
||||
|
||||
let res = poll_fn(|cx| {
|
||||
T::state().waker.register(cx.waker());
|
||||
|
||||
if T::regs().sr().read().eoc() {
|
||||
let res = T::regs().dr().read().rdata();
|
||||
Poll::Ready(res)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
if !was_on {
|
||||
self.stop_adc().await;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_on() -> bool {
|
||||
T::regs().sr().read().adons() || T::regs().cr2().read().adon()
|
||||
}
|
||||
|
||||
pub async fn start_adc(&self) {
|
||||
//defmt::trace!("Turn ADC on");
|
||||
T::regs().cr2().modify(|w| w.set_adon(true));
|
||||
//defmt::trace!("Waiting for ADC to turn on");
|
||||
|
||||
let mut t = Instant::now();
|
||||
|
||||
while !T::regs().sr().read().adons() {
|
||||
yield_now().await;
|
||||
if t.elapsed() > embassy_time::Duration::from_millis(1000) {
|
||||
t = Instant::now();
|
||||
//defmt::trace!("ADC still not on");
|
||||
}
|
||||
}
|
||||
|
||||
//defmt::trace!("ADC on");
|
||||
}
|
||||
|
||||
pub async fn stop_adc(&self) {
|
||||
if T::regs().cr2().read().adon() {
|
||||
//defmt::trace!("ADC should be on, wait for it to start");
|
||||
while !T::regs().csr().read().adons1() {
|
||||
yield_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
//defmt::trace!("Turn ADC off");
|
||||
|
||||
T::regs().cr2().modify(|w| w.set_adon(false));
|
||||
|
||||
//defmt::trace!("Waiting for ADC to turn off");
|
||||
|
||||
while T::regs().csr().read().adons1() {
|
||||
yield_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
self.set_sample_sequence(&[pin.channel()]).await;
|
||||
self.convert().await
|
||||
}
|
||||
|
||||
async fn wait_sample_ready(&self) {
|
||||
//trace!("Waiting for sample channel to be ready");
|
||||
while T::regs().sr().read().rcnr() {
|
||||
yield_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_sample_time(&mut self, pin: &mut impl AdcPin<T>, sample_time: SampleTime) {
|
||||
if Self::get_channel_sample_time(pin.channel()) != sample_time {
|
||||
self.stop_adc().await;
|
||||
unsafe {
|
||||
Self::set_channel_sample_time(pin.channel(), sample_time);
|
||||
}
|
||||
self.start_adc().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_sample_time(&self, pin: &impl AdcPin<T>) -> SampleTime {
|
||||
Self::get_channel_sample_time(pin.channel())
|
||||
}
|
||||
|
||||
/// Sets the channel sample time
|
||||
///
|
||||
/// ## SAFETY:
|
||||
/// - ADON == 0 i.e ADC must not be enabled when this is called.
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
|
||||
match ch {
|
||||
0..=9 => T::regs().smpr3().modify(|reg| reg.set_smp(ch as _, sample_time)),
|
||||
10..=19 => T::regs()
|
||||
.smpr2()
|
||||
.modify(|reg| reg.set_smp(ch as usize - 10, sample_time)),
|
||||
20..=29 => T::regs()
|
||||
.smpr1()
|
||||
.modify(|reg| reg.set_smp(ch as usize - 20, sample_time)),
|
||||
30..=31 => T::regs()
|
||||
.smpr0()
|
||||
.modify(|reg| reg.set_smp(ch as usize - 30, sample_time)),
|
||||
_ => panic!("Invalid channel to sample"),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_channel_sample_time(ch: u8) -> SampleTime {
|
||||
match ch {
|
||||
0..=9 => T::regs().smpr3().read().smp(ch as _),
|
||||
10..=19 => T::regs().smpr2().read().smp(ch as usize - 10),
|
||||
20..=29 => T::regs().smpr1().read().smp(ch as usize - 20),
|
||||
30..=31 => T::regs().smpr0().read().smp(ch as usize - 30),
|
||||
_ => panic!("Invalid channel to sample"),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Sets the sequence to sample the ADC. Must be less than 28 elements.
|
||||
async fn set_sample_sequence(&self, sequence: &[u8]) {
|
||||
assert!(sequence.len() <= 28);
|
||||
let mut iter = sequence.iter();
|
||||
T::regs().sqr1().modify(|w| w.set_l((sequence.len() - 1) as _));
|
||||
for (idx, ch) in iter.by_ref().take(6).enumerate() {
|
||||
T::regs().sqr5().modify(|w| w.set_sq(idx, *ch));
|
||||
}
|
||||
for (idx, ch) in iter.by_ref().take(6).enumerate() {
|
||||
T::regs().sqr4().modify(|w| w.set_sq(idx, *ch));
|
||||
}
|
||||
for (idx, ch) in iter.by_ref().take(6).enumerate() {
|
||||
T::regs().sqr3().modify(|w| w.set_sq(idx, *ch));
|
||||
}
|
||||
for (idx, ch) in iter.by_ref().take(6).enumerate() {
|
||||
T::regs().sqr2().modify(|w| w.set_sq(idx, *ch));
|
||||
}
|
||||
for (idx, ch) in iter.by_ref().take(4).enumerate() {
|
||||
T::regs().sqr1().modify(|w| w.set_sq(idx, *ch));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_res_clks(res: Resolution) -> u32 {
|
||||
match res {
|
||||
Resolution::TwelveBit => 12,
|
||||
Resolution::TenBit => 11,
|
||||
Resolution::EightBit => 9,
|
||||
Resolution::SixBit => 7,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_sample_time_clks(sample_time: SampleTime) -> u32 {
|
||||
match sample_time {
|
||||
SampleTime::Cycles4 => 4,
|
||||
SampleTime::Cycles9 => 9,
|
||||
SampleTime::Cycles16 => 16,
|
||||
SampleTime::Cycles24 => 24,
|
||||
SampleTime::Cycles48 => 48,
|
||||
SampleTime::Cycles96 => 96,
|
||||
SampleTime::Cycles192 => 192,
|
||||
SampleTime::Cycles384 => 384,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sample_time_for_us(&self, us: u32) -> SampleTime {
|
||||
let res_clks = Self::get_res_clks(self.resolution());
|
||||
let us_clks = us * Self::freq().0 / 1_000_000;
|
||||
let clks = us_clks.saturating_sub(res_clks);
|
||||
match clks {
|
||||
0..=4 => SampleTime::Cycles4,
|
||||
5..=9 => SampleTime::Cycles9,
|
||||
10..=16 => SampleTime::Cycles16,
|
||||
17..=24 => SampleTime::Cycles24,
|
||||
25..=48 => SampleTime::Cycles48,
|
||||
49..=96 => SampleTime::Cycles96,
|
||||
97..=192 => SampleTime::Cycles192,
|
||||
193.. => SampleTime::Cycles384,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn us_for_cfg(&self, res: Resolution, sample_time: SampleTime) -> u32 {
|
||||
let res_clks = Self::get_res_clks(res);
|
||||
let sample_clks = Self::get_sample_time_clks(sample_time);
|
||||
(res_clks + sample_clks) * 1_000_000 / Self::freq().0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Adc<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
while !T::regs().sr().read().adons() {}
|
||||
|
||||
T::regs().cr2().modify(|w| w.set_adon(false));
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#[cfg(not(adc_f3_v2))]
|
||||
#[cfg_attr(adc_f1, path = "f1.rs")]
|
||||
#[cfg_attr(adc_f3, path = "f3.rs")]
|
||||
#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
|
||||
#[cfg_attr(adc_v1, path = "v1.rs")]
|
||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
|
||||
|
@ -26,20 +27,20 @@ use crate::peripherals;
|
|||
pub struct Adc<'d, T: Instance> {
|
||||
#[allow(unused)]
|
||||
adc: crate::PeripheralRef<'d, T>,
|
||||
#[cfg(not(adc_f3_v2))]
|
||||
#[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))]
|
||||
sample_time: SampleTime,
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
pub struct State {
|
||||
pub waker: AtomicWaker,
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
|
@ -54,11 +55,11 @@ pub(crate) mod sealed {
|
|||
|
||||
pub trait Instance: InterruptableInstance {
|
||||
fn regs() -> crate::pac::adc::Adc;
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz;
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
|
||||
|
@ -74,9 +75,9 @@ pub(crate) mod sealed {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_g0)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
||||
#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_g0))]
|
||||
#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
||||
|
||||
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
||||
|
@ -89,7 +90,7 @@ foreach_adc!(
|
|||
crate::pac::$inst
|
||||
}
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
return crate::pac::$common_inst
|
||||
}
|
||||
|
@ -99,7 +100,7 @@ foreach_adc!(
|
|||
unsafe { crate::rcc::get_freqs() }.$clock.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
fn state() -> &'static sealed::State {
|
||||
static STATE: sealed::State = sealed::State::new();
|
||||
&STATE
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
|
@ -9,6 +10,7 @@ pub enum Resolution {
|
|||
|
||||
#[cfg(adc_v4)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Resolution {
|
||||
SixteenBit,
|
||||
FourteenBit,
|
||||
|
@ -19,7 +21,7 @@ pub enum Resolution {
|
|||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
{
|
||||
Self::TwelveBit
|
||||
}
|
||||
|
@ -40,7 +42,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
|
|||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +58,7 @@ impl Resolution {
|
|||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ macro_rules! impl_sample_time {
|
|||
($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => {
|
||||
#[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum SampleTime {
|
||||
$(
|
||||
#[doc = concat!($doc, " ADC clock cycles.")]
|
||||
|
@ -18,6 +19,14 @@ macro_rules! impl_sample_time {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<crate::pac::adc::vals::SampleTime> for SampleTime {
|
||||
fn from(sample_time: crate::pac::adc::vals::SampleTime) -> SampleTime {
|
||||
match sample_time {
|
||||
$(crate::pac::adc::vals::SampleTime::$pac_variant => SampleTime::$variant),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::$default
|
||||
|
@ -121,3 +130,19 @@ impl_sample_time!(
|
|||
("601.5", Cycles601_5, CYCLES601_5)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(any(adc_f3_v1_1))]
|
||||
impl_sample_time!(
|
||||
"4",
|
||||
Cycles4,
|
||||
(
|
||||
("4", Cycles4, CYCLES4),
|
||||
("9", Cycles9, CYCLES9),
|
||||
("16", Cycles16, CYCLES16),
|
||||
("24", Cycles24, CYCLES24),
|
||||
("48", Cycles48, CYCLES48),
|
||||
("96", Cycles96, CYCLES96),
|
||||
("192", Cycles192, CYCLES192),
|
||||
("384", Cycles384, CYCLES384)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![macro_use]
|
||||
|
||||
#[cfg_attr(can_bxcan, path = "bxcan.rs")]
|
||||
#[cfg_attr(can_fdcan, path = "fdcan.rs")]
|
||||
#[cfg_attr(any(can_fdcan_v1, can_fdcan_h7), path = "fdcan.rs")]
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
|
|
|
@ -315,6 +315,8 @@ pub(crate) unsafe fn init(config: Config) {
|
|||
adc: adc12_ck,
|
||||
adc34: adc345_ck,
|
||||
pll1_p: None,
|
||||
pll1_q: None, // TODO
|
||||
hse: None, // TODO
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ pub struct Clocks {
|
|||
|
||||
#[cfg(any(stm32g4, rcc_l4))]
|
||||
pub pll1_p: Option<Hertz>,
|
||||
#[cfg(any(stm32h5, stm32h7, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_l4))]
|
||||
#[cfg(any(stm32h5, stm32h7, stm32f2, stm32f4, stm32f7, rcc_l4, stm32g4))]
|
||||
pub pll1_q: Option<Hertz>,
|
||||
#[cfg(any(stm32h5, stm32h7))]
|
||||
pub pll2_p: Option<Hertz>,
|
||||
|
@ -167,7 +167,7 @@ pub struct Clocks {
|
|||
|
||||
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
|
||||
pub lse: Option<Hertz>,
|
||||
#[cfg(any(stm32h5, stm32h7))]
|
||||
#[cfg(any(stm32h5, stm32h7, stm32g4))]
|
||||
pub hse: Option<Hertz>,
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
|
|
|
@ -10,14 +10,14 @@ stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"]
|
|||
stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"]
|
||||
stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"]
|
||||
stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
|
||||
stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"]
|
||||
stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac-adc-pin", "sdmmc"]
|
||||
stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"]
|
||||
stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"]
|
||||
stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
|
||||
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"]
|
||||
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"]
|
||||
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"]
|
||||
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"]
|
||||
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"]
|
||||
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"]
|
||||
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng"]
|
||||
stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"]
|
||||
stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"]
|
||||
stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"]
|
||||
|
@ -41,7 +41,7 @@ ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"]
|
|||
mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"]
|
||||
embassy-stm32-wpan = []
|
||||
not-gpdma = []
|
||||
dac-adc-pin = []
|
||||
dac = []
|
||||
|
||||
cm0 = ["portable-atomic/unsafe-assume-single-core"]
|
||||
|
||||
|
@ -84,7 +84,12 @@ required-features = [ "can",]
|
|||
[[bin]]
|
||||
name = "dac"
|
||||
path = "src/bin/dac.rs"
|
||||
required-features = [ "dac-adc-pin",]
|
||||
required-features = [ "dac",]
|
||||
|
||||
[[bin]]
|
||||
name = "dac_l1"
|
||||
path = "src/bin/dac_l1.rs"
|
||||
required-features = [ "stm32l152re",]
|
||||
|
||||
[[bin]]
|
||||
name = "eth"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
// required-features: dac-adc-pin
|
||||
// required-features: dac
|
||||
|
||||
#[path = "../common.rs"]
|
||||
mod common;
|
||||
|
@ -22,12 +22,13 @@ async fn main(_spawner: Spawner) {
|
|||
// Initialize the board and obtain a Peripherals instance
|
||||
let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
|
||||
|
||||
let adc = peri!(p, ADC);
|
||||
let dac = peri!(p, DAC);
|
||||
let dac_pin = peri!(p, DAC_PIN);
|
||||
let mut adc_pin = unsafe { core::ptr::read(&dac_pin) };
|
||||
|
||||
let mut dac = DacCh1::new(dac, NoDma, dac_pin);
|
||||
let mut adc = Adc::new(p.ADC1, &mut Delay);
|
||||
let mut adc = Adc::new(adc, &mut Delay);
|
||||
|
||||
#[cfg(feature = "stm32h755zi")]
|
||||
let normalization_factor = 256;
|
||||
|
|
86
tests/stm32/src/bin/dac_l1.rs
Normal file
86
tests/stm32/src/bin/dac_l1.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
// required-features: stm32l152re
|
||||
|
||||
#[path = "../common.rs"]
|
||||
mod common;
|
||||
use core::f32::consts::PI;
|
||||
|
||||
use common::*;
|
||||
use defmt::assert;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::adc::Adc;
|
||||
use embassy_stm32::dac::{DacCh1, Value};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use embassy_stm32::{bind_interrupts, peripherals};
|
||||
use embassy_time::Timer;
|
||||
use micromath::F32Ext;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
ADC1 => embassy_stm32::adc::InterruptHandler<peripherals::ADC>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
// Initialize the board and obtain a Peripherals instance
|
||||
let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
|
||||
|
||||
let adc = peri!(p, ADC);
|
||||
let dac = peri!(p, DAC);
|
||||
let dac_pin = peri!(p, DAC_PIN);
|
||||
let mut adc_pin = unsafe { core::ptr::read(&dac_pin) };
|
||||
|
||||
let mut dac = DacCh1::new(dac, NoDma, dac_pin);
|
||||
let mut adc = Adc::new(adc, Irqs);
|
||||
|
||||
#[cfg(feature = "stm32h755zi")]
|
||||
let normalization_factor = 256;
|
||||
#[cfg(any(
|
||||
feature = "stm32f429zi",
|
||||
feature = "stm32f446re",
|
||||
feature = "stm32g071rb",
|
||||
feature = "stm32l152re",
|
||||
))]
|
||||
let normalization_factor: i32 = 16;
|
||||
|
||||
dac.set(Value::Bit8(0));
|
||||
// Now wait a little to obtain a stable value
|
||||
Timer::after_millis(30).await;
|
||||
let offset = adc.read(&mut adc_pin).await;
|
||||
|
||||
for v in 0..=255 {
|
||||
// First set the DAC output value
|
||||
let dac_output_val = to_sine_wave(v);
|
||||
dac.set(Value::Bit8(dac_output_val));
|
||||
|
||||
// Now wait a little to obtain a stable value
|
||||
Timer::after_millis(30).await;
|
||||
|
||||
// Need to steal the peripherals here because PA4 is obviously in use already
|
||||
let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4).await;
|
||||
// Calibrate and normalize the measurement to get close to the dac_output_val
|
||||
let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16;
|
||||
|
||||
info!("value / measured: {} / {}", dac_output_val, measured_normalized);
|
||||
|
||||
// The deviations are quite enormous but that does not matter since this is only a quick test
|
||||
assert!((dac_output_val as i16 - measured_normalized).abs() < 15);
|
||||
}
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
||||
|
||||
fn to_sine_wave(v: u8) -> u8 {
|
||||
if v >= 128 {
|
||||
// top half
|
||||
let r = PI * ((v - 128) as f32 / 128.0);
|
||||
(r.sin() * 128.0 + 127.0) as u8
|
||||
} else {
|
||||
// bottom half
|
||||
let r = PI + PI * (v as f32 / 128.0);
|
||||
(r.sin() * 128.0 + 127.0) as u8
|
||||
}
|
||||
}
|
|
@ -101,14 +101,14 @@ define_peris!(
|
|||
define_peris!(
|
||||
UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
|
||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,
|
||||
DAC = DAC1, DAC_PIN = PA4,
|
||||
ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,
|
||||
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
||||
);
|
||||
#[cfg(feature = "stm32f429zi")]
|
||||
define_peris!(
|
||||
UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1,
|
||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,
|
||||
DAC = DAC, DAC_PIN = PA4,
|
||||
ADC = ADC1, DAC = DAC, DAC_PIN = PA4,
|
||||
CAN = CAN1, CAN_RX = PD0, CAN_TX = PD1,
|
||||
@irq UART = {USART6 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART6>;},
|
||||
);
|
||||
|
@ -116,7 +116,7 @@ define_peris!(
|
|||
define_peris!(
|
||||
UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA2_CH7, UART_RX_DMA = DMA2_CH5,
|
||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,
|
||||
DAC = DAC, DAC_PIN = PA4,
|
||||
ADC = ADC1, DAC = DAC, DAC_PIN = PA4,
|
||||
CAN = CAN1, CAN_RX = PA11, CAN_TX = PA12,
|
||||
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
||||
);
|
||||
|
@ -130,7 +130,7 @@ define_peris!(
|
|||
define_peris!(
|
||||
UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1,
|
||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1,
|
||||
DAC = DAC1, DAC_PIN = PA4,
|
||||
ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,
|
||||
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
||||
);
|
||||
#[cfg(feature = "stm32h7a3zi")]
|
||||
|
@ -191,6 +191,7 @@ define_peris!(
|
|||
define_peris!(
|
||||
UART = USART3, UART_TX = PB10, UART_RX = PB11, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3,
|
||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,
|
||||
ADC = ADC, DAC = DAC, DAC_PIN = PA4,
|
||||
@irq UART = {USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;},
|
||||
);
|
||||
#[cfg(feature = "stm32l552ze")]
|
||||
|
|
Loading…
Reference in a new issue