Electronic – Am I using ARM 7 LPC21xx ADC register correctly

adcarmmicrocontroller

Why is my OVERUN bit 1 when ADC function is placed inside a timer interrupt routine?

As per the User Manual Page 306, the OVERUN

bit is 1 in burst mode if the results of one or more conversions was (were) lost
and overwritten before the conversion that produced the result in the RESULT bits.
This bit is cleared by reading this register.

I am setting up the ADC register to read on two channels using burst mode, and constantly getting the OVERUN bit set to 1 in the debugger. The code is as follows:

unsigned long dummyRead;
unsigned long AD0GDR_Read = AD0GDR;

int channel = (AD0GDR_Read>>24) & 0xF; //Extract Channel Number
int currentResult = (AD0GDR_Read>>6) & 0x3FF; //Extract Conversion Result

if(channel == 0)
{ 
    dummyRead = AD0DR0; //Read to Clear Done flag , Also clears AD0 interrupt
    AD00Result = currentResult;
}
 if(channel == 1)
{
    dummyRead = AD0DR1; //Read to Clear Done flag , Also clears AD0 interrupt
    AD01Result = currentResult;     
}

This works perfectly IF this code is placed inside an ADC Interrupt Service Routine, or simply placed in the main while loop. However, this is not what I need.

Since I want to find the RMS of a sine wave I need to take ADC readings at specific time intervals (see here). Thus I have decided to create a timer interrupt with a pre-determined delay, which once complete, allows for an ADC reading. I have tested this code using a simple LED. A similar example may be found here.

I have coded up something similar and place the ADC function in the Timer ISR, however here is where problems start to occur. I am constantly getting the OVERRUN bit set to 1, which means I am losing conversions.

Code is as follows:

void initTimer0(void)
{
  T0CTCR = 0x0; //Set Timer Mode
  T0PR = 50000-1; //Increment T0TC at every 50000 clock cycles


  T0MR0 = 2 - 1;   //Zero Indexed Count-hence subtracting 1
  T0MCR = (1<<0) | (1<<1);//Set bit0 & bit1 to Interrupt & Reset TC on MR0  

  VICVectAddr4 = (unsigned )timer0ISR; //Pointer Interrupt Function (ISR)
  VICVectCntl4 = (1<<5) | 4; //(bit 5 = 1)->to enable Vectored IRQ slot 
  VICIntEnable = (1<<4); // Enable timer0 interrupt

  T0TCR = (1<<1); // Reset Timer
}

__irq void timer0ISR(void)
{
 long int readVal;
 readVal = T0IR; // Read current IR value 
 IO0PIN ^= (1<<10); // Toggle LED at Pin P0.10

 ADC(); //code as above

 T0IR = readVal; // Write back to IR to clear Interrupt Flag
 VICVectAddr = 0x0; // End of interrupt execution
}

Below is a snapshot of my debugger tool:

enter image description here

I simply cannot see why I am losing conversions, if this is the only place I am reading from the ADC register. It seems as though the location of this code is resulting in the loss of conversions (when placed in interrupt routine).

Any suggestions on why this is so?

You are getting overruns because the ADC in burst mode has completed multiple conversions in between the timer interrupts. – kkrambo

Does the OVERUN bit mean I am simply losing the first conversion, and thus my RMS reading will not be affected, OR am I losing conversions throughout the execution of the program?

EDIT It has been suggested to call the ADC interrupt after the timer interrupt is complete. Should something like this code skeleton be correct?

void initTimer0(void)
{
  VICVectAddr4 = (unsigned )timer0ISR; //Pointer Interrupt Function (ISR)
  VICVectCntl4 = 0x00000025; //(bit 5 = 1)->to enable Vectored IRQ slot 
  VICIntEnable = (1<<5); // Enable timer1 interrupt
}

__irq void timer0ISR(void)
{
  VICIntEnable |= (1<<18) ;
  VICVectCntl0 = (1<<5) | 18 ;
  VICVectAddr0 = (unsigned) AD0ISR;
}

 __irq void AD0ISR(void) //AD0 Interrupt Function
{
  //ADC function above
}

The above code structure does not give an OVERUN error. Any hints as to why this may be so?

you should use the ADC interrupt to handle completion of the conversion and read the results. When the ADC completes the single conversion it will not automatically start another conversion but instead it will wait for the timer interrupt to trigger another conversion. -kkrambo

The problem with this is that: the ADC interrupt routine gets called from outside the timer routine.. somehow. I cannot seem to understand this since I am not sure if this is the correct behavior. This means that even though there is no overrun bit from the ADC register when placed inside it's own interrupt, it is being read at a much higher frequency than that of the timer interrupt.

I tested this by setting one bit to 'high' in both ISR. The bit in the timer ISR behaves as expected and goes high every 1 ms, while that of the ADC goes to 'high' every 22 us.

Best Answer

If you want to take readings at a specific rate determined by a timer then you should not be using burst mode. In burst mode the ADC will automatically start the next conversion when the previous conversion completes. You are getting overruns because the ADC in burst mode has completed multiple conversions in between the timer interrupts. And your software did not read each conversion between timer interrupts.

Instead you should use single conversion mode (BURST = 0). When the timer interrupt occurs the timer interrupt handler should trigger the ADC to start a conversion. You probably don't want to wait for the conversion to complete within the timer interrupt handler. Instead you should use the ADC interrupt to handle completion of the conversion and read the results. When the ADC completes the single conversion it will not automatically start another conversion but instead it will wait for the timer interrupt to trigger another conversion.

Update: I will try to spell it out more clearly:

  1. Enable the timer and setup the timer period for the desired sample rate.
  2. Enable the timer ISR so that the timer interrupt handler is called at the desired sample rate.
  3. Enable the ADC in single conversion mode (NOT burst mode) so that it performs a single conversion each time it is triggered (i.e. started).
  4. Enable the ADC ISR so that the ADC interrupt handler is called when the ADC conversion is complete.
  5. Within the timer interrupt handler, trigger the ADC to start a conversion and then exit. Do not attempt to read the ADC results within the timer ISR.
  6. Within the ADC interrupt handler, read the results of the ADC conversion.

Sampling multiple channels: Some ADCs will scan through all selected channels in single conversion mode. In that case the above technique would still be good. But apparently this ADC supports only one channel in single conversion mode and you must use burst mode to scan through multiple selected channel. In this case use the following steps.

  1. Enable the timer and setup the timer period for the desired sample rate.
  2. Enable the timer ISR so that the timer interrupt handler is called at the desired sample rate.
  3. Enable the ADC in burst mode so that it scans through multiple channels.
  4. Enable the ADC ISR so that the ADC interrupt handler is called when the ADC conversion is complete.
  5. Within the timer interrupt handler, trigger the ADC to start a conversion and then exit. Do not attempt to read the ADC results within the timer ISR.
  6. Within the ADC interrupt handler, read the results of the ADC conversion. If this conversion is for the last channel in the scan set then stop the ADC. The ADC will be restarted when the next timer ISR occurs.