Skip to content
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

Use DMA for ADC and DAC #85

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Use DMA for ADC and DAC #85

wants to merge 9 commits into from

Conversation

rhgndf
Copy link
Contributor

@rhgndf rhgndf commented Aug 9, 2024

Uses the DMA with the ADC to get 2.4MSPS for oversampling and DAC at 480KSPS. I observed a lower noise floor with higher sampling rate for ADC and a reduced 1KHz tone and lower noise on the DAC. This also reduces the ppm error for non integer decimation ratios.

The 2.4MSPS for ADC is obtained by: PLL clock 72MHz, ADC prescaler 2, sampling time 2.5 clock cycles, total 15 clock cycle per sample.

The 480KSPS for DAC is obtained by: PLL clock 72MHz, TIM6 divides by 150.

Its probably possible to get to 4.8MSPS. That would require 15 clock cycles per sample at 72MHz.

This runs per sample:
https://github.com/rhgndf/AIOC/blob/f95b47d4669d32a8a14b0feb2888d116414072de/stm32/aioc-fw/Src/audio_dsp.c#L52

and compiles to two instructions, probably with the extra code around it its 3-4 instructions per sample, which should run fast enough

4463            add     r3, ip
f831 cb02       ldrh.w  ip, [r1], #2

@skuep
Copy link
Owner

skuep commented Aug 11, 2024

Thanks a lot for this interesting pull request! I am currently very busy, but I plan to have an AIOC maintenance week at end of September.

Lower noise floor sounds nice. How much can you reduce the ppm errors on some sample rates? From what I can tell, all sample rates can be achieved perfectly, after downsampling?

@rhgndf
Copy link
Contributor Author

rhgndf commented Aug 12, 2024

I might plan to add the same thing to the DAC side also.

The only error left is probably only the error of the crystal.

@rhgndf rhgndf changed the title Use DMA for ADC Use DMA for ADC and DAC Aug 13, 2024
@rhgndf
Copy link
Contributor Author

rhgndf commented Aug 13, 2024

Some notes while implementing this:

Setting the DAC DMA buffer too large results in the usb audio buffer in tud_audio_read underflowing. The current buffer size does not seem to cause underflows. The size of the usb buffer could be increased or the DAC sample rate tuned for better performance.

The sigma-delta modulator might be quite expensive for the CPU. At 480ksps, we have around 150 clock cycles per sample. The most expensive part is the saturating addition.

@skuep
Copy link
Owner

skuep commented Sep 18, 2024

I have had a look at this and while I like the idea, I am not sure if this is too much added complexity while I am still not sure about the benefit.

Lower sampling noise is nice, but usually the radio channel trumps the noise floor by a huge margin, when the squelch opens. The errors in the sample rate have not yet resulted in any complaints, so not sure about that either.

I would like to keep this here, because I imagine it was a lot of work, and it may be that the situation arises in the future where this code could come in handy.

@rhgndf
Copy link
Contributor Author

rhgndf commented Sep 18, 2024

Thanks for looking at this!

I have been using this code since oversampling eliminated a weird ~1KHz tone on both transmit and receive side. Receiving a clean signal will result in an additional 1KHz tone received in the computer and transmitting will cause a 1KHz tone to be transmitted out in the RF signal. I am not sure what causes it, but it is probably the microcontroller since oversampling attenuates it by a lot.

As for the noise floor, I have noticed a increase in SNR for voice signals after doing ADC oversampling, and better audio quality during transmit. This is probably because due to the code we effectively get a 16bit ADC and DAC which is what the computer will provide in the usb audio stream.

One way to reduce complexity will be to do away with the fractional side and simply do integer rate, that would remove a lot of the code to keep track of fractional samples.

@skuep
Copy link
Owner

skuep commented Jan 4, 2025

