ronbentley1
Published

ARDUINO Microcontroller, External Interrupt Handling

A framework for the handling and asynchronous processing of multiple external interrupts in a new way.

AdvancedProtip2,857
ARDUINO Microcontroller, External Interrupt Handling

Things used in this project

Hardware components

Arduino Mega 2560
Arduino Mega 2560
The Mega 2560 was used for the development of the framework. Other boards are also suitable, but selection is more influenced by the number of external interrupts to be handled concurrently and clock speed.
×1

Story

Read more

Schematics

Conceptual overview of the framework.

To aid understanding of the structure of the framework.

Code

TAB Introduction - A00_Interrupt_Framework_README_v2.03.ino

C/C++
Introduction segment
/* (External) Interrupt Queue Framwork, Ron D Bentley (Stafford UK)
 * ____________________________________________________________________________________________________
 * Copyright (c) Ron D Bentley (UK)
 * The extent this licence shall be limited to Non-profit Use.
 * Permission is hereby granted, free of charge, for non-profit purposes to any person obtaining
 * a copy of this software and associated documentation files (the "Software"), to deal in the 
 * Software without restriction, including without limitation the rights to use, copy, modify, merge, 
 * publish, distribute, sublicense copies of the Software, and to permit persons to whom the Software 
 * is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice, acknowledgements and this permission notice shall be included in all 
 * copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS LIMITED TO NON-PROFIT USE AND IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR 
 * A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ____________________________________________________________________________________________________
 * 
 * Version History    _________________________________________________________________________________
 *
 *18 Apr 2020:v2.03.1 Point release - corrected '#define millis_max' in E10_Interrupt Handlers tab, which  
 *                    was incorrectly set for 64 bit operation.  Corect value is 0xffffffff, which
 *                    refects 32 bit long unsigned integer architecture.
 * 4 Mar  2020:v2.03  Efficiency improvements regarding scanning IQ for existing unique interrupts.  New                    
 *                    approach no longer scans chain, but uses binary flags, one for each generic interrupt.
 *                    These are set/cleared only if a generic interrupt handler is configured for unique 
 *                    interrupts. Note, the bit set/cleared refers to the number of the generic interrupt 
 *                    handler and not the actual linked interrupt that triggered on the assigned generic 
 *                    handler.
 *  1 Mar  2020:v2.02 Introduced concept of 'generic' interrupt ISRs to delink actual dgital interrupts 
 *                    pins from a particular ISR.
 * 27 Feb 2020:v2.01  Further extension to the interrupt channel data structure ('interrupt_config_data')
 *                    to provide greater flexiblity per digital interrupt input.  
 *                    This version now allows each interrupt to be treated individually for 
 *                    unique/nonunique interrupts plus independent interrupt debounce.
 * 24 Feb 2020:v2.00  Modified to consolidate the interrupt channel data into a more helpful                    
 *                    structure for end users to change, as required.
 * 23 Feb 2020:v1.04  Introduction of further flexibility by addition of modifiable variables
 *                    to allow pinMode input types and attachInterrupt trigger types.
 * 22 Feb 2020:v1.03  Modifications to deal with interrupt 'bounce'. 
 * 22 Feb 2020:v1.02  Layout work to program structure, including end user configuration tab
 * 20 Feb 2020:v1.01  Extensions to allow unique/nonunique interrupt handling. 
 * 13 Feb 2020:v1.00  Initial version, concepts and development.
 * ___________________________________________________________________________________________________
 * 
 * README..._________________________________________________________________________________________
 * This is a framework that can be used to manage and process any number of external
 * interrupts, from 1 up to the maximum number support by the microcontroller.
 * It works by using a queue into which triggered interrupts are placed, ready for
 * processing outside the interrupt process by main code. 
 * 
 * Note that interrupts are queued, that is, they are placed in the queue on a last in last out
 * basis and, conversely, they are taken out of the queue on a first in, first out basis (FIFO).  
 * That is, on the basis of the order in which they have been triggered.
 * 
 * The aproach/method assumes nothing about the overlaying end user appliaction,
 * and how each interrupt is procssed is entirely down to the needs of the solution sought. 
 * 
 * The framework has been initially written for the MEGA 2560 board, which allows 
 * up to 6 separate external interrupts, these being on digital pins 2, 3, 18, 19, 20 and 21.
 * 
 * The framework is flexible in through editing of the interrupt control data defined in the 
 * array/table 'interrupt_config_data', such that:
 * 1. the number of interrupts enabled (made 'active'/'inactive') may be adjusted from 1 up 
 *    to 6 (the maximum external interrupts permissible on the MEGA 2560).
 *    
 * 2. For each digital input interrupt pin, it is possible to define a number of parameters that steer
 *    how the interrupt will be dealt with and also how it is associated with the priority defined
 *    by the microcontroller specifications.
 *    
 *    This flexibility is achieved through the use of a number of configurable parameters which can  
 *    be defined within the 'C00_Configurations' tab (see below).
 *    
 * Configurable parameters:
 * A. Assignimg parameters to each digital interrupt:
 *    In the 'C00_Configurations' tab, the data declaration 'interrupt_config_data' contains all of the 
 *    variable parameters that define how each external interrupt should be handled/processed.  The attributes
 *    define by this declaration are as follows:
 *
 *    'interrupt_config_data' Array/table row values (by column):
 *    For each row (external interrupt), decide how each external interrupt is to be configured:
 *      active/inactive flag        - set the value to an 'active' or inactive state.  
 *                                    If 'inactive' no interrupts will be processed for this externl interrupt.
 *      digial pin number           - set the digital pin number associated with this external interrupt.                              
 *                                    Refer to the board documentation if using a micrcontroller otherthan the MEGA 2560.
 *      pinMode mode value          - set the required pinMode mode value, INPUT, INPUT_PULLUP, etc for the defined                               
 *                                    interrupt pin.
 *      trigger type                - set the interrupt trigger type for this interrupt, eg Falling, Change, Rising, etc.             
 *      unique/nonunique processing - set to 'true' if unique processing required, 'false' otherwise'.
 *                                    If set to 'true' then only one interrupt of the same number will be held in the 
 *                                    interrupt queue at a time.  Otherwise, the interrupt queue may contain any number of
 *                                    interrupts, up to the defined size of the interrupt queue free chain.
 *      debounce value              - this is the time that interrupt handler for this external interrupt will wait between 
 *                                    interrupts before deciding that the interrupt can be processed as a new interrupt event.                                      
 *                                    The value is in milliseconds.
 *      interrupt number            - this parameter defines the externa interrupt number assigned by the microcontroller                                   
 *                                    to the associated digital interrupt pin.
 *    
 * B. Changing the size of the inetrrupt queue (IQ):
 *    In the 'C00_Configurations' tab: 
 *    a.  If configuring for all unique interrupt processing set the defined value 'max_IQ_free_chain_blocks'
 *        to be the same value as the number of active digital interrupts, ie 'max_digital_inputs'.
 *    b). If configuring for nonunique interrupt prcessing then consider the following:
 *        i)  The defined value 'max_IQ_free_chain_blocks' needs to be set large enough to handle the
 *            maximum number of interrupts likley to be in the queue at any one time (before it is processed
 *            by 'scan_IQ()'). Otherwise, there may be no space for triggered inetrrupts in the queue.
 *        ii) The interrupt handlers do have debounce coding to deal with spurious interrupts, but do 
 *            ensure that the preset value defined by 'interrupt_debounce' is suitable to ensure that 
 *            genuine interrupts are not missed.  The number given by the define value is in mlliseconds (msecs).
 *            
 * C. Diagnostics are defined in the tab 'D00 Diags'.  Use these routines or add your own.
 *    Note that: 
 *    1.  diagnostics may be turned on/off at compile time by setting the boolean variable
 *        'diags_on' (in the 'D00 Diags' tab) to 'true' for diagnostics and 'false' for no diagnostics.
 *    2.  if using the print queue routines provided than these will disable interrupts whilst they are printed
 *        and re-enable then afterwards.  This ensures no conflict if interrrupts are received during the print cycle.
 *        
 *    
 * 
 */

