MUFF WIGGLER Forum Index
 FAQ & Terms of UseFAQ & Terms Of Use   Wiggler RadioMW Radio   Muff Wiggler TwitterTwitter   Support the site @ PatreonPatreon 
 SearchSearch   RegisterSign up   Log inLog in 
WIGGLING 'LITE' IN GUEST MODE

noise from analog inputs on the 'b?
MUFF WIGGLER Forum Index -> nw2s  
Author noise from analog inputs on the 'b?
gattis
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()
{
}
scottwilson
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
gattis
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?
gattis
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
scottwilson
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
gattis
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. applause

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!
scottwilson
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
MUFF WIGGLER Forum Index -> nw2s  
Page 1 of 1
Powered by phpBB © phpBB Group