AnalogRead interferes with Digital Interrupt

Posts: 16
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

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.

Return to “ESP32 Arduino”

Who is online

Users browsing this forum: Google [Bot] and 6 guests