mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 08:12:30 +00:00
Merge pull request #2199 from adamgreig/stm32-dac
STM32 DAC: update to new PAC, combine ch1/ch2, fix trigger selection
This commit is contained in:
commit
8b46343b34
@ -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-f6d1ffc1a25f208b5cd6b1024bff246592da1949" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd" }
|
||||
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-f6d1ffc1a25f208b5cd6b1024bff246592da1949", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
|
@ -65,7 +65,6 @@ fn main() {
|
||||
match r.kind {
|
||||
// Generate singletons per pin, not per port
|
||||
"gpio" => {
|
||||
println!("{}", p.name);
|
||||
let port_letter = p.name.strip_prefix("GPIO").unwrap();
|
||||
for pin_num in 0..16 {
|
||||
singletons.push(format!("P{}{}", port_letter, pin_num));
|
||||
@ -997,8 +996,8 @@ fn main() {
|
||||
// SDMMCv1 uses the same channel for both directions, so just implement for RX
|
||||
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
|
||||
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
|
||||
(("dac", "CH1"), quote!(crate::dac::DmaCh1)),
|
||||
(("dac", "CH2"), quote!(crate::dac::DmaCh2)),
|
||||
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
|
||||
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
|
||||
]
|
||||
.into();
|
||||
|
||||
@ -1352,15 +1351,6 @@ fn main() {
|
||||
|
||||
if let Some(core) = core_name {
|
||||
println!("cargo:rustc-cfg={}_{}", &chip_name[..chip_name.len() - 2], core);
|
||||
} else {
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);
|
||||
}
|
||||
|
||||
// ========
|
||||
// stm32f3 wildcard features used in RCC
|
||||
|
||||
if chip_name.starts_with("stm32f3") {
|
||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]);
|
||||
}
|
||||
|
||||
// =======
|
||||
@ -1375,16 +1365,25 @@ fn main() {
|
||||
if &chip_name[..8] == "stm32wba" {
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..8]); // stm32wba
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..10]); // stm32wba52
|
||||
println!("cargo:rustc-cfg=package_{}", &chip_name[10..11]);
|
||||
println!("cargo:rustc-cfg=flashsize_{}", &chip_name[11..12]);
|
||||
} else {
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429
|
||||
println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
|
||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
|
||||
println!("cargo:rustc-cfg=package_{}", &chip_name[9..10]);
|
||||
println!("cargo:rustc-cfg=flashsize_{}", &chip_name[10..11]);
|
||||
}
|
||||
|
||||
// Handle time-driver-XXXX features.
|
||||
if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {}
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);
|
||||
// Mark the L4+ chips as they have many differences to regular L4.
|
||||
if &chip_name[..7] == "stm32l4" {
|
||||
if "pqrs".contains(&chip_name[7..8]) {
|
||||
println!("cargo:rustc-cfg=stm32l4_plus");
|
||||
} else {
|
||||
println!("cargo:rustc-cfg=stm32l4_nonplus");
|
||||
}
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
|
@ -1,136 +1,66 @@
|
||||
//! Provide access to the STM32 digital-to-analog converter (DAC).
|
||||
#![macro_use]
|
||||
|
||||
//! Provide access to the STM32 digital-to-analog converter (DAC).
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::dma::NoDma;
|
||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||
use crate::pac::dac;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
mod tsel;
|
||||
pub use tsel::TriggerSel;
|
||||
|
||||
/// Operating mode for DAC channel
|
||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Custom Errors
|
||||
pub enum Error {
|
||||
UnconfiguredChannel,
|
||||
InvalidValue,
|
||||
pub enum Mode {
|
||||
/// Normal mode, channel is connected to external pin with buffer enabled.
|
||||
NormalExternalBuffered,
|
||||
/// Normal mode, channel is connected to external pin and internal peripherals
|
||||
/// with buffer enabled.
|
||||
NormalBothBuffered,
|
||||
/// Normal mode, channel is connected to external pin with buffer disabled.
|
||||
NormalExternalUnbuffered,
|
||||
/// Normal mode, channel is connected to internal peripherals with buffer disabled.
|
||||
NormalInternalUnbuffered,
|
||||
/// Sample-and-hold mode, channel is connected to external pin with buffer enabled.
|
||||
SampleHoldExternalBuffered,
|
||||
/// Sample-and-hold mode, channel is connected to external pin and internal peripherals
|
||||
/// with buffer enabled.
|
||||
SampleHoldBothBuffered,
|
||||
/// Sample-and-hold mode, channel is connected to external pin and internal peripherals
|
||||
/// with buffer disabled.
|
||||
SampleHoldBothUnbuffered,
|
||||
/// Sample-and-hold mode, channel is connected to internal peripherals with buffer disabled.
|
||||
SampleHoldInternalUnbuffered,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// DAC Channels
|
||||
pub enum Channel {
|
||||
Ch1,
|
||||
Ch2,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
const fn index(&self) -> usize {
|
||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||
impl Mode {
|
||||
fn mode(&self) -> dac::vals::Mode {
|
||||
match self {
|
||||
Channel::Ch1 => 0,
|
||||
Channel::Ch2 => 1,
|
||||
Mode::NormalExternalBuffered => dac::vals::Mode::NORMAL_EXT_BUFEN,
|
||||
Mode::NormalBothBuffered => dac::vals::Mode::NORMAL_EXT_INT_BUFEN,
|
||||
Mode::NormalExternalUnbuffered => dac::vals::Mode::NORMAL_EXT_BUFDIS,
|
||||
Mode::NormalInternalUnbuffered => dac::vals::Mode::NORMAL_INT_BUFDIS,
|
||||
Mode::SampleHoldExternalBuffered => dac::vals::Mode::SAMPHOLD_EXT_BUFEN,
|
||||
Mode::SampleHoldBothBuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFEN,
|
||||
Mode::SampleHoldBothUnbuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFDIS,
|
||||
Mode::SampleHoldInternalUnbuffered => dac::vals::Mode::SAMPHOLD_INT_BUFDIS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Trigger sources for CH1
|
||||
pub enum Ch1Trigger {
|
||||
#[cfg(dac_v3)]
|
||||
Tim1,
|
||||
Tim2,
|
||||
#[cfg(not(dac_v3))]
|
||||
Tim3,
|
||||
#[cfg(dac_v3)]
|
||||
Tim4,
|
||||
#[cfg(dac_v3)]
|
||||
Tim5,
|
||||
Tim6,
|
||||
Tim7,
|
||||
#[cfg(dac_v3)]
|
||||
Tim8,
|
||||
Tim15,
|
||||
#[cfg(dac_v3)]
|
||||
Hrtim1Dactrg1,
|
||||
#[cfg(dac_v3)]
|
||||
Hrtim1Dactrg2,
|
||||
#[cfg(dac_v3)]
|
||||
Lptim1,
|
||||
#[cfg(dac_v3)]
|
||||
Lptim2,
|
||||
#[cfg(dac_v3)]
|
||||
Lptim3,
|
||||
Exti9,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl Ch1Trigger {
|
||||
fn tsel(&self) -> dac::vals::Tsel1 {
|
||||
match self {
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Tim1 => dac::vals::Tsel1::TIM1_TRGO,
|
||||
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
|
||||
#[cfg(not(dac_v3))]
|
||||
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Tim4 => dac::vals::Tsel1::TIM4_TRGO,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Tim5 => dac::vals::Tsel1::TIM5_TRGO,
|
||||
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
||||
Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Tim8 => dac::vals::Tsel1::TIM8_TRGO,
|
||||
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Hrtim1Dactrg1 => dac::vals::Tsel1::HRTIM1_DACTRG1,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Hrtim1Dactrg2 => dac::vals::Tsel1::HRTIM1_DACTRG2,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Lptim1 => dac::vals::Tsel1::LPTIM1_OUT,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Lptim2 => dac::vals::Tsel1::LPTIM2_OUT,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Lptim3 => dac::vals::Tsel1::LPTIM3_OUT,
|
||||
Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
|
||||
Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Trigger sources for CH2
|
||||
pub enum Ch2Trigger {
|
||||
Tim6,
|
||||
Tim8,
|
||||
Tim7,
|
||||
Tim5,
|
||||
Tim2,
|
||||
Tim4,
|
||||
Exti9,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl Ch2Trigger {
|
||||
fn tsel(&self) -> dac::vals::Tsel2 {
|
||||
match self {
|
||||
Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
|
||||
Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
|
||||
Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
|
||||
Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
|
||||
Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
|
||||
Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
|
||||
Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
|
||||
Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Single 8 or 12 bit value that can be output by the DAC
|
||||
/// Single 8 or 12 bit value that can be output by the DAC.
|
||||
///
|
||||
/// 12-bit values outside the permitted range are silently truncated.
|
||||
pub enum Value {
|
||||
// 8 bit value
|
||||
Bit8(u8),
|
||||
@ -142,7 +72,21 @@ pub enum Value {
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Array variant of [`Value`]
|
||||
/// Dual 8 or 12 bit values that can be output by the DAC channels 1 and 2 simultaneously.
|
||||
///
|
||||
/// 12-bit values outside the permitted range are silently truncated.
|
||||
pub enum DualValue {
|
||||
// 8 bit value
|
||||
Bit8(u8, u8),
|
||||
// 12 bit value stored in a u16, left-aligned
|
||||
Bit12Left(u16, u16),
|
||||
// 12 bit value stored in a u16, right-aligned
|
||||
Bit12Right(u16, u16),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Array variant of [`Value`].
|
||||
pub enum ValueArray<'a> {
|
||||
// 8 bit values
|
||||
Bit8(&'a [u8]),
|
||||
@ -151,398 +95,395 @@ pub enum ValueArray<'a> {
|
||||
// 12 bit values stored in a u16, right-aligned
|
||||
Bit12Right(&'a [u16]),
|
||||
}
|
||||
/// Provide common functions for DAC channels
|
||||
pub trait DacChannel<T: Instance, Tx> {
|
||||
const CHANNEL: Channel;
|
||||
|
||||
/// Enable trigger of the given channel
|
||||
fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_ten(Self::CHANNEL.index(), on);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set mode register of the given channel
|
||||
#[cfg(any(dac_v2, dac_v3))]
|
||||
fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> {
|
||||
T::regs().mcr().modify(|reg| {
|
||||
reg.set_mode(Self::CHANNEL.index(), val);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set enable register of the given channel
|
||||
fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_en(Self::CHANNEL.index(), on);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enable the DAC channel `ch`
|
||||
fn enable_channel(&mut self) -> Result<(), Error> {
|
||||
self.set_channel_enable(true)
|
||||
}
|
||||
|
||||
/// Disable the DAC channel `ch`
|
||||
fn disable_channel(&mut self) -> Result<(), Error> {
|
||||
self.set_channel_enable(false)
|
||||
}
|
||||
|
||||
/// Perform a software trigger on `ch`
|
||||
fn trigger(&mut self) {
|
||||
T::regs().swtrigr().write(|reg| {
|
||||
reg.set_swtrig(Self::CHANNEL.index(), true);
|
||||
});
|
||||
}
|
||||
|
||||
/// Set a value to be output by the DAC on trigger.
|
||||
///
|
||||
/// The `value` is written to the corresponding "data holding register".
|
||||
fn set(&mut self, value: Value) -> Result<(), Error> {
|
||||
match value {
|
||||
Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||
Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||
Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Hold two DAC channels
|
||||
/// Driver for a single DAC channel.
|
||||
///
|
||||
/// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously.
|
||||
///
|
||||
/// # Example for obtaining both DAC channels
|
||||
///
|
||||
/// ```ignore
|
||||
/// // DMA channels and pins may need to be changed for your controller
|
||||
/// let (dac_ch1, dac_ch2) =
|
||||
/// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
|
||||
/// ```
|
||||
pub struct Dac<'d, T: Instance, TxCh1, TxCh2> {
|
||||
ch1: DacCh1<'d, T, TxCh1>,
|
||||
ch2: DacCh2<'d, T, TxCh2>,
|
||||
}
|
||||
|
||||
/// DAC CH1
|
||||
///
|
||||
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
|
||||
pub struct DacCh1<'d, T: Instance, Tx> {
|
||||
/// To consume T
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
#[allow(unused)] // For chips whose DMA is not (yet) supported
|
||||
dma: PeripheralRef<'d, Tx>,
|
||||
}
|
||||
|
||||
/// DAC CH2
|
||||
///
|
||||
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
|
||||
pub struct DacCh2<'d, T: Instance, Tx> {
|
||||
/// Instead of PeripheralRef to consume T
|
||||
/// If you want to use both channels, either together or independently,
|
||||
/// create a [`Dac`] first and use it to access each channel.
|
||||
pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
#[allow(unused)] // For chips whose DMA is not (yet) supported
|
||||
dma: PeripheralRef<'d, Tx>,
|
||||
#[allow(unused)]
|
||||
dma: PeripheralRef<'d, DMA>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> {
|
||||
/// Obtain DAC CH1
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
dma: impl Peripheral<P = Tx> + 'd,
|
||||
pin: impl Peripheral<P = impl DacPin<T, 1>> + crate::gpio::sealed::Pin + 'd,
|
||||
) -> Self {
|
||||
pin.set_as_analog();
|
||||
into_ref!(peri, dma);
|
||||
T::enable_and_reset();
|
||||
pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>;
|
||||
pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>;
|
||||
|
||||
let mut dac = Self { _peri: peri, dma };
|
||||
impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
|
||||
const IDX: usize = (N - 1) as usize;
|
||||
|
||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||
#[cfg(any(dac_v2, dac_v3))]
|
||||
dac.set_channel_mode(0).unwrap();
|
||||
dac.enable_channel().unwrap();
|
||||
dac.set_trigger_enable(true).unwrap();
|
||||
|
||||
dac
|
||||
}
|
||||
|
||||
/// Select a new trigger for this channel
|
||||
/// Create a new `DacChannel` instance, consuming the underlying DAC peripheral.
|
||||
///
|
||||
/// **Important**: This disables the channel!
|
||||
pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
|
||||
unwrap!(self.disable_channel());
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_tsel1(trigger.tsel());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write `data` to the DAC CH1 via DMA.
|
||||
/// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
|
||||
///
|
||||
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
||||
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
||||
/// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
|
||||
/// The channel is enabled on creation and begins to drive the output pin.
|
||||
/// Note that some methods, such as `set_trigger()` and `set_mode()`, will
|
||||
/// disable the channel; you must re-enable it with `enable()`.
|
||||
///
|
||||
/// **Important:** Channel 1 has to be configured for the DAC instance!
|
||||
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
||||
where
|
||||
Tx: DmaCh1<T>,
|
||||
{
|
||||
let channel = Channel::Ch1.index();
|
||||
debug!("Writing to channel {}", channel);
|
||||
|
||||
// Enable DAC and DMA
|
||||
T::regs().cr().modify(|w| {
|
||||
w.set_en(channel, true);
|
||||
w.set_dmaen(channel, true);
|
||||
});
|
||||
|
||||
let tx_request = self.dma.request();
|
||||
let dma_channel = &mut self.dma;
|
||||
|
||||
let tx_options = crate::dma::TransferOptions {
|
||||
circular,
|
||||
half_transfer_ir: false,
|
||||
complete_transfer_ir: !circular,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||
let tx_f = match data {
|
||||
ValueArray::Bit8(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr8r(channel).as_ptr() as *mut u8,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Left(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12l(channel).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Right(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12r(channel).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
tx_f.await;
|
||||
|
||||
// finish dma
|
||||
// TODO: Do we need to check any status registers here?
|
||||
T::regs().cr().modify(|w| {
|
||||
// Disable the DAC peripheral
|
||||
w.set_en(channel, false);
|
||||
// Disable the DMA. TODO: Is this necessary?
|
||||
w.set_dmaen(channel, false);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
|
||||
/// Obtain DAC CH2
|
||||
/// By default, triggering is disabled, but it can be enabled using
|
||||
/// [`DacChannel::set_trigger()`].
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
dma: impl Peripheral<P = Tx> + 'd,
|
||||
pin: impl Peripheral<P = impl DacPin<T, 2>> + crate::gpio::sealed::Pin + 'd,
|
||||
dma: impl Peripheral<P = DMA> + 'd,
|
||||
pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::sealed::Pin> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(dma, pin);
|
||||
pin.set_as_analog();
|
||||
into_ref!(_peri, dma);
|
||||
T::enable_and_reset();
|
||||
|
||||
let mut dac = Self {
|
||||
phantom: PhantomData,
|
||||
dma,
|
||||
};
|
||||
|
||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||
#[cfg(any(dac_v2, dac_v3))]
|
||||
dac.set_channel_mode(0).unwrap();
|
||||
dac.enable_channel().unwrap();
|
||||
dac.set_trigger_enable(true).unwrap();
|
||||
|
||||
#[cfg(any(dac_v5, dac_v6, dac_v7))]
|
||||
dac.set_hfsel();
|
||||
dac.enable();
|
||||
dac
|
||||
}
|
||||
|
||||
/// Select a new trigger for this channel
|
||||
pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
|
||||
unwrap!(self.disable_channel());
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_tsel2(trigger.tsel());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write `data` to the DAC CH2 via DMA.
|
||||
/// Create a new `DacChannel` instance where the external output pin is not used,
|
||||
/// so the DAC can only be used to generate internal signals.
|
||||
/// The GPIO pin is therefore available to be used for other functions.
|
||||
///
|
||||
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
||||
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
||||
/// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
|
||||
/// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.
|
||||
/// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the
|
||||
/// channel; you must re-enable it with `enable()`.
|
||||
///
|
||||
/// **Important:** Channel 2 has to be configured for the DAC instance!
|
||||
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
||||
where
|
||||
Tx: DmaCh2<T>,
|
||||
{
|
||||
let channel = Channel::Ch2.index();
|
||||
debug!("Writing to channel {}", channel);
|
||||
|
||||
// Enable DAC and DMA
|
||||
T::regs().cr().modify(|w| {
|
||||
w.set_en(channel, true);
|
||||
w.set_dmaen(channel, true);
|
||||
});
|
||||
|
||||
let tx_request = self.dma.request();
|
||||
let dma_channel = &mut self.dma;
|
||||
|
||||
let tx_options = crate::dma::TransferOptions {
|
||||
circular,
|
||||
half_transfer_ir: false,
|
||||
complete_transfer_ir: !circular,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||
let tx_f = match data {
|
||||
ValueArray::Bit8(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr8r(channel).as_ptr() as *mut u8,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Left(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12l(channel).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Right(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12r(channel).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
tx_f.await;
|
||||
|
||||
// finish dma
|
||||
// TODO: Do we need to check any status registers here?
|
||||
T::regs().cr().modify(|w| {
|
||||
// Disable the DAC peripheral
|
||||
w.set_en(channel, false);
|
||||
// Disable the DMA. TODO: Is this necessary?
|
||||
w.set_dmaen(channel, false);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> {
|
||||
/// Create a new DAC instance with both channels.
|
||||
/// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
|
||||
///
|
||||
/// This is used to obtain two independent channels via `split()` for use e.g. with DMA.
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
dma_ch1: impl Peripheral<P = TxCh1> + 'd,
|
||||
dma_ch2: impl Peripheral<P = TxCh2> + 'd,
|
||||
pin_ch1: impl Peripheral<P = impl DacPin<T, 1>> + crate::gpio::sealed::Pin + 'd,
|
||||
pin_ch2: impl Peripheral<P = impl DacPin<T, 2>> + crate::gpio::sealed::Pin + 'd,
|
||||
) -> Self {
|
||||
pin_ch1.set_as_analog();
|
||||
pin_ch2.set_as_analog();
|
||||
into_ref!(peri, dma_ch1, dma_ch2);
|
||||
/// By default, triggering is disabled, but it can be enabled using
|
||||
/// [`DacChannel::set_trigger()`].
|
||||
#[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]
|
||||
pub fn new_internal(_peri: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = DMA> + 'd) -> Self {
|
||||
into_ref!(dma);
|
||||
T::enable_and_reset();
|
||||
|
||||
let mut dac_ch1 = DacCh1 {
|
||||
_peri: peri,
|
||||
dma: dma_ch1,
|
||||
};
|
||||
|
||||
let mut dac_ch2 = DacCh2 {
|
||||
let mut dac = Self {
|
||||
phantom: PhantomData,
|
||||
dma: dma_ch2,
|
||||
dma,
|
||||
};
|
||||
#[cfg(any(dac_v5, dac_v6, dac_v7))]
|
||||
dac.set_hfsel();
|
||||
dac.set_mode(Mode::NormalInternalUnbuffered);
|
||||
dac.enable();
|
||||
dac
|
||||
}
|
||||
|
||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||
#[cfg(any(dac_v2, dac_v3))]
|
||||
dac_ch1.set_channel_mode(0).unwrap();
|
||||
dac_ch1.enable_channel().unwrap();
|
||||
dac_ch1.set_trigger_enable(true).unwrap();
|
||||
/// Enable or disable this channel.
|
||||
pub fn set_enable(&mut self, on: bool) {
|
||||
critical_section::with(|_| {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_en(Self::IDX, on);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(any(dac_v2, dac_v3))]
|
||||
dac_ch2.set_channel_mode(0).unwrap();
|
||||
dac_ch2.enable_channel().unwrap();
|
||||
dac_ch2.set_trigger_enable(true).unwrap();
|
||||
/// Enable this channel.
|
||||
pub fn enable(&mut self) {
|
||||
self.set_enable(true)
|
||||
}
|
||||
|
||||
Self {
|
||||
ch1: dac_ch1,
|
||||
ch2: dac_ch2,
|
||||
/// Disable this channel.
|
||||
pub fn disable(&mut self) {
|
||||
self.set_enable(false)
|
||||
}
|
||||
|
||||
/// Set the trigger source for this channel.
|
||||
///
|
||||
/// This method disables the channel, so you may need to re-enable afterwards.
|
||||
pub fn set_trigger(&mut self, source: TriggerSel) {
|
||||
critical_section::with(|_| {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_en(Self::IDX, false);
|
||||
reg.set_tsel(Self::IDX, source as u8);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Enable or disable triggering for this channel.
|
||||
pub fn set_triggering(&mut self, on: bool) {
|
||||
critical_section::with(|_| {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_ten(Self::IDX, on);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Software trigger this channel.
|
||||
pub fn trigger(&mut self) {
|
||||
T::regs().swtrigr().write(|reg| {
|
||||
reg.set_swtrig(Self::IDX, true);
|
||||
});
|
||||
}
|
||||
|
||||
/// Set mode of this channel.
|
||||
///
|
||||
/// This method disables the channel, so you may need to re-enable afterwards.
|
||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||
pub fn set_mode(&mut self, mode: Mode) {
|
||||
critical_section::with(|_| {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_en(Self::IDX, false);
|
||||
});
|
||||
T::regs().mcr().modify(|reg| {
|
||||
reg.set_mode(Self::IDX, mode.mode());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Write a new value to this channel.
|
||||
///
|
||||
/// If triggering is not enabled, the new value is immediately output; otherwise,
|
||||
/// it will be output after the next trigger.
|
||||
pub fn set(&mut self, value: Value) {
|
||||
match value {
|
||||
Value::Bit8(v) => T::regs().dhr8r(Self::IDX).write(|reg| reg.set_dhr(v)),
|
||||
Value::Bit12Left(v) => T::regs().dhr12l(Self::IDX).write(|reg| reg.set_dhr(v)),
|
||||
Value::Bit12Right(v) => T::regs().dhr12r(Self::IDX).write(|reg| reg.set_dhr(v)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the DAC into CH1 and CH2 for independent use.
|
||||
pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) {
|
||||
/// Read the current output value of the DAC.
|
||||
pub fn read(&self) -> u16 {
|
||||
T::regs().dor(Self::IDX).read().dor()
|
||||
}
|
||||
|
||||
/// Set HFSEL as appropriate for the current peripheral clock frequency.
|
||||
#[cfg(dac_v5)]
|
||||
fn set_hfsel(&mut self) {
|
||||
if T::frequency() >= crate::time::mhz(80) {
|
||||
critical_section::with(|_| {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_hfsel(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set HFSEL as appropriate for the current peripheral clock frequency.
|
||||
#[cfg(any(dac_v6, dac_v7))]
|
||||
fn set_hfsel(&mut self) {
|
||||
if T::frequency() >= crate::time::mhz(160) {
|
||||
critical_section::with(|_| {
|
||||
T::regs().mcr().modify(|reg| {
|
||||
reg.set_hfsel(0b10);
|
||||
});
|
||||
});
|
||||
} else if T::frequency() >= crate::time::mhz(80) {
|
||||
critical_section::with(|_| {
|
||||
T::regs().mcr().modify(|reg| {
|
||||
reg.set_hfsel(0b01);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_dma_methods {
|
||||
($n:literal, $trait:ident) => {
|
||||
impl<'d, T: Instance, DMA> DacChannel<'d, T, $n, DMA>
|
||||
where
|
||||
DMA: $trait<T>,
|
||||
{
|
||||
/// Write `data` to this channel via DMA.
|
||||
///
|
||||
/// To prevent delays or glitches when outputing a periodic waveform, the `circular`
|
||||
/// flag can be set. This configures a circular DMA transfer that continually outputs
|
||||
/// `data`. Note that for performance reasons in circular mode the transfer-complete
|
||||
/// interrupt is disabled.
|
||||
#[cfg(not(gpdma))]
|
||||
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) {
|
||||
// Enable DAC and DMA
|
||||
T::regs().cr().modify(|w| {
|
||||
w.set_en(Self::IDX, true);
|
||||
w.set_dmaen(Self::IDX, true);
|
||||
});
|
||||
|
||||
let tx_request = self.dma.request();
|
||||
let dma_channel = &mut self.dma;
|
||||
|
||||
let tx_options = crate::dma::TransferOptions {
|
||||
circular,
|
||||
half_transfer_ir: false,
|
||||
complete_transfer_ir: !circular,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||
let tx_f = match data {
|
||||
ValueArray::Bit8(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr8r(Self::IDX).as_ptr() as *mut u8,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Left(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12l(Self::IDX).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
ValueArray::Bit12Right(buf) => unsafe {
|
||||
crate::dma::Transfer::new_write(
|
||||
dma_channel,
|
||||
tx_request,
|
||||
buf,
|
||||
T::regs().dhr12r(Self::IDX).as_ptr() as *mut u16,
|
||||
tx_options,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
tx_f.await;
|
||||
|
||||
T::regs().cr().modify(|w| {
|
||||
w.set_en(Self::IDX, false);
|
||||
w.set_dmaen(Self::IDX, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_dma_methods!(1, DacDma1);
|
||||
impl_dma_methods!(2, DacDma2);
|
||||
|
||||
impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> {
|
||||
fn drop(&mut self) {
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
/// DAC driver.
|
||||
///
|
||||
/// Use this struct when you want to use both channels, either together or independently.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Pins may need to be changed for your specific device.
|
||||
/// let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC, NoDma, NoDma, p.PA4, p.PA5).split();
|
||||
/// ```
|
||||
pub struct Dac<'d, T: Instance, DMACh1 = NoDma, DMACh2 = NoDma> {
|
||||
ch1: DacChannel<'d, T, 1, DMACh1>,
|
||||
ch2: DacChannel<'d, T, 2, DMACh2>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> {
|
||||
/// Create a new `Dac` instance, consuming the underlying DAC peripheral.
|
||||
///
|
||||
/// This struct allows you to access both channels of the DAC, where available. You can either
|
||||
/// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use
|
||||
/// the two channels together.
|
||||
///
|
||||
/// The channels are enabled on creation and begins to drive their output pins.
|
||||
/// Note that some methods, such as `set_trigger()` and `set_mode()`, will
|
||||
/// disable the channel; you must re-enable them with `enable()`.
|
||||
///
|
||||
/// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
|
||||
/// method on the underlying channels.
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
dma_ch1: impl Peripheral<P = DMACh1> + 'd,
|
||||
dma_ch2: impl Peripheral<P = DMACh2> + 'd,
|
||||
pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::sealed::Pin> + 'd,
|
||||
pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::sealed::Pin> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2);
|
||||
pin_ch1.set_as_analog();
|
||||
pin_ch2.set_as_analog();
|
||||
// Enable twice to increment the DAC refcount for each channel.
|
||||
T::enable_and_reset();
|
||||
T::enable_and_reset();
|
||||
Self {
|
||||
ch1: DacCh1 {
|
||||
phantom: PhantomData,
|
||||
dma: dma_ch1,
|
||||
},
|
||||
ch2: DacCh2 {
|
||||
phantom: PhantomData,
|
||||
dma: dma_ch2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Dac` instance where the external output pins are not used,
|
||||
/// so the DAC can only be used to generate internal signals but the GPIO
|
||||
/// pins remain available for other functions.
|
||||
///
|
||||
/// This struct allows you to access both channels of the DAC, where available. You can either
|
||||
/// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two
|
||||
/// channels together.
|
||||
///
|
||||
/// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.
|
||||
/// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the
|
||||
/// channel; you must re-enable them with `enable()`.
|
||||
///
|
||||
/// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
|
||||
/// method on the underlying channels.
|
||||
#[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]
|
||||
pub fn new_internal(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
dma_ch1: impl Peripheral<P = DMACh1> + 'd,
|
||||
dma_ch2: impl Peripheral<P = DMACh2> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(dma_ch1, dma_ch2);
|
||||
// Enable twice to increment the DAC refcount for each channel.
|
||||
T::enable_and_reset();
|
||||
T::enable_and_reset();
|
||||
Self {
|
||||
ch1: DacCh1 {
|
||||
phantom: PhantomData,
|
||||
dma: dma_ch1,
|
||||
},
|
||||
ch2: DacCh2 {
|
||||
phantom: PhantomData,
|
||||
dma: dma_ch2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Split this `Dac` into separate channels.
|
||||
///
|
||||
/// You can access and move the channels around separately after splitting.
|
||||
pub fn split(self) -> (DacCh1<'d, T, DMACh1>, DacCh2<'d, T, DMACh2>) {
|
||||
(self.ch1, self.ch2)
|
||||
}
|
||||
|
||||
/// Get mutable reference to CH1
|
||||
pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> {
|
||||
/// Temporarily access channel 1.
|
||||
pub fn ch1(&mut self) -> &mut DacCh1<'d, T, DMACh1> {
|
||||
&mut self.ch1
|
||||
}
|
||||
|
||||
/// Get mutable reference to CH2
|
||||
pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> {
|
||||
/// Temporarily access channel 2.
|
||||
pub fn ch2(&mut self) -> &mut DacCh2<'d, T, DMACh2> {
|
||||
&mut self.ch2
|
||||
}
|
||||
|
||||
/// Get reference to CH1
|
||||
pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> {
|
||||
&self.ch1
|
||||
/// Simultaneously update channels 1 and 2 with a new value.
|
||||
///
|
||||
/// If triggering is not enabled, the new values are immediately output;
|
||||
/// otherwise, they will be output after the next trigger.
|
||||
pub fn set(&mut self, values: DualValue) {
|
||||
match values {
|
||||
DualValue::Bit8(v1, v2) => T::regs().dhr8rd().write(|reg| {
|
||||
reg.set_dhr(0, v1);
|
||||
reg.set_dhr(1, v2);
|
||||
}),
|
||||
DualValue::Bit12Left(v1, v2) => T::regs().dhr12ld().write(|reg| {
|
||||
reg.set_dhr(0, v1);
|
||||
reg.set_dhr(1, v2);
|
||||
}),
|
||||
DualValue::Bit12Right(v1, v2) => T::regs().dhr12rd().write(|reg| {
|
||||
reg.set_dhr(0, v1);
|
||||
reg.set_dhr(1, v2);
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get reference to CH2
|
||||
pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> {
|
||||
&self.ch2
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> {
|
||||
const CHANNEL: Channel = Channel::Ch1;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> {
|
||||
const CHANNEL: Channel = Channel::Ch2;
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
@ -552,34 +493,36 @@ pub(crate) mod sealed {
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
||||
dma_trait!(DmaCh1, Instance);
|
||||
dma_trait!(DmaCh2, Instance);
|
||||
dma_trait!(DacDma1, Instance);
|
||||
dma_trait!(DacDma2, Instance);
|
||||
|
||||
/// Marks a pin that can be used with the DAC
|
||||
pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(dac, $inst:ident) => {
|
||||
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 })
|
||||
}
|
||||
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 })
|
||||
}
|
||||
|
||||
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
|
||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
|
||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
|
||||
}
|
||||
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
||||
// TODO: Increment refcount?
|
||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
|
||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
|
||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
|
||||
}
|
||||
|
||||
fn disable_with_cs(_cs: critical_section::CriticalSection) {
|
||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
|
||||
}
|
||||
}
|
||||
fn disable_with_cs(_cs: critical_section::CriticalSection) {
|
||||
// TODO: Decrement refcount?
|
||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
||||
|
||||
impl crate::dac::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::dac::Dac {
|
||||
|
282
embassy-stm32/src/dac/tsel.rs
Normal file
282
embassy-stm32/src/dac/tsel.rs
Normal file
@ -0,0 +1,282 @@
|
||||
/// Trigger selection for STM32F0.
|
||||
#[cfg(stm32f0)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim3 = 1,
|
||||
Tim7 = 2,
|
||||
Tim15 = 3,
|
||||
Tim2 = 4,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F1.
|
||||
#[cfg(stm32f1)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
#[cfg(any(stm32f100, stm32f105, stm32f107))]
|
||||
Tim3 = 1,
|
||||
#[cfg(any(stm32f101, stm32f103))]
|
||||
Tim8 = 1,
|
||||
Tim7 = 2,
|
||||
#[cfg(any(stm32f101, stm32f103, stm32f105, stm32f107))]
|
||||
Tim5 = 3,
|
||||
#[cfg(all(stm32f100, any(flashsize_4, flashsize_6, flashsize_8, flashsize_b)))]
|
||||
Tim15 = 3,
|
||||
#[cfg(all(stm32f100, any(flashsize_c, flashsize_d, flashsize_e)))]
|
||||
/// Can be remapped to TIM15 with MISC_REMAP in AFIO_MAPR2.
|
||||
Tim5Or15 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F2/F4/F7/L4, except F410 or L4+.
|
||||
#[cfg(all(any(stm32f2, stm32f4, stm32f7, stm32l4_nonplus), not(stm32f410)))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim8 = 1,
|
||||
#[cfg(not(any(stm32l45x, stm32l46x)))]
|
||||
Tim7 = 2,
|
||||
Tim5 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F410.
|
||||
#[cfg(stm32f410)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim5 = 3,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F301/2 and 318.
|
||||
#[cfg(any(stm32f301, stm32f302, stm32f318))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
#[cfg(stm32f302)]
|
||||
/// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1.
|
||||
Tim3 = 1,
|
||||
Tim15 = 3,
|
||||
Tim2 = 4,
|
||||
#[cfg(all(stm32f302, any(flashsize_6, flashsize_8)))]
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F303/3x8 (excluding 318 which is like 301, and 378 which is 37x).
|
||||
#[cfg(any(stm32f303, stm32f328, stm32f358, stm32f398))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
/// * DAC1: defaults to TIM8 but can be remapped to TIM3 with DAC_TRIG_RMP in SYSCFG_CFGR1
|
||||
/// * DAC2: always TIM3
|
||||
Tim8Or3 = 1,
|
||||
Tim7 = 2,
|
||||
Tim15 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F37x.
|
||||
#[cfg(any(stm32f373, stm32f378))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim3 = 1,
|
||||
Tim7 = 2,
|
||||
/// TIM5 on DAC1, TIM18 on DAC2
|
||||
Dac1Tim5Dac2Tim18 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F334.
|
||||
#[cfg(stm32f334)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
/// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1.
|
||||
Tim3 = 1,
|
||||
Tim7 = 2,
|
||||
/// Can be remapped to HRTIM_DACTRG1 using DAC1_TRIG3_RMP in SYSCFG_CFGR3.
|
||||
Tim15OrHrtimDacTrg1 = 3,
|
||||
Tim2 = 4,
|
||||
/// Requires DAC_TRIG5_RMP set in SYSCFG_CFGR3.
|
||||
HrtimDacTrg2 = 5,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32L0.
|
||||
#[cfg(stm32l0)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim3 = 1,
|
||||
Tim3Ch3 = 2,
|
||||
Tim21 = 3,
|
||||
Tim2 = 4,
|
||||
Tim7 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32L1.
|
||||
#[cfg(stm32l1)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim7 = 2,
|
||||
Tim9 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for L4+, L5, U5, H7.
|
||||
#[cfg(any(stm32l4_plus, stm32l5, stm32u5, stm32h7))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
Tim1 = 1,
|
||||
Tim2 = 2,
|
||||
Tim4 = 3,
|
||||
Tim5 = 4,
|
||||
Tim6 = 5,
|
||||
Tim7 = 6,
|
||||
Tim8 = 7,
|
||||
Tim15 = 8,
|
||||
#[cfg(all(stm32h7, hrtim))]
|
||||
Hrtim1DacTrg1 = 9,
|
||||
#[cfg(all(stm32h7, hrtim))]
|
||||
Hrtim1DacTrg2 = 10,
|
||||
Lptim1 = 11,
|
||||
#[cfg(not(stm32u5))]
|
||||
Lptim2 = 12,
|
||||
#[cfg(stm32u5)]
|
||||
Lptim3 = 12,
|
||||
Exti9 = 13,
|
||||
#[cfg(any(stm32h7ax, stm32h7bx))]
|
||||
/// RM0455 suggests this might be LPTIM2 on DAC1 and LPTIM3 on DAC2,
|
||||
/// but it's probably wrong. Please let us know if you find out.
|
||||
Lptim3 = 14,
|
||||
#[cfg(any(stm32h72x, stm32h73x))]
|
||||
Tim23 = 14,
|
||||
#[cfg(any(stm32h72x, stm32h73x))]
|
||||
Tim24 = 15,
|
||||
}
|
||||
|
||||
/// Trigger selection for H5.
|
||||
#[cfg(stm32h5)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
Tim1 = 1,
|
||||
Tim2 = 2,
|
||||
#[cfg(any(stm32h56x, stm32h57x))]
|
||||
Tim4 = 3,
|
||||
#[cfg(stm32h503)]
|
||||
Tim3 = 3,
|
||||
#[cfg(any(stm32h56x, stm32h57x))]
|
||||
Tim5 = 4,
|
||||
Tim6 = 5,
|
||||
Tim7 = 6,
|
||||
#[cfg(any(stm32h56x, stm32h57x))]
|
||||
Tim8 = 7,
|
||||
#[cfg(any(stm32h56x, stm32h57x))]
|
||||
Tim15 = 8,
|
||||
Lptim1 = 11,
|
||||
Lptim2 = 12,
|
||||
Exti9 = 13,
|
||||
}
|
||||
|
||||
/// Trigger selection for G0.
|
||||
#[cfg(stm32g0)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
Tim1 = 1,
|
||||
Tim2 = 2,
|
||||
Tim3 = 3,
|
||||
Tim6 = 5,
|
||||
Tim7 = 6,
|
||||
Tim15 = 8,
|
||||
Lptim1 = 11,
|
||||
Lptim2 = 12,
|
||||
Exti9 = 13,
|
||||
}
|
||||
|
||||
/// Trigger selection for G4.
|
||||
#[cfg(stm32g4)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
/// * DAC1, DAC2, DAC4: TIM8
|
||||
/// * DAC3: TIM1
|
||||
Dac124Tim8Dac3Tim1 = 1,
|
||||
Tim7 = 2,
|
||||
Tim15 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Tim6 = 7,
|
||||
Tim3 = 8,
|
||||
HrtimDacRstTrg1 = 9,
|
||||
HrtimDacRstTrg2 = 10,
|
||||
HrtimDacRstTrg3 = 11,
|
||||
HrtimDacRstTrg4 = 12,
|
||||
HrtimDacRstTrg5 = 13,
|
||||
HrtimDacRstTrg6 = 14,
|
||||
/// * DAC1, DAC4: HRTIM_DAC_TRG1
|
||||
/// * DAC2: HRTIM_DAC_TRG2
|
||||
/// * DAC3: HRTIM_DAC_TRG3
|
||||
HrtimDacTrg123 = 15,
|
||||
}
|
||||
|
||||
/// Trigger selection for WL.
|
||||
#[cfg(stm32wl)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
Tim1 = 1,
|
||||
Tim2 = 2,
|
||||
Lptim1 = 11,
|
||||
Lptim2 = 12,
|
||||
Lptim3 = 13,
|
||||
Exti9 = 14,
|
||||
}
|
||||
|
||||
impl TriggerSel {
|
||||
pub fn tsel(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
}
|
@ -346,10 +346,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
|
||||
None => {
|
||||
cfg_if::cfg_if! {
|
||||
// For some chips PREDIV is always two, and cannot be changed
|
||||
if #[cfg(any(
|
||||
stm32f302xd, stm32f302xe, stm32f303xd,
|
||||
stm32f303xe, stm32f398xe
|
||||
))] {
|
||||
if #[cfg(any(flashsize_d, flashsize_e))] {
|
||||
let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0);
|
||||
(
|
||||
Hertz((HSI_FREQ.0 / divisor) * multiplier),
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||
use embassy_stm32::dac::{DacCh1, Value};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -14,11 +14,10 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
info!("Hello World, dude!");
|
||||
|
||||
let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4);
|
||||
unwrap!(dac.set_trigger_enable(false));
|
||||
|
||||
loop {
|
||||
for v in 0..=255 {
|
||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
||||
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use defmt::*;
|
||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||
use embassy_stm32::dac::{DacCh1, Value};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use embassy_stm32::Config;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
@ -46,11 +46,10 @@ fn main() -> ! {
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
||||
unwrap!(dac.set_trigger_enable(false));
|
||||
|
||||
loop {
|
||||
for v in 0..=255 {
|
||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
||||
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,21 +4,15 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::dac::{DacChannel, ValueArray};
|
||||
use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
|
||||
use embassy_stm32::pac::timer::vals::{Mms, Opm};
|
||||
use embassy_stm32::peripherals::{TIM6, TIM7};
|
||||
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
||||
use micromath::F32Ext;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
pub type Dac1Type =
|
||||
embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
|
||||
|
||||
pub type Dac2Type =
|
||||
embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
@ -63,7 +57,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn dac_task1(mut dac: Dac1Type) {
|
||||
async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
||||
let data: &[u8; 256] = &calculate_array::<256>();
|
||||
|
||||
info!("TIM6 frequency is {}", TIM6::frequency());
|
||||
@ -77,8 +71,9 @@ async fn dac_task1(mut dac: Dac1Type) {
|
||||
error!("Reload value {} below threshold!", reload);
|
||||
}
|
||||
|
||||
dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap();
|
||||
dac.enable_channel().unwrap();
|
||||
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6);
|
||||
dac.set_triggering(true);
|
||||
dac.enable();
|
||||
|
||||
TIM6::enable_and_reset();
|
||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
@ -100,14 +95,12 @@ async fn dac_task1(mut dac: Dac1Type) {
|
||||
// Loop technically not necessary if DMA circular mode is enabled
|
||||
loop {
|
||||
info!("Loop DAC1");
|
||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
||||
error!("Could not write to dac: {}", e);
|
||||
}
|
||||
dac.write(ValueArray::Bit8(data), true).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn dac_task2(mut dac: Dac2Type) {
|
||||
async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
||||
let data: &[u8; 256] = &calculate_array::<256>();
|
||||
|
||||
info!("TIM7 frequency is {}", TIM7::frequency());
|
||||
@ -127,7 +120,9 @@ async fn dac_task2(mut dac: Dac2Type) {
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap();
|
||||
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7);
|
||||
dac.set_triggering(true);
|
||||
dac.enable();
|
||||
|
||||
debug!(
|
||||
"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
|
||||
@ -138,9 +133,7 @@ async fn dac_task2(mut dac: Dac2Type) {
|
||||
data.len()
|
||||
);
|
||||
|
||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
||||
error!("Could not write to dac: {}", e);
|
||||
}
|
||||
dac.write(ValueArray::Bit8(data), true).await;
|
||||
}
|
||||
|
||||
fn to_sine_wave(v: u8) -> u8 {
|
||||
|
@ -3,7 +3,7 @@
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||
use embassy_stm32::dac::{DacCh1, Value};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -13,11 +13,10 @@ fn main() -> ! {
|
||||
info!("Hello World!");
|
||||
|
||||
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
||||
unwrap!(dac.set_trigger_enable(false));
|
||||
|
||||
loop {
|
||||
for v in 0..=255 {
|
||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
||||
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,21 +4,15 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::dac::{DacChannel, ValueArray};
|
||||
use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
|
||||
use embassy_stm32::pac::timer::vals::{Mms, Opm};
|
||||
use embassy_stm32::peripherals::{TIM6, TIM7};
|
||||
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
||||
use micromath::F32Ext;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
pub type Dac1Type =
|
||||
embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
|
||||
|
||||
pub type Dac2Type =
|
||||
embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let config = embassy_stm32::Config::default();
|
||||
@ -34,7 +28,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn dac_task1(mut dac: Dac1Type) {
|
||||
async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
||||
let data: &[u8; 256] = &calculate_array::<256>();
|
||||
|
||||
info!("TIM6 frequency is {}", TIM6::frequency());
|
||||
@ -48,8 +42,9 @@ async fn dac_task1(mut dac: Dac1Type) {
|
||||
error!("Reload value {} below threshold!", reload);
|
||||
}
|
||||
|
||||
dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap();
|
||||
dac.enable_channel().unwrap();
|
||||
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6);
|
||||
dac.set_triggering(true);
|
||||
dac.enable();
|
||||
|
||||
TIM6::enable_and_reset();
|
||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
@ -71,14 +66,12 @@ async fn dac_task1(mut dac: Dac1Type) {
|
||||
// Loop technically not necessary if DMA circular mode is enabled
|
||||
loop {
|
||||
info!("Loop DAC1");
|
||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
||||
error!("Could not write to dac: {}", e);
|
||||
}
|
||||
dac.write(ValueArray::Bit8(data), true).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn dac_task2(mut dac: Dac2Type) {
|
||||
async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
||||
let data: &[u8; 256] = &calculate_array::<256>();
|
||||
|
||||
info!("TIM7 frequency is {}", TIM7::frequency());
|
||||
@ -98,7 +91,9 @@ async fn dac_task2(mut dac: Dac2Type) {
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap();
|
||||
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7);
|
||||
dac.set_triggering(true);
|
||||
dac.enable();
|
||||
|
||||
debug!(
|
||||
"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
|
||||
@ -109,9 +104,7 @@ async fn dac_task2(mut dac: Dac2Type) {
|
||||
data.len()
|
||||
);
|
||||
|
||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
||||
error!("Could not write to dac: {}", e);
|
||||
}
|
||||
dac.write(ValueArray::Bit8(data), true).await;
|
||||
}
|
||||
|
||||
fn to_sine_wave(v: u8) -> u8 {
|
||||
|
@ -10,7 +10,7 @@ use common::*;
|
||||
use defmt::assert;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::adc::Adc;
|
||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||
use embassy_stm32::dac::{DacCh1, Value};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use embassy_time::{Delay, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
@ -26,9 +26,7 @@ async fn main(_spawner: Spawner) {
|
||||
#[cfg(any(feature = "stm32h755zi", feature = "stm32g071rb"))]
|
||||
let dac_peripheral = p.DAC1;
|
||||
|
||||
let mut dac: DacCh1<'_, _, NoDma> = DacCh1::new(dac_peripheral, NoDma, p.PA4);
|
||||
unwrap!(dac.set_trigger_enable(false));
|
||||
|
||||
let mut dac = DacCh1::new(dac_peripheral, NoDma, p.PA4);
|
||||
let mut adc = Adc::new(p.ADC1, &mut Delay);
|
||||
|
||||
#[cfg(feature = "stm32h755zi")]
|
||||
@ -36,7 +34,7 @@ async fn main(_spawner: Spawner) {
|
||||
#[cfg(any(feature = "stm32f429zi", feature = "stm32g071rb"))]
|
||||
let normalization_factor: i32 = 16;
|
||||
|
||||
unwrap!(dac.set(Value::Bit8(0)));
|
||||
dac.set(Value::Bit8(0));
|
||||
// Now wait a little to obtain a stable value
|
||||
Timer::after_millis(30).await;
|
||||
let offset = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4);
|
||||
@ -44,7 +42,7 @@ async fn main(_spawner: Spawner) {
|
||||
for v in 0..=255 {
|
||||
// First set the DAC output value
|
||||
let dac_output_val = to_sine_wave(v);
|
||||
unwrap!(dac.set(Value::Bit8(dac_output_val)));
|
||||
dac.set(Value::Bit8(dac_output_val));
|
||||
|
||||
// Now wait a little to obtain a stable value
|
||||
Timer::after_millis(30).await;
|
||||
|
Loading…
Reference in New Issue
Block a user