3
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ You can even use the ADC interrupt. Though you would still get overrun error in burst mode, but it won't be a problem if you simply ignore the overrun error. It is only to show you that some reading have been overwritten and you are reading the latest reading. \$\endgroup\$ – MaNyYaCk Jan 13 '18 at 5:09
  • 1
    \$\begingroup\$ The above code produced no overrun error when placed inside an ADC interrupt... \$\endgroup\$ – Rrz0 Jan 13 '18 at 5:59
3
\$\begingroup\$

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.
\$\endgroup\$
  • 1
    \$\begingroup\$ So I should call the ADC ISR from within the timer ISR? I am not sure how I should go about that. I made an edit to my question with what I have done in my code. Strangely enough, when I put the ADC interrupt inside the timer interrupt, the override bit is gone. \$\endgroup\$ – Rrz0 Jan 13 '18 at 7:48
  • \$\begingroup\$ However another problem is encountered as explained above. \$\endgroup\$ – Rrz0 Jan 13 '18 at 10:04
  • \$\begingroup\$ @Rrz0, No you should not call the ADC ISR from the timer ISR. That's not what I wrote. You misunderstood. \$\endgroup\$ – kkrambo Jan 13 '18 at 14:57
  • \$\begingroup\$ Thanks, that is much clearer! However, as specified in the question, I do need the burst mode enabled since I am reading from more than one register. Or isn't burst mode needed for this... ? I might have a misunderstanding with regards to the burst mode functionality. \$\endgroup\$ – Rrz0 Jan 13 '18 at 15:19
  • \$\begingroup\$ @Rrz0, If you need to use burst mode to scan through multiple channels then you should start the ADC conversion in the timer ISR and stop the ADC conversions after the last channel is complete. The ADC conversions will be restarted when the next timer ISR occurs. \$\endgroup\$ – kkrambo Jan 13 '18 at 15:38
1
\$\begingroup\$

After several hours of testing, and with the helpful comments of kkrambo I have found out the following.

  • The Timer Interrupt Service Routine is read once every 1 ms, as per my calculated timer.
  • If burst mode is enabled, the ADC register inside it's Interrupt Service Routine is read every 22 micro-seconds, irrespective of the Timer ISR since it is called by the CPU.

Therefore there will be an OVERRUN error if the ADC conversion is placed directly inside the Timer ISR since the ADC has completed approximately 50 conversions between timer interrupts.

This also explains why there is no OVERRUN error when the ADC is placed inside it's own ISR, since this always completes conversions once every 22 micro-seconds.

The question still remains whether the conversions lost will affect my overall RMS reading of the sine wave.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.