diff options
| author | JuliDi <[email protected]> | 2023-06-25 11:54:25 +0200 |
|---|---|---|
| committer | JuliDi <[email protected]> | 2023-06-25 11:54:25 +0200 |
| commit | 8cafaa1f3c9b75e8dba30a7f37f60d9fee6e65e2 (patch) | |
| tree | 5856e79b94ec49d5bea351afd85cf809dfefc4a8 /embassy-stm32 | |
| parent | df944edeef738590f481d35ee9e2a1afb09601fa (diff) | |
add docs, cleanup
Diffstat (limited to 'embassy-stm32')
| -rw-r--r-- | embassy-stm32/src/dac/mod.rs | 322 |
1 files changed, 155 insertions, 167 deletions
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 3dcd6b771..d95674ff0 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | //! Provide access to the STM32 digital-to-analog converter (DAC). | ||
| 3 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 4 | 5 | ||
| 5 | use embassy_hal_common::{into_ref, PeripheralRef}; | 6 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| @@ -11,6 +12,7 @@ use crate::{peripherals, Peripheral}; | |||
| 11 | 12 | ||
| 12 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 13 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 15 | /// Curstom Errors | ||
| 14 | pub enum Error { | 16 | pub enum Error { |
| 15 | UnconfiguredChannel, | 17 | UnconfiguredChannel, |
| 16 | InvalidValue, | 18 | InvalidValue, |
| @@ -18,6 +20,7 @@ pub enum Error { | |||
| 18 | 20 | ||
| 19 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 21 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 20 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 22 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 23 | /// DAC Channels | ||
| 21 | pub enum Channel { | 24 | pub enum Channel { |
| 22 | Ch1, | 25 | Ch1, |
| 23 | Ch2, | 26 | Ch2, |
| @@ -34,6 +37,7 @@ impl Channel { | |||
| 34 | 37 | ||
| 35 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 38 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 36 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 40 | /// Trigger sources for CH1 | ||
| 37 | pub enum Ch1Trigger { | 41 | pub enum Ch1Trigger { |
| 38 | Tim6, | 42 | Tim6, |
| 39 | Tim3, | 43 | Tim3, |
| @@ -60,6 +64,7 @@ impl Ch1Trigger { | |||
| 60 | 64 | ||
| 61 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 65 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 62 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 66 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 67 | /// Trigger sources for CH2 | ||
| 63 | pub enum Ch2Trigger { | 68 | pub enum Ch2Trigger { |
| 64 | Tim6, | 69 | Tim6, |
| 65 | Tim8, | 70 | Tim8, |
| @@ -109,7 +114,7 @@ pub enum ValueArray<'a> { | |||
| 109 | // 12 bit values stored in a u16, right-aligned | 114 | // 12 bit values stored in a u16, right-aligned |
| 110 | Bit12Right(&'a [u16]), | 115 | Bit12Right(&'a [u16]), |
| 111 | } | 116 | } |
| 112 | 117 | /// Provide common functions for DAC channels | |
| 113 | pub trait DacChannel<T: Instance, Tx> { | 118 | pub trait DacChannel<T: Instance, Tx> { |
| 114 | const CHANNEL: Channel; | 119 | const CHANNEL: Channel; |
| 115 | 120 | ||
| @@ -157,7 +162,7 @@ pub trait DacChannel<T: Instance, Tx> { | |||
| 157 | 162 | ||
| 158 | /// Set a value to be output by the DAC on trigger. | 163 | /// Set a value to be output by the DAC on trigger. |
| 159 | /// | 164 | /// |
| 160 | /// The `value` is written to the corresponding "data holding register" | 165 | /// The `value` is written to the corresponding "data holding register". |
| 161 | fn set(&mut self, value: Value) -> Result<(), Error> { | 166 | fn set(&mut self, value: Value) -> Result<(), Error> { |
| 162 | match value { | 167 | match value { |
| 163 | Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), | 168 | Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), |
| @@ -166,25 +171,51 @@ pub trait DacChannel<T: Instance, Tx> { | |||
| 166 | } | 171 | } |
| 167 | Ok(()) | 172 | Ok(()) |
| 168 | } | 173 | } |
| 174 | |||
| 175 | /// Write `data` to the DAC channel via DMA. | ||
| 176 | /// | ||
| 177 | /// `circular` sets the DMA to circular mode. | ||
| 178 | async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 179 | where | ||
| 180 | Tx: Dma<T>; | ||
| 169 | } | 181 | } |
| 170 | 182 | ||
| 183 | /// Hold two DAC channels | ||
| 184 | /// | ||
| 185 | /// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously. | ||
| 186 | /// | ||
| 187 | /// # Example for obtaining both DAC channels | ||
| 188 | /// | ||
| 189 | /// ```no_run | ||
| 190 | /// // DMA channels and pins may need to be changed for your controller | ||
| 191 | /// let (dac_ch1, dac_ch2) = | ||
| 192 | /// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); | ||
| 193 | /// ``` | ||
| 171 | pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { | 194 | pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { |
| 172 | ch1: DacCh1<'d, T, TxCh1>, | 195 | ch1: DacCh1<'d, T, TxCh1>, |
| 173 | ch2: DacCh2<'d, T, TxCh2>, | 196 | ch2: DacCh2<'d, T, TxCh2>, |
| 174 | } | 197 | } |
| 175 | 198 | ||
| 199 | /// DAC CH1 | ||
| 200 | /// | ||
| 201 | /// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously. | ||
| 176 | pub struct DacCh1<'d, T: Instance, Tx> { | 202 | pub struct DacCh1<'d, T: Instance, Tx> { |
| 203 | /// To consume T | ||
| 177 | _peri: PeripheralRef<'d, T>, | 204 | _peri: PeripheralRef<'d, T>, |
| 178 | dma: PeripheralRef<'d, Tx>, | 205 | dma: PeripheralRef<'d, Tx>, |
| 179 | } | 206 | } |
| 180 | 207 | ||
| 208 | /// DAC CH2 | ||
| 209 | /// | ||
| 210 | /// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously. | ||
| 181 | pub struct DacCh2<'d, T: Instance, Tx> { | 211 | pub struct DacCh2<'d, T: Instance, Tx> { |
| 212 | /// Instead of PeripheralRef to consume T | ||
| 182 | phantom: PhantomData<&'d mut T>, | 213 | phantom: PhantomData<&'d mut T>, |
| 183 | dma: PeripheralRef<'d, Tx>, | 214 | dma: PeripheralRef<'d, Tx>, |
| 184 | } | 215 | } |
| 185 | 216 | ||
| 186 | impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { | 217 | impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { |
| 187 | /// Perform initialisation steps for the DAC | 218 | /// Obtain DAC CH1 |
| 188 | pub fn new( | 219 | pub fn new( |
| 189 | peri: impl Peripheral<P = T> + 'd, | 220 | peri: impl Peripheral<P = T> + 'd, |
| 190 | dma: impl Peripheral<P = Tx> + 'd, | 221 | dma: impl Peripheral<P = Tx> + 'd, |
| @@ -204,7 +235,8 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { | |||
| 204 | 235 | ||
| 205 | dac | 236 | dac |
| 206 | } | 237 | } |
| 207 | /// Select a new trigger for CH1 (disables the channel) | 238 | |
| 239 | /// Select a new trigger for this channel | ||
| 208 | pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { | 240 | pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { |
| 209 | unwrap!(self.disable_channel()); | 241 | unwrap!(self.disable_channel()); |
| 210 | T::regs().cr().modify(|reg| { | 242 | T::regs().cr().modify(|reg| { |
| @@ -212,91 +244,11 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { | |||
| 212 | }); | 244 | }); |
| 213 | Ok(()) | 245 | Ok(()) |
| 214 | } | 246 | } |
| 215 | |||
| 216 | /// Write `data` to the DAC CH1 via DMA. | ||
| 217 | /// | ||
| 218 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 219 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 220 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 221 | /// | ||
| 222 | /// **Important:** Channel 1 has to be configured for the DAC instance! | ||
| 223 | pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 224 | where | ||
| 225 | Tx: Dma<T>, | ||
| 226 | { | ||
| 227 | let channel = Channel::Ch1.index(); | ||
| 228 | debug!("Writing to channel {}", channel); | ||
| 229 | |||
| 230 | // Enable DAC and DMA | ||
| 231 | T::regs().cr().modify(|w| { | ||
| 232 | w.set_en(channel, true); | ||
| 233 | w.set_dmaen(channel, true); | ||
| 234 | }); | ||
| 235 | |||
| 236 | let tx_request = self.dma.request(); | ||
| 237 | let dma_channel = &self.dma; | ||
| 238 | |||
| 239 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 240 | let tx_f = match data { | ||
| 241 | ValueArray::Bit8(buf) => unsafe { | ||
| 242 | Transfer::new_write( | ||
| 243 | dma_channel, | ||
| 244 | tx_request, | ||
| 245 | buf, | ||
| 246 | T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||
| 247 | TransferOptions { | ||
| 248 | circular, | ||
| 249 | half_transfer_ir: false, | ||
| 250 | complete_transfer_ir: !circular, | ||
| 251 | }, | ||
| 252 | ) | ||
| 253 | }, | ||
| 254 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 255 | Transfer::new_write( | ||
| 256 | dma_channel, | ||
| 257 | tx_request, | ||
| 258 | buf, | ||
| 259 | T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||
| 260 | TransferOptions { | ||
| 261 | circular, | ||
| 262 | half_transfer_ir: false, | ||
| 263 | complete_transfer_ir: !circular, | ||
| 264 | }, | ||
| 265 | ) | ||
| 266 | }, | ||
| 267 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 268 | Transfer::new_write( | ||
| 269 | dma_channel, | ||
| 270 | tx_request, | ||
| 271 | buf, | ||
| 272 | T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||
| 273 | TransferOptions { | ||
| 274 | circular, | ||
| 275 | half_transfer_ir: false, | ||
| 276 | complete_transfer_ir: !circular, | ||
| 277 | }, | ||
| 278 | ) | ||
| 279 | }, | ||
| 280 | }; | ||
| 281 | |||
| 282 | tx_f.await; | ||
| 283 | |||
| 284 | // finish dma | ||
| 285 | // TODO: Do we need to check any status registers here? | ||
| 286 | T::regs().cr().modify(|w| { | ||
| 287 | // Disable the DAC peripheral | ||
| 288 | w.set_en(channel, false); | ||
| 289 | // Disable the DMA. TODO: Is this necessary? | ||
| 290 | w.set_dmaen(channel, false); | ||
| 291 | }); | ||
| 292 | |||
| 293 | Ok(()) | ||
| 294 | } | ||
| 295 | } | 247 | } |
| 296 | 248 | ||
| 297 | impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | 249 | impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { |
| 298 | /// Perform initialisation steps for the DAC | 250 | /// Obtain DAC CH2 |
| 299 | pub fn new_ch2( | 251 | pub fn new( |
| 300 | _peri: impl Peripheral<P = T> + 'd, | 252 | _peri: impl Peripheral<P = T> + 'd, |
| 301 | dma: impl Peripheral<P = Tx> + 'd, | 253 | dma: impl Peripheral<P = Tx> + 'd, |
| 302 | _pin: impl Peripheral<P = impl DacPin<T, 2>> + 'd, | 254 | _pin: impl Peripheral<P = impl DacPin<T, 2>> + 'd, |
| @@ -319,7 +271,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | |||
| 319 | dac | 271 | dac |
| 320 | } | 272 | } |
| 321 | 273 | ||
| 322 | /// Select a new trigger for CH1 (disables the channel) | 274 | /// Select a new trigger for this channel |
| 323 | pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { | 275 | pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { |
| 324 | unwrap!(self.disable_channel()); | 276 | unwrap!(self.disable_channel()); |
| 325 | T::regs().cr().modify(|reg| { | 277 | T::regs().cr().modify(|reg| { |
| @@ -327,89 +279,12 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | |||
| 327 | }); | 279 | }); |
| 328 | Ok(()) | 280 | Ok(()) |
| 329 | } | 281 | } |
| 330 | |||
| 331 | /// Write `data` to the DAC CH1 via DMA. | ||
| 332 | /// | ||
| 333 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 334 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 335 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 336 | /// | ||
| 337 | /// **Important:** Channel 1 has to be configured for the DAC instance! | ||
| 338 | pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 339 | where | ||
| 340 | Tx: Dma<T>, | ||
| 341 | { | ||
| 342 | let channel = Channel::Ch2.index(); | ||
| 343 | debug!("Writing to channel {}", channel); | ||
| 344 | |||
| 345 | // Enable DAC and DMA | ||
| 346 | T::regs().cr().modify(|w| { | ||
| 347 | w.set_en(channel, true); | ||
| 348 | w.set_dmaen(channel, true); | ||
| 349 | }); | ||
| 350 | |||
| 351 | let tx_request = self.dma.request(); | ||
| 352 | let dma_channel = &self.dma; | ||
| 353 | |||
| 354 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 355 | let tx_f = match data { | ||
| 356 | ValueArray::Bit8(buf) => unsafe { | ||
| 357 | Transfer::new_write( | ||
| 358 | dma_channel, | ||
| 359 | tx_request, | ||
| 360 | buf, | ||
| 361 | T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||
| 362 | TransferOptions { | ||
| 363 | circular, | ||
| 364 | half_transfer_ir: false, | ||
| 365 | complete_transfer_ir: !circular, | ||
| 366 | }, | ||
| 367 | ) | ||
| 368 | }, | ||
| 369 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 370 | Transfer::new_write( | ||
| 371 | dma_channel, | ||
| 372 | tx_request, | ||
| 373 | buf, | ||
| 374 | T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||
| 375 | TransferOptions { | ||
| 376 | circular, | ||
| 377 | half_transfer_ir: false, | ||
| 378 | complete_transfer_ir: !circular, | ||
| 379 | }, | ||
| 380 | ) | ||
| 381 | }, | ||
| 382 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 383 | Transfer::new_write( | ||
| 384 | dma_channel, | ||
| 385 | tx_request, | ||
| 386 | buf, | ||
| 387 | T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||
| 388 | TransferOptions { | ||
| 389 | circular, | ||
| 390 | half_transfer_ir: false, | ||
| 391 | complete_transfer_ir: !circular, | ||
| 392 | }, | ||
| 393 | ) | ||
| 394 | }, | ||
| 395 | }; | ||
| 396 | |||
| 397 | tx_f.await; | ||
| 398 | |||
| 399 | // finish dma | ||
| 400 | // TODO: Do we need to check any status registers here? | ||
| 401 | T::regs().cr().modify(|w| { | ||
| 402 | // Disable the DAC peripheral | ||
| 403 | w.set_en(channel, false); | ||
| 404 | // Disable the DMA. TODO: Is this necessary? | ||
| 405 | w.set_dmaen(channel, false); | ||
| 406 | }); | ||
| 407 | |||
| 408 | Ok(()) | ||
| 409 | } | ||
| 410 | } | 282 | } |
| 411 | 283 | ||
| 412 | impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { | 284 | impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { |
| 285 | /// Create a new DAC instance with both channels. | ||
| 286 | /// | ||
| 287 | /// This is used to obtain two independent channels via `split()` for use e.g. with DMA. | ||
| 413 | pub fn new( | 288 | pub fn new( |
| 414 | peri: impl Peripheral<P = T> + 'd, | 289 | peri: impl Peripheral<P = T> + 'd, |
| 415 | dma_ch1: impl Peripheral<P = TxCh1> + 'd, | 290 | dma_ch1: impl Peripheral<P = TxCh1> + 'd, |
| @@ -447,22 +322,27 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { | |||
| 447 | } | 322 | } |
| 448 | } | 323 | } |
| 449 | 324 | ||
| 325 | /// Split the DAC into CH1 and CH2 for independent use. | ||
| 450 | pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { | 326 | pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { |
| 451 | (self.ch1, self.ch2) | 327 | (self.ch1, self.ch2) |
| 452 | } | 328 | } |
| 453 | 329 | ||
| 330 | /// Get mutable reference to CH1 | ||
| 454 | pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { | 331 | pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { |
| 455 | &mut self.ch1 | 332 | &mut self.ch1 |
| 456 | } | 333 | } |
| 457 | 334 | ||
| 335 | /// Get mutable reference to CH2 | ||
| 458 | pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { | 336 | pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { |
| 459 | &mut self.ch2 | 337 | &mut self.ch2 |
| 460 | } | 338 | } |
| 461 | 339 | ||
| 340 | /// Get reference to CH1 | ||
| 462 | pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { | 341 | pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { |
| 463 | &self.ch1 | 342 | &self.ch1 |
| 464 | } | 343 | } |
| 465 | 344 | ||
| 345 | /// Get reference to CH2 | ||
| 466 | pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { | 346 | pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { |
| 467 | &self.ch2 | 347 | &self.ch2 |
| 468 | } | 348 | } |
| @@ -470,10 +350,117 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { | |||
| 470 | 350 | ||
| 471 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> { | 351 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> { |
| 472 | const CHANNEL: Channel = Channel::Ch1; | 352 | const CHANNEL: Channel = Channel::Ch1; |
| 353 | |||
| 354 | /// Write `data` to the DAC CH1 via DMA. | ||
| 355 | /// | ||
| 356 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 357 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 358 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 359 | /// | ||
| 360 | /// **Important:** Channel 1 has to be configured for the DAC instance! | ||
| 361 | async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 362 | where | ||
| 363 | Tx: Dma<T>, | ||
| 364 | { | ||
| 365 | write_inner(Self::CHANNEL, &self.dma, data, circular).await | ||
| 366 | } | ||
| 473 | } | 367 | } |
| 474 | 368 | ||
| 475 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> { | 369 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> { |
| 476 | const CHANNEL: Channel = Channel::Ch2; | 370 | const CHANNEL: Channel = Channel::Ch2; |
| 371 | |||
| 372 | /// Write `data` to the DAC CH2 via DMA. | ||
| 373 | /// | ||
| 374 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 375 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 376 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 377 | /// | ||
| 378 | /// **Important:** Channel 2 has to be configured for the DAC instance! | ||
| 379 | async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 380 | where | ||
| 381 | Tx: Dma<T>, | ||
| 382 | { | ||
| 383 | write_inner(Self::CHANNEL, &self.dma, data, circular).await | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | /// Shared utility function to perform the actual DMA config and write. | ||
| 388 | async fn write_inner<T: Instance, Tx>( | ||
| 389 | ch: Channel, | ||
| 390 | dma: &PeripheralRef<'_, Tx>, | ||
| 391 | data: ValueArray<'_>, | ||
| 392 | circular: bool, | ||
| 393 | ) -> Result<(), Error> | ||
| 394 | where | ||
| 395 | Tx: Dma<T>, | ||
| 396 | { | ||
| 397 | let channel = ch.index(); | ||
| 398 | debug!("Writing to channel {}", channel); | ||
| 399 | |||
| 400 | // Enable DAC and DMA | ||
| 401 | T::regs().cr().modify(|w| { | ||
| 402 | w.set_en(channel, true); | ||
| 403 | w.set_dmaen(channel, true); | ||
| 404 | }); | ||
| 405 | |||
| 406 | let tx_request = dma.request(); | ||
| 407 | let dma_channel = dma; | ||
| 408 | |||
| 409 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 410 | let tx_f = match data { | ||
| 411 | ValueArray::Bit8(buf) => unsafe { | ||
| 412 | Transfer::new_write( | ||
| 413 | dma_channel, | ||
| 414 | tx_request, | ||
| 415 | buf, | ||
| 416 | T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||
| 417 | TransferOptions { | ||
| 418 | circular, | ||
| 419 | half_transfer_ir: false, | ||
| 420 | complete_transfer_ir: !circular, | ||
| 421 | }, | ||
| 422 | ) | ||
| 423 | }, | ||
| 424 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 425 | Transfer::new_write( | ||
| 426 | dma_channel, | ||
| 427 | tx_request, | ||
| 428 | buf, | ||
| 429 | T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||
| 430 | TransferOptions { | ||
| 431 | circular, | ||
| 432 | half_transfer_ir: false, | ||
| 433 | complete_transfer_ir: !circular, | ||
| 434 | }, | ||
| 435 | ) | ||
| 436 | }, | ||
| 437 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 438 | Transfer::new_write( | ||
| 439 | dma_channel, | ||
| 440 | tx_request, | ||
| 441 | buf, | ||
| 442 | T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||
| 443 | TransferOptions { | ||
| 444 | circular, | ||
| 445 | half_transfer_ir: false, | ||
| 446 | complete_transfer_ir: !circular, | ||
| 447 | }, | ||
| 448 | ) | ||
| 449 | }, | ||
| 450 | }; | ||
| 451 | |||
| 452 | tx_f.await; | ||
| 453 | |||
| 454 | // finish dma | ||
| 455 | // TODO: Do we need to check any status registers here? | ||
| 456 | T::regs().cr().modify(|w| { | ||
| 457 | // Disable the DAC peripheral | ||
| 458 | w.set_en(channel, false); | ||
| 459 | // Disable the DMA. TODO: Is this necessary? | ||
| 460 | w.set_dmaen(channel, false); | ||
| 461 | }); | ||
| 462 | |||
| 463 | Ok(()) | ||
| 477 | } | 464 | } |
| 478 | 465 | ||
| 479 | pub(crate) mod sealed { | 466 | pub(crate) mod sealed { |
| @@ -485,6 +472,7 @@ pub(crate) mod sealed { | |||
| 485 | pub trait Instance: sealed::Instance + RccPeripheral + 'static {} | 472 | pub trait Instance: sealed::Instance + RccPeripheral + 'static {} |
| 486 | dma_trait!(Dma, Instance); | 473 | dma_trait!(Dma, Instance); |
| 487 | 474 | ||
| 475 | /// Marks a pin that can be used with the DAC | ||
| 488 | pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} | 476 | pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} |
| 489 | 477 | ||
| 490 | foreach_peripheral!( | 478 | foreach_peripheral!( |
