AnalogRead interferes with Digital Interrupt

rodmcm
Posts: 65
Joined: Sat Sep 02, 2017 3:31 am

AnalogRead interferes with Digital Interrupt

Postby rodmcm » Wed Sep 12, 2018 11:11 pm

I have deduced that reading an analog under a timed interrupt interferes with my digital interrupt. A Google search shows others have had this problem as well.. I tried some of the workarounds but no success

Has anyone a proper solution for this.
The attached code is a much simplified program. If the analog read line is bypassed the frequency measurement works great. With it in I get a constant reading of 33333 Hz

Code: Select all

 
// Analog Measurement
volatile int SampleTotal=750;
volatile int ADCSampleCount=0;                      // counts samples in AnalogValue
volatile int ADCState;                              // state counter for sampling 1: sample, 2: finish 
volatile uint8_t AnalogPin;                     
volatile int AnalogVal[750];
int ADCNo=1;
float Max1;
float Min1;

// frequency measurement
const byte        interruptPin = 25;              // Assign the interrupt pin
volatile uint64_t StartValue;                     // First interrupt value
volatile uint64_t PeriodCount;                    // period in counts of 0.000001 of a second
float             Freg;                           // frequency
int               RPM;                            // 4 cyclinder RPM - Freq x 120     

 // timer setups          
hw_timer_t * timer0 = NULL;                   // timer0 used for analogs pointer to a variable of type hw_timer_t
hw_timer_t * timer1 = NULL;                   // timer1 used for digital/freq measurement pointer to a variable of type hw_timer_t 

portMUX_TYPE Freqmux = portMUX_INITIALIZER_UNLOCKED;   // allows for synchronising between the ISR and the main code ?????????
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;  



//-------------------------------------------------------------
// Digital Event Interrupt
// This event triggers on the falling value on the interrupt pin
// ISR located in IRAM
//-------------------------------------------------------------
void IRAM_ATTR handleInterrupt() 
{
   portENTER_CRITICAL_ISR(&Freqmux);
    uint64_t TempVal= timerRead(timer1);        // value of timer at interrupt
    PeriodCount= TempVal - StartValue;          // period count between rising edges in 0.000001 of a second
    StartValue = TempVal;                       // puts latest reading as start for next calculation
    portEXIT_CRITICAL_ISR(&Freqmux);
}

//----------------------------------------------------------
// ADC Interrupt
// This interrupt is triggered by the timer0 and samples the
// analogs until the total sample is exceeded
// ISR located in IRAM
//------------------------------------------------------------

void IRAM_ATTR onTimer() 
 {
   portENTER_CRITICAL_ISR(&mux);
   if ((ADCSampleCount<SampleTotal)&&(ADCState==1))        
      {
       AnalogVal[ADCSampleCount]=analogRead(AnalogPin);        
       ADCSampleCount++;
      }
     if (ADCSampleCount>=SampleTotal)
      {
       ADCSampleCount=0;                               // sets for next sample
       ADCState = 2;                                   // signals completion to main program
      }
   portEXIT_CRITICAL_ISR(&mux);
 } // end ISR


//-----------------------------------
// Clears the analog Array
//-------------------------------------
void ClearAnalogArray()
{
  for (int i=0;i<750;i++)
  AnalogVal[i]=0;
}

//--------------------------------------------
// Calculates max and min from 750 samples
//---------------------------------------------
void AnalogMaxMin()
{
  Max1=0;
  Min1=4095;
   for (int i=0;i<SampleTotal;i++)
 {
  if (AnalogVal[i]>Max1) Max1=AnalogVal[i];     
  if (AnalogVal[i]<Min1) Min1=AnalogVal[i];
 }
 Max1=3.0*Max1/4095;                       // just for a test
 Min1=3.0*Min1/4095;                       // just for a test 

}


//-----------------------------------------------
//Gets frequency from interrupt reading
//----------------------------------------------
 void CalcFreq()
 {
  portENTER_CRITICAL(&Freqmux);
  Freg =1000000.00/PeriodCount;                       // PeriodCount in 0.000001 of a second
  portEXIT_CRITICAL(&Freqmux);
 }


