Author |
noise from analog inputs on the 'b? |
br>gattis |
br>Can someone try and upload this sketch and see if they get a high-pitched tea-kettle-like noise? It just samples input from the 1st analog input and outputs it to DAC1. I get this noise on mine and by writing the sampled waveform to the SD card, I reproduce the noise on my computer, so I've narrowed it down to the Analog Inputs as opposed to the DAC. Or possibly some issue with the code...
Code: |
void setup()
{
adc_setup () ; // setup ADC
pmc_enable_periph_clk (TC_INTERFACE_ID + 0*3+0) ; // clock the TC0 channel 0
TcChannel * t = &(TC0->TC_CHANNEL)[0] ; // pointer to TC0 registers for its channel 0
t->TC_CCR = TC_CCR_CLKDIS ; // disable internal clocking while setup regs
t->TC_IDR = 0xFFFFFFFF ; // disable interrupts
t->TC_SR ; // read int status reg to clear pending
t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 | // use TCLK1 (prescale by 2, = 42MHz)
TC_CMR_WAVE | // waveform mode
TC_CMR_WAVSEL_UP_RC | // count-up PWM using RC as threshold
TC_CMR_EEVT_XC0 | // Set external events from XC0 (this setup TIOB as output)
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
t->TC_RC = 875 ; // counter resets on RC, so sets period in terms of 42MHz clock
t->TC_RA = 440 ; // roughly square wave
t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ; // set clear and set from RA and RC compares
t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ; // re-enable local clocking and switch to hardware trigger source.
dac_setup () ; // setup up DAC auto-triggered at 48kHz
}
void dac_setup ()
{
pmc_enable_periph_clk (DACC_INTERFACE_ID) ; // start clocking DAC
DACC->DACC_CR = DACC_CR_SWRST ; // reset DAC
DACC->DACC_MR =
DACC_MR_TRGEN_EN | DACC_MR_TRGSEL (1) | // trigger 1 = TIO output of TC0
(0 << DACC_MR_USER_SEL_Pos) | // select channel 0
DACC_MR_REFRESH (0x0F) | // bit of a guess... I'm assuming refresh not needed at 48kHz
(24 << DACC_MR_STARTUP_Pos) ; // 24 = 1536 cycles which I think is in range 23..45us since DAC clock = 42MHz
DACC->DACC_IDR = 0xFFFFFFFF ; // no interrupts
DACC->DACC_CHER = DACC_CHER_CH0 << 0 ; // enable chan0
}
void dac_write (int val)
{
DACC->DACC_CDR = val & 0xFFF ;
}
void adc_setup ()
{
NVIC_EnableIRQ (ADC_IRQn) ; // enable ADC interrupt vector
ADC->ADC_IDR = 0xFFFFFFFF ; // disable interrupts
ADC->ADC_IER = 0x80 ; // enable AD7 End-Of-Conv interrupt (Arduino pin A0)
ADC->ADC_CHDR = 0xFFFF ; // disable all channels
ADC->ADC_CHER = 0x80 ; // enable just A0
ADC->ADC_CGR = 0x15555555 ; // All gains set to x1
ADC->ADC_COR = 0x00000000 ; // All offsets off
ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | (1 << 1) | ADC_MR_TRGEN ; // 1 = trig source TIO from TC0
}
// Circular buffer, power of two.
#define BUFSIZE 0x400
#define BUFMASK 0x3FF
volatile int samples [BUFSIZE] ;
volatile int sptr = 0 ;
volatile int isr_count = 0 ; // this was for debugging
#ifdef __cplusplus
extern "C"
{
#endif
void ADC_Handler (void)
{
if (ADC->ADC_ISR & ADC_ISR_EOC7) // ensure there was an End-of-Conversion and we read the ISR reg
{
int val = *(ADC->ADC_CDR+7) ; // get conversion result
samples [sptr] = val ; // stick in circular buffer
sptr = (sptr+1) & BUFMASK ; // move pointer
dac_write (0xFFF & ~val) ; // copy inverted to DAC output FIFO
}
isr_count ++ ;
}
#ifdef __cplusplus
}
#endif
void loop()
{
}
| br> br> |
|
br>scottwilson |
br>I'll be able to try this in a few days as soon as I get the io shipments out...
The first thing I'd try tho is to raise the sample rate to 96khz (or some other higher value) and see if the frequency of the noise changes. If so, then it's aliasing noise. Since these ADCs are SAR and not oversampling, they are better for measuring CV than sampling audio, unfortunately.
If the frequency doesn't change (which is also a possibility) then it's more likely due to some noise in the system that would need to be tracked down. Can you send the sampled waveform? A spectrogram would be interesting.
As a comparison, most audio ADCs sample fewer bits at a higher rate (this is sigma-delta) and have a dedicated hardware antialising filter that changes from (for example) 1-bit at 256xFs to 16 bits at Fs... These are less good for measuring absolute DC (like CV) and better at changing signals like audio.
-s br> br> |
|
br>gattis |
br> scottwilson wrote: | I'll be able to try this in a few days as soon as I get the io shipments out...
The first thing I'd try tho is to raise the sample rate to 96khz (or some other higher value) and see if the frequency of the noise changes. If so, then it's aliasing noise. Since these ADCs are SAR and not oversampling, they are better for measuring CV than sampling audio, unfortunately.
If the frequency doesn't change (which is also a possibility) then it's more likely due to some noise in the system that would need to be tracked down. Can you send the sampled waveform? A spectrogram would be interesting.
As a comparison, most audio ADCs sample fewer bits at a higher rate (this is sigma-delta) and have a dedicated hardware antialising filter that changes from (for example) 1-bit at 256xFs to 16 bits at Fs... These are less good for measuring absolute DC (like CV) and better at changing signals like audio.
-s |
I was able to make it slightly better by putting the ADC into FREERUN mode with: Code: | ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | (1 << 1) | ADC_MR_TRGEN | (1 << 7); | but I still get some noise, less squeal but same amount of white noise. The squeal does change character a bit as I change the sample rate via TC_RC so it could be aliasing noise. I'll play around more.
Wonder what bitrate digital audio the digital inputs could take if I wanted a more clean input? br> br> |
|
br>gattis |
br>here's the background noise when I sample at 24khz (12-bit), used audacity to remove the DC bias and normalize
https://dl.dropboxusercontent.com/u/60227120/noise.wav
Here's the spectrum:
some harmonic peaks at 551Hz, 1102Hz, 1656Hz etc
and really narrow high harmonic peaks at 4909Hz, 9819Hz, etc br> br> |
|
br>scottwilson |
br>There are some examples around the web of using DMA to get around 1Msps, so once you've got DMA hooked up, you should be able to reasonably oversample 4 or 8 times and then the limiting factor would be how good of a downsampling filter you can write in integer math...
As you say tho, there is still a fair amount of noise... even if you get rid of the harmonic noise, the S/N ratio will be about 10 usable bits would be my guess.
s br> br> |
|
br>gattis |
br> scottwilson wrote: | There are some examples around the web of using DMA to get around 1Msps, so once you've got DMA hooked up, you should be able to reasonably oversample 4 or 8 times and then the limiting factor would be how good of a downsampling filter you can write in integer math...
As you say tho, there is still a fair amount of noise... even if you get rid of the harmonic noise, the S/N ratio will be about 10 usable bits would be my guess.
s |
Yea I got the timer-based code to get to the full 1Msps sampling rate, sending every sample straight to the DAC and it's much lower noise now. Some white noise but tolerable.
Code: |
t->TC_RC = 42;
t->TC_RA = 21;
|
Got DMA working too but the only code out there I could find polls until the DMA is finished buffering. The timer-based sampling works on interrupts so I can do more stuff in the background.
Time to try out some DSP stuff using the arm math routines! br> br> |
|
br>scottwilson |
br>So the difference is that with DMA, the CPU isn't moving any data and therefore is taking zero CPU cycles. If you don't use DMA, you're having to put the data in a register and then move it from a register to a memory location, all using CPU.
The example I saw was using DMA to read and I _thought_ the DMA routine was fired by a timer that would then log the data... so timer based, rather than polling. I'll have to look again. I was trying to get DMA working with the SD card.
s br> br> |
|
Powered by phpBB © phpBB Group
|