├── examples ├── ARDUINO_NANO_delayed_with_parameters_revoke │ ├── connection.png │ └── ARDUINO_NANO_delayed_with_parameters_revoke.ino ├── ARDUINO_NANO_delayed_with_parameters │ └── ARDUINO_NANO_delayed_with_parameters.ino ├── MKR_WAN_1310_one_timer │ └── MKR_WAN_1310_one_timer.ino └── MKR_WAN_1310_five_timers │ └── MKR_WAN_1310_five_timers.ino ├── library.properties ├── keywords.txt ├── LICENSE ├── README.md └── antirtos.h /examples/ARDUINO_NANO_delayed_with_parameters_revoke/connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeSpeakEnglish/ANTIRTOS/HEAD/examples/ARDUINO_NANO_delayed_with_parameters_revoke/connection.png -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ANTIRTOS 2 | version=1.0.4 3 | author=Aleksei Tertychnyi 4 | maintainer=Aleksei Tertychnyi 5 | sentence= No any RTOS needed, you will see - seamlessly manage all your tasks without unnecessary waiting, keep your interrupts blazing fast. 6 | paragraph= Unleash your app. full potential: no more getting stuck in interrupts or wasting time with dummy delays! Seamlessly manage your tasks, keep your interrupts ultra-fast regardless of task complexity. 7 | category=Timing 8 | url=https://github.com/WeSpeakEnglish/ANTIRTOS 9 | architectures=* 10 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Datatypes (KEYWORD1) 3 | ####################################### 4 | ANTIRTOS KEYWORD1 5 | fQ KEYWORD1 6 | fQP KEYWORD1 7 | del_fQ KEYWORD1 8 | del_fQP KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | 14 | ################################### 15 | # Class fQ 16 | ################################### 17 | push KEYWORD2 18 | pull KEYWORD2 19 | 20 | ################################### 21 | # Class fQp 22 | ################################### 23 | push KEYWORD2 24 | pull KEYWORD2 25 | 26 | ################################### 27 | # Class del_fQ 28 | ################################### 29 | push_delayed KEYWORD2 30 | push KEYWORD2 31 | pull KEYWORD2 32 | tick KEYWORD2 33 | revoke KEYWORD2 34 | 35 | ################################### 36 | # Class del_fQP 37 | ################################### 38 | push_delayed KEYWORD2 39 | push KEYWORD2 40 | pull KEYWORD2 41 | tick KEYWORD2 42 | revoke KEYWORD2 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2020-2025] [Aleksei Tertychnyi] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/ARDUINO_NANO_delayed_with_parameters/ARDUINO_NANO_delayed_with_parameters.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef 4 | struct pinOut{ // structure (index - pin number, logic - 1/0 = ON/OFF) 5 | int index; 6 | bool logic; 7 | } pinout ; 8 | 9 | del_fQP Q1(8); // maximum 8 function pointers with parameters in queue 10 | 11 | void writePin(pinout cmd){ // write a pin true =- ON 12 | 13 | digitalWrite(cmd.index, cmd.logic); 14 | 15 | } 16 | 17 | void setup() { 18 | // put your setup code here, to run once: 19 | pinMode(13, OUTPUT); 20 | pinMode(12, OUTPUT); 21 | 22 | TCCR1A = 0x00; //Normal Mode 23 | TCCR1B = 0x00; //TC1 is OFF 24 | TCNT1 = 0; 25 | OCR1A = 6250; //0.1s delay; prescaler 256 26 | bitSet(TIMSK1, OCIE1A); //local intterupt is active 27 | TCCR1B |= bit(CS12); //Start TC1 with prescale 256 28 | 29 | Q1.push_delayed(writePin,{12,true},20); //yellow led ON after 2 sec. (0.1*20 = 2 seconds) 30 | Q1.push_delayed(writePin,{12,false},30); //yellow led OFF after 3 sec. 31 | Q1.push_delayed(writePin,{13,true},50); //red led ON after 5 sec. 32 | Q1.push_delayed(writePin,{13,false},80); //red led OFF after 8 sec. 33 | 34 | } 35 | 36 | void loop() { 37 | // put your main code here, to run repeatedly: 38 | Q1.pull(); // pull from the queue 39 | 40 | } 41 | 42 | ISR(TIMER1_COMPA_vect) // timer interrupt ticks one per 0.1 sec 43 | { 44 | TCNT1 = 0; 45 | OCR1A = 6250; 46 | Q1.tick(); // execute tick method for make delayed functionality works 47 | } 48 | 49 | -------------------------------------------------------------------------------- /examples/ARDUINO_NANO_delayed_with_parameters_revoke/ARDUINO_NANO_delayed_with_parameters_revoke.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef 4 | struct pinOut { // structure (index - pin number, logic - 1/0 = ON/OFF) 5 | int index; 6 | int logic; 7 | int period; 8 | } pinout ; 9 | 10 | del_fQP Q1(10); // maximum 10 function pointers with parameters in queue 11 | 12 | void writePin(pinout cmd) { // write a pin logic 13 | 14 | digitalWrite(cmd.index, cmd.logic); 15 | if (cmd.logic) // inverting logic 16 | cmd.logic = LOW; 17 | else 18 | cmd.logic = HIGH; 19 | 20 | Q1.push_delayed(writePin, cmd, cmd.period); //add this function into a queue again 21 | } 22 | 23 | void revokeF(pinout dummy){ // compatibility 24 | Q1.revoke(writePin); 25 | 26 | } 27 | 28 | void setup() { 29 | // put your setup code here, to run once: 30 | pinMode(5, OUTPUT); 31 | pinMode(6, OUTPUT); 32 | pinMode(7, OUTPUT); 33 | pinMode(8, OUTPUT); 34 | pinMode(9, OUTPUT); 35 | pinMode(10, OUTPUT); 36 | pinMode(11, OUTPUT); 37 | pinMode(12, OUTPUT); 38 | 39 | //timet setup 40 | TCCR1A = 0x00; //Normal Mode 41 | TCCR1B = 0x00; //TC1 is OFF 42 | TCNT1 = 0; 43 | OCR1A = 6250; //0.1s delay; prescaler 256 44 | bitSet(TIMSK1, OCIE1A); //local intterupt is active 45 | TCCR1B |= bit(CS12); //Start TC1 with prescale 256 46 | 47 | Q1.push_delayed(writePin, {5, HIGH, 5}, 5); // period 1 sec. 48 | Q1.push_delayed(writePin, {6, HIGH, 10}, 10); // period 2 sec. 49 | Q1.push_delayed(writePin, {7, HIGH, 20}, 20); // period 4 sec. 50 | Q1.push_delayed(writePin, {8, HIGH, 40}, 40); // period 8 sec. 51 | Q1.push_delayed(writePin, {9, HIGH, 80}, 80); // period 16 sec. 52 | Q1.push_delayed(writePin, {10, HIGH, 160}, 160); // period 32 sec. 53 | Q1.push_delayed(writePin, {11, HIGH, 320}, 320); // period 64 sec. 54 | Q1.push_delayed(writePin, {12, HIGH, 640}, 640); //period 128 sec. 55 | Q1.push_delayed(revokeF,{0,0,0},3000); // stop after 300 sec. 56 | 57 | } 58 | 59 | void loop() { 60 | // put your main code here, to run repeatedly: 61 | Q1.pull(); // pull from the queue 62 | } 63 | 64 | ISR(TIMER1_COMPA_vect) // timer interrupt ticks one per 0.1 sec 65 | { 66 | TCNT1 = 0; 67 | OCR1A = 6250; 68 | Q1.tick(); // execute tick method for make delayed functionality works 69 | } 70 | -------------------------------------------------------------------------------- /examples/MKR_WAN_1310_one_timer/MKR_WAN_1310_one_timer.ino: -------------------------------------------------------------------------------- 1 | //SAMD21D ARDUINO MKR WAN 1310 2 | // utilize only one timer (TIMER_TC3), on different intervals push task functions and parameters into queue 3 | 4 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 5 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. 6 | #define TIMER_INTERRUPT_DEBUG 0 7 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 8 | 9 | // Select only one to be true for SAMD21. Must must be placed at the beginning before #include "SAMDTimerInterrupt.h" 10 | #define USING_TIMER_TC3 true // Only TC3 can be used for SAMD51 11 | #define USING_TIMER_TC4 false // Not to use with Servo library 12 | #define USING_TIMER_TC5 false 13 | #define USING_TIMER_TCC false 14 | #define USING_TIMER_TCC1 false 15 | #define USING_TIMER_TCC2 false // Don't use this, can crash on some boards 16 | 17 | #include "SAMDTimerInterrupt.h" 18 | #include "SAMD_ISR_Timer.h" 19 | 20 | #include 21 | 22 | // TC3, TC4, TC5 max permissible TIMER_INTERVAL_MS is 1398.101 ms, larger will overflow, therefore not permitted 23 | // Use TCC, TCC1, TCC2 for longer TIMER_INTERVAL_MS 24 | #define HW_TIMER_INTERVAL_MS 50L 25 | 26 | #define TIMER_INTERVAL_1S 1000L 27 | #define TIMER_INTERVAL_2S 2000L 28 | 29 | // Init selected SAMD timer 30 | SAMDTimer ITimer3(TIMER_TC3); 31 | 32 | // Init SAMD_ISR_Timer 33 | // Each SAMD_ISR_Timer can service 16 different ISR-based timers 34 | SAMD_ISR_Timer ISR_Timer; 35 | 36 | void TimerHandler(void) 37 | { 38 | ISR_Timer.run(); 39 | } 40 | 41 | class testClass{ // it is not used here, just like example how you may pass complex argument to your functions in queue 42 | public: 43 | int array[10]={0,0,0,0,0,0,0,0,0,0}; 44 | float argument = 0.0; 45 | }; 46 | 47 | fQP F1(10); 48 | fQP F2(10); // it is not used here, just like example how you may pass complex argument to your functions in queue 49 | 50 | void printMessage1(uint32_t milliseconds) //ISR function excutes when push button at pinD2 is pressed 51 | { 52 | Serial.print("TIMER_INTERVAL_1S elapsed"); 53 | Serial.print(" at "); 54 | Serial.println(milliseconds); 55 | } 56 | 57 | 58 | void printMessage2(uint32_t milliseconds) //ISR function excutes when push button at pinD3 is pressed 59 | { 60 | Serial.print("TIMER_INTERVAL_2S elapsed"); 61 | Serial.print(" at "); 62 | Serial.println(milliseconds); 63 | } 64 | 65 | 66 | void doingSomething1(){ 67 | F1.push(printMessage1, millis()); // add your task into queue, keep your interrupt as fast as possible 68 | } 69 | 70 | void doingSomething2() 71 | { 72 | F1.push(printMessage2, millis()); // add your task into queue, keep your interrupt as fast as possible 73 | } 74 | 75 | 76 | 77 | void setup() 78 | { 79 | Serial.begin(115200); 80 | while (!Serial && millis() < 5000); 81 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 82 | 83 | // Interval in millisecs 84 | if (ITimer3.attachInterruptInterval_MS(HW_TIMER_INTERVAL_MS, TimerHandler)) 85 | { 86 | Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(millis()); 87 | } 88 | else 89 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 90 | 91 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 92 | // You can use up to 16 timer for each ISR_Timer 93 | ISR_Timer.setInterval(TIMER_INTERVAL_1S, doingSomething1); 94 | ISR_Timer.setInterval(TIMER_INTERVAL_2S, doingSomething2); 95 | } 96 | 97 | void loop() { 98 | // put your main code here, to run repeatedly: 99 | F1.pull(); 100 | 101 | } 102 | -------------------------------------------------------------------------------- /examples/MKR_WAN_1310_five_timers/MKR_WAN_1310_five_timers.ino: -------------------------------------------------------------------------------- 1 | 2 | // The example demonstrate usage of several different times on SAMD21 platforms like MKR WAN. 3 | // In each timer interrupt we push our task function and a parameter to another queue. 4 | // All the tasks easily handled in loop via pulling them from the queues. 5 | // The interrupts are kept short, revealing the unblocking of the rest of the programme. 6 | 7 | 8 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 9 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. 10 | #define TIMER_INTERRUPT_DEBUG 0 11 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 12 | 13 | // Must must be placed at the beginning before #include "SAMDTimerInterrupt.h" 14 | #define USING_TIMER_TC3 true 15 | #define USING_TIMER_TC4 true 16 | #define USING_TIMER_TC5 true 17 | #define USING_TIMER_TCC true 18 | #define USING_TIMER_TCC1 true 19 | #define USING_TIMER_TCC2 false // Don't use this, can crash on some boards 20 | 21 | #include "SAMDTimerInterrupt.h" 22 | #include "SAMD_ISR_Timer.h" 23 | 24 | #include 25 | 26 | // TC3, TC4, TC5 max permissible TIMER_INTERVAL_MS is 1398 ms, larger will overflow, therefore not permitted 27 | // Use TCC, TCC1, TCC2 for longer TIMER_INTERVAL_MS 28 | uint32_t TIMER_INTERVAL_MS[MAX_TIMER] = { 200, 500, 1000, 2000, 5000 }; 29 | 30 | SAMDTimer ITimer0(TIMER_TC3); 31 | SAMDTimer ITimer1(TIMER_TC4); 32 | SAMDTimer ITimer2(TIMER_TC5); 33 | SAMDTimer ITimer3(TIMER_TCC); 34 | SAMDTimer ITimer4(TIMER_TCC1); 35 | 36 | fQP F1(10); 37 | fQP F2(10); 38 | fQP F3(10); 39 | fQP F4(10); 40 | fQP F5(10); 41 | 42 | void TimerHandler_TIMER_TC3() 43 | { 44 | F1.push(printMessage1, millis()); // add your task into queue, keep your interrupt as fast as possible 45 | } 46 | 47 | void TimerHandler_TIMER_TC4() 48 | { 49 | F2.push(printMessage2, millis()); // add your task into queue, keep your interrupt as fast as possible 50 | } 51 | 52 | void TimerHandler_TIMER_TC5() 53 | { 54 | F3.push(printMessage3, millis()); // add your task into queue, keep your interrupt as fast as possible 55 | } 56 | 57 | void TimerHandler_TIMER_TCC() 58 | { 59 | F4.push(printMessage4, millis()); // add your task into queue, keep your interrupt as fast as possible 60 | } 61 | 62 | void TimerHandler_TIMER_TCC1() 63 | { 64 | F5.push(printMessage5, millis()); // add your task into queue, keep your interrupt as fast as possible 65 | } 66 | 67 | void printMessage1(uint32_t milliseconds) //ISR function excutes when push button at pinD2 is pressed 68 | { 69 | Serial.print("TIMER_TC3 elapsed"); 70 | Serial.print(" at "); 71 | Serial.println(milliseconds); 72 | } 73 | 74 | 75 | void printMessage2(uint32_t milliseconds) //ISR function excutes when push button at pinD3 is pressed 76 | { 77 | Serial.print("TIMER_TC4 elapsed"); 78 | Serial.print(" at "); 79 | Serial.println(milliseconds); 80 | } 81 | 82 | void printMessage3(uint32_t milliseconds) //ISR function excutes when push button at pinD3 is pressed 83 | { 84 | Serial.print("TIMER_TC5 elapsed"); 85 | Serial.print(" at "); 86 | Serial.println(milliseconds); 87 | } 88 | 89 | void printMessage4(uint32_t milliseconds) //ISR function excutes when push button at pinD2 is pressed 90 | { 91 | Serial.print("TIMER_TCC elapsed"); 92 | Serial.print(" at "); 93 | Serial.println(milliseconds); 94 | } 95 | 96 | 97 | void printMessage5(uint32_t milliseconds) //ISR function excutes when push button at pinD3 is pressed 98 | { 99 | Serial.print("TIMER_TCC1 elapsed"); 100 | Serial.print(" at "); 101 | Serial.println(milliseconds); 102 | } 103 | 104 | void setup() 105 | { 106 | Serial.begin(115200); 107 | while (!Serial && millis() < 5000); 108 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 109 | 110 | #if USING_TIMER_TC3 111 | if (ITimer0.attachInterruptInterval_MS(TIMER_INTERVAL_MS[TIMER_TC3], TimerHandler_TIMER_TC3)) 112 | Serial.println("Starting TIMER_TC3 OK, millis() = " + String(millis())); 113 | else 114 | Serial.println("Can't set TIMER_TC3. Select another freq. or timer"); 115 | #endif 116 | 117 | #if USING_TIMER_TC4 118 | if (ITimer1.attachInterruptInterval_MS(TIMER_INTERVAL_MS[TIMER_TC4], TimerHandler_TIMER_TC4)) 119 | Serial.println("Starting TIMER_TC4 OK, millis() = " + String(millis())); 120 | else 121 | Serial.println("Can't set TIMER_TC4. Select another freq. or timer"); 122 | #endif 123 | 124 | #if USING_TIMER_TC5 125 | if (ITimer2.attachInterruptInterval_MS(TIMER_INTERVAL_MS[TIMER_TC5], TimerHandler_TIMER_TC5)) 126 | Serial.println("Starting TIMER_TC5 OK, millis() = " + String(millis())); 127 | else 128 | Serial.println("Can't set TIMER_TC5. Select another freq. or timer"); 129 | #endif 130 | 131 | #if USING_TIMER_TCC 132 | if (ITimer3.attachInterruptInterval_MS(TIMER_INTERVAL_MS[TIMER_TCC], TimerHandler_TIMER_TCC)) 133 | Serial.println("Starting TIMER_TCC OK, millis() = " + String(millis())); 134 | else 135 | Serial.println("Can't set TIMER_TCC. Select another freq. or timer"); 136 | #endif 137 | 138 | #if USING_TIMER_TCC1 139 | if (ITimer4.attachInterruptInterval_MS(TIMER_INTERVAL_MS[TIMER_TCC1], TimerHandler_TIMER_TCC1)) 140 | Serial.println("Starting TIMER_TCC1 OK, millis() = " + String(millis())); 141 | else 142 | Serial.println("Can't set TIMER_TCC1. Select another freq. or timer"); 143 | #endif 144 | 145 | } 146 | 147 | void loop() { 148 | // put your main code here, to run repeatedly: 149 | F1.pull(); 150 | F2.pull(); 151 | F3.pull(); 152 | F4.pull(); 153 | F5.pull(); 154 | } 155 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # ANTIRTOS 5 | ### *No any RTOS needed, you will see...* 6 | ANTIRTOS is an ultra-lightweight, robust, secure, and efficient universal C++ library designed for task management in IoT and embedded applications. It is coded in one header-only file, that provides four versions of main task queue class : **fQ** is the basic class, **fQP** adds an argument for the queued functions, **del_fQ** adds an delay before the function is executed, and **del_fQP** add both enhancements. 7 | 8 | Each queue object contains an array of pointers to functions to execute sequentially. The "push" procedure adds a function to the back of the queue, while the "pull" procedure executes the next function and removes it from the queue. Several queues can co-exist concurrently, and the execution of the functions occurs outside of interrupts in a non-blocking way. 9 | 10 | ANTIRTOS is suitable for a variety of devices, from simple microcontrollers to complex embedded systems where the complexity and overhead of an RTOS are not justified. 11 | 12 | This is an universal branch with dynamic memory (heap) allocation approach. 13 | 14 | A newer version called [ANTIRTOS_MODERN](https://github.com/WeSpeakEnglish/ANTIRTOS_MODERN) leverages the standard library (std) using tuples and static allocation to simplify its usage. However, it is not supported on some platforms yet. 15 | 16 | ### Benefits: 17 | 1. Interrupts kept fast and controllable, no blocking anymore. 18 | 2. Easy to debug and understand. 19 | 3. Get an easy way of multitasking. 20 | 4. No dummy waiting/blockings. Wait by doing! 21 | 5. No need to have a deal with critical sections/mutexes/semaphores, no tasks stack memory overflows. Keep your project based on straight forward architecture 22 | 23 | Get rid of complexity! 24 | Allow your MCU to perform many tasks while using a very small amount of microcontroller memory. Manage function pointer queues in a simple way by simply placing your tasks there and pulling them from a main loop. This approach allows you to keep interrupts fast and at the same time control multitasking in a simple and transparent way. Pass arguments to you function, they will be saved in separate queue and handled. 25 | 26 | ### Let’s explain a bit on examples. 27 | 28 | 0. To start using the library - include the header: 29 | ```cpp 30 | #include 31 | ``` 32 | 33 | 1. Create queues in an easy way 34 | ```cpp 35 | fQ F1(4); // first queue is 4 elements(function pointers) long 36 | fQ F2(4); // second queue is 4 elements(function pointers) long 37 | fQ F3(8); // third queue is 8 elements(function pointers) long 38 | fQP F4(3); // third queue is 3 elements(function pointers) 39 | // functions are receiving int32_t argument 40 | ``` 41 | 42 | 43 | 2. Wherever you want, just push your function pointers (and arguments if they needed). 44 | 45 | ```cpp 46 | void button1Interrupt(){ 47 | F1.push(Below used following functions); // void dealAssociatedButton1() – is your task for this button 48 | } 49 | 50 | void button2Interrupt(){ 51 | F2.push(dealAssociatedButton2); // void dealAssociatedButton2() – is your task for this button 52 | F2.push(secondDealAssociatedButton2); // void secondDealAssociatedButton2() – 53 | // is your second task for this button 54 | } 55 | void timer1ElapsedInterrupt(){ 56 | F3.push(dealAssociatedTimer1); // void dealAssociatedTimer1() – 57 | // is your task for this timer1 event 58 | } 59 | void timer2ElapsedInterrupt(){ 60 | F4.push(dealAssociatedTimer2, yourIntArg); // void dealAssociatedTimer2(int yourIntArg) – 61 | // is your task for this timer2 event and the variable yourIntArg 62 | // passed like an argument 63 | } 64 | ``` 65 | 66 | 3. In the main loop: 67 | ```cpp 68 | void loop() { 69 | // put your main code here, to run repeatedly: 70 | F1.pull(); 71 | F2.pull(); 72 | F3.pull(); 73 | F4.pull(); 74 | } 75 | ``` 76 | 77 | This is it. All the interrupts kept as fast as possible; all the task functions/procedures handled. 78 | If you need to wait, do the job: 79 | ```cpp 80 | while(!neededFlag){ 81 | F1.pull(); 82 | F2.pull(); 83 | } 84 | ``` 85 | Or to delay for some time? Utilize a function like: 86 | ```cpp 87 | void DelayOnF1(uint64_t delay){ 88 | uint64_t targetTime = delay + millis(); 89 | while(millis() < targetTime) 90 | F1.pull(); 91 | } 92 | ``` 93 | 94 | 95 | An example: 96 | ```cpp 97 | digitalWrite(13, HIGH); // sets the digital pin 13 on 98 | //delay(1000); //not needed any more 99 | DelayOnF1(1000); // wait by doing 100 | val = analogRead(3); // read the input pin 101 | ``` 102 | 103 | 104 | If you need to pass several arguments – no problem, you may use your own class for a queue: 105 | ```cpp 106 | class testClass{ // it is not used here, just like example how you may pass complex argument to your functions in queue 107 | public: 108 | int array[10]={0,0,0,0,0,0,0,0,0,0}; 109 | float argument = 0.0; 110 | }; 111 | fQP F2(10); 112 | ``` 113 | Instances of your class passed to functions must be of constant size. 114 | 115 | ### Delayed functions 116 | Do you need just to delay some function from execution? Do not wait any more! 117 | Initialize: 118 | ```cpp 119 | del_fQ F5(8); // 'delayed' queue 120 | 121 | ``` 122 | put where you want (here example of 2 functions put into queue): 123 | ```cpp 124 | F5.push_delayed(your_func_1, 1000); // function your_func_1() will be delayed for 1000 'ticks' 125 | F5.push_delayed(your_func_2, 2000); // function your_func_2() will be delayed for 2000 'ticks' 126 | ``` 127 | 128 | in main loop (or other periodic loop) just need to: 129 | ```cpp 130 | void loop() { 131 | ....... 132 | F5.pull(); //execute in loop just this super fast function; 133 | } 134 | ``` 135 | in some timer or periodic function: 136 | ```cpp 137 | F5.tick(); // execute for 'ticks' in timer so the queue class instance will know then to initiate execution 138 | ``` 139 | 140 | ### Delayed functions with parameters 141 | Do you need to delay the execution of a function that receives a parameter? With ANTIRTOS you can do it easily! 142 | Initialize: 143 | ```cpp 144 | del_fQP F6(8); // // maximum 8 'delayed' functions pointers with parameters in queue 145 | 146 | ``` 147 | put where you want (here example of 2 functions put into queue): 148 | ```cpp 149 | F6.push_delayed(your_func_1, 3.14, 1000); // function your_func_1(3.14) will be delayed for 1000 'ticks' 150 | F6.push_delayed(your_func_2, 3.15, 2000); // function your_func_2(3.15) will be delayed for 2000 'ticks' 151 | ``` 152 | 153 | in main loop (or other periodic loop) just need to: 154 | ```cpp 155 | void loop() { 156 | ....... 157 | F6.pull(); //execute in loop just this super fast function; 158 | } 159 | ``` 160 | in some timer or periodic function: 161 | ```cpp 162 | F6.tick(); // execute for 'ticks' in timer so the queue class instance will know then to initiate execution 163 | ``` 164 | 165 | ### Revocation function from conveyers 166 | You may easily revoke your tasks from delayed functional queues like following: 167 | ```cpp 168 | F5.revoke(yourTask); // revoke function (all of the same if there are several of them) from the F5 queue 169 | ``` 170 | Kindly find an example of usage revocation on [Wokwi](https://wokwi.com/projects/411101121732784129)! 171 | 172 | That's it. Enjoy! 173 | 174 | 175 | If you are not sure of interrupts priorities, push to different queues in each interrupt 176 | 177 | You even may improve your job by dividing all your functions into "weight" groups: 178 | 1. Divide all your functions into groups: fast (in one pass), slow-to-complete, and middle functions. 179 | 2. Create a separate queue of pointers to these functions for each type. 180 | 3. Execute the fastest functions instead of waiting inside of the middle functions. 181 | 4. Use the medium and fast pulls for waiting inside of slow procedures. 182 | 183 | Try it on [Wokwi](https://wokwi.com/projects/410932957331738625)! 184 | 185 | -------------------------------------------------------------------------------- /antirtos.h: -------------------------------------------------------------------------------- 1 | // under MIT license, Aleksei Tertychnyi 2 | 3 | #ifndef antirtos_h 4 | #define antirtos_h 5 | /** 6 | * @class fQ 7 | * @brief A queue-based task scheduler for function pointers without parameters. 8 | * 9 | * This class provides a mechanism to manage and execute tasks represented as function pointers. 10 | * Tasks are executed in the order they are added to the queue. 11 | */ 12 | class fQ { 13 | public: 14 | /** 15 | * @typedef fP 16 | * @brief Function pointer type that takes no parameters. 17 | */ 18 | typedef void (*fP)(void); 19 | 20 | /** 21 | * @brief Constructor to initialize the queue with a specified size. 22 | * @param sizeQ The maximum number of tasks the queue can hold. 23 | */ 24 | fQ(int sizeQ); 25 | 26 | /** 27 | * @brief Destructor to clean up dynamically allocated resources. 28 | */ 29 | ~fQ(); 30 | 31 | /** 32 | * @brief Pushes a function pointer into the queue. 33 | * @param pointerF The function pointer to be added to the queue. 34 | * @return 0 if the function was successfully added; 1 if the queue is full. 35 | */ 36 | int push(fP pointerF); 37 | 38 | /** 39 | * @brief Pulls the next function from the queue and executes it. 40 | * @return 0 if a function was successfully executed; 1 if the queue is empty. 41 | */ 42 | int pull(void); 43 | 44 | private: 45 | int first; ///< Index of the first element in the queue. 46 | volatile int last; ///< Index of the last element in the queue. 47 | int length; ///< Maximum size of the queue. 48 | fP* fQueue; ///< Queue for function pointers. 49 | }; 50 | 51 | fQ::fQ(int sizeQ) { // initialization of Queue (constructor) 52 | fQueue = new fP[sizeQ]; 53 | last = 0; 54 | first = 0; 55 | length = sizeQ; 56 | }; 57 | 58 | fQ::~fQ() { // destructor 59 | delete [] fQueue; 60 | }; 61 | 62 | int fQ::push(fP pointerF) { // push element from the queue 63 | if ((last + 1) % length == first) { 64 | return 1; 65 | } 66 | fQueue[last++] = pointerF; 67 | last = last % length; 68 | return 0; 69 | }; 70 | 71 | int fQ::pull(void) { // pull element from the queue 72 | if (last != first) { 73 | fQueue[first++](); 74 | first = first % length; 75 | return 0; 76 | } 77 | else { 78 | return 1; 79 | } 80 | }; 81 | 82 | /** 83 | * @class fQP 84 | * @brief A queue-based task scheduler for function pointers with parameter support. 85 | * 86 | * This template class allows function pointers with associated parameters to be queued 87 | * for execution. Tasks are executed in the order they are added. 88 | * 89 | * @tparam T The type of the parameter passed to the function pointers. 90 | */ 91 | template 92 | class fQP { 93 | public: 94 | /** 95 | * @brief Constructor to initialize the queue with a specified size. 96 | * @param sizeQ The maximum number of tasks the queue can hold. 97 | */ 98 | fQP(int sizeQ); 99 | 100 | /** 101 | * @brief Destructor to clean up dynamically allocated resources. 102 | */ 103 | ~fQP(); 104 | 105 | /** 106 | * @brief Pushes a function pointer and its parameter into the queue. 107 | * @param pointerF The function pointer to be added to the queue. 108 | * @param parameterQ The parameter associated with the function pointer. 109 | * @return 0 if the function was successfully added; 1 if the queue is full. 110 | */ 111 | int push(void (*pointerF)(T), T parameterQ); 112 | 113 | /** 114 | * @brief Pulls the next function from the queue and executes it with its parameter. 115 | * @return 0 if a function was successfully executed; 1 if the queue is empty. 116 | */ 117 | int pull(); 118 | 119 | private: 120 | int first; ///< Index of the first element in the queue. 121 | volatile int last; ///< Index of the last element in the queue. 122 | int length; ///< Maximum size of the queue. 123 | typedef void (*fP)(T); ///< Function pointer type that accepts a parameter of type T. 124 | fP* FP_Queue; ///< Queue for function pointers. 125 | T* PARAMS_array; ///< Queue for parameters associated with the function pointers. 126 | }; 127 | 128 | 129 | template 130 | fQP::fQP(int sizeQ) { // constructor 131 | FP_Queue = new fP[sizeQ]; 132 | PARAMS_array = new T[sizeQ]; 133 | last = 0; 134 | first = 0; 135 | length = sizeQ; 136 | } 137 | 138 | template 139 | fQP::~fQP() { //destructor 140 | delete[] FP_Queue; 141 | delete[] PARAMS_array; 142 | } 143 | 144 | template 145 | int fQP::push(void (*pointerF)(T), T parameterQ) { //push your task into queue 146 | if ((last + 1) % length == first) return 1; 147 | FP_Queue[last] = pointerF; 148 | PARAMS_array[last] = parameterQ; 149 | last = (last + 1) % length; 150 | return 0; 151 | } 152 | 153 | template 154 | int fQP::pull() { // pulls task and parameters from the queue and execute 155 | fP pullVar; 156 | if (last != first) { 157 | T Params = PARAMS_array[first]; 158 | pullVar = FP_Queue[first]; 159 | first = (first + 1) % length; 160 | pullVar(Params); 161 | return 0; 162 | } 163 | else { 164 | return 1; 165 | } 166 | } 167 | 168 | /** 169 | * @class del_fQ 170 | * @brief A queue-based task scheduler for function pointers without parameters, with optional delayed execution. 171 | * 172 | * This class provides a mechanism to manage function pointers with optional delays in their execution. 173 | * Tasks can be executed immediately or scheduled for future execution based on a time delay. 174 | * 175 | */ 176 | class del_fQ { 177 | public: 178 | /** 179 | * @typedef fP 180 | * @brief Function pointer type that takes no parameters. 181 | */ 182 | typedef void (*fP)(void); 183 | 184 | /** 185 | * @brief Constructor to initialize the queue with a specified size. 186 | * @param sizeQ The maximum number of tasks the queue can hold. 187 | */ 188 | del_fQ(int sizeQ); 189 | 190 | /** 191 | * @brief Destructor to clean up dynamically allocated resources. 192 | */ 193 | ~del_fQ(); 194 | 195 | /** 196 | * @brief Pushes a function pointer into the delayed queue for future execution. 197 | * @param pointerF The function pointer to be added to the delayed queue. 198 | * @param delayTime The delay in ticks before the function is executed. 199 | * @return 0 if the function was successfully added; 1 if the delayed queue is full. 200 | */ 201 | int push_delayed(fP pointerF, unsigned long delayTime); 202 | 203 | /** 204 | * @brief Pushes a function pointer into the queue for immediate execution. 205 | * @param pointerF The function pointer to be added to the queue. 206 | * @return 0 if the function was successfully added; 1 if the queue is full. 207 | */ 208 | int push(fP pointerF); 209 | 210 | /** 211 | * @brief Periodic function to manage delayed tasks. Call this method in an ISR or main loop. 212 | */ 213 | void tick(void); 214 | 215 | /** 216 | * @brief Pulls the next function from the queue and executes it. 217 | * @return 0 if a function was successfully executed; 1 if the queue is empty. 218 | */ 219 | int pull(void); 220 | 221 | /** 222 | * @brief Revokes a function pointer from the delayed queue. 223 | * @param pointerF The function pointer to be removed. 224 | * @return 0 if the function pointer was successfully revoked; 1 if it was not found. 225 | */ 226 | int revoke(fP pointerF); 227 | 228 | private: 229 | int first; ///< Index of the first element in the queue. 230 | volatile int last; ///< Index of the last element in the queue. 231 | int length; ///< Maximum size of the queue. 232 | unsigned long time; ///< Current tick count for managing delays. 233 | fP *fQueue; ///< Queue for immediate function pointers. 234 | fP *del_fQueue; ///< Queue for delayed function pointers. 235 | bool *execArr; ///< Execution flags for delayed tasks. 236 | unsigned long *execTime; ///< Execution times for delayed tasks. 237 | }; 238 | 239 | del_fQ::del_fQ(int sizeQ) { // constructor 240 | fQueue = new fP[sizeQ]; 241 | del_fQueue = new fP[sizeQ]; 242 | execArr = new bool[sizeQ]; 243 | execTime = new unsigned long[sizeQ]; 244 | last = 0; 245 | first = 0; 246 | time = 0; 247 | for (unsigned int i = 0; i < sizeQ; i++) { 248 | execArr[i] = false; 249 | } 250 | length = sizeQ; 251 | }; 252 | 253 | del_fQ::~del_fQ() { // destructor 254 | delete [] fQueue; 255 | delete [] del_fQueue; 256 | delete [] execArr; 257 | delete [] execTime; 258 | }; 259 | 260 | int del_fQ::push_delayed(fP pointerF, unsigned long delayTime) { // push element from the queue 261 | bool fullQ = true; // is Queue full? 262 | for (unsigned int i = 0; i < length; i++) { 263 | if (!execArr[i] ) { 264 | del_fQueue[i] = pointerF; // put pointer into exec queue 265 | execArr[i] = true; // true flag for execution 266 | execTime[i] = time + delayTime; //calc execution time, no worry if overload 267 | fullQ = false; 268 | break; 269 | } 270 | } 271 | if (fullQ) return 1; 272 | return 0; 273 | }; 274 | 275 | void del_fQ::tick(void) { // tick method to provide delay functionality, put it into periodic routine 276 | static unsigned int i = 0 ; // uses in search cycle every tick 277 | for (i = 0; i < length; i++) { 278 | if (execTime[i] == time) 279 | if (execArr[i]) { 280 | push(del_fQueue[i]); // bump into normal queue part of delayed Queue 281 | execArr[i] = false; 282 | } 283 | } 284 | time++; 285 | } 286 | 287 | int del_fQ::revoke(fP pointerF) { // revocation of task from the queue in case you do not need it any more 288 | int result = 1; 289 | for (int i = 0; i < length; i++) { 290 | if (del_fQueue[i] == pointerF) { 291 | execArr[i] = false; 292 | result = 0; 293 | } 294 | } 295 | return result; 296 | } 297 | 298 | int del_fQ::push(fP pointerF) { // push element from the queue 299 | if ((last + 1) % length == first) { 300 | return 1; 301 | } 302 | fQueue[last++] = pointerF; 303 | last = last % length; 304 | return 0; 305 | }; 306 | 307 | int del_fQ::pull(void) { // pull element from the queue 308 | if (last != first) { 309 | fQueue[first++](); 310 | first = first % length; 311 | return 0; 312 | } 313 | else { 314 | return 1; 315 | } 316 | }; 317 | 318 | 319 | /** 320 | * @class del_fQP 321 | * @brief A queue-based task scheduler for function pointers with optional delayed execution. 322 | * 323 | * This template class manages function pointers and their execution. It supports both immediate 324 | * and delayed execution of tasks with associated parameters. The class is suitable for 325 | * embedded applications with constrained resources. 326 | * 327 | * @tparam T The type of the parameter passed to the function pointers. 328 | */ 329 | template 330 | class del_fQP { 331 | public: 332 | /** 333 | * @typedef fP 334 | * @brief Function pointer type that accepts a parameter of type T. 335 | */ 336 | typedef void (*fP)(T); 337 | 338 | /** 339 | * @brief Constructor to initialize the queue with a given size. 340 | * @param sizeQ The maximum number of tasks the queue can hold. 341 | */ 342 | del_fQP(int sizeQ); 343 | 344 | /** 345 | * @brief Destructor to clean up dynamically allocated resources. 346 | */ 347 | ~del_fQP(); 348 | 349 | /** 350 | * @brief Pushes a function pointer with its parameter into the queue for immediate execution. 351 | * @param pointerF The function pointer to be pushed. 352 | * @param parameterQ The parameter associated with the function pointer. 353 | * @return 0 if the function was successfully added; 1 if the queue is full. 354 | */ 355 | int push(void (*pointerF)(T), T parameterQ); 356 | 357 | /** 358 | * @brief Pushes a function pointer with its parameter into the delayed queue for future execution. 359 | * @param pointerF The function pointer to be added to the delayed queue. 360 | * @param parameterQ The parameter associated with the function pointer. 361 | * @param delayTime The delay in ticks before the function is executed. 362 | * @return 0 if the function was successfully added; 1 if the delayed queue is full. 363 | */ 364 | int push_delayed(void (*pointerF)(T), T parameterQ, unsigned long delayTime); 365 | 366 | /** 367 | * @brief Periodic function to manage delayed tasks. Call this method in an ISR or main loop. 368 | */ 369 | void tick(void); 370 | 371 | /** 372 | * @brief Revokes a function pointer from the delayed queue. 373 | * @param pointerF The function pointer to be removed. 374 | * @return 0 if the function pointer was successfully revoked; 1 if it was not found. 375 | */ 376 | int revoke(void (*pointerF)(T)); 377 | 378 | /** 379 | * @brief Pulls the next function from the queue and executes it. 380 | * @return 0 if a function was successfully executed; 1 if the queue is empty. 381 | */ 382 | int pull(); 383 | 384 | private: 385 | int first; ///< Index of the first element in the queue. 386 | volatile int last; ///< Index of the last element in the queue. 387 | int length; ///< Maximum size of the queue. 388 | unsigned long time; ///< Current tick count for managing delays. 389 | fP *FP_Queue; ///< Queue for immediate function pointers. 390 | fP *del_FP_Queue; ///< Queue for delayed function pointers. 391 | bool *execArr; ///< Execution flags for delayed tasks. 392 | unsigned long *execTime; ///< Execution times for delayed tasks. 393 | T *PARAMS_array; ///< Parameters for immediate tasks. 394 | T *delayed_PARAMS_array; ///< Parameters for delayed tasks. 395 | }; 396 | 397 | template 398 | del_fQP::del_fQP(int sizeQ) { 399 | FP_Queue = new fP[sizeQ]; 400 | del_FP_Queue = new fP[sizeQ]; 401 | execArr = new bool[sizeQ]; 402 | PARAMS_array = new T[sizeQ]; 403 | delayed_PARAMS_array = new T[sizeQ]; 404 | execTime = new unsigned long[sizeQ]; 405 | last = 0; 406 | first = 0; 407 | time = 0; 408 | for (unsigned int i = 0; i < sizeQ; i++) { 409 | execArr[i] = false; 410 | } 411 | length = sizeQ; 412 | } 413 | 414 | template 415 | del_fQP::~del_fQP() { 416 | delete[] FP_Queue; 417 | delete[] del_FP_Queue; 418 | delete[] PARAMS_array; 419 | delete[] delayed_PARAMS_array; 420 | delete [] execArr; 421 | delete [] execTime; 422 | } 423 | 424 | template 425 | int del_fQP::push(void (*pointerF)(T), T parameterQ) { 426 | if ((last + 1) % length == first) return 1; 427 | FP_Queue[last] = pointerF; 428 | PARAMS_array[last] = parameterQ; 429 | last = (last + 1) % length; 430 | return 0; 431 | } 432 | 433 | template 434 | int del_fQP::push_delayed(void (*pointerF)(T), T parameterQ, unsigned long delayTime) { 435 | bool fullQ = true; // is Queue full? 436 | for (unsigned int i = 0; i < length; i++) { 437 | if (!execArr[i] ) { 438 | del_FP_Queue[i] = pointerF; // put function pointer into exec queue 439 | delayed_PARAMS_array[i] = parameterQ; // put parameter into exec queue 440 | execArr[i] = true; // true flag for execution 441 | execTime[i] = time + delayTime; // calc execution time, no worry if overload 442 | fullQ = false; 443 | break; 444 | } 445 | } 446 | if (fullQ) return 1; 447 | return 0; 448 | } 449 | 450 | template 451 | void del_fQP::tick(void) { 452 | static unsigned int i = 0 ; //uses in search cycle every tick 453 | for (i = 0; i < length; i++) { 454 | if (execTime[i] == time) 455 | if (execArr[i]) { 456 | push(del_FP_Queue[i], delayed_PARAMS_array[i]); // bump into normal queue part of delayed Queue 457 | execArr[i] = false; 458 | } 459 | } 460 | time++; 461 | } 462 | template 463 | int del_fQP::revoke(void (*pointerF)(T)) { // revocation method, revokes 464 | int result = 1; 465 | for (int i = 0; i < length; i++) { 466 | if (del_FP_Queue[i] == pointerF) { 467 | execArr[i] = false; 468 | result = 0; 469 | } 470 | } 471 | return result; 472 | } 473 | 474 | template 475 | int del_fQP::pull() { 476 | fP pullVar; 477 | if (last != first) { 478 | T Params = PARAMS_array[first]; 479 | pullVar = FP_Queue[first]; 480 | first = (first + 1) % length; 481 | pullVar(Params); 482 | return 0; 483 | } 484 | else { 485 | return 1; 486 | } 487 | } 488 | 489 | #endif 490 | --------------------------------------------------------------------------------