|
| 1 | +/**************************************************************************************************************************** |
| 2 | + ISR_8_PWMs_Array.ino |
| 3 | + For AVR ATmega164, ATmega324, ATmega644, ATmega1284 with MightyCore |
| 4 | + Written by Khoi Hoang |
| 5 | +
|
| 6 | + Built by Khoi Hoang https://github.com/khoih-prog/ATmega_Slow_PWM |
| 7 | + Licensed under MIT license |
| 8 | +
|
| 9 | + Now with we can use these new 16 ISR-based PWM channels, while consuming only 1 hwarware Timer. |
| 10 | + Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) |
| 11 | + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers |
| 12 | + Therefore, their executions are not blocked by bad-behaving functions / tasks. |
| 13 | + This important feature is absolutely necessary for mission-critical tasks. |
| 14 | +*****************************************************************************************************************************/ |
| 15 | + |
| 16 | +// Select just 1 TIMER to be true |
| 17 | +#define USE_TIMER_1 true |
| 18 | +#define USE_TIMER_2 false |
| 19 | +// TIMER_3 Only valid for ATmega1284 and ATmega324PB (not ready in core yet) |
| 20 | +#define USE_TIMER_3 false |
| 21 | +// TIMER_4 Only valid for ATmega324PB, not ready in core yet |
| 22 | +#define USE_TIMER_4 false |
| 23 | + |
| 24 | +// These define's must be placed at the beginning before #include "ATmega_Slow_PWM.h" |
| 25 | +// _PWM_LOGLEVEL_ from 0 to 4 |
| 26 | +// Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. |
| 27 | +#define _PWM_LOGLEVEL_ 3 |
| 28 | + |
| 29 | +#if (_PWM_LOGLEVEL_ > 3) |
| 30 | + #if USE_TIMER_1 |
| 31 | + #warning Using Timer1 |
| 32 | + #elif USE_TIMER_3 |
| 33 | + #warning Using Timer3 |
| 34 | + #endif |
| 35 | +#endif |
| 36 | + |
| 37 | +#define USING_MICROS_RESOLUTION true //false |
| 38 | + |
| 39 | +// Default is true, uncomment to false |
| 40 | +//#define CHANGING_PWM_END_OF_CYCLE false |
| 41 | + |
| 42 | +// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error |
| 43 | +#include "ATmega_Slow_PWM.h" |
| 44 | + |
| 45 | +#include <SimpleTimer.h> // https://github.com/jfturcot/SimpleTimer |
| 46 | + |
| 47 | +#define LED_OFF HIGH |
| 48 | +#define LED_ON LOW |
| 49 | + |
| 50 | +#ifndef LED_BUILTIN |
| 51 | + #define LED_BUILTIN 13 |
| 52 | +#endif |
| 53 | + |
| 54 | +#ifndef LED_BLUE |
| 55 | + #define LED_BLUE 10 |
| 56 | +#endif |
| 57 | + |
| 58 | +#ifndef LED_RED |
| 59 | + #define LED_RED 11 |
| 60 | +#endif |
| 61 | + |
| 62 | +#define USING_HW_TIMER_INTERVAL_MS true |
| 63 | + |
| 64 | +// Don't change these numbers to make higher Timer freq. System can hang |
| 65 | +#define HW_TIMER_INTERVAL_MS 0.1f |
| 66 | +#define HW_TIMER_INTERVAL_FREQ 10000L |
| 67 | + |
| 68 | +volatile uint32_t startMicros = 0; |
| 69 | + |
| 70 | +// Init ATmega_Slow_PWM, each can service 16 different ISR-based PWM channels |
| 71 | +ATmega_Slow_PWM ISR_PWM; |
| 72 | + |
| 73 | +////////////////////////////////////////////////////// |
| 74 | + |
| 75 | +void TimerHandler() |
| 76 | +{ |
| 77 | + ISR_PWM.run(); |
| 78 | +} |
| 79 | + |
| 80 | +////////////////////////////////////////////////////// |
| 81 | + |
| 82 | +#define PIN_D0 0 |
| 83 | +#define PIN_D1 1 |
| 84 | +#define PIN_D2 2 |
| 85 | +#define PIN_D3 3 |
| 86 | +#define PIN_D4 4 |
| 87 | +#define PIN_D5 5 |
| 88 | +#define PIN_D6 6 |
| 89 | + |
| 90 | +////////////////////////////////////////////////////// |
| 91 | + |
| 92 | +// You can assign pins here. Be careful to select good pin to use or crash, e.g pin 6-11 |
| 93 | +uint32_t PWM_Pin[] = |
| 94 | +{ |
| 95 | + LED_BUILTIN, PIN_D0, PIN_D1, PIN_D2, PIN_D3, PIN_D4, PIN_D5, PIN_D6 |
| 96 | +}; |
| 97 | + |
| 98 | +#define NUMBER_ISR_PWMS ( sizeof(PWM_Pin) / sizeof(uint32_t) ) |
| 99 | + |
| 100 | +// You can assign any interval for any timer here, in Hz |
| 101 | +float PWM_Freq[] = |
| 102 | +{ |
| 103 | + 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, |
| 104 | +}; |
| 105 | + |
| 106 | +// You can assign any interval for any timer here, in Microseconds |
| 107 | +float PWM_DutyCycle[] = |
| 108 | +{ |
| 109 | + 5.0, 10.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0 |
| 110 | +}; |
| 111 | + |
| 112 | +typedef void (*irqCallback) (); |
| 113 | + |
| 114 | + |
| 115 | +// In Portenta_H7, avoid doing something fancy in ISR, for example complex Serial.print with String() argument |
| 116 | +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment |
| 117 | +// Or you can get this run-time error / crash |
| 118 | +void doingSomething0() |
| 119 | +{ |
| 120 | +} |
| 121 | + |
| 122 | +void doingSomething1() |
| 123 | +{ |
| 124 | +} |
| 125 | + |
| 126 | +void doingSomething2() |
| 127 | +{ |
| 128 | +} |
| 129 | + |
| 130 | +void doingSomething3() |
| 131 | +{ |
| 132 | +} |
| 133 | + |
| 134 | +void doingSomething4() |
| 135 | +{ |
| 136 | +} |
| 137 | + |
| 138 | +void doingSomething5() |
| 139 | +{ |
| 140 | +} |
| 141 | + |
| 142 | +void doingSomething6() |
| 143 | +{ |
| 144 | +} |
| 145 | + |
| 146 | +void doingSomething7() |
| 147 | +{ |
| 148 | +} |
| 149 | + |
| 150 | + |
| 151 | +irqCallback irqCallbackStartFunc[] = |
| 152 | +{ |
| 153 | + doingSomething0, doingSomething1, doingSomething2, doingSomething3, |
| 154 | + doingSomething4, doingSomething5, doingSomething6, doingSomething7 |
| 155 | +}; |
| 156 | + |
| 157 | +//////////////////////////////////////////////// |
| 158 | + |
| 159 | +void setup() |
| 160 | +{ |
| 161 | + Serial.begin(115200); |
| 162 | + while (!Serial && millis() < 5000); |
| 163 | + |
| 164 | + delay(2000); |
| 165 | + |
| 166 | + Serial.print(F("\nStarting ISR_8_PWMs_Array on ")); Serial.println(BOARD_NAME); |
| 167 | + Serial.println(ATMEGA_SLOW_PWM_VERSION); |
| 168 | + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); |
| 169 | + |
| 170 | + // Timer0 is used for micros(), millis(), delay(), etc and can't be used |
| 171 | + // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 |
| 172 | + // Timer 2 is 8-bit timer, only for higher frequency |
| 173 | + // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency |
| 174 | + |
| 175 | +#if USING_HW_TIMER_INTERVAL_MS |
| 176 | + |
| 177 | +///////////////////////////////////////// |
| 178 | + |
| 179 | +#if USE_TIMER_1 |
| 180 | + |
| 181 | + ITimer1.init(); |
| 182 | + |
| 183 | + // Using ATmega324 with 16MHz CPU clock , |
| 184 | + // For 16-bit timer 1, set frequency from 0.2385 to some KHz |
| 185 | + // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz |
| 186 | + |
| 187 | + if (ITimer1.attachInterruptInterval(HW_TIMER_INTERVAL_MS, TimerHandler)) |
| 188 | + { |
| 189 | + Serial.print(F("Starting ITimer1 OK, micros() = ")); Serial.println(micros()); |
| 190 | + } |
| 191 | + else |
| 192 | + Serial.println(F("Can't set ITimer1. Select another freq. or timer")); |
| 193 | + |
| 194 | +#elif USE_TIMER_2 |
| 195 | + |
| 196 | + ITimer2.init(); |
| 197 | + |
| 198 | + if (ITimer2.attachInterruptInterval(HW_TIMER_INTERVAL_MS, TimerHandler)) |
| 199 | + { |
| 200 | + Serial.print(F("Starting ITimer2 OK, micros() = ")); Serial.println(micros()); |
| 201 | + } |
| 202 | + else |
| 203 | + Serial.println(F("Can't set ITimer2. Select another freq. or timer")); |
| 204 | + |
| 205 | +#elif USE_TIMER_3 |
| 206 | + |
| 207 | + ITimer3.init(); |
| 208 | + |
| 209 | + if (ITimer3.attachInterruptInterval(HW_TIMER_INTERVAL_MS, TimerHandler)) |
| 210 | + { |
| 211 | + Serial.print(F("Starting ITimer3 OK, micros() = ")); Serial.println(micros()); |
| 212 | + } |
| 213 | + else |
| 214 | + Serial.println(F("Can't set ITimer3. Select another freq. or timer")); |
| 215 | + |
| 216 | +#endif |
| 217 | + |
| 218 | +///////////////////////////////////////// |
| 219 | + |
| 220 | +#else |
| 221 | + |
| 222 | +///////////////////////////////////////// |
| 223 | + |
| 224 | +#if USE_TIMER_1 |
| 225 | + |
| 226 | + ITimer1.init(); |
| 227 | + |
| 228 | + // Using ATmega324 with 16MHz CPU clock , |
| 229 | + // For 16-bit timer 1, set frequency from 0.2385 to some KHz |
| 230 | + // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz |
| 231 | + |
| 232 | + if (ITimer1.attachInterrupt(HW_TIMER_INTERVAL_FREQ, TimerHandler)) |
| 233 | + { |
| 234 | + Serial.print(F("Starting ITimer1 OK, micros() = ")); Serial.println(micros()); |
| 235 | + } |
| 236 | + else |
| 237 | + Serial.println(F("Can't set ITimer1. Select another freq. or timer")); |
| 238 | + |
| 239 | +#elif USE_TIMER_2 |
| 240 | + |
| 241 | + ITimer2.init(); |
| 242 | + |
| 243 | + if (ITimer2.attachInterrupt(HW_TIMER_INTERVAL_FREQ, TimerHandler)) |
| 244 | + { |
| 245 | + Serial.print(F("Starting ITimer2 OK, micros() = ")); Serial.println(micros()); |
| 246 | + } |
| 247 | + else |
| 248 | + Serial.println(F("Can't set ITimer2. Select another freq. or timer")); |
| 249 | + |
| 250 | +#elif USE_TIMER_3 |
| 251 | + |
| 252 | + ITimer3.init(); |
| 253 | + |
| 254 | + if (ITimer3.attachInterrupt(HW_TIMER_INTERVAL_FREQ, TimerHandler)) |
| 255 | + { |
| 256 | + Serial.print(F("Starting ITimer3 OK, micros() = ")); Serial.println(micros()); |
| 257 | + } |
| 258 | + else |
| 259 | + Serial.println(F("Can't set ITimer3. Select another freq. or timer")); |
| 260 | + |
| 261 | +#endif |
| 262 | + |
| 263 | +#endif |
| 264 | + |
| 265 | + |
| 266 | + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary |
| 267 | + // You can use up to 16 timer for each ISR_PWM |
| 268 | + for (uint16_t i = 0; i < NUMBER_ISR_PWMS; i++) |
| 269 | + { |
| 270 | + //void setPWM(uint32_t pin, float frequency, float dutycycle |
| 271 | + // , timer_callback_p StartCallback = nullptr, timer_callback_p StopCallback = nullptr) |
| 272 | + |
| 273 | + // You can use this with PWM_Freq in Hz |
| 274 | + ISR_PWM.setPWM(PWM_Pin[i], PWM_Freq[i], PWM_DutyCycle[i], irqCallbackStartFunc[i]); |
| 275 | + } |
| 276 | +} |
| 277 | + |
| 278 | +void loop() |
| 279 | +{ |
| 280 | +} |
0 commit comments