How can I pause an interrupt?

I have an ISR( ADC_vect ) interrupt defined that fills a volatile ring buffer from an ADC channel, switches channel and fills another. Once both are filled, a volatile flag is set.

The main loop checks the flag and calls a function to copy the ring buffers and process an FHT on each. I want to pause the interrupt while this function is called, but for some reason my setup function is called. I thought the setup function was only ever called once.

I've tried using cli(); before the function call, followed by a sei(); after the function call.
I've tried clearing and setting the ADATE bit in the ADCSRA register before and after the function call.
I've tried clearing and setting the ADEN and ADSC bits in the ADCSRA register before and after the function call.

The setup function keeps getting called and the function I want to run doesn't appear to be called at all.

My setup defines register settings and the flags:

void setup( void )
{

  // ADCSRA.
  ADCSRA = B00101101;

  // ADEN  - 0  // Disable ADC.
  // ADSC  - 0  // Disable conversions.
  // ADATE - 1  // Enable ADC auto trigger.
  // ADIF  - 0  // Set by hardware
  // ADIE  - 1  // Enable interrupt.
  // ADPS2 - 1  // }
  // ADPS1 - 0  // }- ADC prescaler = 32.
  // ADPS0 - 1  // }

  // ADCSRB.
  ADCSRB = B00000000;

  // N/A        //
  // ACME  - 0  // Disable Analogue Comparator Mux.
  // N/A        //
  // N/A        //
  // N/A        //
  // ADTS2 - 0  // }
  // ADTS1 - 0  // }- Free Running Mode.
  // ADTS0 - 0  // }

  // ADMUX.
  ADMUX = B01100000;
  
  // REFS1 - 0  // }- Use Vcc as reference.
  // REFS0 - 1  // } 
  // ADLAR - 1  // Left adjust ADC results (8-bit mode).
  // N/A        //
  // MUX3  - 0  // } 
  // MUX2  - 0  // }- ADC input 0.
  // MUX1  - 0  // } 
  // MUX0  - 0  // } 

   // DIDR0.
  DIDR0 = B00111111;

  // N/A        // }- Pins ADC7D & ADC6D do not have input buffers.
  // N/A        // }
  // ADC5D - 1  // Disable digital input ADC5.
  // ADC4D - 1  // Disable digital input ADC4.
  // ADC3D - 1  // Disable digital input ADC3.
  // ADC2D - 1  // Disable digital input ADC2.
  // ADC1D - 1  // Disable digital input ADC1.
  // ADC0D - 1  // Disable digital input ADC0.

  // DIDR1.
  DIDR1 = B00000011;

  // Clear buffers.
  memset( (void *) ADCBuffer, 0, sizeof( ADCBuffer ));

  // Set up counters and flags.
  ADCCounter = 0;
  ChannelIndex = 0;
  FHTStart = false;

  if ( DEBUG ) Serial.begin( 115200 );

  if ( DEBUG ) Serial.println( "Initialisation complete" );
  
  delay( 1000 );

  startADC();
}

The ISR is pretty simple:

ISR( ADC_vect )
{
  // Fill next available buffer slot with ADC value.
  ADCBuffer[ChannelIndex][ADCCounter] = ADCH; // Read ADC value.

  // If the ADC buffer for the channel is full, switch channel.
  if ( ++ADCCounter >= FHT_N )
  {
    ADCCounter = 0;

    if ( ++ChannelIndex >= CHANNELS )
    {
      ChannelIndex = 0;
      FHTStart = true; // Set flag to enable start of FHT.
    }

    // Switch to next channel.
    ADMUX &= 0xE0;
    ADMUX |= admux[ChannelIndex];
  }
}

And the main code is:

void loop( void )
{
  if ( FHTStart )
  {
//    cli();
//    bitclr( ADCSRA, ADATE );
    stopADC();
    if ( DEBUG )
    {
      Serial.println( "Calling FHT routine" );
      delay( 1000 );
    }


    getFHTOutput();
    FHTStart = false;
//    sei();
//    bitset( ADCSRA, ADATE );
    startADC();
  }

}

At the moment the getFHTOutput function just prints some debug into to serial, except it never shows!
I see the main loop debug output but I get the setup() function debug output in each loop.

Why is the setup function being called when I try and restart the interrupt?
Is there a better way?

Is there a better way?

Indeed there is. Post ALL of your code, not just snippets.

You left out, for instance, how FHTStart is defined.

Ok, here it is, warts 'n all.

#include "Spectrum_Analyser_2.h"

volatile uint8_t  ADCBuffer[CHANNELS][FHT_N];
volatile uint8_t  ChannelIndex;
volatile uint16_t ADCCounter;
volatile boolean  FHTStart; // Flag to allow FHT data to be processed.

byte bandvals[CHANNELS][FREQ_BANDS];    // Frequency bands.
byte peakvals[CHANNELS][FREQ_BANDS];    // Peak hold values.

