SpenceKonde / megaTinyCore

Arduino core for the tinyAVR 0/1/2-series - Ones's digit 2,4,5,7 (pincount, 8,14,20,24), tens digit 0, 1, or 2 (featureset), preceded by flash in kb. Library maintainers: porting help available!
Other
551 stars 142 forks source link

Add DAC waveform generation library #214

Closed DrMarkEy closed 3 years ago

DrMarkEy commented 4 years ago

It would be very nice if the tone() command could utilize the DAC for real sine wave forms instead of PWM as described here: https://www.xtronical.com/testing-the-dacaudio-hardware/

SpenceKonde commented 4 years ago

Oh cute! Wonder if it works as well with 1-series tinyAVR? I imagine it would...

The output of that diverges sufficiently from that of the stock tone() function (that is, a sketch + hardware that expects normal tone() likely wouldn't be happy with that and vise-versa) that it should be implemented as a library, not in the core tone() function. Among other things, a sensible API would require additional parameters...

SpenceKonde commented 4 years ago

maybe DACwave.h? calculate the table at compiletime, declare it const so it can live in flash... then two implementations of the waveform generation function itself - one that was blocking and could be run at full resolution and ran for a specified length of time, and one that was non-blocking, set an interrupt on a TCB and updated the DAC value there. For the non-blocking implementation, I think you'd need to run at lower resolution (as in, not updating it at every point in the curve), because the interrupts would just come in too fast to handle otherwise; ex a 10kHz sinewave with 256 points defined would want to update 2.5 times per microsecond, which ain't gonna happen via interrupts... Heck, even in blocking version the timing gets tricky - you're well into "counting clocks" territory at even 1kHz frequencies if want high resolution output.

Sounds like fun to write (finally something to do with all this assembly knowledge I've been picking up? Inline assembly is probably the way to go for the blocking implementation , but I need to stick to higher priority action items in the near term.

SpenceKonde commented 3 years ago

Hum. DAC is rated for 270 ksps with 4.3V ref, 350 otherwise. In interrupt driven mode, I'm counting - (balls to the bare metal wall) like 25 clocks per interrupt... counting the interrupt overhead). If you had a 256-byte long lookup table, running at 20 MHz, targeting 200ksps, you'd only be spending 25% of the time in the DAC ISR, which isn't terrible...Even 250ksps would be usable... (you could make it highly responsive though - if you had a bunch of tables in flash, you could swap between them and stuff from the non-interrupt code? and obviously changing the frequency is also possible by changing CCMP of the TCB thats driving it.

A blocking implementation would have a lot more freedom while reaching the pinnacle of "8-bit avr throwing data at a mediocre on-chip dac" performance... (interestingly enough, the DAC on the new Dx-series is rated for fewer SPS - but that may just be because it's a 10-bit DAC, and if you were banging data out to it at the same speed it would perform just as well (they arranged the registers so you can just write to the high 8-bits with a single register write) and/or due to very optimistic specs on the tiny 1-series)

SpenceKonde commented 3 years ago

Okay, I'm closing this - it would be a great thing to have, but it's a job for someone else and it is not specific to the core.