//----------------------------------------------------------------
// Setup
//----------------------------------------------------------------
void setup() 
{ 

 Serial.begin(115200);                          // Debug
 pinMode(interruptPin, INPUT_PULLUP);           // sets pin high
 
 // analog stuff
 analogSetWidth(12);
 analogSetAttenuation(ADC_11db);
 analogReadResolution(12);                       //0-4095
 ADCNo=1;                                        // Analog input counter                                
 AnalogPin=34;                                   // Analog Pin allocation
 ADCState=1;                                     //1 = sampling 2 = completed 

// Analog Interrupt SetUp
 timer0 = timerBegin(0, 80, true);               // this returns a pointer to the hw_timer_t global variable
                                                 // 0 = first timer (analog)
                                                 // 80 is prescaler so 80MHZ divided by 80 = 1MHZ signal ie 0.000001 of a second
                                                 // true - counts up
  timerAttachInterrupt(timer0, &onTimer, true);  // attaches the timer to a pointer to the interrupt routine onTimer, true means rising edge
  timerAlarmWrite(timer0, 300, true);            // this attaches the timer to the interrupt every 300 counts (0.0003 second = 3/10mS)
                                                 // thus 750 counts = 0.225 sec
                                                 // true means the counter will reset after each interrupt
  timerAlarmEnable(timer0);  
  delay(200);
                                               
 // frequency interrupt setup
  attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, FALLING); // attaches pin to interrupt on Falling Edge
  timer1 = timerBegin(1, 80, true);                                               // this returns a pointer to the hw_timer_t global variable
                                                                                  // 1 = first timer(freq)
                                                                                  // 80 is prescaler so 80MHZ divided by 80 = 1MHZ signal ie 0.000001 of a second
                                                                                  // true - counts up
  timerStart(timer1);                                                             // Start the timer
  delay(200);   
  
  Serial.println(" Setup Complete");
}    // setup



//-----------------------------------------------------------------
// Main Program
//-----------------------------------------------------------------
void loop() 
{
// Calculates values at end of analog sample  
//--------------------------------------------------------- 
 if (ADCState==2)                      //Samplng completed
   {   
   AnalogMaxMin();                     // Finds the Max and Min values
   Serial.print("Max V  :");Serial.println(Max1,2);
   Serial.print("Min V  :");Serial.println(Min1,2);
   CalcFreq();                          // Calculates the frequency
   Serial.print("Frequency   ");Serial.println(Freg,2);
   ClearAnalogArray();
   ADCState=1;                         //Reset sampling 
   }  // sample completed  
} // end loop

Rki009
Posts: 1
Joined: Sun Oct 07, 2018 5:36 pm

Re: AnalogRead interferes with Digital Interrupt

Postby Rki009 » Sun Oct 07, 2018 6:34 pm

In general interrupt routines should do very little processing. If a routine like analogRead() is needed it should be executed in the main processing loop or in a separate task. I measure about 10 us for analogRead() to execute. There is also a digital interrupt with a frequency of 33333 Hz. or 30us between triggers. The processor will be VERY busy trying to handle both of these service routines! So the net result is you are likely missing interrupts because the processor is swamped.

Some notes:
- reliable interrupt handling on anything like the ESP32 above 1000 interrupts per second requires special attention
- interrupts routines should do very very little processing; best queue raw data and set a flag. Process the data at a non interrupt level.
- use a FIFO queue or data buffers to collect interrupt level data for bulk processing at non interrupt processing
- avoid any routines that take time! Don't call analogRead() at interrupt level
- use separate processing tasks and an interrupt semaphore to trigger non interrupt level processing
- adjust task processing priorities to help busy tasks
- the ESP32 has 2 processors, balance interrupts and associated tasks between the two processors

For your program. I would create an analogRead task and trigger it with the onTimer routine and semaphore.

Who is online

Users browsing this forum: No registered users and 90 guests