TAB Configuration - C00_Configurations.ino

C/C++
Configuration segment
// Copyright (c) Ron D Bentley (UK), see copyright notice
//
//  This tab contains the parameters available to configure the interrupt handlers and associated
//  supporting structures.

bool  diags_on = true;  //  set to 'true' for diagnostics, otherwise 'false' for none

#define active    1
#define inactive  -1

//  **** Interrupt configurable varables/values ****
#define max_digital_inputs  6   //  Max digital interrupts pins available on Mega 2650 board.
                                //  The number of pins to be interrupt monitored.
                                //  DO NOT  vary this number, simply change active flag and associated
                                //  interrupt data, even if using with a different board.

// Note that the array/table 'interrupt_config_data' is sized for the maximum number of digital
// interrupt pins for the Mega 2560.  If selecting fewer interrupt pins, simple deactivate the
// lines not required by setting column 0 value t0 'inactive'

volatile int interrupt_config_data[max_digital_inputs][7]=
{
active,     21,         INPUT,      RISING,     false,           15,           0, // generic interrupt handler 0 entries
active,     20,         INPUT,      RISING,     false,           15,           1, // generic interrupt handler 1 entries
active,     19,         INPUT,      RISING,     false,           15,           2, // generic interrupt handler 2 entries
active,     18,         INPUT,      RISING,     false,           15,           3, // generic interrupt handler 3 entries
active,      2,         INPUT,      RISING,     false,           15,           4, // generic interrupt handler 4 entries
active,      3,         INPUT,      RISING,     false,           15,           5};// generic interrupt handler 5 entries
/*  ^        ^             ^           ^           ^              ^            ^
    |        |             |           |           |              |            |
 active    digital      pinMode       interrupt  unique/      interrupt     interrupt number
inactive  interrupt    mode value      trigger  nonunique     debounce        this pin is 
 flag      pin no.                      type      flag      value (msecs)      linked to

*/
//  ****Interrupt Queue configurable values ****
#define max_IQ_free_chain_blocks 32       //  This defines the number of free blocks in the chain.
                                          //  At least the maximum number of interrupts being monitored,
                                          //  if all interrupts are defined as unique.
                                          //  Must be > than this value,  if nonunique interrupts are being allowed

