Components and supplies
Arduino Mega 2560
Project description
Code
TAB Main Loop - H00_Main_Segment.ino
c_cpp
Main Loop segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3void loop() 4{int interrupt; 5/* The following section of code will allow interrupts to be processed at each loop cycle 6 Add whatever other code for the specific application required to process the interrupts received plus 7 any other requirements of a noninterrupt nature. 8 9 Design your code structure as necessary, but note that to obtain interrupts 10 from the interrupt queue it is necessary to call the routine 'scanIQ()'. This will examine 11 the interrupt queue and if an unprocessed interrupts exists the result of the call 12 returns the generic interrupt and and associated interrupt dat that triggered the interrupt, 13 if available. 14 15 For example, 'interrupt=scanIQ();'. 16 Results of this call will be: 17 1. an interrupt has been obtained from the IQ. In which case the following varables 18 will be set up: 19 int_number - actual real world interrupt number 20 int_pin - actual digital pin number triggering the interrupt 21 int_pinmode - pinMode value used to set up this digital interrupt pin 22 int_trigger - interrupt trigger value used to set up this digital interrupt 23 int_unique - flag that defines if interrupt processing is unique or nonunique 24 int_debounce - debounce value in msecs for this interrupt 25 2. if no interrupt is returned (none in the IQ) then all of the above variables will be set 26 to 'no_interrupt_request'. 27*/ 28do { // Keep processing interrupts whilst there are interrupts in the queue.. 29 interrupt = scan_IQ(); // get the next interrupt in IQ if there is one. 30 if (interrupt != no_interrupt_request) 31 {/* 32 Process this interrupt request. 'interrupt' defines the 33 generic interrupt number that triggered. Other variables 34 (see above) give all other interrupt atrributes if required 35 Insert whatever code appropriate here, if any, 36 when not processing an interrupt request */ 37 switch (int_number) 38 { 39 case 0: // external interrupt 0 40 // place your code for this interrupt number here... 41 42 break; 43 case 1: // external interrupt 1 44 // place your code for this interrupt number here... 45 46 break; 47 case 2: // external interrupt 2 48 // place your code for this interrupt number here... 49 50 break; 51 case 3: // external interrupt 3 52 // place your code for this interrupt number here... 53 54 break; 55 case 4: // external interrupt 4 56 // place your code for this interrupt number here... 57 58 break; 59 case 5: // external interrupt 5 60 // place your code for this interrupt number here... 61 62 break; 63 default: 64 break; 65 } 66 } 67 } 68 while (interrupt != no_interrupt_request); 69 /* 70 No interrupts left in the queue, so do other things.... 71 Insert whatever code appropriate here, if any, 72 when not processing an interrupt request. */ 73 74 75 76} 77
TAB Diagnostics - E90_Diags.ino
c_cpp
Diagnostic segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// Insert any diagnostic routines in this tab, and use 'diags_on' to switch 4// them on/off (true/false) 5// Uses 'initialisation_complete' flag to ensure external interrupts will not 6// corrupt the printing process. 7// 8 9void print_IQ() 10{int ptr, count; 11 if (diags_on) 12 { 13 noInterrupts(); 14 initialisation_complete = false; // block interrupt processing 15 interrupts(); 16 count = num_IQ_interrupts; 17 ptr = start_of_IQ_chain; 18 Serial.println("_______________________________"); 19 Serial.println(" INTERRUPT QUEUE"); 20 Serial.print(" Num in IQ = "); 21 Serial.println(count); 22 Serial.print(" start of IQ chain = "); 23 Serial.println(ptr); 24 Serial.print(" end of IQ chain = "); 25 Serial.println(end_of_IQ_chain); 26 if (count > 0) 27 { 28 do 29 { 30 Serial.print("IQ["); 31 Serial.print(ptr); 32 Serial.print("][0] = "); 33 Serial.print(IQ[ptr][0]); 34 Serial.print(char(9)); 35 Serial.print("IQ["); 36 Serial.print(ptr); 37 Serial.print("][1] = "); 38 Serial.println(IQ[ptr][1]); 39 ptr = IQ[ptr][0]; // look at next entry/block 40 count --; 41 } 42 while ( (ptr != end_of_chain_value) && (count > 0)); 43 } 44 Serial.println(""); 45 Serial.println("_______________________________"); 46 Serial.println(""); 47 Serial.flush(); 48 noInterrupts(); 49 initialisation_complete = true; // can now allow interrupts to be processed 50 interrupts(); 51 } 52} 53 54void print_free_chain() 55{int ptr, count; 56if (diags_on) 57 { 58 noInterrupts(); 59 initialisation_complete = false; // block interrupt processing 60 interrupts(); 61 count = num_free_IQ_blocks; 62 ptr = start_of_free_IQ_chain; 63 Serial.println(""); 64 Serial.println("_______________________________"); 65 Serial.println(" FREE CHAIN"); 66 Serial.print(" Num in free chain = "); 67 Serial.println(count); 68 Serial.print("start of free chain = "); 69 Serial.println(ptr); 70 if (count > 0) 71 { 72 do 73 { 74 Serial.print("IQ["); 75 Serial.print(ptr); 76 Serial.print("][0] = "); 77 Serial.print(IQ[ptr][0]); 78 Serial.print(char(9)); 79 Serial.print("IQ["); 80 Serial.print(ptr); 81 Serial.print("][1] = "); 82 Serial.println(IQ[ptr][1]); 83 ptr = IQ[ptr][0]; // look at next entry/block 84 count --; 85 } 86 while ( (ptr != end_of_chain_value) && (count > 0)); 87 } 88 Serial.println("_______________________________"); 89 Serial.println(""); 90 Serial.flush(); 91 noInterrupts(); 92 initialisation_complete = true; // can now allow interrupts to be processed 93 interrupts(); 94 } 95} 96
TAB Queue Handlers - E00_Queue_Handlers.ino
c_cpp
Queue Handler segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// This tab contains all of the routines to establish and maintain interrupt 4// queue management in a queued framework. 5// 6// A free chain of blocks is set up that are then allocated to interrupt handlers 7// to record that they have been triggered. The interrupt trigger digital pin will 8// be recorded in the allocated queue block. 9// 10// 'IQ' is used as the blocks of memory to be allocated/deallocated on request. 11// 'IQ' comprises a number of free blocks each of 2 integers, the first word of each block 12// contains a forward pointer to next block or end of chain value(free/used), the second word 13// should be treated as undefined for free blocks. 14// When a free block is allocated to an interrupt, it is inserted at the beginning of the 15// interrupt queue with the first word of the block being a forward pointer to next interrupt 16// block and the second word containing the digital pin number of the triggered inerrupt. 17// 18// Note: 19// 1. The number of free blocks in the IQ free chain is defined by 'max_IQ_free__chain_blocks'. 20// this should be tailored to the number of interrupts being monitored, as... 21// 2. the implementation is as a queue, first in first out (FIFO). 22// 3. Interrupts in the IQ can be unique or nonunique (multiple). This is controlled via the 23// defined boolean variable 'unique_interrupts' in configuration data (C00_Configurations tab)- 24// ~ set to 'true' for unique entries or 25// ~ set to 'false' for nonunique entries in the IQ. This is set 26// 27 28int unique_entry_flags=0; // used to determine if given interrupt has 'unique' flag set in IQ 29 30volatile int IQ[max_IQ_free_chain_blocks][2]; 31 32// Pointers to manage the free blocks in the IQ 33volatile int start_of_free_IQ_chain; 34volatile int num_free_IQ_blocks; 35 36// Pointers to manage the allocated blocks in the IQ 37volatile int start_of_IQ_chain = -1; 38volatile int end_of_IQ_chain = -1; 39volatile int num_IQ_interrupts = 0; 40 41// general values to mnage IQ processes 42#define end_of_chain_value -1 43 44#define fail -1 45#define success 1 46 47#define no_interrupt_request -1 48#define no_entry -1 49 50int int_number; // used by scan_IQ to set up the parametsr for the triggered interrupt if they are needed 51int int_pin; // ditto 52int int_pinmode; // ditto 53int int_trigger; // ditto 54int int_unique; // ditto 55int int_debounce; // ditto 56// 57// Set up the free IQ chain as defined by 'max_IQ_free__chain_blocks' 58// This is called just once per start/reset from te setup() process. 59// 60void create_IQ_free_chain() 61{int ptr, last_IQ_block; 62 last_IQ_block=max_IQ_free_chain_blocks-1; 63 for (ptr=0; ptr < max_IQ_free_chain_blocks;ptr++) 64 { 65 if (ptr == last_IQ_block){IQ[ptr][0]=end_of_chain_value;} // set end of chain 66 else {IQ[ptr][0]=ptr+1;} // set forward block pointer 67 IQ[ptr][1] = -1; // clear data word out of range for interrupt numbers 68 } 69 num_free_IQ_blocks = max_IQ_free_chain_blocks; 70 start_of_free_IQ_chain=0; 71} 72// 73// Allocates a block from the free chain, if one exists. 74// NOTE: assumes that interrupts are disabled by calling function(s). 75// 76int acquire_block_from_free_IQ() 77{int block; 78 if (num_free_IQ_blocks > 0) 79 { // There is at least 1 free block left. 80 // Take the next block off the free chain. 81 num_free_IQ_blocks--; // reduce free blocks available. 82 block = start_of_free_IQ_chain; 83 start_of_free_IQ_chain = IQ[block][0]; // take the first free block off the free chain. 84 IQ[block][0] = end_of_chain_value; // set the forward pointer in this free block to out of range. 85 IQ[block][1] = -1; // ensure the value held in the new block data word is initialised to other than an interrupt value. 86 return block; // return with the 'address' of the free block provided to calling routine. 87 } 88 return fail; // no free blocks! Return error condition. 89} 90// 91// Returns the given block back to the free chain. 92// NOTE: assumes that interrupts are disabled by calling function(s). 93// 94int relinquish_block_to_free_IQ(int block) 95{ 96 if (num_free_IQ_blocks < max_IQ_free_chain_blocks) 97 { // there is space to add this block back to the free chain. 98 num_free_IQ_blocks++; // increase number of free blocks on free chain by 1 99 IQ[block][0] = start_of_free_IQ_chain; 100 IQ[block][1] = -1; // clear data word 101 start_of_free_IQ_chain = block; 102 return success; // relinquish was successful. 103 } 104 return fail; // free chain seems to be full of free blocks! No space to add another. 105} 106// 107// The function creates an entry in the interrupt queue for the given interrupt. 108// 109int insert_into_interrupt_IQ(int interrupt) 110{ int block; 111 block = acquire_block_from_free_IQ(); 112 if (block == fail) 113 { // no free block available! 114 return fail; 115 } 116 // we have a free block, so chain it into the interrupt queue 117 // placing it at the end of the current queue. 118 if (num_IQ_interrupts == 0) 119 { // queue is empty, so set start pointer 120 start_of_IQ_chain = block; 121 } 122 else 123 { // at least on entry in IQ queue, so modify forward pointer of last block 124 IQ[end_of_IQ_chain][0] = block; 125 } 126 // now deal with rest of updates 127 num_IQ_interrupts++; 128 end_of_IQ_chain = block; 129 IQ[end_of_IQ_chain][0] = end_of_chain_value; // set new end of chain 130 IQ[end_of_IQ_chain][1] = interrupt; 131 return success; 132} 133 134// 135// See if there are any outstanding (unprocessed) ineterrupts requests in the 136// interrupt queue. If so, take the first one on the queue and return it to the 137// free chain. The answer to the function is either the interrupt that was triggered 138// or that no outstanding request exists. 139// 140// Note that this function is not entered via an interrupt routine, so we do need to 141// set noInterrupts and reset them after processing to ensure exclusivity. 142// 143int scan_IQ() 144{ int IQ_block, interrupt; 145 noInterrupts(); // ensure exclusive access of the IQ and its pointers 146 if (num_IQ_interrupts == 0) 147 { 148 // Clear down the interrupt parameters for this interrupt, 149 // solely for end user reference if required 150 int_number = no_interrupt_request; 151 int_pin = no_interrupt_request; 152 int_pinmode = no_interrupt_request; 153 int_trigger = no_interrupt_request; 154 int_unique = no_interrupt_request; 155 int_debounce= no_interrupt_request; 156 interrupts(); 157 return no_interrupt_request; 158 } 159 // take the first entry off the IQ as this is the next interrupt to be processed. 160 num_IQ_interrupts--; 161 IQ_block = start_of_IQ_chain; 162 start_of_IQ_chain = IQ[IQ_block][0]; // point to next interrupt to be processed, or end of chain 163 if (num_IQ_interrupts == 0) 164 {// last used block to be relinquished, so adjust end of chain pointer 165 end_of_IQ_chain = end_of_chain_value; 166 } 167 interrupt = IQ[IQ_block][1]; // the interrupt that was activated 168 relinquish_block_to_free_IQ(IQ_block); // put this block back on the free IQ chain 169 // 170 // set up the interrupt parameters for this interrupt, 171 // solely for end user reference if required 172 int_number = interrupt_config_data[interrupt][6]; 173 int_pin = interrupt_config_data[interrupt][1]; 174 int_pinmode = interrupt_config_data[interrupt][2]; 175 int_trigger = interrupt_config_data[interrupt][3]; 176 int_unique = interrupt_config_data[interrupt][4]; 177 int_debounce= interrupt_config_data[interrupt][5]; 178 // check if this is a 'unique' interrupt type. 179 // If so, clear 'unique_entry_flags' bit for this generic interrupt number 180 if (int_unique) 181 { 182 bitWrite(unique_entry_flags, interrupt,false); // clear the unique interrupt in IQ flag 183 } 184 interrupts(); 185 return interrupt; // return with the internal value of the interrupt handler that was assigned to this pin 186} 187
TAB Interrupt Handlers - E10_Interrupt_Handlers.ino
c_cpp
Interrupt Handlers segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// This tab provides the basis for developing applications that require 4// multiple interrupts, occurring from inputs to digital pins. 5// It is a shell to be used with whatever applications are required to have 6// a need for multiple interrupt driven inputs. 7// 8// This code is for the Mega 2650 board and the digital pins are set up to trigger when a pin is RISING. 9// Check if using other boards, as this arrangement of pins is not appropriate for some. 10// 11// Interrupt handler routines/functions, one for each possible interrupt 12// up to the max defined (at most 6 on the Mega 2560 board) 13// 14 15volatile int interrupt_debounce_value; 16volatile int unique_interrupts; 17 18volatile bool initialisation_complete = false; // used by interrupt handlers to stop them processing too soon 19 20volatile long unsigned int millis_now, millis_elapsed; 21 22#define millis_max 0xffffffff // max value the millis function will achieve before cycling back to 0 23 24// 25// Each interrupt handler will call this function (when triggered) passing it the its associated interrupt 26// number. If possible, an entry will be inserted into the IQ. 27// 28int process_interrupt(int interrupt) 29{ int ptr, result; 30 /*if (interrupt_config_data[interrupt][0]==inactive) 31 { return fail;} // ensures a spurious interrupt will not be processed, only active ones 32 unique_interrupts = interrupt_config_data[interrupt][4]; */ 33 if (interrupt_config_data[interrupt][4]) // 'unique' marker set up for this generic interrupt type 34 { // check if there is already an interrupt entry in the IQ for this interrupt pin. 35 if (bitRead(unique_entry_flags, interrupt)) 36 { // value is set to true, so must already in the IQ. 37 return fail; 38 } 39 // not already in the IQ, so set flag and insert entry 40 result = insert_into_interrupt_IQ(interrupt); 41 if (result == success) 42 { 43 bitWrite(unique_entry_flags, interrupt, true); // set the flag for this generic interrupt to true 44 } 45 return result; // either success or fail. 46 } 47 // this is a nonunique interrupt request 48 return insert_into_interrupt_IQ(interrupt); // result will be either success or fail 49} 50 51void generic_handler0() 52{static long unsigned int millis_previous; 53 if (initialisation_complete == true) 54 { // all variables are initialised so we are okay to continue to process this interrupt. 55 // deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 56 // interrupts. 57 millis_now = millis(); 58 if (millis_now < millis_previous) 59 { // millis timer has cycled since last tested, so adjust 60 millis_elapsed = millis_max - millis_previous + millis_now + 1; 61 } 62 else 63 { // millis_now is in advance of previous millis 64 millis_elapsed = millis_now - millis_previous + 1; 65 } 66 interrupt_debounce_value = interrupt_config_data[0][5]; 67 if (millis_elapsed >= interrupt_debounce_value) 68 { // treat this interrupt as a new one and not associated with a 'bounce' 69 process_interrupt(0); 70 } 71 millis_previous = millis_now; 72 } 73} 74 75void generic_handler1() 76{static long unsigned int millis_previous; 77 if (initialisation_complete == true) 78 { // all variables are initialised so we are okay to continue to process this interrupt. 79 // deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 80 // interrupts. 81 millis_now = millis(); 82 if (millis_now < millis_previous) 83 { // millis timer has cycled since last tested, so adjust 84 millis_elapsed = millis_max - millis_previous + millis_now + 1; 85 } 86 else 87 { // millis_now is in advance of previous millis 88 millis_elapsed = millis_now - millis_previous + 1; 89 } 90 interrupt_debounce_value = interrupt_config_data[1][5]; 91 if (millis_elapsed >= interrupt_debounce_value) 92 { // treat this interrupt as a new one and not associated with a 'bounce' 93 process_interrupt(1); 94 } 95 millis_previous = millis_now; 96 } 97} 98 99void generic_handler2() 100{static long unsigned int millis_previous; 101 if (initialisation_complete == true) 102 { // all variables are initialised so we are okay to continue to process this interrupt. 103 // deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 104 // interrupts. 105 millis_now = millis(); 106 if (millis_now < millis_previous) 107 { // millis timer has cycled since last tested, so adjust 108 millis_elapsed = millis_max - millis_previous + millis_now + 1; 109 } 110 else 111 { // millis_now is in advance of previous millis 112 millis_elapsed = millis_now - millis_previous + 1; 113 } 114 interrupt_debounce_value = interrupt_config_data[2][5]; 115 if (millis_elapsed >= interrupt_debounce_value) 116 { // treat this interrupt as a new one and not associated with a 'bounce' 117 process_interrupt(2); 118 } 119 millis_previous = millis_now; 120 } 121} 122 123void generic_handler3() 124{static long unsigned int millis_previous; 125 if (initialisation_complete == true) 126 { // all variables are initialised so we are okay to continue to process this interrupt. 127 // deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 128 // interrupts. 129 millis_now = millis(); 130 if (millis_now < millis_previous) 131 { // millis timer has cycled since last tested, so adjust 132 millis_elapsed = millis_max - millis_previous + millis_now + 1; 133 } 134 else 135 { // millis_now is in advance of previous millis 136 millis_elapsed = millis_now - millis_previous + 1; 137 } 138 interrupt_debounce_value = interrupt_config_data[3][5]; 139 if (millis_elapsed >= interrupt_debounce_value) 140 { // treat this interrupt as a new one and not associated with a 'bounce' 141 process_interrupt(3); 142 } 143 millis_previous = millis_now; 144 } 145} 146 147void generic_handler4() 148{static long unsigned int millis_previous; 149 if (initialisation_complete == true) 150 { // all variables are initialised so we are okay to continue to process this interrupt. 151 // deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 152 // interrupts. 153 millis_now = millis(); 154 if (millis_now < millis_previous) 155 { // millis timer has cycled since last tested, so adjust 156 millis_elapsed = millis_max - millis_previous + millis_now + 1; 157 } 158 else 159 { // millis_now is in advance of previous millis 160 millis_elapsed = millis_now - millis_previous + 1; 161 } 162 interrupt_debounce_value = interrupt_config_data[4][5]; 163 if (millis_elapsed >= interrupt_debounce_value) 164 { // treat this interrupt as a new one and not associated with a 'bounce' 165 process_interrupt(4); 166 } 167 millis_previous = millis_now; 168 } 169} 170 171void generic_handler5() 172{static long unsigned int millis_previous; 173 if (initialisation_complete == true) 174 { // all variables are initialised so we are okay to continue to process this interrupt. 175 // deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 176 // interrupts. 177 millis_now = millis(); 178 if (millis_now < millis_previous) 179 { // millis timer has cycled since last tested, so adjust 180 millis_elapsed = millis_max - millis_previous + millis_now + 1; 181 } 182 else 183 { // millis_now is in advance of previous millis 184 millis_elapsed = millis_now - millis_previous + 1; 185 } 186 interrupt_debounce_value = interrupt_config_data[5][5]; 187 if (millis_elapsed >= interrupt_debounce_value) 188 { // treat this interrupt as a new one and not associated with a 'bounce' 189 process_interrupt(5); 190 } 191 millis_previous = millis_now; 192 } 193} 194 195volatile (*interrupt_handler_addresses[max_digital_inputs])()= 196{ 197generic_handler0, 198generic_handler1, 199generic_handler2, 200generic_handler3, 201generic_handler4, 202generic_handler5 203}; 204
Arduino External Interrupt Processing
See the User Guide for full background and instructions in its use
TAB Configuration - C00_Configurations.ino
c_cpp
Configuration segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// This tab contains the parameters available to configure the interrupt handlers and associated 4// supporting structures. 5 6bool diags_on = true; // set to 'true' for diagnostics, otherwise 'false' for none 7 8#define active 1 9#define inactive -1 10 11// **** Interrupt configurable varables/values **** 12#define max_digital_inputs 6 // Max digital interrupts pins available on Mega 2650 board. 13 // The number of pins to be interrupt monitored. 14 // DO NOT vary this number, simply change active flag and associated 15 // interrupt data, even if using with a different board. 16 17// Note that the array/table 'interrupt_config_data' is sized for the maximum number of digital 18// interrupt pins for the Mega 2560. If selecting fewer interrupt pins, simple deactivate the 19// lines not required by setting column 0 value t0 'inactive' 20 21volatile int interrupt_config_data[max_digital_inputs][7]= 22{ 23active, 21, INPUT, RISING, false, 15, 0, // generic interrupt handler 0 entries 24active, 20, INPUT, RISING, false, 15, 1, // generic interrupt handler 1 entries 25active, 19, INPUT, RISING, false, 15, 2, // generic interrupt handler 2 entries 26active, 18, INPUT, RISING, false, 15, 3, // generic interrupt handler 3 entries 27active, 2, INPUT, RISING, false, 15, 4, // generic interrupt handler 4 entries 28active, 3, INPUT, RISING, false, 15, 5};// generic interrupt handler 5 entries 29/* ^ ^ ^ ^ ^ ^ ^ 30 | | | | | | | 31 active digital pinMode interrupt unique/ interrupt interrupt number 32inactive interrupt mode value trigger nonunique debounce this pin is 33 flag pin no. type flag value (msecs) linked to 34 35*/ 36// ****Interrupt Queue configurable values **** 37#define max_IQ_free_chain_blocks 32 // This defines the number of free blocks in the chain. 38 // At least the maximum number of interrupts being monitored, 39 // if all interrupts are defined as unique. 40 // Must be > than this value, if nonunique interrupts are being allowed 41
TAB Testing - T00_Testing.ino
c_cpp
Testing and Test Plans segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3/* 4 * Version 2.03 Testing Strategy: 5 * 1. To test the correct setting and clearing of generic interrupt flags were these are configured 6 * as for 'unique' entries in the IQ. 7 * 2. To rerun the soak performnce testing run under version 2.02 and to record tiings for 8 * a. averge IQ inserts 9 * b. averge IQ extracts. 10 * 11 * The results obtained showed that: 12 * i. the average time to insert an interrupt into the interrupt queue is 15 microseconds, 13 * ii. the average time to remove an interrupt from the interrupt queue is 12 microseconds. 14 * 15 * Observation - whilst the changes did not make any signifant performance improvement, it did result 16 * in maginally less code generation. 17*/ 18
TAB Introduction - A00_Interrupt_Framework_README_v2.03.ino
c_cpp
Introduction segment
1/* (External) Interrupt Queue Framwork, Ron D Bentley (Stafford UK) 2 * ____________________________________________________________________________________________________ 3 * Copyright (c) Ron D Bentley (UK) 4 * The extent this licence shall be limited to Non-profit Use. 5 * Permission is hereby granted, free of charge, for non-profit purposes to any person obtaining 6 * a copy of this software and associated documentation files (the "Software"), to deal in the 7 * Software without restriction, including without limitation the rights to use, copy, modify, merge, 8 * publish, distribute, sublicense copies of the Software, and to permit persons to whom the Software 9 * is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice, acknowledgements and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS LIMITED TO NON-PROFIT USE AND IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR 16 * A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 19 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 * ____________________________________________________________________________________________________ 21 * 22 * Version History _________________________________________________________________________________ 23 * 24 *18 Apr 2020:v2.03.1 Point release - corrected '#define millis_max' in E10_Interrupt Handlers tab, which 25 * was incorrectly set for 64 bit operation. Corect value is 0xffffffff, which 26 * refects 32 bit long unsigned integer architecture. 27 * 4 Mar 2020:v2.03 Efficiency improvements regarding scanning IQ for existing unique interrupts. New 28 * approach no longer scans chain, but uses binary flags, one for each generic interrupt. 29 * These are set/cleared only if a generic interrupt handler is configured for unique 30 * interrupts. Note, the bit set/cleared refers to the number of the generic interrupt 31 * handler and not the actual linked interrupt that triggered on the assigned generic 32 * handler. 33 * 1 Mar 2020:v2.02 Introduced concept of 'generic' interrupt ISRs to delink actual dgital interrupts 34 * pins from a particular ISR. 35 * 27 Feb 2020:v2.01 Further extension to the interrupt channel data structure ('interrupt_config_data') 36 * to provide greater flexiblity per digital interrupt input. 37 * This version now allows each interrupt to be treated individually for 38 * unique/nonunique interrupts plus independent interrupt debounce. 39 * 24 Feb 2020:v2.00 Modified to consolidate the interrupt channel data into a more helpful 40 * structure for end users to change, as required. 41 * 23 Feb 2020:v1.04 Introduction of further flexibility by addition of modifiable variables 42 * to allow pinMode input types and attachInterrupt trigger types. 43 * 22 Feb 2020:v1.03 Modifications to deal with interrupt 'bounce'. 44 * 22 Feb 2020:v1.02 Layout work to program structure, including end user configuration tab 45 * 20 Feb 2020:v1.01 Extensions to allow unique/nonunique interrupt handling. 46 * 13 Feb 2020:v1.00 Initial version, concepts and development. 47 * ___________________________________________________________________________________________________ 48 * 49 * README..._________________________________________________________________________________________ 50 * This is a framework that can be used to manage and process any number of external 51 * interrupts, from 1 up to the maximum number support by the microcontroller. 52 * It works by using a queue into which triggered interrupts are placed, ready for 53 * processing outside the interrupt process by main code. 54 * 55 * Note that interrupts are queued, that is, they are placed in the queue on a last in last out 56 * basis and, conversely, they are taken out of the queue on a first in, first out basis (FIFO). 57 * That is, on the basis of the order in which they have been triggered. 58 * 59 * The aproach/method assumes nothing about the overlaying end user appliaction, 60 * and how each interrupt is procssed is entirely down to the needs of the solution sought. 61 * 62 * The framework has been initially written for the MEGA 2560 board, which allows 63 * up to 6 separate external interrupts, these being on digital pins 2, 3, 18, 19, 20 and 21. 64 * 65 * The framework is flexible in through editing of the interrupt control data defined in the 66 * array/table 'interrupt_config_data', such that: 67 * 1. the number of interrupts enabled (made 'active'/'inactive') may be adjusted from 1 up 68 * to 6 (the maximum external interrupts permissible on the MEGA 2560). 69 * 70 * 2. For each digital input interrupt pin, it is possible to define a number of parameters that steer 71 * how the interrupt will be dealt with and also how it is associated with the priority defined 72 * by the microcontroller specifications. 73 * 74 * This flexibility is achieved through the use of a number of configurable parameters which can 75 * be defined within the 'C00_Configurations' tab (see below). 76 * 77 * Configurable parameters: 78 * A. Assignimg parameters to each digital interrupt: 79 * In the 'C00_Configurations' tab, the data declaration 'interrupt_config_data' contains all of the 80 * variable parameters that define how each external interrupt should be handled/processed. The attributes 81 * define by this declaration are as follows: 82 * 83 * 'interrupt_config_data' Array/table row values (by column): 84 * For each row (external interrupt), decide how each external interrupt is to be configured: 85 * active/inactive flag - set the value to an 'active' or inactive state. 86 * If 'inactive' no interrupts will be processed for this externl interrupt. 87 * digial pin number - set the digital pin number associated with this external interrupt. 88 * Refer to the board documentation if using a micrcontroller otherthan the MEGA 2560. 89 * pinMode mode value - set the required pinMode mode value, INPUT, INPUT_PULLUP, etc for the defined 90 * interrupt pin. 91 * trigger type - set the interrupt trigger type for this interrupt, eg Falling, Change, Rising, etc. 92 * unique/nonunique processing - set to 'true' if unique processing required, 'false' otherwise'. 93 * If set to 'true' then only one interrupt of the same number will be held in the 94 * interrupt queue at a time. Otherwise, the interrupt queue may contain any number of 95 * interrupts, up to the defined size of the interrupt queue free chain. 96 * debounce value - this is the time that interrupt handler for this external interrupt will wait between 97 * interrupts before deciding that the interrupt can be processed as a new interrupt event. 98 * The value is in milliseconds. 99 * interrupt number - this parameter defines the externa interrupt number assigned by the microcontroller 100 * to the associated digital interrupt pin. 101 * 102 * B. Changing the size of the inetrrupt queue (IQ): 103 * In the 'C00_Configurations' tab: 104 * a. If configuring for all unique interrupt processing set the defined value 'max_IQ_free_chain_blocks' 105 * to be the same value as the number of active digital interrupts, ie 'max_digital_inputs'. 106 * b). If configuring for nonunique interrupt prcessing then consider the following: 107 * i) The defined value 'max_IQ_free_chain_blocks' needs to be set large enough to handle the 108 * maximum number of interrupts likley to be in the queue at any one time (before it is processed 109 * by 'scan_IQ()'). Otherwise, there may be no space for triggered inetrrupts in the queue. 110 * ii) The interrupt handlers do have debounce coding to deal with spurious interrupts, but do 111 * ensure that the preset value defined by 'interrupt_debounce' is suitable to ensure that 112 * genuine interrupts are not missed. The number given by the define value is in mlliseconds (msecs). 113 * 114 * C. Diagnostics are defined in the tab 'D00 Diags'. Use these routines or add your own. 115 * Note that: 116 * 1. diagnostics may be turned on/off at compile time by setting the boolean variable 117 * 'diags_on' (in the 'D00 Diags' tab) to 'true' for diagnostics and 'false' for no diagnostics. 118 * 2. if using the print queue routines provided than these will disable interrupts whilst they are printed 119 * and re-enable then afterwards. This ensures no conflict if interrrupts are received during the print cycle. 120 * 121 * 122 * 123 */ 124
TAB Setup() - G00_Setup.ino
c_cpp
Setup() segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3void setup() { 4 int input, interrupt_pin, pinmode_type, trigger_type; 5 6 initialisation_complete = false; // ensures no interrupts are processed before ready 7 8 // set up the free chain for interrupt queue handling 9 start_of_IQ_chain = -1; // start with no interrupt blocks in IQ chain 10 end_of_IQ_chain = -1; // start with no interrupt blocks in IQ chain 11 num_IQ_interrupts = 0; // start with no interrupt blocks in IQ chain 12 create_IQ_free_chain(); // set up the free chain of IQ blocks 13 14 if (diags_on) 15 { 16 Serial.begin(115200); 17 } 18 19 // establish the interrupt handler routines to the defined input pins 20 // and associated parameter values. 21 22 for (input=0; input < max_digital_inputs; input++) 23 { 24 if (interrupt_config_data[input][0] == active) 25 { // This interrupt and associated pin is active, so set up structures for it 26 interrupt_pin =interrupt_config_data[input][1]; 27 pinmode_type =interrupt_config_data[input][2]; 28 trigger_type =interrupt_config_data[input][3]; 29 pinMode(interrupt_pin,pinmode_type); 30 attachInterrupt(digitalPinToInterrupt(interrupt_pin), 31 interrupt_handler_addresses[input], 32 trigger_type); 33 } 34 } 35 // 36 // Place any other set up code here, but BEFORE 'initialisation_complete = true' 37 // 38 39 // 40 // End of user inserted set up code 41 // 42 initialisation_complete = true; // can now allow interrupts to be processed 43} 44
TAB Diagnostics - E90_Diags.ino
c_cpp
Diagnostic segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// Insert any diagnostic routines in this tab, and use 'diags_on' to switch 4// them on/off (true/false) 5// Uses 'initialisation_complete' flag to ensure external interrupts will not 6// corrupt the printing process. 7// 8 9void print_IQ() 10{int ptr, count; 11 if (diags_on) 12 { 13 noInterrupts(); 14 initialisation_complete = false; // block interrupt processing 15 interrupts(); 16 count = num_IQ_interrupts; 17 ptr = start_of_IQ_chain; 18 Serial.println("_______________________________"); 19 Serial.println(" INTERRUPT QUEUE"); 20 Serial.print(" Num in IQ = "); 21 Serial.println(count); 22 Serial.print(" start of IQ chain = "); 23 Serial.println(ptr); 24 Serial.print(" end of IQ chain = "); 25 Serial.println(end_of_IQ_chain); 26 if (count > 0) 27 { 28 do 29 { 30 Serial.print("IQ["); 31 Serial.print(ptr); 32 Serial.print("][0] = "); 33 Serial.print(IQ[ptr][0]); 34 Serial.print(char(9)); 35 Serial.print("IQ["); 36 Serial.print(ptr); 37 Serial.print("][1] = "); 38 Serial.println(IQ[ptr][1]); 39 ptr = IQ[ptr][0]; // look at next entry/block 40 count --; 41 } 42 while ( (ptr != end_of_chain_value) && (count > 0)); 43 } 44 Serial.println(""); 45 Serial.println("_______________________________"); 46 Serial.println(""); 47 Serial.flush(); 48 noInterrupts(); 49 initialisation_complete = true; // can now allow interrupts to be processed 50 interrupts(); 51 } 52} 53 54void print_free_chain() 55{int ptr, count; 56if (diags_on) 57 { 58 noInterrupts(); 59 initialisation_complete = false; // block interrupt processing 60 interrupts(); 61 count = num_free_IQ_blocks; 62 ptr = start_of_free_IQ_chain; 63 Serial.println(""); 64 Serial.println("_______________________________"); 65 Serial.println(" FREE CHAIN"); 66 Serial.print(" Num in free chain = "); 67 Serial.println(count); 68 Serial.print("start of free chain = "); 69 Serial.println(ptr); 70 if (count > 0) 71 { 72 do 73 { 74 Serial.print("IQ["); 75 Serial.print(ptr); 76 Serial.print("][0] = "); 77 Serial.print(IQ[ptr][0]); 78 Serial.print(char(9)); 79 Serial.print("IQ["); 80 Serial.print(ptr); 81 Serial.print("][1] = "); 82 Serial.println(IQ[ptr][1]); 83 ptr = IQ[ptr][0]; // look at next entry/block 84 count --; 85 } 86 while ( (ptr != end_of_chain_value) && (count > 0)); 87 } 88 Serial.println("_______________________________"); 89 Serial.println(""); 90 Serial.flush(); 91 noInterrupts(); 92 initialisation_complete = true; // can now allow interrupts to be processed 93 interrupts(); 94 } 95} 96
TAB Main Loop - H00_Main_Segment.ino
c_cpp
Main Loop segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3void loop() 4{int interrupt; 5/* The following section of code will allow interrupts to be processed at each loop cycle 6 Add whatever other code for the specific application required to process the interrupts received plus 7 any other requirements of a noninterrupt nature. 8 9 Design your code structure as necessary, but note that to obtain interrupts 10 from the interrupt queue it is necessary to call the routine 'scanIQ()'. This will examine 11 the interrupt queue and if an unprocessed interrupts exists the result of the call 12 returns the generic interrupt and and associated interrupt dat that triggered the interrupt, 13 if available. 14 15 For example, 'interrupt=scanIQ();'. 16 Results of this call will be: 17 1. an interrupt has been obtained from the IQ. In which case the following varables 18 will be set up: 19 int_number - actual real world interrupt number 20 int_pin - actual digital pin number triggering the interrupt 21 int_pinmode - pinMode value used to set up this digital interrupt pin 22 int_trigger - interrupt trigger value used to set up this digital interrupt 23 int_unique - flag that defines if interrupt processing is unique or nonunique 24 int_debounce - debounce value in msecs for this interrupt 25 2. if no interrupt is returned (none in the IQ) then all of the above variables will be set 26 to 'no_interrupt_request'. 27*/ 28do { // Keep processing interrupts whilst there are interrupts in the queue.. 29 interrupt = scan_IQ(); // get the next interrupt in IQ if there is one. 30 if (interrupt != no_interrupt_request) 31 {/* 32 Process this interrupt request. 'interrupt' defines the 33 generic interrupt number that triggered. Other variables 34 (see above) give all other interrupt atrributes if required 35 Insert whatever code appropriate here, if any, 36 when not processing an interrupt request */ 37 switch (int_number) 38 { 39 case 0: // external interrupt 0 40 // place your code for this interrupt number here... 41 42 break; 43 case 1: // external interrupt 1 44 // place your code for this interrupt number here... 45 46 break; 47 case 2: // external interrupt 2 48 // place your code for this interrupt number here... 49 50 break; 51 case 3: // external interrupt 3 52 // place your code for this interrupt number here... 53 54 break; 55 case 4: // external interrupt 4 56 // place your code for this interrupt number here... 57 58 break; 59 case 5: // external interrupt 5 60 // place your code for this interrupt number here... 61 62 break; 63 default: 64 break; 65 } 66 } 67 } 68 while (interrupt != no_interrupt_request); 69 /* 70 No interrupts left in the queue, so do other things.... 71 Insert whatever code appropriate here, if any, 72 when not processing an interrupt request. */ 73 74 75 76} 77
TAB Introduction - A00_Interrupt_Framework_README_v2.03.ino
c_cpp
Introduction segment
1/* (External) Interrupt Queue Framwork, Ron D Bentley (Stafford UK) 2 * ____________________________________________________________________________________________________ 3 * Copyright (c) Ron D Bentley (UK) 4 * The extent this licence shall be limited to Non-profit Use. 5 * Permission is hereby granted, free of charge, for non-profit purposes to any person obtaining 6 * a copy of this software and associated documentation files (the "Software"), to deal in the 7 * Software without restriction, including without limitation the rights to use, copy, modify, merge, 8 * publish, distribute, sublicense copies of the Software, and to permit persons to whom the Software 9 * is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice, acknowledgements and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS LIMITED TO NON-PROFIT USE AND IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR 16 * A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 19 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 * ____________________________________________________________________________________________________ 21 * 22 * Version History _________________________________________________________________________________ 23 * 24 *18 Apr 2020:v2.03.1 Point release - corrected '#define millis_max' in E10_Interrupt Handlers tab, which 25 * was incorrectly set for 64 bit operation. Corect value is 0xffffffff, which 26 * refects 32 bit long unsigned integer architecture. 27 * 4 Mar 2020:v2.03 Efficiency improvements regarding scanning IQ for existing unique interrupts. New 28 * approach no longer scans chain, but uses binary flags, one for each generic interrupt. 29 * These are set/cleared only if a generic interrupt handler is configured for unique 30 * interrupts. Note, the bit set/cleared refers to the number of the generic interrupt 31 * handler and not the actual linked interrupt that triggered on the assigned generic 32 * handler. 33 * 1 Mar 2020:v2.02 Introduced concept of 'generic' interrupt ISRs to delink actual dgital interrupts 34 * pins from a particular ISR. 35 * 27 Feb 2020:v2.01 Further extension to the interrupt channel data structure ('interrupt_config_data') 36 * to provide greater flexiblity per digital interrupt input. 37 * This version now allows each interrupt to be treated individually for 38 * unique/nonunique interrupts plus independent interrupt debounce. 39 * 24 Feb 2020:v2.00 Modified to consolidate the interrupt channel data into a more helpful 40 * structure for end users to change, as required. 41 * 23 Feb 2020:v1.04 Introduction of further flexibility by addition of modifiable variables 42 * to allow pinMode input types and attachInterrupt trigger types. 43 * 22 Feb 2020:v1.03 Modifications to deal with interrupt 'bounce'. 44 * 22 Feb 2020:v1.02 Layout work to program structure, including end user configuration tab 45 * 20 Feb 2020:v1.01 Extensions to allow unique/nonunique interrupt handling. 46 * 13 Feb 2020:v1.00 Initial version, concepts and development. 47 * ___________________________________________________________________________________________________ 48 * 49 * README..._________________________________________________________________________________________ 50 * This is a framework that can be used to manage and process any number of external 51 * interrupts, from 1 up to the maximum number support by the microcontroller. 52 * It works by using a queue into which triggered interrupts are placed, ready for 53 * processing outside the interrupt process by main code. 54 * 55 * Note that interrupts are queued, that is, they are placed in the queue on a last in last out 56 * basis and, conversely, they are taken out of the queue on a first in, first out basis (FIFO). 57 * That is, on the basis of the order in which they have been triggered. 58 * 59 * The aproach/method assumes nothing about the overlaying end user appliaction, 60 * and how each interrupt is procssed is entirely down to the needs of the solution sought. 61 * 62 * The framework has been initially written for the MEGA 2560 board, which allows 63 * up to 6 separate external interrupts, these being on digital pins 2, 3, 18, 19, 20 and 21. 64 * 65 * The framework is flexible in through editing of the interrupt control data defined in the 66 * array/table 'interrupt_config_data', such that: 67 * 1. the number of interrupts enabled (made 'active'/'inactive') may be adjusted from 1 up 68 * to 6 (the maximum external interrupts permissible on the MEGA 2560). 69 * 70 * 2. For each digital input interrupt pin, it is possible to define a number of parameters that steer 71 * how the interrupt will be dealt with and also how it is associated with the priority defined 72 * by the microcontroller specifications. 73 * 74 * This flexibility is achieved through the use of a number of configurable parameters which can 75 * be defined within the 'C00_Configurations' tab (see below). 76 * 77 * Configurable parameters: 78 * A. Assignimg parameters to each digital interrupt: 79 * In the 'C00_Configurations' tab, the data declaration 'interrupt_config_data' contains all of the 80 * variable parameters that define how each external interrupt should be handled/processed. The attributes 81 * define by this declaration are as follows: 82 * 83 * 'interrupt_config_data' Array/table row values (by column): 84 * For each row (external interrupt), decide how each external interrupt is to be configured: 85 * active/inactive flag - set the value to an 'active' or inactive state. 86 * If 'inactive' no interrupts will be processed for this externl interrupt. 87 * digial pin number - set the digital pin number associated with this external interrupt. 88 * Refer to the board documentation if using a micrcontroller otherthan the MEGA 2560. 89 * pinMode mode value - set the required pinMode mode value, INPUT, INPUT_PULLUP, etc for the defined 90 * interrupt pin. 91 * trigger type - set the interrupt trigger type for this interrupt, eg Falling, Change, Rising, etc. 92 * unique/nonunique processing - set to 'true' if unique processing required, 'false' otherwise'. 93 * If set to 'true' then only one interrupt of the same number will be held in the 94 * interrupt queue at a time. Otherwise, the interrupt queue may contain any number of 95 * interrupts, up to the defined size of the interrupt queue free chain. 96 * debounce value - this is the time that interrupt handler for this external interrupt will wait between 97 * interrupts before deciding that the interrupt can be processed as a new interrupt event. 98 * The value is in milliseconds. 99 * interrupt number - this parameter defines the externa interrupt number assigned by the microcontroller 100 * to the associated digital interrupt pin. 101 * 102 * B. Changing the size of the inetrrupt queue (IQ): 103 * In the 'C00_Configurations' tab: 104 * a. If configuring for all unique interrupt processing set the defined value 'max_IQ_free_chain_blocks' 105 * to be the same value as the number of active digital interrupts, ie 'max_digital_inputs'. 106 * b). If configuring for nonunique interrupt prcessing then consider the following: 107 * i) The defined value 'max_IQ_free_chain_blocks' needs to be set large enough to handle the 108 * maximum number of interrupts likley to be in the queue at any one time (before it is processed 109 * by 'scan_IQ()'). Otherwise, there may be no space for triggered inetrrupts in the queue. 110 * ii) The interrupt handlers do have debounce coding to deal with spurious interrupts, but do 111 * ensure that the preset value defined by 'interrupt_debounce' is suitable to ensure that 112 * genuine interrupts are not missed. The number given by the define value is in mlliseconds (msecs). 113 * 114 * C. Diagnostics are defined in the tab 'D00 Diags'. Use these routines or add your own. 115 * Note that: 116 * 1. diagnostics may be turned on/off at compile time by setting the boolean variable 117 * 'diags_on' (in the 'D00 Diags' tab) to 'true' for diagnostics and 'false' for no diagnostics. 118 * 2. if using the print queue routines provided than these will disable interrupts whilst they are printed 119 * and re-enable then afterwards. This ensures no conflict if interrrupts are received during the print cycle. 120 * 121 * 122 * 123 */ 124
TAB Configuration - C00_Configurations.ino
c_cpp
Configuration segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// This tab contains the parameters available to configure the interrupt handlers and associated 4// supporting structures. 5 6bool diags_on = true; // set to 'true' for diagnostics, otherwise 'false' for none 7 8#define active 1 9#define inactive -1 10 11// **** Interrupt configurable varables/values **** 12#define max_digital_inputs 6 // Max digital interrupts pins available on Mega 2650 board. 13 // The number of pins to be interrupt monitored. 14 // DO NOT vary this number, simply change active flag and associated 15 // interrupt data, even if using with a different board. 16 17// Note that the array/table 'interrupt_config_data' is sized for the maximum number of digital 18// interrupt pins for the Mega 2560. If selecting fewer interrupt pins, simple deactivate the 19// lines not required by setting column 0 value t0 'inactive' 20 21volatile int interrupt_config_data[max_digital_inputs][7]= 22{ 23active, 21, INPUT, RISING, false, 15, 0, // generic interrupt handler 0 entries 24active, 20, INPUT, RISING, false, 15, 1, // generic interrupt handler 1 entries 25active, 19, INPUT, RISING, false, 15, 2, // generic interrupt handler 2 entries 26active, 18, INPUT, RISING, false, 15, 3, // generic interrupt handler 3 entries 27active, 2, INPUT, RISING, false, 15, 4, // generic interrupt handler 4 entries 28active, 3, INPUT, RISING, false, 15, 5};// generic interrupt handler 5 entries 29/* ^ ^ ^ ^ ^ ^ ^ 30 | | | | | | | 31 active digital pinMode interrupt unique/ interrupt interrupt number 32inactive interrupt mode value trigger nonunique debounce this pin is 33 flag pin no. type flag value (msecs) linked to 34 35*/ 36// ****Interrupt Queue configurable values **** 37#define max_IQ_free_chain_blocks 32 // This defines the number of free blocks in the chain. 38 // At least the maximum number of interrupts being monitored, 39 // if all interrupts are defined as unique. 40 // Must be > than this value, if nonunique interrupts are being allowed 41
TAB Interrupt Handlers - E10_Interrupt_Handlers.ino
c_cpp
Interrupt Handlers segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3// 4 This tab provides the basis for developing applications that require 5// multiple 6 interrupts, occurring from inputs to digital pins. 7// It is a shell to be used 8 with whatever applications are required to have 9// a need for multiple interrupt 10 driven inputs. 11// 12// This code is for the Mega 2650 board and the digital 13 pins are set up to trigger when a pin is RISING. 14// Check if using other boards, 15 as this arrangement of pins is not appropriate for some. 16// 17// Interrupt 18 handler routines/functions, one for each possible interrupt 19// up to the max 20 defined (at most 6 on the Mega 2560 board) 21// 22 23volatile int interrupt_debounce_value; 24 25volatile int unique_interrupts; 26 27volatile bool initialisation_complete 28 = false; // used by interrupt handlers to stop them processing too soon 29 30volatile 31 long unsigned int millis_now, millis_elapsed; 32 33#define millis_max 0xffffffff 34 // max value the millis function will achieve before cycling back to 0 35 36// 37// 38 Each interrupt handler will call this function (when triggered) passing it the 39 its associated interrupt 40// number. If possible, an entry will be inserted 41 into the IQ. 42// 43int process_interrupt(int interrupt) 44{ int ptr, result; 45 46 /*if (interrupt_config_data[interrupt][0]==inactive) 47 { return fail;} // ensures 48 a spurious interrupt will not be processed, only active ones 49 unique_interrupts 50 = interrupt_config_data[interrupt][4]; */ 51 if (interrupt_config_data[interrupt][4]) 52 // 'unique' marker set up for this generic interrupt type 53 { // check if 54 there is already an interrupt entry in the IQ for this interrupt pin. 55 if 56 (bitRead(unique_entry_flags, interrupt)) 57 { // value is set to true, so must 58 already in the IQ. 59 return fail; 60 } 61 // not already in the 62 IQ, so set flag and insert entry 63 result = insert_into_interrupt_IQ(interrupt); 64 65 if (result == success) 66 { 67 bitWrite(unique_entry_flags, interrupt, 68 true); // set the flag for this generic interrupt to true 69 } 70 return 71 result; // either success or fail. 72 } 73 // this is a nonunique interrupt 74 request 75 return insert_into_interrupt_IQ(interrupt); // result will be either 76 success or fail 77} 78 79void generic_handler0() 80{static long unsigned int 81 millis_previous; 82 if (initialisation_complete == true) 83 { // all variables 84 are initialised so we are okay to continue to process this interrupt. 85 // 86 deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 87 88 // interrupts. 89 millis_now = millis(); 90 if (millis_now < millis_previous) 91 92 { // millis timer has cycled since last tested, so adjust 93 millis_elapsed 94 = millis_max - millis_previous + millis_now + 1; 95 } 96 else 97 { // 98 millis_now is in advance of previous millis 99 millis_elapsed = millis_now 100 - millis_previous + 1; 101 } 102 interrupt_debounce_value = interrupt_config_data[0][5]; 103 104 if (millis_elapsed >= interrupt_debounce_value) 105 { // treat this interrupt 106 as a new one and not associated with a 'bounce' 107 process_interrupt(0); 108 109 } 110 millis_previous = millis_now; 111 } 112} 113 114void generic_handler1() 115 116{static long unsigned int millis_previous; 117 if (initialisation_complete 118 == true) 119 { // all variables are initialised so we are okay to continue to 120 process this interrupt. 121 // deal with potential 'bouncing' of this interrupt. 122 The code will 'debounce' spurious 123 // interrupts. 124 millis_now = millis(); 125 126 if (millis_now < millis_previous) 127 { // millis timer has cycled since 128 last tested, so adjust 129 millis_elapsed = millis_max - millis_previous 130 + millis_now + 1; 131 } 132 else 133 { // millis_now is in advance of previous 134 millis 135 millis_elapsed = millis_now - millis_previous + 1; 136 } 137 138 interrupt_debounce_value = interrupt_config_data[1][5]; 139 if (millis_elapsed 140 >= interrupt_debounce_value) 141 { // treat this interrupt as a new one and 142 not associated with a 'bounce' 143 process_interrupt(1); 144 } 145 millis_previous 146 = millis_now; 147 } 148} 149 150void generic_handler2() 151{static long unsigned 152 int millis_previous; 153 if (initialisation_complete == true) 154 { // all variables 155 are initialised so we are okay to continue to process this interrupt. 156 // 157 deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 158 159 // interrupts. 160 millis_now = millis(); 161 if (millis_now < millis_previous) 162 163 { // millis timer has cycled since last tested, so adjust 164 millis_elapsed 165 = millis_max - millis_previous + millis_now + 1; 166 } 167 else 168 { // 169 millis_now is in advance of previous millis 170 millis_elapsed = millis_now 171 - millis_previous + 1; 172 } 173 interrupt_debounce_value = interrupt_config_data[2][5]; 174 175 if (millis_elapsed >= interrupt_debounce_value) 176 { // treat this 177 interrupt as a new one and not associated with a 'bounce' 178 process_interrupt(2); 179 180 } 181 millis_previous = millis_now; 182 } 183} 184 185void generic_handler3() 186 187{static long unsigned int millis_previous; 188 if (initialisation_complete 189 == true) 190 { // all variables are initialised so we are okay to continue to 191 process this interrupt. 192 // deal with potential 'bouncing' of this interrupt. 193 The code will 'debounce' spurious 194 // interrupts. 195 millis_now = millis(); 196 197 if (millis_now < millis_previous) 198 { // millis timer has cycled since 199 last tested, so adjust 200 millis_elapsed = millis_max - millis_previous 201 + millis_now + 1; 202 } 203 else 204 { // millis_now is in advance of previous 205 millis 206 millis_elapsed = millis_now - millis_previous + 1; 207 } 208 209 interrupt_debounce_value = interrupt_config_data[3][5]; 210 if (millis_elapsed 211 >= interrupt_debounce_value) 212 { // treat this interrupt as a new one and 213 not associated with a 'bounce' 214 process_interrupt(3); 215 } 216 millis_previous 217 = millis_now; 218 } 219} 220 221void generic_handler4() 222{static long unsigned 223 int millis_previous; 224 if (initialisation_complete == true) 225 { // all variables 226 are initialised so we are okay to continue to process this interrupt. 227 // 228 deal with potential 'bouncing' of this interrupt. The code will 'debounce' spurious 229 230 // interrupts. 231 millis_now = millis(); 232 if (millis_now < millis_previous) 233 234 { // millis timer has cycled since last tested, so adjust 235 millis_elapsed 236 = millis_max - millis_previous + millis_now + 1; 237 } 238 else 239 { // 240 millis_now is in advance of previous millis 241 millis_elapsed = millis_now 242 - millis_previous + 1; 243 } 244 interrupt_debounce_value = interrupt_config_data[4][5]; 245 246 if (millis_elapsed >= interrupt_debounce_value) 247 { // treat this 248 interrupt as a new one and not associated with a 'bounce' 249 process_interrupt(4); 250 251 } 252 millis_previous = millis_now; 253 } 254} 255 256void generic_handler5() 257 258{static long unsigned int millis_previous; 259 if (initialisation_complete 260 == true) 261 { // all variables are initialised so we are okay to continue to 262 process this interrupt. 263 // deal with potential 'bouncing' of this interrupt. 264 The code will 'debounce' spurious 265 // interrupts. 266 millis_now = millis(); 267 268 if (millis_now < millis_previous) 269 { // millis timer has cycled since 270 last tested, so adjust 271 millis_elapsed = millis_max - millis_previous + 272 millis_now + 1; 273 } 274 else 275 { // millis_now is in advance of previous 276 millis 277 millis_elapsed = millis_now - millis_previous + 1; 278 } 279 280 interrupt_debounce_value = interrupt_config_data[5][5]; 281 if (millis_elapsed 282 >= interrupt_debounce_value) 283 { // treat this interrupt as a new one and 284 not associated with a 'bounce' 285 process_interrupt(5); 286 } 287 millis_previous 288 = millis_now; 289 } 290} 291 292volatile (*interrupt_handler_addresses[max_digital_inputs])()= 293{ 294generic_handler0, 295generic_handler1, 296generic_handler2, 297generic_handler3, 298generic_handler4, 299generic_handler5 300}; 301
TAB Setup() - G00_Setup.ino
c_cpp
Setup() segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3void 4 setup() { 5 int input, interrupt_pin, pinmode_type, trigger_type; 6 7 initialisation_complete 8 = false; // ensures no interrupts are processed before ready 9 10 // set 11 up the free chain for interrupt queue handling 12 start_of_IQ_chain = -1; 13 // start with no interrupt blocks in IQ chain 14 end_of_IQ_chain = 15 -1; // start with no interrupt blocks in IQ chain 16 num_IQ_interrupts = 17 0; // start with no interrupt blocks in IQ chain 18 create_IQ_free_chain(); 19 // set up the free chain of IQ blocks 20 21 if (diags_on) 22 { 23 24 Serial.begin(115200); 25 } 26 27 // establish the interrupt handler routines 28 to the defined input pins 29 // and associated parameter values. 30 31 for 32 (input=0; input < max_digital_inputs; input++) 33 { 34 if (interrupt_config_data[input][0] 35 == active) 36 { // This interrupt and associated pin is active, so set up structures 37 for it 38 interrupt_pin =interrupt_config_data[input][1]; 39 pinmode_type 40 =interrupt_config_data[input][2]; 41 trigger_type =interrupt_config_data[input][3]; 42 43 pinMode(interrupt_pin,pinmode_type); 44 attachInterrupt(digitalPinToInterrupt(interrupt_pin), 45 46 interrupt_handler_addresses[input], 47 trigger_type); 48 49 } 50 } 51 // 52 // Place any other set up code here, but BEFORE 'initialisation_complete 53 = true' 54 // 55 56 // 57 // End of user inserted set up code 58 // 59 60 initialisation_complete = true; // can now allow interrupts to be processed 61} 62
TAB Testing - T00_Testing.ino
c_cpp
Testing and Test Plans segment
1// Copyright (c) Ron D Bentley (UK), see copyright notice 2// 3/* 4 5 * Version 2.03 Testing Strategy: 6 * 1. To test the correct setting and 7 clearing of generic interrupt flags were these are configured 8 * as for 9 'unique' entries in the IQ. 10 * 2. To rerun the soak performnce testing run 11 under version 2.02 and to record tiings for 12 * a. averge IQ inserts 13 14 * b. averge IQ extracts. 15 * 16 * The results obtained showed that: 17 18 * i. the average time to insert an interrupt into the interrupt queue 19 is 15 microseconds, 20 * ii. the average time to remove an interrupt from 21 the interrupt queue is 12 microseconds. 22 * 23 * Observation - whilst the 24 changes did not make any signifant performance improvement, it did result 25 * 26 in maginally less code generation. 27*/ 28
Arduino External Interrupt Processing
See the User Guide for full background and instructions in its use
Downloadable files
Conceptual overview of the framework.
To aid understanding of the structure of the framework.
Conceptual overview of the framework.
Conceptual overview of the framework.
To aid understanding of the structure of the framework.
Conceptual overview of the framework.
Comments
Only logged in users can leave comments
ronbentley1
5 Followers
•28 Projects
1
1
ARDUINO Microcontroller, External Interrupt Handling | Arduino Project Hub
Anonymous user
3 years ago
Great project! Just one comment, in the manual on page 11, columns 3 and 4 of the configuration file don't match the column descriptions defined on page 10. They are switched.