diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-06-29 08:54:28 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-06-29 08:54:28 +0000 |
| commit | 6eb46c419c9f2f071d3c80323e2c68c25b617e20 (patch) | |
| tree | a741b72ea7b403e1d25ab4b2c60a7a9e4a411c6b | |
| parent | 01101e3df0619dca85ab692be8b7c38f249b44e8 (diff) | |
| parent | 96f1525ffe675b7e3ca26f038bc558488c03af9b (diff) | |
Merge pull request #1565 from JuliDi/main
Implement DMA for DAC on STM32
| -rw-r--r-- | embassy-stm32/build.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/dac.rs | 260 | ||||
| -rw-r--r-- | embassy-stm32/src/dac/mod.rs | 570 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/bdma.rs | 32 | ||||
| -rw-r--r-- | embassy-stm32/src/sdmmc/mod.rs | 6 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/dac.rs | 9 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/dac.rs | 9 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/dac.rs | 17 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/dac_dma.rs | 137 |
9 files changed, 757 insertions, 285 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 40103d322..fa66da1f6 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -699,6 +699,8 @@ fn main() { | |||
| 699 | // SDMMCv1 uses the same channel for both directions, so just implement for RX | 699 | // SDMMCv1 uses the same channel for both directions, so just implement for RX |
| 700 | (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), | 700 | (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), |
| 701 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), | 701 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), |
| 702 | (("dac", "CH1"), quote!(crate::dac::DmaCh1)), | ||
| 703 | (("dac", "CH2"), quote!(crate::dac::DmaCh2)), | ||
| 702 | ] | 704 | ] |
| 703 | .into(); | 705 | .into(); |
| 704 | 706 | ||
diff --git a/embassy-stm32/src/dac.rs b/embassy-stm32/src/dac.rs deleted file mode 100644 index 631118877..000000000 --- a/embassy-stm32/src/dac.rs +++ /dev/null | |||
| @@ -1,260 +0,0 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | use embassy_hal_common::{into_ref, PeripheralRef}; | ||
| 4 | |||
| 5 | use crate::pac::dac; | ||
| 6 | use crate::rcc::RccPeripheral; | ||
| 7 | use crate::{peripherals, Peripheral}; | ||
| 8 | |||
| 9 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 10 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 11 | pub enum Error { | ||
| 12 | UnconfiguredChannel, | ||
| 13 | InvalidValue, | ||
| 14 | } | ||
| 15 | |||
| 16 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 17 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 18 | pub enum Channel { | ||
| 19 | Ch1, | ||
| 20 | Ch2, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl Channel { | ||
| 24 | fn index(&self) -> usize { | ||
| 25 | match self { | ||
| 26 | Channel::Ch1 => 0, | ||
| 27 | Channel::Ch2 => 1, | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 33 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 34 | pub enum Ch1Trigger { | ||
| 35 | Tim6, | ||
| 36 | Tim3, | ||
| 37 | Tim7, | ||
| 38 | Tim15, | ||
| 39 | Tim2, | ||
| 40 | Exti9, | ||
| 41 | Software, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl Ch1Trigger { | ||
| 45 | fn tsel(&self) -> dac::vals::Tsel1 { | ||
| 46 | match self { | ||
| 47 | Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, | ||
| 48 | Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, | ||
| 49 | Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, | ||
| 50 | Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, | ||
| 51 | Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO, | ||
| 52 | Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9, | ||
| 53 | Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE, | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 59 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 60 | pub enum Ch2Trigger { | ||
| 61 | Tim6, | ||
| 62 | Tim8, | ||
| 63 | Tim7, | ||
| 64 | Tim5, | ||
| 65 | Tim2, | ||
| 66 | Tim4, | ||
| 67 | Exti9, | ||
| 68 | Software, | ||
| 69 | } | ||
| 70 | |||
| 71 | impl Ch2Trigger { | ||
| 72 | fn tsel(&self) -> dac::vals::Tsel2 { | ||
| 73 | match self { | ||
| 74 | Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO, | ||
| 75 | Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO, | ||
| 76 | Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO, | ||
| 77 | Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO, | ||
| 78 | Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO, | ||
| 79 | Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO, | ||
| 80 | Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9, | ||
| 81 | Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 87 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 88 | pub enum Alignment { | ||
| 89 | Left, | ||
| 90 | Right, | ||
| 91 | } | ||
| 92 | |||
| 93 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 94 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 95 | pub enum Value { | ||
| 96 | Bit8(u8), | ||
| 97 | Bit12(u16, Alignment), | ||
| 98 | } | ||
| 99 | |||
| 100 | pub struct Dac<'d, T: Instance> { | ||
| 101 | channels: u8, | ||
| 102 | _peri: PeripheralRef<'d, T>, | ||
| 103 | } | ||
| 104 | |||
| 105 | impl<'d, T: Instance> Dac<'d, T> { | ||
| 106 | pub fn new_1ch(peri: impl Peripheral<P = T> + 'd, _ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd) -> Self { | ||
| 107 | into_ref!(peri); | ||
| 108 | Self::new_inner(peri, 1) | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn new_2ch( | ||
| 112 | peri: impl Peripheral<P = T> + 'd, | ||
| 113 | _ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd, | ||
| 114 | _ch2: impl Peripheral<P = impl DacPin<T, 2>> + 'd, | ||
| 115 | ) -> Self { | ||
| 116 | into_ref!(peri); | ||
| 117 | Self::new_inner(peri, 2) | ||
| 118 | } | ||
| 119 | |||
| 120 | fn new_inner(peri: PeripheralRef<'d, T>, channels: u8) -> Self { | ||
| 121 | T::enable(); | ||
| 122 | T::reset(); | ||
| 123 | |||
| 124 | T::regs().cr().modify(|reg| { | ||
| 125 | for ch in 0..channels { | ||
| 126 | reg.set_en(ch as usize, true); | ||
| 127 | } | ||
| 128 | }); | ||
| 129 | |||
| 130 | Self { channels, _peri: peri } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Check the channel is configured | ||
| 134 | fn check_channel_exists(&self, ch: Channel) -> Result<(), Error> { | ||
| 135 | if ch == Channel::Ch2 && self.channels < 2 { | ||
| 136 | Err(Error::UnconfiguredChannel) | ||
| 137 | } else { | ||
| 138 | Ok(()) | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { | ||
| 143 | self.check_channel_exists(ch)?; | ||
| 144 | T::regs().cr().modify(|reg| { | ||
| 145 | reg.set_en(ch.index(), on); | ||
| 146 | }); | ||
| 147 | Ok(()) | ||
| 148 | } | ||
| 149 | |||
| 150 | pub fn enable_channel(&mut self, ch: Channel) -> Result<(), Error> { | ||
| 151 | self.set_channel_enable(ch, true) | ||
| 152 | } | ||
| 153 | |||
| 154 | pub fn disable_channel(&mut self, ch: Channel) -> Result<(), Error> { | ||
| 155 | self.set_channel_enable(ch, false) | ||
| 156 | } | ||
| 157 | |||
| 158 | pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { | ||
| 159 | self.check_channel_exists(Channel::Ch1)?; | ||
| 160 | unwrap!(self.disable_channel(Channel::Ch1)); | ||
| 161 | T::regs().cr().modify(|reg| { | ||
| 162 | reg.set_tsel1(trigger.tsel()); | ||
| 163 | }); | ||
| 164 | Ok(()) | ||
| 165 | } | ||
| 166 | |||
| 167 | pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { | ||
| 168 | self.check_channel_exists(Channel::Ch2)?; | ||
| 169 | unwrap!(self.disable_channel(Channel::Ch2)); | ||
| 170 | T::regs().cr().modify(|reg| { | ||
| 171 | reg.set_tsel2(trigger.tsel()); | ||
| 172 | }); | ||
| 173 | Ok(()) | ||
| 174 | } | ||
| 175 | |||
| 176 | pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { | ||
| 177 | self.check_channel_exists(ch)?; | ||
| 178 | T::regs().swtrigr().write(|reg| { | ||
| 179 | reg.set_swtrig(ch.index(), true); | ||
| 180 | }); | ||
| 181 | Ok(()) | ||
| 182 | } | ||
| 183 | |||
| 184 | pub fn trigger_all(&mut self) { | ||
| 185 | T::regs().swtrigr().write(|reg| { | ||
| 186 | reg.set_swtrig(Channel::Ch1.index(), true); | ||
| 187 | reg.set_swtrig(Channel::Ch2.index(), true); | ||
| 188 | }); | ||
| 189 | } | ||
| 190 | |||
| 191 | pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { | ||
| 192 | self.check_channel_exists(ch)?; | ||
| 193 | match value { | ||
| 194 | Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), | ||
| 195 | Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), | ||
| 196 | Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), | ||
| 197 | } | ||
| 198 | Ok(()) | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | pub(crate) mod sealed { | ||
| 203 | pub trait Instance { | ||
| 204 | fn regs() -> &'static crate::pac::dac::Dac; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | pub trait Instance: sealed::Instance + RccPeripheral + 'static {} | ||
| 209 | |||
| 210 | pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} | ||
| 211 | |||
| 212 | foreach_peripheral!( | ||
| 213 | (dac, $inst:ident) => { | ||
| 214 | // H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented | ||
| 215 | #[cfg(rcc_h7)] | ||
| 216 | impl crate::rcc::sealed::RccPeripheral for peripherals::$inst { | ||
| 217 | fn frequency() -> crate::time::Hertz { | ||
| 218 | critical_section::with(|_| unsafe { | ||
| 219 | crate::rcc::get_freqs().apb1 | ||
| 220 | }) | ||
| 221 | } | ||
| 222 | |||
| 223 | fn reset() { | ||
| 224 | critical_section::with(|_| { | ||
| 225 | crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true)); | ||
| 226 | crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false)); | ||
| 227 | }) | ||
| 228 | } | ||
| 229 | |||
| 230 | fn enable() { | ||
| 231 | critical_section::with(|_| { | ||
| 232 | crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); | ||
| 233 | }) | ||
| 234 | } | ||
| 235 | |||
| 236 | fn disable() { | ||
| 237 | critical_section::with(|_| { | ||
| 238 | crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)); | ||
| 239 | }) | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | #[cfg(rcc_h7)] | ||
| 244 | impl crate::rcc::RccPeripheral for peripherals::$inst {} | ||
| 245 | |||
| 246 | impl crate::dac::sealed::Instance for peripherals::$inst { | ||
| 247 | fn regs() -> &'static crate::pac::dac::Dac { | ||
| 248 | &crate::pac::$inst | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | impl crate::dac::Instance for peripherals::$inst {} | ||
| 253 | }; | ||
| 254 | ); | ||
| 255 | |||
| 256 | macro_rules! impl_dac_pin { | ||
| 257 | ($inst:ident, $pin:ident, $ch:expr) => { | ||
| 258 | impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {} | ||
| 259 | }; | ||
| 260 | } | ||
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs new file mode 100644 index 000000000..1dc13949d --- /dev/null +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -0,0 +1,570 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | //! Provide access to the STM32 digital-to-analog converter (DAC). | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | |||
| 6 | use embassy_hal_common::{into_ref, PeripheralRef}; | ||
| 7 | |||
| 8 | use crate::pac::dac; | ||
| 9 | use crate::rcc::RccPeripheral; | ||
| 10 | use crate::{peripherals, Peripheral}; | ||
| 11 | |||
| 12 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 14 | /// Curstom Errors | ||
| 15 | pub enum Error { | ||
| 16 | UnconfiguredChannel, | ||
| 17 | InvalidValue, | ||
| 18 | } | ||
| 19 | |||
| 20 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 21 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 22 | /// DAC Channels | ||
| 23 | pub enum Channel { | ||
| 24 | Ch1, | ||
| 25 | Ch2, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl Channel { | ||
| 29 | const fn index(&self) -> usize { | ||
| 30 | match self { | ||
| 31 | Channel::Ch1 => 0, | ||
| 32 | Channel::Ch2 => 1, | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 38 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 39 | /// Trigger sources for CH1 | ||
| 40 | pub enum Ch1Trigger { | ||
| 41 | Tim6, | ||
| 42 | Tim3, | ||
| 43 | Tim7, | ||
| 44 | Tim15, | ||
| 45 | Tim2, | ||
| 46 | Exti9, | ||
| 47 | Software, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl Ch1Trigger { | ||
| 51 | fn tsel(&self) -> dac::vals::Tsel1 { | ||
| 52 | match self { | ||
| 53 | Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, | ||
| 54 | Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, | ||
| 55 | Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, | ||
| 56 | Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, | ||
| 57 | Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO, | ||
| 58 | Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9, | ||
| 59 | Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE, | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 65 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 66 | /// Trigger sources for CH2 | ||
| 67 | pub enum Ch2Trigger { | ||
| 68 | Tim6, | ||
| 69 | Tim8, | ||
| 70 | Tim7, | ||
| 71 | Tim5, | ||
| 72 | Tim2, | ||
| 73 | Tim4, | ||
| 74 | Exti9, | ||
| 75 | Software, | ||
| 76 | } | ||
| 77 | |||
| 78 | impl Ch2Trigger { | ||
| 79 | fn tsel(&self) -> dac::vals::Tsel2 { | ||
| 80 | match self { | ||
| 81 | Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO, | ||
| 82 | Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO, | ||
| 83 | Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO, | ||
| 84 | Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO, | ||
| 85 | Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO, | ||
| 86 | Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO, | ||
| 87 | Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9, | ||
| 88 | Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 94 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 95 | /// Single 8 or 12 bit value that can be output by the DAC | ||
| 96 | pub enum Value { | ||
| 97 | // 8 bit value | ||
| 98 | Bit8(u8), | ||
| 99 | // 12 bit value stored in a u16, left-aligned | ||
| 100 | Bit12Left(u16), | ||
| 101 | // 12 bit value stored in a u16, right-aligned | ||
| 102 | Bit12Right(u16), | ||
| 103 | } | ||
| 104 | |||
| 105 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 106 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 107 | /// Array variant of [`Value`] | ||
| 108 | pub enum ValueArray<'a> { | ||
| 109 | // 8 bit values | ||
| 110 | Bit8(&'a [u8]), | ||
| 111 | // 12 bit value stored in a u16, left-aligned | ||
| 112 | Bit12Left(&'a [u16]), | ||
| 113 | // 12 bit values stored in a u16, right-aligned | ||
| 114 | Bit12Right(&'a [u16]), | ||
| 115 | } | ||
| 116 | /// Provide common functions for DAC channels | ||
| 117 | pub trait DacChannel<T: Instance, Tx> { | ||
| 118 | const CHANNEL: Channel; | ||
| 119 | |||
| 120 | /// Enable trigger of the given channel | ||
| 121 | fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> { | ||
| 122 | T::regs().cr().modify(|reg| { | ||
| 123 | reg.set_ten(Self::CHANNEL.index(), on); | ||
| 124 | }); | ||
| 125 | Ok(()) | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Set mode register of the given channel | ||
| 129 | #[cfg(dac_v2)] | ||
| 130 | fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { | ||
| 131 | T::regs().mcr().modify(|reg| { | ||
| 132 | reg.set_mode(Self::CHANNEL.index(), val); | ||
| 133 | }); | ||
| 134 | Ok(()) | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Set enable register of the given channel | ||
| 138 | fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> { | ||
| 139 | T::regs().cr().modify(|reg| { | ||
| 140 | reg.set_en(Self::CHANNEL.index(), on); | ||
| 141 | }); | ||
| 142 | Ok(()) | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Enable the DAC channel `ch` | ||
| 146 | fn enable_channel(&mut self) -> Result<(), Error> { | ||
| 147 | self.set_channel_enable(true) | ||
| 148 | } | ||
| 149 | |||
| 150 | /// Disable the DAC channel `ch` | ||
| 151 | fn disable_channel(&mut self) -> Result<(), Error> { | ||
| 152 | self.set_channel_enable(false) | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Perform a software trigger on `ch` | ||
| 156 | fn trigger(&mut self) { | ||
| 157 | T::regs().swtrigr().write(|reg| { | ||
| 158 | reg.set_swtrig(Self::CHANNEL.index(), true); | ||
| 159 | }); | ||
| 160 | } | ||
| 161 | |||
| 162 | /// Set a value to be output by the DAC on trigger. | ||
| 163 | /// | ||
| 164 | /// The `value` is written to the corresponding "data holding register". | ||
| 165 | fn set(&mut self, value: Value) -> Result<(), Error> { | ||
| 166 | match value { | ||
| 167 | Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), | ||
| 168 | Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), | ||
| 169 | Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), | ||
| 170 | } | ||
| 171 | Ok(()) | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Hold two DAC channels | ||
| 176 | /// | ||
| 177 | /// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously. | ||
| 178 | /// | ||
| 179 | /// # Example for obtaining both DAC channels | ||
| 180 | /// | ||
| 181 | /// ```ignore | ||
| 182 | /// // DMA channels and pins may need to be changed for your controller | ||
| 183 | /// let (dac_ch1, dac_ch2) = | ||
| 184 | /// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); | ||
| 185 | /// ``` | ||
| 186 | pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { | ||
| 187 | ch1: DacCh1<'d, T, TxCh1>, | ||
| 188 | ch2: DacCh2<'d, T, TxCh2>, | ||
| 189 | } | ||
| 190 | |||
| 191 | /// DAC CH1 | ||
| 192 | /// | ||
| 193 | /// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously. | ||
| 194 | pub struct DacCh1<'d, T: Instance, Tx> { | ||
| 195 | /// To consume T | ||
| 196 | _peri: PeripheralRef<'d, T>, | ||
| 197 | #[allow(unused)] // For chips whose DMA is not (yet) supported | ||
| 198 | dma: PeripheralRef<'d, Tx>, | ||
| 199 | } | ||
| 200 | |||
| 201 | /// DAC CH2 | ||
| 202 | /// | ||
| 203 | /// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously. | ||
| 204 | pub struct DacCh2<'d, T: Instance, Tx> { | ||
| 205 | /// Instead of PeripheralRef to consume T | ||
| 206 | phantom: PhantomData<&'d mut T>, | ||
| 207 | #[allow(unused)] // For chips whose DMA is not (yet) supported | ||
| 208 | dma: PeripheralRef<'d, Tx>, | ||
| 209 | } | ||
| 210 | |||
| 211 | impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { | ||
| 212 | /// Obtain DAC CH1 | ||
| 213 | pub fn new( | ||
| 214 | peri: impl Peripheral<P = T> + 'd, | ||
| 215 | dma: impl Peripheral<P = Tx> + 'd, | ||
| 216 | _pin: impl Peripheral<P = impl DacPin<T, 1>> + 'd, | ||
| 217 | ) -> Self { | ||
| 218 | into_ref!(peri, dma); | ||
| 219 | T::enable(); | ||
| 220 | T::reset(); | ||
| 221 | |||
| 222 | let mut dac = Self { _peri: peri, dma }; | ||
| 223 | |||
| 224 | // Configure each activated channel. All results can be `unwrap`ed since they | ||
| 225 | // will only error if the channel is not configured (i.e. ch1, ch2 are false) | ||
| 226 | #[cfg(dac_v2)] | ||
| 227 | dac.set_channel_mode(0).unwrap(); | ||
| 228 | dac.enable_channel().unwrap(); | ||
| 229 | dac.set_trigger_enable(true).unwrap(); | ||
| 230 | |||
| 231 | dac | ||
| 232 | } | ||
| 233 | |||
| 234 | /// Select a new trigger for this channel | ||
| 235 | /// | ||
| 236 | /// **Important**: This disables the channel! | ||
| 237 | pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { | ||
| 238 | unwrap!(self.disable_channel()); | ||
| 239 | T::regs().cr().modify(|reg| { | ||
| 240 | reg.set_tsel1(trigger.tsel()); | ||
| 241 | }); | ||
| 242 | Ok(()) | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Write `data` to the DAC CH1 via DMA. | ||
| 246 | /// | ||
| 247 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 248 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 249 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 250 | /// | ||
| 251 | /// **Important:** Channel 1 has to be configured for the DAC instance! | ||
| 252 | #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) | ||
| 253 | pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 254 | where | ||
| 255 | Tx: DmaCh1<T>, | ||
| 256 | { | ||
| 257 | let channel = Channel::Ch1.index(); | ||
| 258 | debug!("Writing to channel {}", channel); | ||
| 259 | |||
| 260 | // Enable DAC and DMA | ||
| 261 | T::regs().cr().modify(|w| { | ||
| 262 | w.set_en(channel, true); | ||
| 263 | w.set_dmaen(channel, true); | ||
| 264 | }); | ||
| 265 | |||
| 266 | let tx_request = self.dma.request(); | ||
| 267 | let dma_channel = &self.dma; | ||
| 268 | |||
| 269 | let tx_options = crate::dma::TransferOptions { | ||
| 270 | circular, | ||
| 271 | half_transfer_ir: false, | ||
| 272 | complete_transfer_ir: !circular, | ||
| 273 | ..Default::default() | ||
| 274 | }; | ||
| 275 | |||
| 276 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 277 | let tx_f = match data { | ||
| 278 | ValueArray::Bit8(buf) => unsafe { | ||
| 279 | crate::dma::Transfer::new_write( | ||
| 280 | dma_channel, | ||
| 281 | tx_request, | ||
| 282 | buf, | ||
| 283 | T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||
| 284 | tx_options, | ||
| 285 | ) | ||
| 286 | }, | ||
| 287 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 288 | crate::dma::Transfer::new_write( | ||
| 289 | dma_channel, | ||
| 290 | tx_request, | ||
| 291 | buf, | ||
| 292 | T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||
| 293 | tx_options, | ||
| 294 | ) | ||
| 295 | }, | ||
| 296 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 297 | crate::dma::Transfer::new_write( | ||
| 298 | dma_channel, | ||
| 299 | tx_request, | ||
| 300 | buf, | ||
| 301 | T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||
| 302 | tx_options, | ||
| 303 | ) | ||
| 304 | }, | ||
| 305 | }; | ||
| 306 | |||
| 307 | tx_f.await; | ||
| 308 | |||
| 309 | // finish dma | ||
| 310 | // TODO: Do we need to check any status registers here? | ||
| 311 | T::regs().cr().modify(|w| { | ||
| 312 | // Disable the DAC peripheral | ||
| 313 | w.set_en(channel, false); | ||
| 314 | // Disable the DMA. TODO: Is this necessary? | ||
| 315 | w.set_dmaen(channel, false); | ||
| 316 | }); | ||
| 317 | |||
| 318 | Ok(()) | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | ||
| 323 | /// Obtain DAC CH2 | ||
| 324 | pub fn new( | ||
| 325 | _peri: impl Peripheral<P = T> + 'd, | ||
| 326 | dma: impl Peripheral<P = Tx> + 'd, | ||
| 327 | _pin: impl Peripheral<P = impl DacPin<T, 2>> + 'd, | ||
| 328 | ) -> Self { | ||
| 329 | into_ref!(_peri, dma); | ||
| 330 | T::enable(); | ||
| 331 | T::reset(); | ||
| 332 | |||
| 333 | let mut dac = Self { | ||
| 334 | phantom: PhantomData, | ||
| 335 | dma, | ||
| 336 | }; | ||
| 337 | |||
| 338 | // Configure each activated channel. All results can be `unwrap`ed since they | ||
| 339 | // will only error if the channel is not configured (i.e. ch1, ch2 are false) | ||
| 340 | #[cfg(dac_v2)] | ||
| 341 | dac.set_channel_mode(0).unwrap(); | ||
| 342 | dac.enable_channel().unwrap(); | ||
| 343 | dac.set_trigger_enable(true).unwrap(); | ||
| 344 | |||
| 345 | dac | ||
| 346 | } | ||
| 347 | |||
| 348 | /// Select a new trigger for this channel | ||
| 349 | pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { | ||
| 350 | unwrap!(self.disable_channel()); | ||
| 351 | T::regs().cr().modify(|reg| { | ||
| 352 | reg.set_tsel2(trigger.tsel()); | ||
| 353 | }); | ||
| 354 | Ok(()) | ||
| 355 | } | ||
| 356 | |||
| 357 | /// Write `data` to the DAC CH2 via DMA. | ||
| 358 | /// | ||
| 359 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 360 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 361 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 362 | /// | ||
| 363 | /// **Important:** Channel 2 has to be configured for the DAC instance! | ||
| 364 | #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) | ||
| 365 | pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 366 | where | ||
| 367 | Tx: DmaCh2<T>, | ||
| 368 | { | ||
| 369 | let channel = Channel::Ch2.index(); | ||
| 370 | debug!("Writing to channel {}", channel); | ||
| 371 | |||
| 372 | // Enable DAC and DMA | ||
| 373 | T::regs().cr().modify(|w| { | ||
| 374 | w.set_en(channel, true); | ||
| 375 | w.set_dmaen(channel, true); | ||
| 376 | }); | ||
| 377 | |||
| 378 | let tx_request = self.dma.request(); | ||
| 379 | let dma_channel = &self.dma; | ||
| 380 | |||
| 381 | let tx_options = crate::dma::TransferOptions { | ||
| 382 | circular, | ||
| 383 | half_transfer_ir: false, | ||
| 384 | complete_transfer_ir: !circular, | ||
| 385 | ..Default::default() | ||
| 386 | }; | ||
| 387 | |||
| 388 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 389 | let tx_f = match data { | ||
| 390 | ValueArray::Bit8(buf) => unsafe { | ||
| 391 | crate::dma::Transfer::new_write( | ||
| 392 | dma_channel, | ||
| 393 | tx_request, | ||
| 394 | buf, | ||
| 395 | T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||
| 396 | tx_options, | ||
| 397 | ) | ||
| 398 | }, | ||
| 399 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 400 | crate::dma::Transfer::new_write( | ||
| 401 | dma_channel, | ||
| 402 | tx_request, | ||
| 403 | buf, | ||
| 404 | T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||
| 405 | tx_options, | ||
| 406 | ) | ||
| 407 | }, | ||
| 408 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 409 | crate::dma::Transfer::new_write( | ||
| 410 | dma_channel, | ||
| 411 | tx_request, | ||
| 412 | buf, | ||
| 413 | T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||
| 414 | tx_options, | ||
| 415 | ) | ||
| 416 | }, | ||
| 417 | }; | ||
| 418 | |||
| 419 | tx_f.await; | ||
| 420 | |||
| 421 | // finish dma | ||
| 422 | // TODO: Do we need to check any status registers here? | ||
| 423 | T::regs().cr().modify(|w| { | ||
| 424 | // Disable the DAC peripheral | ||
| 425 | w.set_en(channel, false); | ||
| 426 | // Disable the DMA. TODO: Is this necessary? | ||
| 427 | w.set_dmaen(channel, false); | ||
| 428 | }); | ||
| 429 | |||
| 430 | Ok(()) | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 434 | impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { | ||
| 435 | /// Create a new DAC instance with both channels. | ||
| 436 | /// | ||
| 437 | /// This is used to obtain two independent channels via `split()` for use e.g. with DMA. | ||
| 438 | pub fn new( | ||
| 439 | peri: impl Peripheral<P = T> + 'd, | ||
| 440 | dma_ch1: impl Peripheral<P = TxCh1> + 'd, | ||
| 441 | dma_ch2: impl Peripheral<P = TxCh2> + 'd, | ||
| 442 | _pin_ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd, | ||
| 443 | _pin_ch2: impl Peripheral<P = impl DacPin<T, 2>> + 'd, | ||
| 444 | ) -> Self { | ||
| 445 | into_ref!(peri, dma_ch1, dma_ch2); | ||
| 446 | T::enable(); | ||
| 447 | T::reset(); | ||
| 448 | |||
| 449 | let mut dac_ch1 = DacCh1 { | ||
| 450 | _peri: peri, | ||
| 451 | dma: dma_ch1, | ||
| 452 | }; | ||
| 453 | |||
| 454 | let mut dac_ch2 = DacCh2 { | ||
| 455 | phantom: PhantomData, | ||
| 456 | dma: dma_ch2, | ||
| 457 | }; | ||
| 458 | |||
| 459 | // Configure each activated channel. All results can be `unwrap`ed since they | ||
| 460 | // will only error if the channel is not configured (i.e. ch1, ch2 are false) | ||
| 461 | #[cfg(dac_v2)] | ||
| 462 | dac_ch1.set_channel_mode(0).unwrap(); | ||
| 463 | dac_ch1.enable_channel().unwrap(); | ||
| 464 | dac_ch1.set_trigger_enable(true).unwrap(); | ||
| 465 | |||
| 466 | #[cfg(dac_v2)] | ||
| 467 | dac_ch2.set_channel_mode(0).unwrap(); | ||
| 468 | dac_ch2.enable_channel().unwrap(); | ||
| 469 | dac_ch2.set_trigger_enable(true).unwrap(); | ||
| 470 | |||
| 471 | Self { | ||
| 472 | ch1: dac_ch1, | ||
| 473 | ch2: dac_ch2, | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | /// Split the DAC into CH1 and CH2 for independent use. | ||
| 478 | pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { | ||
| 479 | (self.ch1, self.ch2) | ||
| 480 | } | ||
| 481 | |||
| 482 | /// Get mutable reference to CH1 | ||
| 483 | pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { | ||
| 484 | &mut self.ch1 | ||
| 485 | } | ||
| 486 | |||
| 487 | /// Get mutable reference to CH2 | ||
| 488 | pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { | ||
| 489 | &mut self.ch2 | ||
| 490 | } | ||
| 491 | |||
| 492 | /// Get reference to CH1 | ||
| 493 | pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { | ||
| 494 | &self.ch1 | ||
| 495 | } | ||
| 496 | |||
| 497 | /// Get reference to CH2 | ||
| 498 | pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { | ||
| 499 | &self.ch2 | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> { | ||
| 504 | const CHANNEL: Channel = Channel::Ch1; | ||
| 505 | } | ||
| 506 | |||
| 507 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> { | ||
| 508 | const CHANNEL: Channel = Channel::Ch2; | ||
| 509 | } | ||
| 510 | |||
| 511 | pub(crate) mod sealed { | ||
| 512 | pub trait Instance { | ||
| 513 | fn regs() -> &'static crate::pac::dac::Dac; | ||
| 514 | } | ||
| 515 | } | ||
| 516 | |||
| 517 | pub trait Instance: sealed::Instance + RccPeripheral + 'static {} | ||
| 518 | dma_trait!(DmaCh1, Instance); | ||
| 519 | dma_trait!(DmaCh2, Instance); | ||
| 520 | |||
| 521 | /// Marks a pin that can be used with the DAC | ||
| 522 | pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} | ||
| 523 | |||
| 524 | foreach_peripheral!( | ||
| 525 | (dac, $inst:ident) => { | ||
| 526 | // H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented | ||
| 527 | #[cfg(rcc_h7)] | ||
| 528 | impl crate::rcc::sealed::RccPeripheral for peripherals::$inst { | ||
| 529 | fn frequency() -> crate::time::Hertz { | ||
| 530 | critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 }) | ||
| 531 | } | ||
| 532 | |||
| 533 | fn reset() { | ||
| 534 | critical_section::with(|_| { | ||
| 535 | crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true)); | ||
| 536 | crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false)); | ||
| 537 | }) | ||
| 538 | } | ||
| 539 | |||
| 540 | fn enable() { | ||
| 541 | critical_section::with(|_| { | ||
| 542 | crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); | ||
| 543 | }) | ||
| 544 | } | ||
| 545 | |||
| 546 | fn disable() { | ||
| 547 | critical_section::with(|_| { | ||
| 548 | crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)) | ||
| 549 | }) | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | #[cfg(rcc_h7)] | ||
| 554 | impl crate::rcc::RccPeripheral for peripherals::$inst {} | ||
| 555 | |||
| 556 | impl crate::dac::sealed::Instance for peripherals::$inst { | ||
| 557 | fn regs() -> &'static crate::pac::dac::Dac { | ||
| 558 | &crate::pac::$inst | ||
| 559 | } | ||
| 560 | } | ||
| 561 | |||
| 562 | impl crate::dac::Instance for peripherals::$inst {} | ||
| 563 | }; | ||
| 564 | ); | ||
| 565 | |||
| 566 | macro_rules! impl_dac_pin { | ||
| 567 | ($inst:ident, $pin:ident, $ch:expr) => { | ||
| 568 | impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {} | ||
| 569 | }; | ||
| 570 | } | ||
diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index a307c803c..5a87888b7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs | |||
| @@ -21,11 +21,22 @@ use crate::pac::bdma::{regs, vals}; | |||
| 21 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 21 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| 22 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 22 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 23 | #[non_exhaustive] | 23 | #[non_exhaustive] |
| 24 | pub struct TransferOptions {} | 24 | pub struct TransferOptions { |
| 25 | /// Enable circular DMA | ||
| 26 | pub circular: bool, | ||
| 27 | /// Enable half transfer interrupt | ||
| 28 | pub half_transfer_ir: bool, | ||
| 29 | /// Enable transfer complete interrupt | ||
| 30 | pub complete_transfer_ir: bool, | ||
| 31 | } | ||
| 25 | 32 | ||
| 26 | impl Default for TransferOptions { | 33 | impl Default for TransferOptions { |
| 27 | fn default() -> Self { | 34 | fn default() -> Self { |
| 28 | Self {} | 35 | Self { |
| 36 | circular: false, | ||
| 37 | half_transfer_ir: false, | ||
| 38 | complete_transfer_ir: true, | ||
| 39 | } | ||
| 29 | } | 40 | } |
| 30 | } | 41 | } |
| 31 | 42 | ||
| @@ -253,7 +264,7 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 253 | mem_len: usize, | 264 | mem_len: usize, |
| 254 | incr_mem: bool, | 265 | incr_mem: bool, |
| 255 | data_size: WordSize, | 266 | data_size: WordSize, |
| 256 | _options: TransferOptions, | 267 | options: TransferOptions, |
| 257 | ) -> Self { | 268 | ) -> Self { |
| 258 | let ch = channel.regs().ch(channel.num()); | 269 | let ch = channel.regs().ch(channel.num()); |
| 259 | 270 | ||
| @@ -283,7 +294,15 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 283 | } | 294 | } |
| 284 | w.set_dir(dir.into()); | 295 | w.set_dir(dir.into()); |
| 285 | w.set_teie(true); | 296 | w.set_teie(true); |
| 286 | w.set_tcie(true); | 297 | w.set_tcie(options.complete_transfer_ir); |
| 298 | w.set_htie(options.half_transfer_ir); | ||
| 299 | if options.circular { | ||
| 300 | w.set_circ(vals::Circ::ENABLED); | ||
| 301 | debug!("Setting circular mode"); | ||
| 302 | } else { | ||
| 303 | w.set_circ(vals::Circ::DISABLED); | ||
| 304 | } | ||
| 305 | w.set_pl(vals::Pl::VERYHIGH); | ||
| 287 | w.set_en(true); | 306 | w.set_en(true); |
| 288 | }); | 307 | }); |
| 289 | 308 | ||
| @@ -310,8 +329,9 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 310 | pub fn is_running(&mut self) -> bool { | 329 | pub fn is_running(&mut self) -> bool { |
| 311 | let ch = self.channel.regs().ch(self.channel.num()); | 330 | let ch = self.channel.regs().ch(self.channel.num()); |
| 312 | let en = ch.cr().read().en(); | 331 | let en = ch.cr().read().en(); |
| 332 | let circular = ch.cr().read().circ() == vals::Circ::ENABLED; | ||
| 313 | let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; | 333 | let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; |
| 314 | en && !tcif | 334 | en && (circular || !tcif) |
| 315 | } | 335 | } |
| 316 | 336 | ||
| 317 | /// Gets the total remaining transfers for the channel | 337 | /// Gets the total remaining transfers for the channel |
| @@ -477,6 +497,8 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { | |||
| 477 | let ch = self.channel.regs().ch(self.channel.num()); | 497 | let ch = self.channel.regs().ch(self.channel.num()); |
| 478 | 498 | ||
| 479 | // Disable the channel. Keep the IEs enabled so the irqs still fire. | 499 | // Disable the channel. Keep the IEs enabled so the irqs still fire. |
| 500 | // If the channel is enabled and transfer is not completed, we need to perform | ||
| 501 | // two separate write access to the CR register to disable the channel. | ||
| 480 | ch.cr().write(|w| { | 502 | ch.cr().write(|w| { |
| 481 | w.set_teie(true); | 503 | w.set_teie(true); |
| 482 | w.set_htie(true); | 504 | w.set_htie(true); |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 80a336a48..698292bff 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -227,7 +227,11 @@ const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOp | |||
| 227 | fifo_threshold: Some(crate::dma::FifoThreshold::Full), | 227 | fifo_threshold: Some(crate::dma::FifoThreshold::Full), |
| 228 | }; | 228 | }; |
| 229 | #[cfg(all(sdmmc_v1, not(dma)))] | 229 | #[cfg(all(sdmmc_v1, not(dma)))] |
| 230 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {}; | 230 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { |
| 231 | circular: false, | ||
| 232 | half_transfer_ir: false, | ||
| 233 | complete_transfer_ir: true, | ||
| 234 | }; | ||
| 231 | 235 | ||
| 232 | /// SDMMC configuration | 236 | /// SDMMC configuration |
| 233 | /// | 237 | /// |
diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs index d97ae7082..3a6216712 100644 --- a/examples/stm32f4/src/bin/dac.rs +++ b/examples/stm32f4/src/bin/dac.rs | |||
| @@ -4,7 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::dac::{Channel, Dac, Value}; | 7 | use embassy_stm32::dac::{DacCh1, DacChannel, Value}; |
| 8 | use embassy_stm32::dma::NoDma; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 10 | #[embassy_executor::main] | 11 | #[embassy_executor::main] |
| @@ -12,12 +13,12 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 12 | let p = embassy_stm32::init(Default::default()); | 13 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World, dude!"); | 14 | info!("Hello World, dude!"); |
| 14 | 15 | ||
| 15 | let mut dac = Dac::new_1ch(p.DAC, p.PA4); | 16 | let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4); |
| 16 | 17 | ||
| 17 | loop { | 18 | loop { |
| 18 | for v in 0..=255 { | 19 | for v in 0..=255 { |
| 19 | unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); | 20 | unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); |
| 20 | unwrap!(dac.trigger(Channel::Ch1)); | 21 | dac.trigger(); |
| 21 | } | 22 | } |
| 22 | } | 23 | } |
| 23 | } | 24 | } |
diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index f12716370..586b4154b 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs | |||
| @@ -4,7 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | use cortex_m_rt::entry; | 5 | use cortex_m_rt::entry; |
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_stm32::dac::{Channel, Dac, Value}; | 7 | use embassy_stm32::dac::{DacCh1, DacChannel, Value}; |
| 8 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::time::mhz; | 9 | use embassy_stm32::time::mhz; |
| 9 | use embassy_stm32::Config; | 10 | use embassy_stm32::Config; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -19,12 +20,12 @@ fn main() -> ! { | |||
| 19 | config.rcc.pll1.q_ck = Some(mhz(100)); | 20 | config.rcc.pll1.q_ck = Some(mhz(100)); |
| 20 | let p = embassy_stm32::init(config); | 21 | let p = embassy_stm32::init(config); |
| 21 | 22 | ||
| 22 | let mut dac = Dac::new_1ch(p.DAC1, p.PA4); | 23 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); |
| 23 | 24 | ||
| 24 | loop { | 25 | loop { |
| 25 | for v in 0..=255 { | 26 | for v in 0..=255 { |
| 26 | unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); | 27 | unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); |
| 27 | unwrap!(dac.trigger(Channel::Ch1)); | 28 | dac.trigger(); |
| 28 | } | 29 | } |
| 29 | } | 30 | } |
| 30 | } | 31 | } |
diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index a36ed5d90..ade43eb35 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs | |||
| @@ -3,26 +3,21 @@ | |||
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_stm32::dac::{Channel, Dac, Value}; | 6 | use embassy_stm32::dac::{DacCh1, DacChannel, Value}; |
| 7 | use embassy_stm32::pac; | 7 | use embassy_stm32::dma::NoDma; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | #[cortex_m_rt::entry] | 10 | #[cortex_m_rt::entry] |
| 11 | fn main() -> ! { | 11 | fn main() -> ! { |
| 12 | info!("Hello World!"); | ||
| 13 | |||
| 14 | pac::RCC.apb1enr1().modify(|w| { | ||
| 15 | w.set_dac1en(true); | ||
| 16 | }); | ||
| 17 | |||
| 18 | let p = embassy_stm32::init(Default::default()); | 12 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | ||
| 19 | 14 | ||
| 20 | let mut dac = Dac::new_1ch(p.DAC1, p.PA4); | 15 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); |
| 21 | 16 | ||
| 22 | loop { | 17 | loop { |
| 23 | for v in 0..=255 { | 18 | for v in 0..=255 { |
| 24 | unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); | 19 | unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); |
| 25 | unwrap!(dac.trigger(Channel::Ch1)); | 20 | dac.trigger(); |
| 26 | } | 21 | } |
| 27 | } | 22 | } |
| 28 | } | 23 | } |
diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs new file mode 100644 index 000000000..c27cc03e1 --- /dev/null +++ b/examples/stm32l4/src/bin/dac_dma.rs | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dac::{DacChannel, ValueArray}; | ||
| 8 | use embassy_stm32::pac::timer::vals::{Mms, Opm}; | ||
| 9 | use embassy_stm32::peripherals::{TIM6, TIM7}; | ||
| 10 | use embassy_stm32::rcc::low_level::RccPeripheral; | ||
| 11 | use embassy_stm32::time::Hertz; | ||
| 12 | use embassy_stm32::timer::low_level::Basic16bitInstance; | ||
| 13 | use micromath::F32Ext; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | pub type Dac1Type = | ||
| 17 | embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; | ||
| 18 | |||
| 19 | pub type Dac2Type = | ||
| 20 | embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(spawner: Spawner) { | ||
| 24 | let config = embassy_stm32::Config::default(); | ||
| 25 | |||
| 26 | // Initialize the board and obtain a Peripherals instance | ||
| 27 | let p: embassy_stm32::Peripherals = embassy_stm32::init(config); | ||
| 28 | |||
| 29 | // Obtain two independent channels (p.DAC1 can only be consumed once, though!) | ||
| 30 | let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); | ||
| 31 | |||
| 32 | spawner.spawn(dac_task1(dac_ch1)).ok(); | ||
| 33 | spawner.spawn(dac_task2(dac_ch2)).ok(); | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::task] | ||
| 37 | async fn dac_task1(mut dac: Dac1Type) { | ||
| 38 | let data: &[u8; 256] = &calculate_array::<256>(); | ||
| 39 | |||
| 40 | info!("TIM6 frequency is {}", TIM6::frequency()); | ||
| 41 | const FREQUENCY: Hertz = Hertz::hz(200); | ||
| 42 | |||
| 43 | // Compute the reload value such that we obtain the FREQUENCY for the sine | ||
| 44 | let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; | ||
| 45 | |||
| 46 | // Depends on your clock and on the specific chip used, you may need higher or lower values here | ||
| 47 | if reload < 10 { | ||
| 48 | error!("Reload value {} below threshold!", reload); | ||
| 49 | } | ||
| 50 | |||
| 51 | dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); | ||
| 52 | dac.enable_channel().unwrap(); | ||
| 53 | |||
| 54 | TIM6::enable(); | ||
| 55 | TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); | ||
| 56 | TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); | ||
| 57 | TIM6::regs().cr1().modify(|w| { | ||
| 58 | w.set_opm(Opm::DISABLED); | ||
| 59 | w.set_cen(true); | ||
| 60 | }); | ||
| 61 | |||
| 62 | debug!( | ||
| 63 | "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", | ||
| 64 | TIM6::frequency(), | ||
| 65 | FREQUENCY, | ||
| 66 | reload, | ||
| 67 | reload as u16, | ||
| 68 | data.len() | ||
| 69 | ); | ||
| 70 | |||
| 71 | // Loop technically not necessary if DMA circular mode is enabled | ||
| 72 | loop { | ||
| 73 | info!("Loop DAC1"); | ||
| 74 | if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { | ||
| 75 | error!("Could not write to dac: {}", e); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | #[embassy_executor::task] | ||
| 81 | async fn dac_task2(mut dac: Dac2Type) { | ||
| 82 | let data: &[u8; 256] = &calculate_array::<256>(); | ||
| 83 | |||
| 84 | info!("TIM7 frequency is {}", TIM7::frequency()); | ||
| 85 | |||
| 86 | const FREQUENCY: Hertz = Hertz::hz(600); | ||
| 87 | let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; | ||
| 88 | |||
| 89 | if reload < 10 { | ||
| 90 | error!("Reload value {} below threshold!", reload); | ||
| 91 | } | ||
| 92 | |||
| 93 | TIM7::enable(); | ||
| 94 | TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); | ||
| 95 | TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); | ||
| 96 | TIM7::regs().cr1().modify(|w| { | ||
| 97 | w.set_opm(Opm::DISABLED); | ||
| 98 | w.set_cen(true); | ||
| 99 | }); | ||
| 100 | |||
| 101 | dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); | ||
| 102 | |||
| 103 | debug!( | ||
| 104 | "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", | ||
| 105 | TIM7::frequency(), | ||
| 106 | FREQUENCY, | ||
| 107 | reload, | ||
| 108 | reload as u16, | ||
| 109 | data.len() | ||
| 110 | ); | ||
| 111 | |||
| 112 | if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { | ||
| 113 | error!("Could not write to dac: {}", e); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | fn to_sine_wave(v: u8) -> u8 { | ||
| 118 | if v >= 128 { | ||
| 119 | // top half | ||
| 120 | let r = 3.14 * ((v - 128) as f32 / 128.0); | ||
| 121 | (r.sin() * 128.0 + 127.0) as u8 | ||
| 122 | } else { | ||
| 123 | // bottom half | ||
| 124 | let r = 3.14 + 3.14 * (v as f32 / 128.0); | ||
| 125 | (r.sin() * 128.0 + 127.0) as u8 | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | fn calculate_array<const N: usize>() -> [u8; N] { | ||
| 130 | let mut res = [0; N]; | ||
| 131 | let mut i = 0; | ||
| 132 | while i < N { | ||
| 133 | res[i] = to_sine_wave(i as u8); | ||
| 134 | i += 1; | ||
| 135 | } | ||
| 136 | res | ||
| 137 | } | ||