TAB Queue Handlers - E00_Queue_Handlers.ino

C/C++
Queue Handler segment
// Copyright (c) Ron D Bentley (UK), see copyright notice
//
//  This tab contains all of the routines to establish and maintain interrupt
//  queue management in a queued framework.
//
//  A free chain of blocks is set up that are then allocated to interrupt handlers
//  to record that they have been triggered. The interrupt trigger digital pin will
//  be recorded in the allocated queue block.
//
//  'IQ' is used as the blocks of memory to be allocated/deallocated on request.
//  'IQ' comprises a number of free blocks each of 2 integers, the first word of each block
//  contains a forward pointer to next block or end of chain value(free/used), the second word
//  should be treated as undefined for free blocks.
//  When a free block is allocated to an interrupt, it is inserted at the beginning of the
//  interrupt queue with the first word of the block being a forward pointer to next interrupt 
//  block and the second word containing the digital pin number of the triggered inerrupt. 
//
//  Note:
//      1.  The number of free blocks in the IQ free chain is defined by 'max_IQ_free__chain_blocks'.
//          this should be tailored to the number of interrupts being monitored, as...
//      2.  the implementation is as a queue, first in first out (FIFO).
//      3.  Interrupts in the IQ can be unique or nonunique (multiple).  This is controlled via the
//          defined boolean variable 'unique_interrupts' in configuration data (C00_Configurations tab)-  
//            ~ set to 'true' for unique entries or
//            ~ set to 'false' for nonunique entries in the IQ. This is set
//          

int unique_entry_flags=0;   //  used to determine if given interrupt has 'unique' flag set in IQ

volatile int IQ[max_IQ_free_chain_blocks][2]; 

//  Pointers to manage the free blocks in the IQ
volatile int start_of_free_IQ_chain; 
volatile int num_free_IQ_blocks;

//  Pointers to manage the allocated blocks in the IQ
volatile int start_of_IQ_chain =  -1;
volatile int end_of_IQ_chain =    -1;
volatile int num_IQ_interrupts =   0;

//  general values to mnage IQ processes
#define end_of_chain_value    -1

#define fail                  -1
#define success                1

#define no_interrupt_request  -1
#define no_entry              -1

