Skip to content

DAC Improvements - Dual DAC support and DMA #213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

boondocklabs
Copy link
Contributor

  • Add DualDac with simultaneous channel writes, with DMA support
  • Added SampleFormat trait and impls supporting Q1.7, Q1.11, Q1.15 signed and unsigned 8/11b types, left/right alignment
  • Added DacTriggerSource and DacIncrementSource traits for selecting external trigger sources with DAC instance constraints
  • Added DMA support to existing single channel DACs

Here's an example of using a circular phase angle buffer which is DMA'd into CORDIC in SinCos mode, with DualDac configured to DMA samples directly from CORDIC RDATA register in Q1.15 format, and triggered by Tim6 at 1MHz. This produces a quadrature output on both DAC1 channels, with zero CPU.

DualDac Quadrature CORDIC DMA

* Add `DualDac` with simultaneous channel writes, with DMA support
* Added `SampleFormat` trait and impls supporting Q1.7, Q1.11, Q1.15 signed and unsigned 8/11b types, left/right alignment
* Added `DacTriggerSource` and `DacIncrementSource` traits for selecting external trigger sources with DAC instance constraints
* Added DMA support to existing single channel DACs
…amples. Remove ALIGNMENT associated type from SampleFormat
pub struct SampleQ11;
impl SampleFormat for SampleQ11 {
const DEPTH: SampleDepth = SampleDepth::Bits12;
const ALIGNMENT: Alignment = Alignment::Left;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have very limited experiance with fixed point formats, but should this really be identical to SampleQ15?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, G4 does claim support for Q1.11 and Q1.15 (1 sign bit, 11 or 15 fractional bits) so I included them both as types, but Q1.15 is a truncation as the DAC only outputs 12 bits and discards the lower 4 bits, and the fixed crate doesn't have sub 16b types (both scalar types are I1F15, hence the same shift and mask). So yes, they are identical.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use Q15 and Q31 in the CORDIC peripheral as well. Maybe these should be coalesced to avoid repeat definitions?

@usbalbin
Copy link
Member

This is very nice!

Do you have any example of using the dual mode or dma that you can share? :)

@boondocklabs
Copy link
Contributor Author

This is very nice!

Do you have any example of using the dual mode or dma that you can share? :)

I'll try and get an example pushed today. My current codebase depends on some other modifications for CORDIC DMA (which I'll also open PRs for), but I can make something standalone.

@usbalbin
Copy link
Member

Sounds great!

I'll go to sleep now, time zones.. wont be back for a bounch of hours. Thanks so far :)

@AdinAck
Copy link
Contributor

AdinAck commented Jun 23, 2025

...My current codebase depends on some other modifications for CORDIC DMA (which I'll also open PRs for)...

Wow! 🤩 I am excited to see that!

This simple example configures DAC1 in DualDac mode and outputs a wrapping ramp on PA4, and its inverse on PA5 using fixed point Q1.15 format.
@boondocklabs
Copy link
Contributor Author

This is very nice!
Do you have any example of using the dual mode or dma that you can share? :)

I'll try and get an example pushed today. My current codebase depends on some other modifications for CORDIC DMA (which I'll also open PRs for), but I can make something standalone.

I pushed a simple example. I can get a DMA example up at some point - maybe after the CORDIC DMA patches are in and can show an example of CORDIC direct to DAC via DMA.

@boondocklabs
Copy link
Contributor Author

...My current codebase depends on some other modifications for CORDIC DMA (which I'll also open PRs for)...

Wow! 🤩 I am excited to see that!

Nothing too profound :) but ended up creating a "split DMA", since peripherals like CORDIC/FMAC have both read and write sides, and a Transfer consumes the peripheral, so you can't just impl the necessary traits on the existing struct and have both read and write DMA. Just need to better integrate it so it acts as a singleton with type states (this is what I did with FMAC that I'll be PR'ing shortly)

@usbalbin
Copy link
Member

...My current codebase depends on some other modifications for CORDIC DMA (which I'll also open PRs for)...

Wow! 🤩 I am excited to see that!

Nothing too profound :) but ended up creating a "split DMA", since peripherals like CORDIC/FMAC have both read and write sides, and a Transfer consumes the peripheral, so you can't just impl the necessary traits on the existing struct and have both read and write DMA. Just need to better integrate it so it acts as a singleton with type states (this is what I did with FMAC that I'll be PR'ing shortly)

Wow, very exciting! :)


/// Enable DMA for the specified channel
#[inline(always)]
pub fn enable_dma(&mut self, channel: DacChannel, enable: bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does DacCh::enable_dma consume self and turn it into a new type while this one does not?

Also, is it ok to have set_channels still available after dma has been enabled?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants