|
| 1 | +/**************************************************************************************************************************** |
| 2 | + ISR_8_PWMs_Array.ino |
| 3 | + For Arduino AVR ATtiny-based boards (ATtiny3217, etc.) using megaTinyCore |
| 4 | + Written by Khoi Hoang |
| 5 | +
|
| 6 | + Built by Khoi Hoang https://github.com/khoih-prog/ATtiny_Slow_PWM |
| 7 | + Licensed under MIT license |
| 8 | + |
| 9 | + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by |
| 10 | + unsigned long miliseconds), you just consume only one AVRDx-based timer and avoid conflicting with other cores' tasks. |
| 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 | +// Important Note: To use drag-and-drop into CURIOSITY virtual drive if you can program via Arduino IDE |
| 17 | +// For example, check https://ww1.microchip.com/downloads/en/DeviceDoc/40002193A.pdf |
| 18 | + |
| 19 | +#if !( defined(MEGATINYCORE) ) |
| 20 | + #error This is designed only for MEGATINYCORE megaAVR board! Please check your Tools->Board setting |
| 21 | +#endif |
| 22 | + |
| 23 | +// These define's must be placed at the beginning before #include "ATtiny_Slow_PWM.h" |
| 24 | +// _PWM_LOGLEVEL_ from 0 to 4 |
| 25 | +// Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. |
| 26 | +#define _PWM_LOGLEVEL_ 0 |
| 27 | + |
| 28 | +// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64 |
| 29 | +#define MAX_NUMBER_CHANNELS 16 |
| 30 | + |
| 31 | +// Select USING_FULL_CLOCK == true for 20/16MHz to Timer TCBx => shorter timer, but better accuracy |
| 32 | +// Select USING_HALF_CLOCK == true for 10/ 8MHz to Timer TCBx => shorter timer, but better accuracy |
| 33 | +// Select USING_250KHZ == true for 250KHz to Timer TCBx => longer timer, but worse accuracy |
| 34 | +// Not select for default 250KHz to Timer TCBx => longer timer, but worse accuracy |
| 35 | +#define USING_FULL_CLOCK true |
| 36 | +#define USING_HALF_CLOCK false |
| 37 | +#define USING_250KHZ false // Not supported now |
| 38 | + |
| 39 | +// Try to use RTC, TCA0 or TCD0 for millis() |
| 40 | +#define USE_TIMER_0 true // Check if used by millis(), Servo or tone() |
| 41 | +#define USE_TIMER_1 false // Check if used by millis(), Servo or tone() |
| 42 | + |
| 43 | +#if USE_TIMER_0 |
| 44 | + #define CurrentTimer ITimer0 |
| 45 | +#elif USE_TIMER_1 |
| 46 | + #define CurrentTimer ITimer1 |
| 47 | +#else |
| 48 | + #error You must select one Timer |
| 49 | +#endif |
| 50 | + |
| 51 | +#define USING_MICROS_RESOLUTION true //false |
| 52 | + |
| 53 | +// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error |
| 54 | +#include "ATtiny_Slow_PWM.h" |
| 55 | + |
| 56 | +#include <SimpleTimer.h> // https://github.com/jfturcot/SimpleTimer |
| 57 | + |
| 58 | +#define LED_OFF HIGH |
| 59 | +#define LED_ON LOW |
| 60 | + |
| 61 | +#ifdef LED_BUILTIN |
| 62 | + #undef LED_BUILTIN |
| 63 | + |
| 64 | + // To modify according to your board |
| 65 | + // For Curiosity Nano ATtiny3217 => PIN_PA3 |
| 66 | + #if defined(ARDUINO_AVR_CuriosityNano3217) |
| 67 | + #define LED_BUILTIN PIN_PA3 |
| 68 | + #else |
| 69 | + // standard Arduino pin 13 |
| 70 | + #define LED_BUILTIN PIN_PA3 |
| 71 | + #endif |
| 72 | +#endif |
| 73 | + |
| 74 | +#define USING_HW_TIMER_INTERVAL_MS false //true |
| 75 | + |
| 76 | +// Don't change these numbers to make higher Timer freq. System can hang |
| 77 | +#define HW_TIMER_INTERVAL_MS 0.0333f |
| 78 | +#define HW_TIMER_INTERVAL_FREQ 30000L |
| 79 | + |
| 80 | +volatile uint32_t startMicros = 0; |
| 81 | + |
| 82 | +// Init AT_TINY_SLOW_PWM, each can service max 64 different ISR-based PWM channels |
| 83 | +AT_TINY_SLOW_PWM_ISR ISR_PWM; |
| 84 | + |
| 85 | +////////////////////////////////////////////////////// |
| 86 | + |
| 87 | +void TimerHandler() |
| 88 | +{ |
| 89 | + ISR_PWM.run(); |
| 90 | +} |
| 91 | + |
| 92 | +////////////////////////////////////////////////////// |
| 93 | + |
| 94 | +#define PIN_D0 0 |
| 95 | +#define PIN_D1 1 |
| 96 | +#define PIN_D2 2 |
| 97 | +#define PIN_D3 3 |
| 98 | +#define PIN_D4 4 |
| 99 | +#define PIN_D5 5 |
| 100 | +#define PIN_D6 6 |
| 101 | + |
| 102 | +////////////////////////////////////////////////////// |
| 103 | + |
| 104 | +// You can assign pins here. Be careful to select good pin to use or crash, e.g pin 6-11 |
| 105 | +uint32_t PWM_Pin[] = |
| 106 | +{ |
| 107 | + LED_BUILTIN, PIN_D0, PIN_D1, PIN_D2, PIN_D3, PIN_D4, PIN_D5, PIN_D6 |
| 108 | +}; |
| 109 | + |
| 110 | +#define NUMBER_ISR_PWMS ( sizeof(PWM_Pin) / sizeof(uint32_t) ) |
| 111 | + |
| 112 | +// You can assign any interval for any timer here, in Hz |
| 113 | +float PWM_Freq[] = |
| 114 | +{ |
| 115 | + 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, |
| 116 | +}; |
| 117 | + |
| 118 | +// You can assign any interval for any timer here, in Microseconds |
| 119 | +float PWM_DutyCycle[] = |
| 120 | +{ |
| 121 | + 5.0, 10.0, 20.0, 30.0, 40.0, 45.0, 50.0, 55.0 |
| 122 | +}; |
| 123 | + |
| 124 | +typedef void (*irqCallback) (); |
| 125 | + |
| 126 | + |
| 127 | +// In AVRDx, avoid doing something fancy in ISR, for example complex Serial.print with String() argument |
| 128 | +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment |
| 129 | +// Or you can get this run-time error / crash |
| 130 | +void doingSomething0() |
| 131 | +{ |
| 132 | +} |
| 133 | + |
| 134 | +void doingSomething1() |
| 135 | +{ |
| 136 | +} |
| 137 | + |
| 138 | +void doingSomething2() |
| 139 | +{ |
| 140 | +} |
| 141 | + |
| 142 | +void doingSomething3() |
| 143 | +{ |
| 144 | +} |
| 145 | + |
| 146 | +void doingSomething4() |
| 147 | +{ |
| 148 | +} |
| 149 | + |
| 150 | +void doingSomething5() |
| 151 | +{ |
| 152 | +} |
| 153 | + |
| 154 | +void doingSomething6() |
| 155 | +{ |
| 156 | +} |
| 157 | + |
| 158 | +void doingSomething7() |
| 159 | +{ |
| 160 | +} |
| 161 | + |
| 162 | + |
| 163 | +irqCallback irqCallbackStartFunc[] = |
| 164 | +{ |
| 165 | + doingSomething0, doingSomething1, doingSomething2, doingSomething3, |
| 166 | + doingSomething4, doingSomething5, doingSomething6, doingSomething7 |
| 167 | +}; |
| 168 | + |
| 169 | +//////////////////////////////////////////////// |
| 170 | + |
| 171 | +void setup() |
| 172 | +{ |
| 173 | + Serial.begin(115200); |
| 174 | + while (!Serial && millis() < 5000); |
| 175 | + |
| 176 | + Serial.print(F("\nStarting ISR_8_PWMs_Array on ")); Serial.println(BOARD_NAME); |
| 177 | + Serial.println(AT_TINY_SLOW_PWM_VERSION); |
| 178 | + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); |
| 179 | + Serial.print(F("Max number PWM channels = ")); Serial.println(MAX_NUMBER_CHANNELS); |
| 180 | + |
| 181 | + Serial.print(F("TCB Clock Frequency = ")); |
| 182 | + |
| 183 | +#if USING_FULL_CLOCK |
| 184 | + Serial.println(F("Full clock (20/16MHz, etc) for highest accuracy")); |
| 185 | +#elif USING_HALF_CLOCK |
| 186 | + Serial.println(F("Half clock (10/8MHz, etc.) for high accuracy")); |
| 187 | +#else |
| 188 | + Serial.println(F("250KHz for lower accuracy but longer time")); |
| 189 | +#endif |
| 190 | + |
| 191 | +#if USING_HW_TIMER_INTERVAL_MS |
| 192 | + |
| 193 | + CurrentTimer.init(); |
| 194 | + |
| 195 | + if (CurrentTimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS, TimerHandler)) |
| 196 | + { |
| 197 | + Serial.print(F("Starting ITimer OK, micros() = ")); Serial.println(micros()); |
| 198 | + } |
| 199 | + else |
| 200 | + Serial.println(F("Can't set ITimer. Select another freq. or timer")); |
| 201 | + |
| 202 | +#else |
| 203 | + |
| 204 | + CurrentTimer.init(); |
| 205 | + |
| 206 | + if (CurrentTimer.attachInterrupt(HW_TIMER_INTERVAL_FREQ, TimerHandler)) |
| 207 | + { |
| 208 | + Serial.print(F("Starting ITimer OK, micros() = ")); Serial.println(micros()); |
| 209 | + } |
| 210 | + else |
| 211 | + Serial.println(F("Can't set ITimer. Select another freq. or timer")); |
| 212 | + |
| 213 | +#endif // USING_HW_TIMER_INTERVAL_MS |
| 214 | + |
| 215 | + |
| 216 | + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary |
| 217 | + // You can use up to 16 timer for each ISR_PWM |
| 218 | + for (uint16_t i = 0; i < NUMBER_ISR_PWMS; i++) |
| 219 | + { |
| 220 | + //void setPWM(uint32_t pin, float frequency, float dutycycle |
| 221 | + // , timer_callback_p StartCallback = nullptr, timer_callback_p StopCallback = nullptr) |
| 222 | + |
| 223 | + // You can use this with PWM_Freq in Hz |
| 224 | + ISR_PWM.setPWM(PWM_Pin[i], PWM_Freq[i], PWM_DutyCycle[i], irqCallbackStartFunc[i]); |
| 225 | + } |
| 226 | +} |
| 227 | + |
| 228 | +void loop() |
| 229 | +{ |
| 230 | +} |
0 commit comments