I was doing some more work on the analog part of the AIOC (see PR #93) and was remembered to this.

After some time I have passed, I reread your text with a clear mind. The 1kHz tone is very probably from the USB interface (and it was even more noticeable when I had only a single 3.3V regulator for analog and digital power). I think (not sure though) the reason, why it disappears with the oversampling is due to the fractional decimator. I might be more inclined to have a look at a simpler integer decimation solution. What's would be the drawback?

Your notes about the noise made me curious. I could imagine producing some testing firmwares with the oversampling included and let a group of people test this to see if the effort for this is warranted. If it is, it might be interesting to implement this.

@das-Iro
Copy link

das-Iro commented Jan 8, 2025

@rhgndf can you supply a .bin file that I can flash with dfu-util? I would like to measure the performance gain.

@rhgndf
Copy link
Contributor Author

rhgndf commented Jan 9, 2025

it disappears with the oversampling is due to the fractional decimator

I think oversampling on the ADC side sort of 'smooths' out its internal noise. For the DAC side, the sample frequently changes, which also does something similar. This is my guess anyway, not too sure also.

What's would be the drawback?

The biggest drawback is that the sample rate becomes much more inaccurate. Compare 2.4MHz/54 and 72MHz/1632. This is the main reason why I implemented the fractional decimator. The code can be made simpler if the fractional scaling of the single sample when the value changes is removed. Another approach would be to change the ADC frequency and adjust the divisors.

Your notes about the noise made me curious.

You could try using a spectrum analyzer and compare the peaks with the noise floor. I used gnuradio's frequency and waterfall sink. Github doesn't allow grc extension files so I put it in a zip: analyzer.zip

it disappears with the oversampling

If you use check the waterfall of the received audio, you will see it is still there but very very weak which makes it inaudible.

@rhgndf
Copy link
Contributor Author

rhgndf commented Jan 9, 2025

@das-Iro I have put them into the releases page. I have used the v1.2.0 successfully, but haven't tested the v1.3.0 as much.

https://github.com/rhgndf/AIOC/releases

@das-Iro
Copy link

das-Iro commented Jan 9, 2025

Thanks, I have testet the two 1.3.0 version. Here are my results.

Background info: I sometimes use the AIOC while gaming, using qpwgraph (linux pipewire) to pipe the audio in on my headphones. This makes me interested in lower noise from the AIOC.

The difference in Noisefloor is easy to see.
About 16dB reduction of noise.
The 1kHz tones might come from the SOF with sidetones from transmissions of the audio frame. Simple AM modulation from the power supply. These are not changed much.
Multiples of the 1kHz are probably harmonics. (Imagine a rectangular dip on the Vcc)
There is a new tone at ~18.8kHz. I can not hear it but it's present in the oversampling version might be worth addressing.
1 3 0-noisefloor
1 3 0-noisefloor-oversampling

Sanity check for gain levels with open squelch at same volume from radio.
1 3 0-sql-open
1 3 0-sql-open-oversampling

Edit: removed audio files because I'm not sure if my audio recorder has automatic gain.

I think oversampling on the ADC side sort of 'smooths' out its internal noise. For the DAC side, the sample frequently changes, which also does something similar. This is my guess anyway, not too sure also.

There are multiple ways to explain how oversampling works, here I use a given ADC that is downsampled differently. There are some additional caveats I'm not going to explain, just the general principle.
If you have a ADC that can do 2.4MHz of sampling each sample will have the noise for the bandwidth of 1200kHz (1/2 because of Nyquist).
If you sample slower then what the ADC can, the downsampled version will cause aliasing of the noise to add up. The addition will be stochastically (because of complex math) and only grow with the square root. 1200kHz noise BW downsampled to 1200kHz/64 ≈ 19kHz BW (~37kHz sampling) correctly will result in √64 (energy) = 8 (amplitude) better SNR (3 bits). One bit equals 6dB of SNR hence we get 18dB more SNR exactly what we see.
By oversampling, the noise above the required bandwidth can be removed by a simple averaging/boxcart filter before downsampling.

I'll look into and try to mitigate the alleged SOF interference with a hardware mod. (software would also be neat, maybe disable CPU sleep if enabled? to have a more consistent power consumption)

@skuep
Copy link
Owner

skuep commented Jan 9, 2025

Thanks a bunch for testing..I will have a closer look next time I have some spare time.

Just to note, it is not surprising that you can get better ADC resolution (i.e. less quantization noise) with oversampling. My argument earlier was, if this method actually brings any improvement in the noise if you are actually using a radio. This would only be the case if the noise floor of the radio is actually better than the AIOC quantization noise.

Still, any improvement is nice and if it comes essentially for free, it is even better.

Feel free to try eliminating the USB SOF disturbance. I have sunk a lot of time into this in the very beginning and the only thing I found that actually helped was to provide AVDD and DVDD from two separate regulators. In my m memory it should actually hide in the noise floor of the radio.

@das-Iro
Copy link

das-Iro commented Jan 9, 2025

Feel free to try eliminating the USB SOF disturbance. I have sunk a lot of time into this in the very beginning and the only thing I found that actually helped was to provide AVDD and DVDD from two separate regulators. In my m memory it should actually hide in the noise floor of the radio.

If the squelch of my radio is active the AIOC is the noisiest part. I feed the audio to my headphones without a noise gate (squelch for audio)
It's actually the LED at 976Hz that is causing the noise at about 1kHz I think increasing the PWM frequency above the audible range should do it.
I replaced the 220Ω with 220kΩ. Removing it gave even better results.
1 3 0-noisefloor-oversampling-led

@skuep
Copy link
Owner

skuep commented Jan 9, 2025

If the squelch of my radio is active the AIOC is the noisiest part.

Hehe, okay, well played 😁

It's actually the LED at 976Hz that is causing the noise at about 1kHz

Did I really put the led frequency right in the middle of the audio range? Can't believe it, will add it to my to-do list

@rhgndf
Copy link
Contributor Author

rhgndf commented Jan 9, 2025

I think I'm facing a different problem here. In my case the 1KHz tone gets amplified when squelch is open, same on TX. The tone is still there with the oversampling, but its much less noticeable.

v1.3.0:
image

v1.3.0-oversampling:
image

@rhgndf
Copy link
Contributor Author

rhgndf commented Jan 10, 2025

There is a new tone at ~18.8kHz.

This is probably the CPU waking up during the DMA interrupt to do work since it interrupts every half buffer and the buffer size is 256: 2.4MHz/128 = 18750Hz

Reducing the buffer size to 128 should help, but I think we need to check whether there can be missed interrupts when the rate is higher.

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