aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authorJuliDi <[email protected]>2023-06-25 11:54:25 +0200
committerJuliDi <[email protected]>2023-06-25 11:54:25 +0200
commit8cafaa1f3c9b75e8dba30a7f37f60d9fee6e65e2 (patch)
tree5856e79b94ec49d5bea351afd85cf809dfefc4a8 /embassy-stm32
parentdf944edeef738590f481d35ee9e2a1afb09601fa (diff)
add docs, cleanup
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/src/dac/mod.rs322
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).
3use core::marker::PhantomData; 4use core::marker::PhantomData;
4 5
5use embassy_hal_common::{into_ref, PeripheralRef}; 6use 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
14pub enum Error { 16pub 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
21pub enum Channel { 24pub 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
37pub enum Ch1Trigger { 41pub 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
63pub enum Ch2Trigger { 68pub 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
113pub trait DacChannel<T: Instance, Tx> { 118pub 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/// ```
171pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { 194pub 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.
176pub struct DacCh1<'d, T: Instance, Tx> { 202pub 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.
181pub struct DacCh2<'d, T: Instance, Tx> { 211pub 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
186impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { 217impl<'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
297impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { 249impl<'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
412impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { 284impl<'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
471impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> { 351impl<'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
475impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> { 369impl<'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.
388async fn write_inner<T: Instance, Tx>(
389 ch: Channel,
390 dma: &PeripheralRef<'_, Tx>,
391 data: ValueArray<'_>,
392 circular: bool,
393) -> Result<(), Error>
394where
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
479pub(crate) mod sealed { 466pub(crate) mod sealed {
@@ -485,6 +472,7 @@ pub(crate) mod sealed {
485pub trait Instance: sealed::Instance + RccPeripheral + 'static {} 472pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
486dma_trait!(Dma, Instance); 473dma_trait!(Dma, Instance);
487 474
475/// Marks a pin that can be used with the DAC
488pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} 476pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
489 477
490foreach_peripheral!( 478foreach_peripheral!(