int  int_number;    //  used by scan_IQ to set up the parametsr for the triggered interrupt if they are needed
int  int_pin;       //  ditto
int  int_pinmode;   //  ditto
int  int_trigger;   //  ditto
int  int_unique;    //  ditto
int  int_debounce;  //  ditto
//
//  Set up the free IQ chain as defined by 'max_IQ_free__chain_blocks'
//  This is called just once per start/reset from te setup() process.
//
void  create_IQ_free_chain()
{int  ptr, last_IQ_block;
  last_IQ_block=max_IQ_free_chain_blocks-1;
  for (ptr=0; ptr < max_IQ_free_chain_blocks;ptr++)
  {
    if (ptr == last_IQ_block){IQ[ptr][0]=end_of_chain_value;} // set end of chain
    else                     {IQ[ptr][0]=ptr+1;}              // set forward block pointer
    IQ[ptr][1] = -1;  // clear data word out of range for interrupt numbers
  }
  num_free_IQ_blocks = max_IQ_free_chain_blocks;
  start_of_free_IQ_chain=0;
}
//
//  Allocates a block from the free chain, if one exists.
//  NOTE: assumes that interrupts are disabled by calling function(s).
//
int acquire_block_from_free_IQ()
{int  block;
  if (num_free_IQ_blocks > 0)
  { //  There is at least 1 free block left.
    //  Take the next block off the free chain.
    num_free_IQ_blocks--;  //  reduce free blocks available.
    block = start_of_free_IQ_chain;
    start_of_free_IQ_chain = IQ[block][0];  //  take the first free block off the free chain.
    IQ[block][0] = end_of_chain_value;      //  set the forward pointer in this free block to out of range.
    IQ[block][1] = -1;    //  ensure the value held in the new block data word is initialised to other than an interrupt value.
    return block;         //  return with the 'address' of the free block provided to calling routine.
  }
  return  fail;           //  no free blocks! Return error condition.
}
//
// Returns the given block back to the free chain.
// NOTE: assumes that interrupts are disabled by calling function(s).
//
int relinquish_block_to_free_IQ(int block)
{
  if (num_free_IQ_blocks < max_IQ_free_chain_blocks) 
  { //  there is space to add this block back to the free chain.
    num_free_IQ_blocks++;  //  increase number of free blocks on free chain by 1
    IQ[block][0] = start_of_free_IQ_chain;
    IQ[block][1] = -1;    //  clear data word
    start_of_free_IQ_chain = block;
    return  success;      //  relinquish was successful.
  }
  return  fail;           //  free chain seems to be full of free blocks! No space to add another.
}
//
//  The function creates an entry in the interrupt queue for the given interrupt.
//
int insert_into_interrupt_IQ(int interrupt)
{ int  block;
  block = acquire_block_from_free_IQ();
  if (block == fail)
  { //  no free block available!
    return fail; 
  }
  //  we have a free block, so chain it into the interrupt queue
  //  placing it at the end of the current queue.
  if (num_IQ_interrupts == 0)
  { // queue is empty, so set start pointer
    start_of_IQ_chain = block;
  }
  else
  { // at least on entry in IQ queue, so modify forward pointer of last block
    IQ[end_of_IQ_chain][0] = block;
  }
  //  now deal with rest of updates
  num_IQ_interrupts++; 
  end_of_IQ_chain = block;
  IQ[end_of_IQ_chain][0] = end_of_chain_value;  // set new end of chain
  IQ[end_of_IQ_chain][1] = interrupt;
  return success;
}