const byte admux[CHANNELS] = { ADC0D, ADC1D };  // ADC assignments.
const byte bins = FHT_OUT / FREQ_BANDS;         // Number of bins in each band.

const byte sampbins[FREQ_BANDS] = { 0, 1, 2, 3, 5, 12, 27, 60 }; // Log.

float scaleLED;

Adafruit_NeoPixel ledstrip = Adafruit_NeoPixel( FREQ_BANDS * FREQ_LEDS,
                                                LED_DATA_PIN,
                                                NEO_GRBW + NEO_KHZ800);

byte brightness = INIT_BRIGHT;        // Set initial LED brightness.
long refresh = INIT_REFRESH;          // Set initial time (uS) between updates.
volatile byte filling = 0;


//  ---------------------------------------------------------------------------
//  ADC conversion interrupt.
//  ---------------------------------------------------------------------------
ISR( ADC_vect )
{
  // Fill next available buffer slot with ADC value.
  ADCBuffer[ChannelIndex][ADCCounter] = ADCH; // Read ADC value.

  // If the ADC buffer for the channel is full, switch channel.
  if ( ++ADCCounter >= FHT_N )
  {
    ADCCounter = 0;

    if ( ++ChannelIndex >= CHANNELS )
    {
      ChannelIndex = 0;
      FHTStart = true; // Set flag to enable start of FHT.
    }

    // Switch to next channel.
    ADMUX &= 0xE0;
    ADMUX |= admux[ChannelIndex];
  }
}

//  ---------------------------------------------------------------------------
//  Starts the ADC.
//  ---------------------------------------------------------------------------
void startADC( void )
{
  bitset( ADCSRA, ADEN );
  bitset( ADCSRA, ADSC );
//  bitset( ADCSRA, ADATE );
}

//  ---------------------------------------------------------------------------
//  Stops the ADC.
//  ---------------------------------------------------------------------------
void stopADC( void )
{
//  bitclr( ADCSRA, ADATE );
  bitclr( ADCSRA, ADSC );
}

//  ---------------------------------------------------------------------------
//  Initialises the ADC registers and LED.
//  ---------------------------------------------------------------------------
void setup( void )
{

//  cli();

  // ADCSRA.
  ADCSRA = B00101101;

  // ADCSRB.
  ADCSRB = B00000000;

  // ADMUX.
  ADMUX = B01100000;
  
  // DIDR0.
  DIDR0 = B00111111;

  // DIDR1.
  DIDR1 = B00000011;

  // Clear buffers.
  memset( (void *) ADCBuffer, 0, sizeof( ADCBuffer ));

  // Set up counters and flags.
  ADCCounter = 0;
  ChannelIndex = 0;
  FHTStart = false;

  if ( DEBUG ) Serial.begin( 115200 );

  if ( DEBUG ) Serial.println( "Initialisation complete" );
  
  delay( 1000 );

  startADC();
//  sei();  // Activate interrupts.
}

//  ---------------------------------------------------------------------------
/*
    This updates the LED string after receiving an interrupt from Timer1.
*/
//  ---------------------------------------------------------------------------
void updateLED( void )
{
}

//  ---------------------------------------------------------------------------
/*
    This fills the FHT output array with the sorted frequency magnitudes
    depending on the type of output requested, i.e.
        Linear
        Linear (byte)
        Logarithmic
        Octave
*/
//  ---------------------------------------------------------------------------
void getFHTOutput( void )
{
  if ( DEBUG ) Serial.println( "FHT routine called" );
}

//  ---------------------------------------------------------------------------
/*
    Fills the bandvals array with the values of the specific bins in sampbins
    array.
*/
//  ---------------------------------------------------------------------------
void getBands( void )
{

}

//  ---------------------------------------------------------------------------
/*
    Main loop.
*/
//  ---------------------------------------------------------------------------
void loop( void )
{
  if ( FHTStart )
  {
//    cli();
//    bitclr( ADCSRA, ADATE );
    stopADC();
    if ( DEBUG )
    {
      Serial.println( "Calling FHT routine" );
      delay( 1000 );
    }


    getFHTOutput();
    FHTStart = false;
//    sei();
//    bitset( ADCSRA, ADATE );
    startADC();
  }

//    getBands();   // Get frequency band output.
//    updateLED();  // Update LEDs. This is called by interrupt.

}

There’s also a header file with some definitions in, but an awful lot of spiel too!

#define LIN_OUT      0  // Toggle linear output (word).
#define LIN_OUT8     0  // Toggle linear output (byte).
#define LOG_OUT      1  // Toggle logarithmic output (byte).
#define OCTAVE       0  // Toggle octave output (byte).
/*
  FHT_OUT is normally FHT_N/2, except for OCTAVE output where
  FHT_OUT = 8 for FHT_N = 256,
  FHT_OUT = 7 for FHT_N = 128.
*/
#define FHT_N      128  // Number of FHT input bins per channel.
#define FHT_OUT     64  // Number of FHT output bins per channel.

