Confused by ADC?

Post Reply
GrahamM242
Posts: 65
Joined: Fri Feb 21, 2014 6:48 pm

Confused by ADC?

Post by GrahamM242 » Tue Apr 11, 2017 6:39 pm

I'm confused by some behaviour I'm seeing on the Fubarino SD. I'm trying to use the ADC in interrupt mode, so it's configured to read 16 samples before raising an interrupt. I've had some success doing so. I wanted to get a baseline of DC offset, so I added a few analogRead() calls before I switched to using my code to handle the ADC (see InputADC::init()). However, doing this seems to kill things off, and I can't figure out why! I'd put some debug output in the terms of LEDs, but I see that the first LED output in InputADC::init() doesn't seem to get hit.

So, I went looking at wiring_analog.c to see why calling analogRead() seems to be killing my system. However, I can't figure out why calling it when my class is being instantiated causes a problem. Anyone with more wisdom than me see the problem?

Full example attached

Code: Select all

void __USER_ISR InputADC::adcISR()
{
  int * adcPtr;

  // Toggle green LED on D1
  LATDINV = 1<<9;
  
  // Copy the 16 bytes from ADC buffer to audio buffer.
  adcPtr = (int *)(&ADC1BUF0);

  for (int i=0; i<16; i++ )
  {
    analog_rx_buffer[i] = (*adcPtr);
    adcPtr+=4;  // Registers are 0x10 bytes apart - helpful.
  }
  
  clearIntFlag(_ADC_IRQ); // Clear ISR flag
}

void InputADC::init(uint8_t pin)
{
  uint32_t i, sum=0;
  uint8_t ain, channelNumber;

  // Why does putting analogRead in here kill the system?
  // Get a baseline for DC level on input...
  for (i=0; i < 10; i++) {
    sum += analogRead(pin);
  }
  
  // Sample rate driven by T3 - also use by output_pwm
  T3CON = 0;                // Turn the timer off.
  T3CONbits.TCKPS = 0;      // Set the prescaler to 1:1
  TMR3 = 0;                 //Clear the counter 
  PR3 = (F_CPU) / 44100;  // Whatever the audio rate is.
  T3CONSET = 0x8000;         // Turn the timer on 
  
  // Enable output for LEDs (D0,D1,D2)
  TRISD &= 0xF0FF;
  // Clear LEDs
  LATDCLR = 0x700;
  // Set white LED on D0
  LATDSET = 1<<8;
  
  // Configure the ADC channel and ensure it's valid.
  ain = (pin < NUM_DIGITAL_PINS) ? digitalPinToAnalog(pin) : NOT_ANALOG_PIN;
  if (NOT_ANALOG_PIN == ain) {
    return;
  }
  channelNumber = analogInPinToChannel(ain);
  // ADC MUX settings
  AD1PCFGCLR = (1 << channelNumber);
  AD1CHS = (channelNumber & 0xFFFF) << 16;
  
  // ADC settings
  AD1CON1 = 0x0040;     // Timer 3 events drives sampling
  AD1CSSL = 0;      // Disable scanning
  AD1CON3 = 0x000B;       // Sample time = 11 TAD
  AD1CON2 = 0x403C;       // VREF+, AVSS, interrupt every 16 samples
  AD1CON1SET  = 0x8000;   // Turn ADC on
  AD1CON1bits.ASAM = 1;   // Start auto sampling
  
  // Set up interrupts
  setIntVector(_ADC_VECTOR, adcISR);  // Our static handler
  setIntPriority(_ADC_VECTOR, 4, 0);
  clearIntFlag(_ADC_IRQ);
  setIntEnable(_ADC_IRQ); 
}
Attachments
sketch_apr09b.ino.zip
(2.02 KiB) Downloaded 5 times

User avatar
majenko
Site Admin
Posts: 2165
Joined: Wed Nov 09, 2011 7:51 pm
Location: UK
Contact:

Re: Confused by ADC?

Post by majenko » Tue Apr 11, 2017 6:54 pm

The first thing I would do is compare all the ADC and timer registers just after you have configured them both with and without the analogRead() calls. See if there is something being set by those calls that you aren't clearing.
Why not visit my shop? http://majenko.co.uk/catalog
Universal IDE: http://uecide.org
"I was trying to find out if it was possible to only eat one Jaffa Cake. I had to abandon the experiment because I ran out of Jaffa Cakes".

GrahamM242
Posts: 65
Joined: Fri Feb 21, 2014 6:48 pm

Re: Confused by ADC?

Post by GrahamM242 » Tue Apr 11, 2017 10:26 pm

I started with dumping registers, but something else took my attention! From a bit of digging around, it seems that things go wrong around the ISR stage if analogRead() is used. If I don't enable my ISR, things don't lock up. If I don't use analogRead(), things don't lock up.

As part of my debugging, I put a debug indicator (set a pin high) right at the beginning of the ISR. This never got hit, so I know that my ISR never got called. So, I can see that the init routine happens, the interrupt is configured, but my ISR never fires.

Because I've been bitten by persistent interrupts in the past, I tried reading ADC1BUF0 before attempting to enable interrupts to ensure ADC1 did not have an interrupt request pending. This seems to clear the problem, but I'm not sure why my ISR didn't get called without this. This is a little strange, as analogReadConversion() reads ADC1BUF0, so I'm a little stumped. On the other hand I have a workaround so I'm not blocked, but wouldn't mind understanding why it goes wrong!

Post Reply