//
//  See if there are any outstanding (unprocessed) ineterrupts requests in the 
//  interrupt queue.  If so, take the first one on the queue and return it to the
//  free chain.  The answer to the function is either the interrupt that was triggered
//  or that no outstanding request exists.
//
//  Note that this function is not entered via an interrupt routine, so we do need to 
//  set noInterrupts and reset them after processing to ensure exclusivity.
//
int scan_IQ()
{ int IQ_block, interrupt;
  noInterrupts();   //  ensure exclusive access of the IQ and its pointers
  if (num_IQ_interrupts == 0)
  {
    //  Clear down the interrupt parameters for this interrupt, 
    //  solely for end user reference if required
    int_number  = no_interrupt_request;
    int_pin     = no_interrupt_request;
    int_pinmode = no_interrupt_request;
    int_trigger = no_interrupt_request;
    int_unique  = no_interrupt_request;
    int_debounce= no_interrupt_request;
    interrupts(); 
    return no_interrupt_request;
  }
  // take the first entry off the IQ as this is the next interrupt to be processed.
  num_IQ_interrupts--;
  IQ_block = start_of_IQ_chain;
  start_of_IQ_chain = IQ[IQ_block][0];    //  point to next interrupt to be processed, or end of chain
  if (num_IQ_interrupts == 0)
  {// last used block to be relinquished, so adjust end of chain pointer
    end_of_IQ_chain = end_of_chain_value;
  }
  interrupt = IQ[IQ_block][1];            //  the interrupt that was activated
  relinquish_block_to_free_IQ(IQ_block);  //  put this block back on the free IQ chain
  //
  //  set up the interrupt parameters for this interrupt, 
  //  solely for end user reference if required
  int_number  = interrupt_config_data[interrupt][6];
  int_pin     = interrupt_config_data[interrupt][1];
  int_pinmode = interrupt_config_data[interrupt][2];
  int_trigger = interrupt_config_data[interrupt][3];
  int_unique  = interrupt_config_data[interrupt][4];
  int_debounce= interrupt_config_data[interrupt][5];
  //  check if this is a 'unique' interrupt type. 
  //  If so, clear 'unique_entry_flags' bit for this generic interrupt number
  if (int_unique) 
  {
    bitWrite(unique_entry_flags, interrupt,false); // clear the unique interrupt in IQ flag
  }
  interrupts();
  return interrupt; // return with the internal value of the interrupt handler that was assigned to this pin
}

TAB Interrupt Handlers - E10_Interrupt_Handlers.ino

C/C++
Interrupt Handlers segment
// Copyright (c) Ron D Bentley (UK), see copyright notice
//
//  This tab provides the basis for developing applications that require
//  multiple interrupts, occurring from inputs to digital pins.
//  It is a shell to be used with whatever applications are required to have
//  a need for multiple interrupt driven inputs.
//
//  This code is for the Mega 2650 board and the digital pins are set up to trigger when a pin is RISING.
//  Check if using other boards, as this arrangement of pins is not appropriate for some.
//
//  Interrupt handler routines/functions, one for each possible interrupt
//  up to the max defined (at most 6 on the Mega 2560 board)
//

volatile int interrupt_debounce_value; 
volatile int unique_interrupts;

volatile bool initialisation_complete  = false;  // used by interrupt handlers to stop them processing too soon

volatile long unsigned int millis_now, millis_elapsed;

#define millis_max  0xffffffff  //  max value the millis function will achieve before cycling back to 0

//
//  Each interrupt handler will call this function (when triggered) passing it the its associated interrupt 
//  number.  If possible, an entry will be inserted into the IQ.
//
int process_interrupt(int interrupt)
{ int ptr, result;
  /*if (interrupt_config_data[interrupt][0]==inactive)
  { return fail;} // ensures a spurious interrupt will not be processed, only active ones 
  unique_interrupts = interrupt_config_data[interrupt][4]; */
  if (interrupt_config_data[interrupt][4])  //  'unique' marker set up for this generic interrupt type
  { //  check if there is already an interrupt entry in the IQ for this interrupt pin.
    if (bitRead(unique_entry_flags, interrupt)) 
    { // value is set to true, so must already in the IQ.
      return fail;
    }
    //  not already in the IQ, so set flag and insert entry
    result = insert_into_interrupt_IQ(interrupt);
    if (result == success)
    {
      bitWrite(unique_entry_flags, interrupt, true);  // set the flag for this generic interrupt to true
    } 
    return result;  // either success or fail.
  }
  // this is a nonunique interrupt request
  return insert_into_interrupt_IQ(interrupt); // result will be either success or fail
}

void generic_handler0() 
{static long unsigned int millis_previous;
  if (initialisation_complete == true) 
  { //  all variables are initialised so we are okay to continue to process this interrupt.
    //  deal with potential 'bouncing' of this interrupt.  The code will 'debounce' spurious
    //  interrupts.
    millis_now = millis();
    if (millis_now < millis_previous)
    { // millis timer has cycled since last tested, so adjust
        millis_elapsed = millis_max - millis_previous + millis_now + 1;
    }
    else
    { // millis_now is in advance of previous millis 
      millis_elapsed = millis_now - millis_previous + 1;
    }
    interrupt_debounce_value = interrupt_config_data[0][5];
    if (millis_elapsed >= interrupt_debounce_value)
    { //  treat this interrupt as a new one and not associated with a 'bounce' 
      process_interrupt(0); 
    }
    millis_previous = millis_now;    
  }
}

void generic_handler1() 
{static long unsigned int millis_previous;
  if (initialisation_complete == true) 
  { //  all variables are initialised so we are okay to continue to process this interrupt.
    //  deal with potential 'bouncing' of this interrupt.  The code will 'debounce' spurious
    //  interrupts.
    millis_now = millis();
    if (millis_now < millis_previous)
    { // millis timer has cycled since last tested, so adjust
        millis_elapsed = millis_max - millis_previous + millis_now + 1;
    }
    else
    { // millis_now is in advance of previous millis 
      millis_elapsed = millis_now - millis_previous + 1;
    }
    interrupt_debounce_value = interrupt_config_data[1][5];
    if (millis_elapsed >= interrupt_debounce_value)
    { //  treat this interrupt as a new one and not associated with a 'bounce'
      process_interrupt(1); 
    }
    millis_previous = millis_now;
  }
}
 
void generic_handler2() 
{static long unsigned int millis_previous;
  if (initialisation_complete == true) 
  { //  all variables are initialised so we are okay to continue to process this interrupt.
    //  deal with potential 'bouncing' of this interrupt.  The code will 'debounce' spurious
    //  interrupts.
    millis_now = millis();
    if (millis_now < millis_previous)
    { // millis timer has cycled since last tested, so adjust
        millis_elapsed = millis_max - millis_previous + millis_now + 1;
    }
    else
    { // millis_now is in advance of previous millis 
      millis_elapsed = millis_now - millis_previous + 1;
    }
    interrupt_debounce_value = interrupt_config_data[2][5]; 
    if (millis_elapsed >= interrupt_debounce_value)
    { //  treat this interrupt as a new one and not associated with a 'bounce' 
      process_interrupt(2);
    }
    millis_previous = millis_now;   
  }
}

void generic_handler3() 
{static long unsigned int millis_previous;
  if (initialisation_complete == true) 
  { //  all variables are initialised so we are okay to continue to process this interrupt.
    //  deal with potential 'bouncing' of this interrupt.  The code will 'debounce' spurious
    //  interrupts.
    millis_now = millis();
    if (millis_now < millis_previous)
    { // millis timer has cycled since last tested, so adjust
        millis_elapsed = millis_max - millis_previous + millis_now + 1;
    }
    else
    { // millis_now is in advance of previous millis 
      millis_elapsed = millis_now - millis_previous + 1;
    }
    interrupt_debounce_value = interrupt_config_data[3][5];    
    if (millis_elapsed >= interrupt_debounce_value)
    { //  treat this interrupt as a new one and not associated with a 'bounce' 
      process_interrupt(3); 
    }
    millis_previous = millis_now;
  }
}

void generic_handler4()
{static long unsigned int millis_previous;
  if (initialisation_complete == true) 
  { //  all variables are initialised so we are okay to continue to process this interrupt.
    //  deal with potential 'bouncing' of this interrupt.  The code will 'debounce' spurious
    //  interrupts.
    millis_now = millis();
    if (millis_now < millis_previous)
    { // millis timer has cycled since last tested, so adjust
        millis_elapsed = millis_max - millis_previous + millis_now + 1;
    }
    else
    { // millis_now is in advance of previous millis 
      millis_elapsed = millis_now - millis_previous + 1;
    }
    interrupt_debounce_value = interrupt_config_data[4][5];    
    if (millis_elapsed >= interrupt_debounce_value)
    { //  treat this interrupt as a new one and not associated with a 'bounce' 
      process_interrupt(4); 
    }
    millis_previous = millis_now;
  }
}

void generic_handler5() 
{static long unsigned int millis_previous;
  if (initialisation_complete == true) 
  { //  all variables are initialised so we are okay to continue to process this interrupt.
    //  deal with potential 'bouncing' of this interrupt.  The code will 'debounce' spurious
    //  interrupts.
    millis_now = millis();
    if (millis_now < millis_previous)
    { // millis timer has cycled since last tested, so adjust
      millis_elapsed = millis_max - millis_previous + millis_now + 1;
    }
    else
    { // millis_now is in advance of previous millis 
      millis_elapsed = millis_now - millis_previous + 1;
    } 
    interrupt_debounce_value = interrupt_config_data[5][5];
    if (millis_elapsed >= interrupt_debounce_value)
    { //  treat this interrupt as a new one and not associated with a 'bounce' 
      process_interrupt(5);
    }
    millis_previous = millis_now;
  }
}