//  Display -------------------------------------------------------------------
/*
    The settings determine the output that is displayed.
*/
#define CHANNELS        2 // Number of audio channels.
#define FREQ_BANDS      8 // Number of frequency bands per channel.
#define FREQ_LEDS      15 // Number of LEDS representing each band.
#define LED_DATA_PIN    7 // Data pin for WS2812 LED strip.
#define INIT_BRIGHT    50 // Initial LED brightness.
#define INIT_REFRESH 50000 // Initial refresh time for LEDs.


#define DEBUG 1   // Toggles debug output via serial.

//  Includes ------------------------------------------------------------------

#include <Arduino.h>
#include <FHT.h>
#include <TimerOne.h>
#include <Adafruit_NeoPixel.h>

//  Defines -------------------------------------------------------------------

// Bit manipulation.
#define bitset( reg, bit ) ( reg |= ( 1 << bit ))   // Set register bit
#define bitclr( reg, bit ) ( reg &= ~( 1 << bit ))  // Clear register bit.
#define bittst( reg, bit ) ( reg & ( 1 << bit ))    // Test register bit.

// ADC.
#define CHANNELS        2 // Number of audio channels.

// FHT.
#define LIN_OUT      0  // Toggle linear output (word).
#define LIN_OUT8     0  // Toggle linear output (byte).
#define LOG_OUT      1  // Toggle logarithmic output (byte).
#define OCTAVE       0  // Toggle octave output (byte).
#define FHT_N      128  // Number of FHT input bins per channel.
#define FHT_OUT     64  // Number of FHT output bins per channel.

// Display.
#define FREQ_BANDS      8 // Number of frequency bands per channel.
#define FREQ_LEDS      15 // Number of LEDS representing each band.
#define LED_DATA_PIN    7 // Data pin for WS2812 LED strip.
#define INIT_BRIGHT    50 // Initial LED brightness.
#define INIT_REFRESH 50000 // Initial refresh time for LEDs.

//  ===========================================================================
//  Prototypes.
//  ===========================================================================

void startADC( void );
void stopADC( void );
void getFHTOutput( void );
void getBands( void );

I’ve snipped as much as I could to get it within the post limits.

Thanks

You cannot stop ISR from outside. Arduino has no parallel computing. Once the ISR is started it is performed till its end. No instruction form outside part of program is performed, even another interrupt occurs during ISR (ATmega, right?). It is single level interrupt system.

Hi Budvar10.

The main problem I have had thus far was the debug output in the main loop was never showing, leading me to believe that the main loop was never being entered.

Now, I did make the boo-boo of having serial prints in the ISR itself, which I have learned is a no-no, but I've perused enough examples to have seen a lot of use of cli(); and sei();

I could just poll the whole procedure but this is more of a learning exercise for me than a anything and I am picking up a lot of programming etiquette and techniques, but I could do with some pointers.

If it's going back to setup that is the board resetting. That's a sign of either an electrical issue or overwriting an array somewhere or other memory management issues.

What type of Arduino do you have? Some of those 2D arrays are very large. Definitely not going to fit in RAM on an UNO.

The main problem I have had thus far was the debug output in the main loop was never showing, leading me to believe that the main loop was never being entered.

Getting loop() to do anything, the way you have your code written, depends on getting FHTStart to be true. It is initialized to false. It is set to true only in the interrupt handler, and only when certain conditions are met.

That nothing happens in loop() suggests something completely different to me.

@Delta_G I have a mini pro. Compiler says I have used 19% of storage and 44% of dynamic memory.

@PaulS That's because you have a lot more experience and knowledge than I do. I'd be grateful for any help.

I'd be grateful for any help.

Doesn't the fact that FHTStart is never 0 suggest an avenue for investigation?

What are the conditions that need to happen for FHTStart to be set to true?

Do those conditions every occur?

FHTStart is set when the channels loop back to the beginning, i.e. from 1 to 0. The debug output within the main loop is visible (i.e. the "Calling FHT Routine"), as is the "FHT Routine Called", but immediately after that I get "Initialisation complete".

Based on Delta_G's comment I'm working on the theory that something is overwriting memory somewhere.

if you are sure that the device is actually resetting and performing the setup() again, one possibility is that you are enabling an interrupt that doesn't have an ISR defined for it. The default behavior of an interrupt that triggers without a defined ISR is to jump to the reset vector.

It doesn't look like you're enabling any interrupts other than the ADC one, but I don' tknow what's going on in the FHT library you're using.

Try adding this to your sketch and see if it solves the resetting.

EMPTY_INTERRUPT(BADISR_vect);