|
| 1 | +/**************************************************************************************************************************** |
| 2 | + ISR_4_PWMs_Array.ino |
| 3 | + For SAMD21/SAMD51 boards |
| 4 | + Written by Khoi Hoang |
| 5 | +
|
| 6 | + Built by Khoi Hoang https://github.com/khoih-prog/SAMD_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 SAMD 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 | +#if !( defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010) \ |
| 17 | + || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) \ |
| 18 | + || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRNB1500) || defined(ARDUINO_SAMD_MKRVIDOR4000) || defined(__SAMD21G18A__) \ |
| 19 | + || defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(__SAMD21G18A__) ) |
| 20 | + #error This code is designed to run on SAMD21 platform! Please check your Tools->Board setting. |
| 21 | +#endif |
| 22 | + |
| 23 | +// These define's must be placed at the beginning before #include "SAMD_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_ 4 |
| 27 | + |
| 28 | +#define USING_MICROS_RESOLUTION true //false |
| 29 | + |
| 30 | +// Default is true, uncomment to false |
| 31 | +//#define CHANGING_PWM_END_OF_CYCLE false |
| 32 | + |
| 33 | +#define MAX_SAMD_PWM_FREQ 1000 |
| 34 | + |
| 35 | +// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error |
| 36 | +#include "SAMD_Slow_PWM.h" |
| 37 | + |
| 38 | +#define LED_OFF LOW |
| 39 | +#define LED_ON HIGH |
| 40 | + |
| 41 | +#ifndef LED_BUILTIN |
| 42 | + #define LED_BUILTIN 13 |
| 43 | +#endif |
| 44 | + |
| 45 | +// Use 50uS for slow SAMD21 |
| 46 | +#define HW_TIMER_INTERVAL_US 50L |
| 47 | + |
| 48 | +uint64_t startMicros = 0; |
| 49 | + |
| 50 | +// Init SAMD timer TIMER_TC3 |
| 51 | +SAMDTimer ITimer(TIMER_TC3); |
| 52 | + |
| 53 | +// Init SAMD timer TIMER_TCC |
| 54 | +//SAMDTimer ITimer(TIMER_TCC); |
| 55 | + |
| 56 | +// Init SAMD_Slow_PWM |
| 57 | +SAMD_Slow_PWM ISR_PWM; |
| 58 | + |
| 59 | +////////////////////////////////////////////////////// |
| 60 | + |
| 61 | +void TimerHandler() |
| 62 | +{ |
| 63 | + ISR_PWM.run(); |
| 64 | +} |
| 65 | + |
| 66 | +////////////////////////////////////////////////////// |
| 67 | + |
| 68 | +// Use max 4 for slow SAMD21 |
| 69 | +#define NUMBER_ISR_PWMS 4 |
| 70 | + |
| 71 | +#define PIN_D2 2 |
| 72 | +#define PIN_D7 7 |
| 73 | +#define PIN_D8 8 |
| 74 | +#define PIN_D9 9 |
| 75 | +#define PIN_D10 10 |
| 76 | +#define PIN_D11 11 |
| 77 | +#define PIN_D12 12 |
| 78 | + |
| 79 | +////////////////////////////////////////////////////// |
| 80 | + |
| 81 | +#define USING_PWM_FREQUENCY true |
| 82 | + |
| 83 | +////////////////////////////////////////////////////// |
| 84 | + |
| 85 | +// You can assign pins here. Be carefull to select good pin to use or crash, e.g pin 6-11 |
| 86 | +uint32_t PWM_Pin[] = |
| 87 | +{ |
| 88 | + LED_BUILTIN, PIN_D2, PIN_D7, PIN_D8 |
| 89 | +}; |
| 90 | + |
| 91 | +// You can assign any interval for any timer here, in microseconds |
| 92 | +double PWM_Period[] = |
| 93 | +{ |
| 94 | + 1000000.0, 500000.0, 333333.333, 50000.0 |
| 95 | +}; |
| 96 | + |
| 97 | +// You can assign any interval for any timer here, in Hz |
| 98 | +double PWM_Freq[] = |
| 99 | +{ |
| 100 | + 1.0, 2.0, 3.0, 4.0 |
| 101 | +}; |
| 102 | + |
| 103 | +// You can assign any interval for any timer here, in milliseconds |
| 104 | +double PWM_DutyCycle[] = |
| 105 | +{ |
| 106 | + 40.00, 45.00, 50.00, 55.00 |
| 107 | +}; |
| 108 | + |
| 109 | +typedef void (*irqCallback) (); |
| 110 | + |
| 111 | + |
| 112 | +// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument |
| 113 | +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment |
| 114 | +// Or you can get this run-time error / crash |
| 115 | +void doingSomething0() |
| 116 | +{ |
| 117 | +} |
| 118 | + |
| 119 | +void doingSomething1() |
| 120 | +{ |
| 121 | +} |
| 122 | + |
| 123 | +void doingSomething2() |
| 124 | +{ |
| 125 | +} |
| 126 | + |
| 127 | +void doingSomething3() |
| 128 | +{ |
| 129 | +} |
| 130 | + |
| 131 | + |
| 132 | +irqCallback irqCallbackStartFunc[] = |
| 133 | +{ |
| 134 | + doingSomething0, doingSomething1, doingSomething2, doingSomething3 |
| 135 | +}; |
| 136 | + |
| 137 | +//////////////////////////////////////////////// |
| 138 | + |
| 139 | +void setup() |
| 140 | +{ |
| 141 | + Serial.begin(115200); |
| 142 | + while (!Serial); |
| 143 | + |
| 144 | + delay(2000); |
| 145 | + |
| 146 | + Serial.print(F("\nStarting ISR_4_PWMs_Array on ")); Serial.println(BOARD_NAME); |
| 147 | + Serial.println(SAMD_SLOW_PWM_VERSION); |
| 148 | + |
| 149 | + // Interval in microsecs |
| 150 | + if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler)) |
| 151 | + { |
| 152 | + startMicros = micros(); |
| 153 | + Serial.print(F("Starting ITimer OK, micros() = ")); Serial.println(startMicros); |
| 154 | + } |
| 155 | + else |
| 156 | + Serial.println(F("Can't set ITimer. Select another freq. or timer")); |
| 157 | + |
| 158 | +#if 1 |
| 159 | + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary |
| 160 | + // You can use up to 16 timer for each ISR_PWM |
| 161 | + for (uint16_t i = 0; i < NUMBER_ISR_PWMS; i++) |
| 162 | + { |
| 163 | + //void setPWM(uint32_t pin, uint32_t frequency, uint32_t dutycycle |
| 164 | + // , timer_callback_p StartCallback = nullptr, timer_callback_p StopCallback = nullptr) |
| 165 | + |
| 166 | +#if USING_PWM_FREQUENCY |
| 167 | + |
| 168 | + // You can use this with PWM_Freq in Hz |
| 169 | + ISR_PWM.setPWM(PWM_Pin[i], PWM_Freq[i], PWM_DutyCycle[i], irqCallbackStartFunc[i]); |
| 170 | + |
| 171 | +#else |
| 172 | + #if USING_MICROS_RESOLUTION |
| 173 | + // Or using period in microsecs resolution |
| 174 | + ISR_PWM.setPWM_Period(PWM_Pin[i], PWM_Period[i], PWM_DutyCycle[i], irqCallbackStartFunc[i]); |
| 175 | + #else |
| 176 | + // Or using period in millisecs resolution |
| 177 | + ISR_PWM.setPWM_Period(PWM_Pin[i], PWM_Period[i] / 1000, PWM_DutyCycle[i], irqCallbackStartFunc[i]); |
| 178 | + #endif |
| 179 | +#endif |
| 180 | + } |
| 181 | +#endif |
| 182 | +} |
| 183 | + |
| 184 | +void loop() |
| 185 | +{ |
| 186 | +} |
0 commit comments