volatile  (*interrupt_handler_addresses[max_digital_inputs])()=
{
generic_handler0,
generic_handler1,
generic_handler2,
generic_handler3,
generic_handler4,
generic_handler5
};

TAB Diagnostics - E90_Diags.ino

C/C++
Diagnostic segment
// Copyright (c) Ron D Bentley (UK), see copyright notice
//
//  Insert any diagnostic routines in this tab, and use 'diags_on' to switch
//  them on/off (true/false)
//  Uses 'initialisation_complete' flag to ensure external interrupts will not
//  corrupt the printing process. 
//

void print_IQ()
{int ptr, count;
  if (diags_on)
  {
    noInterrupts();
    initialisation_complete = false;    // block interrupt processing
    interrupts(); 
    count = num_IQ_interrupts;
    ptr = start_of_IQ_chain;
    Serial.println("_______________________________");
    Serial.println("       INTERRUPT QUEUE");   
    Serial.print("          Num in IQ = ");
    Serial.println(count);
    Serial.print(" start of IQ chain = ");
    Serial.println(ptr);
    Serial.print("   end of IQ chain = ");
    Serial.println(end_of_IQ_chain);
    if (count > 0)
    {
    do
    {
      Serial.print("IQ[");
      Serial.print(ptr);
      Serial.print("][0] = ");
      Serial.print(IQ[ptr][0]);
      Serial.print(char(9));
      Serial.print("IQ[");
      Serial.print(ptr);
      Serial.print("][1] = ");
      Serial.println(IQ[ptr][1]);
      ptr = IQ[ptr][0]; //  look at next entry/block
      count --;
    }
    while ( (ptr != end_of_chain_value) && (count > 0));
    }
    Serial.println("");
    Serial.println("_______________________________");
    Serial.println("");
    Serial.flush();
    noInterrupts();
    initialisation_complete = true;   // can now allow interrupts to be processed
    interrupts();
  }
}

void print_free_chain()
{int ptr, count;
if (diags_on)
  {
    noInterrupts();
    initialisation_complete = false;   // block interrupt processing
    interrupts();
    count = num_free_IQ_blocks;
    ptr = start_of_free_IQ_chain;
    Serial.println("");
    Serial.println("_______________________________");
    Serial.println("         FREE CHAIN"); 
    Serial.print("  Num in free chain = ");
    Serial.println(count);
    Serial.print("start of free chain = ");
    Serial.println(ptr);
    if (count > 0)
    {
    do
    {
      Serial.print("IQ[");
      Serial.print(ptr);
      Serial.print("][0] = ");
      Serial.print(IQ[ptr][0]);
      Serial.print(char(9));
      Serial.print("IQ[");
      Serial.print(ptr);
      Serial.print("][1] = ");
      Serial.println(IQ[ptr][1]);
      ptr = IQ[ptr][0]; //  look at next entry/block
      count --;
    }
    while ( (ptr != end_of_chain_value) && (count > 0));
    }
    Serial.println("_______________________________");
    Serial.println("");
    Serial.flush();
    noInterrupts();
    initialisation_complete = true;   // can now allow interrupts to be processed
    interrupts();
  }
}

TAB Setup() - G00_Setup.ino

C/C++
Setup() segment
// Copyright (c) Ron D Bentley (UK), see copyright notice
//
void setup() {
  int input, interrupt_pin, pinmode_type, trigger_type;
  
  initialisation_complete = false;  //  ensures no interrupts are processed before ready

  //  set up the free chain for interrupt queue handling
  start_of_IQ_chain       =  -1;    //  start with no interrupt blocks in IQ chain
  end_of_IQ_chain         =  -1;    //  start with no interrupt blocks in IQ chain
  num_IQ_interrupts       =   0;    //  start with no interrupt blocks in IQ chain
  create_IQ_free_chain();           //  set up the free chain of IQ blocks

  if (diags_on)
  {
    Serial.begin(115200);
  }

  //  establish the interrupt handler routines to the defined input pins
  //  and associated parameter values.

  for (input=0; input < max_digital_inputs; input++)
  {
    if (interrupt_config_data[input][0] == active)
    { // This interrupt and associated pin is active, so set up structures for it
      interrupt_pin     =interrupt_config_data[input][1];
      pinmode_type      =interrupt_config_data[input][2];
      trigger_type      =interrupt_config_data[input][3];
      pinMode(interrupt_pin,pinmode_type);
      attachInterrupt(digitalPinToInterrupt(interrupt_pin),
                      interrupt_handler_addresses[input],
                      trigger_type);
    }
  }
  //
  //  Place any other set up code here, but BEFORE 'initialisation_complete = true'
  //

  //
  //  End of user inserted set up code
  //
  initialisation_complete = true;   // can now allow interrupts to be processed
}

TAB Main Loop - H00_Main_Segment.ino

C/C++
Main Loop segment
// Copyright (c) Ron D Bentley (UK), see copyright notice
//
void loop() 
{int  interrupt;
/*  The following section of code will allow interrupts to be processed at each loop cycle
    Add whatever other code for the specific application required to process the interrupts received plus
    any other requirements of a noninterrupt nature.

    Design your code structure as necessary, but note that to obtain interrupts
    from the interrupt queue it is necessary to call the routine 'scanIQ()'.  This will examine
    the interrupt queue and if an unprocessed interrupts exists the result of the call 
    returns the generic interrupt and and associated interrupt dat that triggered the interrupt,
    if available.
    
    For example, 'interrupt=scanIQ();'.
    Results of this call will be:
    1.  an interrupt has been obtained from the IQ.  In which case the following varables 
        will be set up: 
          int_number    - actual real world interrupt number
          int_pin       - actual digital pin number triggering the interrupt
          int_pinmode   - pinMode value used to set up this digital interrupt pin
          int_trigger   - interrupt trigger value used to set up this digital interrupt
          int_unique    - flag that defines if interrupt processing is unique or nonunique
          int_debounce  - debounce value in msecs for this interrupt
    2.  if no interrupt is returned (none in the IQ) then all of the above variables will be set
        to 'no_interrupt_request'.
*/
do { // Keep processing interrupts whilst there are interrupts in the queue..
   interrupt = scan_IQ();  //  get the next interrupt in IQ if there is one.
   if (interrupt != no_interrupt_request)
   {/*  
    Process this interrupt request. 'interrupt' defines the 
    generic interrupt number that triggered.  Other variables 
    (see above) give all other interrupt atrributes if required 
    Insert whatever code appropriate here, if any, 
    when not processing an interrupt request  */
    switch (int_number)
    {
      case 0: // external interrupt 0
        // place your code for this interrupt number here...
        
        break;
      case 1: // external interrupt 1
        // place your code for this interrupt number here...
        
        break;
      case 2: // external interrupt 2
        // place your code for this interrupt number here...
        
        break;
      case 3: // external interrupt 3
        // place your code for this interrupt number here...
        
        break;
      case 4: // external interrupt 4
        // place your code for this interrupt number here...
        
        break;
      case 5: // external interrupt 5
        // place your code for this interrupt number here...
        
        break;
      default:
        break;
    }
   }
  }
  while (interrupt != no_interrupt_request);
  /*  
  No interrupts left in the queue, so do other things....
  Insert whatever code appropriate here, if any, 
  when not processing an interrupt request. */



}

TAB Testing - T00_Testing.ino

C/C++
Testing and Test Plans segment
// Copyright (c) Ron D Bentley (UK), see copyright notice
//
/* 
  * Version 2.03 Testing Strategy:
  * 1.  To test the correct setting and clearing of generic interrupt flags were these are configured 
  *     as for 'unique' entries in the IQ.
  * 2.  To rerun the soak performnce testing run under version 2.02 and to record tiings for 
  *     a.  averge IQ inserts
  *     b.  averge IQ extracts.
  *     
  * The results obtained showed that: 
  *   i.    the average time to insert an interrupt into the interrupt queue is 15 microseconds,
  *   ii.   the average time to remove an interrupt from the interrupt queue is 12 microseconds.
  *   
  * Observation - whilst the changes did not make any signifant performance improvement, it did result
  * in maginally less code generation.
*/

Arduino External Interrupt Processing

See the User Guide for full background and instructions in its use

Credits

ronbentley1

ronbentley1

25 projects • 13 followers

Comments