├── keywords.txt ├── library.json ├── library.properties ├── examples ├── ATTiny85 │ └── ATTiny85.ino ├── FanSpeed │ └── FanSpeed.ino └── Interrupt │ └── Interrupt.ino ├── README.md ├── docs └── issue_template.md ├── TimerOne.cpp ├── config └── known_16bit_timers.h └── TimerOne.h /keywords.txt: -------------------------------------------------------------------------------- 1 | Timer1 KEYWORD2 2 | TimerOne KEYWORD1 3 | initialize KEYWORD2 4 | start KEYWORD2 5 | stop KEYWORD2 6 | restart KEYWORD2 7 | resume KEYWORD2 8 | pwm KEYWORD2 9 | disablePwm KEYWORD2 10 | attachInterrupt KEYWORD2 11 | detachInterrupt KEYWORD2 12 | setPeriod KEYWORD2 13 | setPwmDuty KEYWORD2 14 | isrCallback KEYWORD2 15 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TimerOne", 3 | "keywords": "timer", 4 | "description": "Allow to use the built-in Timer1", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/PaulStoffregen/TimerOne.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": 12 | [ 13 | "atmelavr", 14 | "teensy", 15 | "esp32" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=TimerOne 2 | version=1.2 3 | author=Stoyko Dimitrov, Jesse Tane, Jérôme Despatis, Michael Polli, Dan Clemens, Paul Stoffregen, Hagen Patzke 4 | maintainer=Paul Stoffregen 5 | sentence=Use hardware Timer1 for finer PWM control and/or running an periodic interrupt function 6 | paragraph= 7 | category=Timing 8 | url=http://playground.arduino.cc/Code/Timer1 9 | architectures=avr,esp32 10 | 11 | -------------------------------------------------------------------------------- /examples/ATTiny85/ATTiny85.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #define ledPin 4 3 | int ledState = LOW; 4 | 5 | void setup() { 6 | pinMode(ledPin, OUTPUT); 7 | Timer1.initialize(500000); //The led will blink in a half second time interval 8 | Timer1.attachInterrupt(blinkLed); 9 | } 10 | 11 | void loop() { 12 | } 13 | 14 | void blinkLed(){ 15 | ledState = !ledState; 16 | digitalWrite(ledPin, ledState); 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/FanSpeed/FanSpeed.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // This example creates a PWM signal with 25 kHz carrier. 4 | // 5 | // Arduino's analogWrite() gives you PWM output, but no control over the 6 | // carrier frequency. The default frequency is low, typically 490 or 7 | // 3920 Hz. Sometimes you may need a faster carrier frequency. 8 | // 9 | // The specification for 4-wire PWM fans recommends a 25 kHz frequency 10 | // and allows 21 to 28 kHz. The default from analogWrite() might work 11 | // with some fans, but to follow the specification we need 25 kHz. 12 | // 13 | // http://www.formfactors.org/developer/specs/REV1_2_Public.pdf 14 | // 15 | // Connect the PWM pin to the fan's control wire (usually blue). The 16 | // board's ground must be connected to the fan's ground, and the fan 17 | // needs +12 volt power from the computer or a separate power supply. 18 | 19 | const int fanPin = 4; 20 | 21 | void setup(void) 22 | { 23 | Timer1.initialize(40); // 40 us = 25 kHz 24 | Serial.begin(9600); 25 | } 26 | 27 | void loop(void) 28 | { 29 | // slowly increase the PWM fan speed 30 | // 31 | for (float dutyCycle = 30.0; dutyCycle < 100.0; dutyCycle++) { 32 | Serial.print("PWM Fan, Duty Cycle = "); 33 | Serial.println(dutyCycle); 34 | Timer1.pwm(fanPin, (dutyCycle / 100) * 1023); 35 | delay(500); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TimerOne Library 2 | 3 | Paul Stoffregen's modified TimerOne. This version provides 2 main benefits: 4 | 5 | 1: Optimized inline functions - much faster for the most common usage 6 | 2: Support for more boards (including ATTiny85 except for the PWM functionality) 7 | 8 | http://www.pjrc.com/teensy/td_libs_TimerOne.html 9 | 10 | https://github.com/PaulStoffregen/TimerOne 11 | 12 | Original code 13 | 14 | http://playground.arduino.cc/Code/Timer1 15 | 16 | Open Source License 17 | 18 | TimerOne is free software. You can redistribute it and/or modify it under 19 | the terms of Creative Commons Attribution 3.0 United States License. 20 | To view a copy of this license, visit 21 | 22 | http://creativecommons.org/licenses/by/3.0/us/ 23 | 24 | Paul Stoffregen forked this version from an early copy of TimerOne/TimerThree 25 | which was licensed "Creative Commons Attribution 3.0" and has maintained 26 | the original "CC BY 3.0 US" license terms. 27 | 28 | Other, separately developed updates to TimerOne have been released by other 29 | authors under the GNU GPLv2 license. Multiple copies of this library, bearing 30 | the same name but distributed under different license terms, is unfortunately 31 | confusing. This copy, with nearly all the code redesigned as inline functions, 32 | is provided under the "CC BY 3.0 US" license terms. 33 | 34 | ---- 35 | 36 | ## Changes by Hagen Patzke 37 | 38 | Support added for ESP32 39 | 40 | * 2024-08-27: changed for arduino-esp32 3.0.3 41 | Reason: Timer interface changed, see https://github.com/espressif/arduino-esp32/issues/8776 42 | -------------------------------------------------------------------------------- /examples/Interrupt/Interrupt.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // This example uses the timer interrupt to blink an LED 4 | // and also demonstrates how to share a variable between 5 | // the interrupt and the main program. 6 | 7 | 8 | const int led = LED_BUILTIN; // the pin with a LED 9 | 10 | void setup(void) 11 | { 12 | pinMode(led, OUTPUT); 13 | Timer1.initialize(150000); 14 | Timer1.attachInterrupt(blinkLED); // blinkLED to run every 0.15 seconds 15 | Serial.begin(9600); 16 | } 17 | 18 | 19 | // The interrupt will blink the LED, and keep 20 | // track of how many times it has blinked. 21 | int ledState = LOW; 22 | volatile unsigned long blinkCount = 0; // use volatile for shared variables 23 | 24 | void blinkLED(void) 25 | { 26 | if (ledState == LOW) { 27 | ledState = HIGH; 28 | blinkCount = blinkCount + 1; // increase when LED turns on 29 | } else { 30 | ledState = LOW; 31 | } 32 | digitalWrite(led, ledState); 33 | } 34 | 35 | 36 | // The main program will print the blink count 37 | // to the Arduino Serial Monitor 38 | void loop(void) 39 | { 40 | unsigned long blinkCopy; // holds a copy of the blinkCount 41 | 42 | // to read a variable which the interrupt code writes, we 43 | // must temporarily disable interrupts, to be sure it will 44 | // not change while we are reading. To minimize the time 45 | // with interrupts off, just quickly make a copy, and then 46 | // use the copy while allowing the interrupt to keep working. 47 | noInterrupts(); 48 | blinkCopy = blinkCount; 49 | interrupts(); 50 | 51 | Serial.print("blinkCount = "); 52 | Serial.println(blinkCopy); 53 | delay(100); 54 | } 55 | -------------------------------------------------------------------------------- /docs/issue_template.md: -------------------------------------------------------------------------------- 1 | Please use this form only to report code defects or bugs. 2 | 3 | For any question, even questions directly pertaining to this code, post your question on the forums related to the board you are using. 4 | 5 | Arduino: forum.arduino.cc 6 | Teensy: forum.pjrc.com 7 | ESP8266: www.esp8266.com 8 | ESP32: www.esp32.com 9 | Adafruit Feather/Metro/Trinket: forums.adafruit.com 10 | Particle Photon: community.particle.io 11 | 12 | If you are experiencing trouble but not certain of the cause, or need help using this code, ask on the appropriate forum. This is not the place to ask for support or help, even directly related to this code. Only use this form you are certain you have discovered a defect in this code! 13 | 14 | Please verify the problem occurs when using the very latest version, using the newest version of Arduino and any other related software. 15 | 16 | 17 | ----------------------------- Remove above ----------------------------- 18 | 19 | 20 | 21 | ### Description 22 | 23 | Describe your problem. 24 | 25 | 26 | 27 | ### Steps To Reproduce Problem 28 | 29 | Please give detailed instructions needed for anyone to attempt to reproduce the problem. 30 | 31 | 32 | 33 | ### Hardware & Software 34 | 35 | Board 36 | Shields / modules used 37 | Arduino IDE version 38 | Teensyduino version (if using Teensy) 39 | Version info & package name (from Tools > Boards > Board Manager) 40 | Operating system & version 41 | Any other software or hardware? 42 | 43 | 44 | ### Arduino Sketch 45 | 46 | ```cpp 47 | // Change the code below by your sketch (please try to give the smallest code which demonstrates the problem) 48 | #include 49 | 50 | // libraries: give links/details so anyone can compile your code for the same result 51 | 52 | void setup() { 53 | } 54 | 55 | void loop() { 56 | } 57 | ``` 58 | 59 | 60 | ### Errors or Incorrect Output 61 | 62 | If you see any errors or incorrect output, please show it here. Please use copy & paste to give an exact copy of the message. Details matter, so please show (not merely describe) the actual message or error exactly as it appears. 63 | 64 | 65 | -------------------------------------------------------------------------------- /TimerOne.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Interrupt and PWM utilities for 16 bit Timer1 on ATmega168/328 3 | * Original code by Jesse Tane for http://labs.ideo.com August 2008 4 | * Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support 5 | * Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop 6 | * Modified Oct 2009 by Dan Clemens to work with timer1 of the ATMega1280 or Arduino Mega 7 | * Modified April 2012 by Paul Stoffregen 8 | * Modified again, June 2014 by Paul Stoffregen 9 | * Modified July 2017 by Stoyko Dimitrov - added support for ATTiny85 except for the PWM functionality 10 | * Modified March 2021 by Hagen Patzke - add ESP32 support for TZXDuino on ODROID-GO 11 | * 12 | * This is free software. You can redistribute it and/or modify it under 13 | * the terms of Creative Commons Attribution 3.0 United States License. 14 | * To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ 15 | * or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. 16 | * 17 | */ 18 | 19 | #include "TimerOne.h" 20 | 21 | TimerOne Timer1; // preinstantiate 22 | 23 | #if !defined(ESP32) 24 | unsigned short TimerOne::pwmPeriod = 0; 25 | unsigned char TimerOne::clockSelectBits = 0; 26 | void (*TimerOne::isrCallback)() = TimerOne::isrDefaultUnused; 27 | void TimerOne::isrDefaultUnused() { /* noop */; } 28 | #endif // not ESP32 29 | 30 | // interrupt service routine that wraps a user defined function supplied by attachInterrupt 31 | #if defined (__AVR_ATtiny85__) 32 | ISR(TIMER1_COMPA_vect) 33 | { 34 | Timer1.isrCallback(); 35 | } 36 | #elif defined(__AVR__) 37 | ISR(TIMER1_OVF_vect) 38 | { 39 | Timer1.isrCallback(); 40 | } 41 | #elif defined(__arm__) && defined(TEENSYDUINO) && (defined(KINETISK) || defined(KINETISL)) 42 | void ftm1_isr(void) 43 | { 44 | uint32_t sc = FTM1_SC; 45 | #ifdef KINETISL 46 | if (sc & 0x80) FTM1_SC = sc; 47 | #else 48 | if (sc & 0x80) FTM1_SC = sc & 0x7F; 49 | #endif 50 | Timer1.isrCallback(); 51 | } 52 | #elif defined(__arm__) && defined(TEENSYDUINO) && defined(__IMXRT1062__) 53 | void TimerOne::isr(void) 54 | { 55 | FLEXPWM1_SM3STS = FLEXPWM_SMSTS_RF; 56 | Timer1.isrCallback(); 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /config/known_16bit_timers.h: -------------------------------------------------------------------------------- 1 | #ifndef known_16bit_timers_header_ 2 | #define known_16bit_timers_header_ 3 | 4 | // Wiring-S 5 | // 6 | #if defined(__AVR_ATmega644P__) && defined(WIRING) 7 | #define TIMER1_A_PIN 5 8 | #define TIMER1_B_PIN 4 9 | #define TIMER1_ICP_PIN 6 10 | 11 | // Teensy 2.0 12 | // 13 | #elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY) 14 | #define TIMER1_A_PIN 14 15 | #define TIMER1_B_PIN 15 16 | #define TIMER1_C_PIN 4 17 | #define TIMER1_ICP_PIN 22 18 | #define TIMER1_CLK_PIN 11 19 | #define TIMER3_A_PIN 9 20 | #define TIMER3_ICP_PIN 10 21 | 22 | // Teensy++ 2.0 23 | #elif defined(__AVR_AT90USB1286__) && defined(CORE_TEENSY) 24 | #define TIMER1_A_PIN 25 25 | #define TIMER1_B_PIN 26 26 | #define TIMER1_C_PIN 27 27 | #define TIMER1_ICP_PIN 4 28 | #define TIMER1_CLK_PIN 6 29 | #define TIMER3_A_PIN 16 30 | #define TIMER3_B_PIN 15 31 | #define TIMER3_C_PIN 14 32 | #define TIMER3_ICP_PIN 17 33 | #define TIMER3_CLK_PIN 13 34 | 35 | // Teensy 3.0 36 | // 37 | #elif defined(__MK20DX128__) 38 | #define TIMER1_A_PIN 3 39 | #define TIMER1_B_PIN 4 40 | #define TIMER1_ICP_PIN 4 41 | 42 | // Teensy 3.1 / Teensy 3.2 43 | // 44 | #elif defined(__MK20DX256__) 45 | #define TIMER1_A_PIN 3 46 | #define TIMER1_B_PIN 4 47 | #define TIMER1_ICP_PIN 4 48 | #define TIMER3_A_PIN 32 49 | #define TIMER3_B_PIN 25 50 | #define TIMER3_ICP_PIN 32 51 | 52 | // Teensy 3.5 / Teensy 3.6 53 | // 54 | #elif defined(__MK64FX512__) || defined(__MK66FX1M0__) 55 | #define TIMER1_A_PIN 3 56 | #define TIMER1_B_PIN 4 57 | #define TIMER1_ICP_PIN 4 58 | #define TIMER3_A_PIN 29 59 | #define TIMER3_B_PIN 30 60 | #define TIMER3_ICP_PIN 29 61 | 62 | // Teensy-LC 63 | // 64 | #elif defined(__MKL26Z64__) 65 | #define TIMER1_A_PIN 16 66 | #define TIMER1_B_PIN 17 67 | #define TIMER1_ICP_PIN 17 68 | #define TIMER3_A_PIN 3 69 | #define TIMER3_B_PIN 4 70 | #define TIMER3_ICP_PIN 4 71 | 72 | // Teensy 4.0 73 | // 74 | #elif defined(__IMXRT1062__) 75 | #define TIMER1_A_PIN 7 76 | #define TIMER1_B_PIN 8 77 | #define TIMER3_A_PIN 9 78 | #define TIMER3_B_PIN 6 79 | 80 | // Arduino Mega 81 | // 82 | #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 83 | #define TIMER1_A_PIN 11 84 | #define TIMER1_B_PIN 12 85 | #define TIMER3_A_PIN 5 86 | #define TIMER3_B_PIN 2 87 | #define TIMER3_C_PIN 3 88 | #define TIMER4_A_PIN 6 89 | #define TIMER4_B_PIN 7 90 | #define TIMER4_C_PIN 8 91 | #define TIMER4_ICP_PIN 49 92 | #define TIMER5_A_PIN 46 93 | #define TIMER5_B_PIN 45 94 | #define TIMER5_C_PIN 44 95 | #define TIMER3_ICP_PIN 48 96 | #define TIMER3_CLK_PIN 47 97 | 98 | // Arduino Leonardo, Yun, etc 99 | // 100 | #elif defined(__AVR_ATmega32U4__) 101 | #define TIMER1_A_PIN 9 102 | #define TIMER1_B_PIN 10 103 | #define TIMER1_C_PIN 11 104 | #define TIMER1_ICP_PIN 4 105 | #define TIMER1_CLK_PIN 12 106 | #define TIMER3_A_PIN 5 107 | #define TIMER3_ICP_PIN 13 108 | 109 | // Uno, Duemilanove, LilyPad, etc 110 | // 111 | #elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega328P__) || defined (__AVR_ATmega328__) || defined (__AVR_ATmega8__) 112 | #define TIMER1_A_PIN 9 113 | #define TIMER1_B_PIN 10 114 | #define TIMER1_ICP_PIN 8 115 | #define TIMER1_CLK_PIN 5 116 | 117 | // Minicore generic 118 | // 119 | #elif defined(__AVR_ATmega48PB__) || defined(__AVR_ATmega88PB__) || defined(__AVR_ATmega168PB__) 120 | #define TIMER1_A_PIN 9 121 | #define TIMER1_B_PIN 10 122 | #define TIMER1_ICP_PIN 8 123 | #define TIMER1_CLK_PIN 5 124 | #elif defined(__AVR_ATmega328PB__) 125 | #define TIMER1_A_PIN 9 126 | #define TIMER1_B_PIN 10 127 | #define TIMER1_ICP_PIN 8 128 | #define TIMER1_CLK_PIN 5 129 | #define TIMER3_A_PIN 0 130 | #define TIMER3_B_PIN 2 131 | #define TIMER3_ICP_PIN 25 132 | #define TIMER3_CLK_PIN 26 133 | #define TIMER4_A_PIN 1 134 | #define TIMER4_B_PIN 2 135 | #define TIMER4_ICP_PIN 23 136 | #define TIMER4_CLK_PIN 24 137 | 138 | // attiny167 139 | // 140 | #elif defined (__AVR_ATtiny167__) 141 | #define TIMER1_A_PIN 14 142 | #define TIMER1_B_PIN 11 143 | //#define TIMER1_ICP_PIN 8 144 | //#define TIMER1_CLK_PIN 5 145 | 146 | // Sanguino 147 | // 148 | #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) 149 | #define TIMER1_A_PIN 13 150 | #define TIMER1_B_PIN 12 151 | #define TIMER1_ICP_PIN 14 152 | #define TIMER1_CLK_PIN 1 153 | 154 | // Wildfire - Wicked Devices 155 | // 156 | #elif defined(__AVR_ATmega1284P__) && defined(WILDFIRE_VERSION) && WILDFIRE_VERSION >= 3 157 | #define TIMER1_A_PIN 5 // PD5 158 | #define TIMER1_B_PIN 8 // PD4 159 | #define TIMER1_ICP_PIN 6 // PD6 160 | #define TIMER1_CLK_PIN 23 // PB1 161 | #define TIMER3_A_PIN 12 // PB6 162 | #define TIMER3_B_PIN 13 // PB7 163 | #define TIMER3_ICP_PIN 9 // PB5 164 | #define TIMER3_CLK_PIN 0 // PD0 165 | #elif defined(__AVR_ATmega1284P__) && defined(WILDFIRE_VERSION) && WILDFIRE_VERSION < 3 166 | #define TIMER1_A_PIN 5 // PD5 167 | #define TIMER1_B_PIN 4 // PD4 168 | #define TIMER1_ICP_PIN 6 // PD6 169 | #define TIMER1_CLK_PIN 15 // PB1 170 | #define TIMER3_A_PIN 12 // PB6 171 | #define TIMER3_B_PIN 13 // PB7 172 | #define TIMER3_ICP_PIN 11 // PB5 173 | #define TIMER3_CLK_PIN 0 // PD0 174 | 175 | // Mighty-1284 - Maniacbug 176 | // 177 | #elif defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) 178 | #define TIMER1_A_PIN 12 // PD5 179 | #define TIMER1_B_PIN 13 // PD4 180 | #define TIMER1_ICP_PIN 14 // PD6 181 | #define TIMER1_CLK_PIN 1 // PB1 182 | #define TIMER3_A_PIN 6 // PB6 183 | #define TIMER3_B_PIN 7 // PB7 184 | #define TIMER3_ICP_PIN 5 // PB5 185 | #define TIMER3_CLK_PIN 8 // PD0 186 | 187 | #endif 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /TimerOne.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interrupt and PWM utilities for 16 bit Timer1 on ATmega168/328 3 | * Original code by Jesse Tane for http://labs.ideo.com August 2008 4 | * Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support 5 | * Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop 6 | * Modified April 2012 by Paul Stoffregen - portable to other AVR chips, use inline functions 7 | * Modified again, June 2014 by Paul Stoffregen - support Teensy 3.x & even more AVR chips 8 | * Modified July 2017 by Stoyko Dimitrov - added support for ATTiny85 except for the PWM functionality 9 | * Modified March 2021 by Hagen Patzke - add ESP32 support for TZXDuino on ODROID-GO 10 | * 11 | * 12 | * This is free software. You can redistribute it and/or modify it under 13 | * the terms of Creative Commons Attribution 3.0 United States License. 14 | * To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ 15 | * or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. 16 | * 17 | */ 18 | 19 | #ifndef TimerOne_h_ 20 | #define TimerOne_h_ 21 | 22 | #if defined(ARDUINO) && ARDUINO >= 100 23 | #include "Arduino.h" 24 | #else 25 | #include "WProgram.h" 26 | #endif 27 | 28 | #if defined(ESP32) 29 | // ESP32 code derived from TimerInterrupt_Generic by Khoi Hoang 30 | // ESP32 has an operating system / SDK layer we need to use 31 | #include 32 | #define MAX_ESP32_NUM_TIMERS 4 33 | typedef void (*timer_callback) (void); 34 | #else // not ESP32 35 | #include "config/known_16bit_timers.h" 36 | #if defined (__AVR_ATtiny85__) 37 | #define TIMER1_RESOLUTION 256UL // Timer1 is 8 bit 38 | #elif defined(__AVR__) 39 | #define TIMER1_RESOLUTION 65536UL // Timer1 is 16 bit 40 | #else 41 | #define TIMER1_RESOLUTION 65536UL // assume 16 bits for non-AVR chips 42 | #endif 43 | #endif // (not) ESP32 44 | 45 | // Placing nearly all the code in this .h file allows the functions to be 46 | // inlined by the compiler. In the very common case with constant values 47 | // the compiler will perform all calculations and simply write constants 48 | // to the hardware registers (for example, setPeriod). 49 | 50 | 51 | class TimerOne 52 | { 53 | 54 | #if defined (__AVR_ATtiny85__) 55 | public: 56 | //**************************** 57 | // Configuration 58 | //**************************** 59 | void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { 60 | TCCR1 = _BV(CTC1); //clear timer1 when it matches the value in OCR1C 61 | TIMSK |= _BV(OCIE1A); //enable interrupt when OCR1A matches the timer value 62 | setPeriod(microseconds); 63 | } 64 | void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { 65 | const unsigned long cycles = microseconds * ratio; 66 | if (cycles < TIMER1_RESOLUTION) { 67 | clockSelectBits = _BV(CS10); 68 | pwmPeriod = cycles; 69 | } else 70 | if (cycles < TIMER1_RESOLUTION * 2UL) { 71 | clockSelectBits = _BV(CS11); 72 | pwmPeriod = cycles / 2; 73 | } else 74 | if (cycles < TIMER1_RESOLUTION * 4UL) { 75 | clockSelectBits = _BV(CS11) | _BV(CS10); 76 | pwmPeriod = cycles / 4; 77 | } else 78 | if (cycles < TIMER1_RESOLUTION * 8UL) { 79 | clockSelectBits = _BV(CS12); 80 | pwmPeriod = cycles / 8; 81 | } else 82 | if (cycles < TIMER1_RESOLUTION * 16UL) { 83 | clockSelectBits = _BV(CS12) | _BV(CS10); 84 | pwmPeriod = cycles / 16; 85 | } else 86 | if (cycles < TIMER1_RESOLUTION * 32UL) { 87 | clockSelectBits = _BV(CS12) | _BV(CS11); 88 | pwmPeriod = cycles / 32; 89 | } else 90 | if (cycles < TIMER1_RESOLUTION * 64UL) { 91 | clockSelectBits = _BV(CS12) | _BV(CS11) | _BV(CS10); 92 | pwmPeriod = cycles / 64UL; 93 | } else 94 | if (cycles < TIMER1_RESOLUTION * 128UL) { 95 | clockSelectBits = _BV(CS13); 96 | pwmPeriod = cycles / 128; 97 | } else 98 | if (cycles < TIMER1_RESOLUTION * 256UL) { 99 | clockSelectBits = _BV(CS13) | _BV(CS10); 100 | pwmPeriod = cycles / 256; 101 | } else 102 | if (cycles < TIMER1_RESOLUTION * 512UL) { 103 | clockSelectBits = _BV(CS13) | _BV(CS11); 104 | pwmPeriod = cycles / 512; 105 | } else 106 | if (cycles < TIMER1_RESOLUTION * 1024UL) { 107 | clockSelectBits = _BV(CS13) | _BV(CS11) | _BV(CS10); 108 | pwmPeriod = cycles / 1024; 109 | } else 110 | if (cycles < TIMER1_RESOLUTION * 2048UL) { 111 | clockSelectBits = _BV(CS13) | _BV(CS12); 112 | pwmPeriod = cycles / 2048; 113 | } else 114 | if (cycles < TIMER1_RESOLUTION * 4096UL) { 115 | clockSelectBits = _BV(CS13) | _BV(CS12) | _BV(CS10); 116 | pwmPeriod = cycles / 4096; 117 | } else 118 | if (cycles < TIMER1_RESOLUTION * 8192UL) { 119 | clockSelectBits = _BV(CS13) | _BV(CS12) | _BV(CS11); 120 | pwmPeriod = cycles / 8192; 121 | } else 122 | if (cycles < TIMER1_RESOLUTION * 16384UL) { 123 | clockSelectBits = _BV(CS13) | _BV(CS12) | _BV(CS11) | _BV(CS10); 124 | pwmPeriod = cycles / 16384; 125 | } else { 126 | clockSelectBits = _BV(CS13) | _BV(CS12) | _BV(CS11) | _BV(CS10); 127 | pwmPeriod = TIMER1_RESOLUTION - 1; 128 | } 129 | OCR1A = pwmPeriod; 130 | OCR1C = pwmPeriod; 131 | TCCR1 = _BV(CTC1) | clockSelectBits; 132 | } 133 | 134 | //**************************** 135 | // Run Control 136 | //**************************** 137 | void start() __attribute__((always_inline)) { 138 | TCCR1 = 0; 139 | TCNT1 = 0; 140 | resume(); 141 | } 142 | void stop() __attribute__((always_inline)) { 143 | TCCR1 = _BV(CTC1); 144 | } 145 | void restart() __attribute__((always_inline)) { 146 | start(); 147 | } 148 | void resume() __attribute__((always_inline)) { 149 | TCCR1 = _BV(CTC1) | clockSelectBits; 150 | } 151 | 152 | //**************************** 153 | // PWM outputs 154 | //**************************** 155 | //Not implemented yet for ATTiny85 156 | //TO DO 157 | 158 | //**************************** 159 | // Interrupt Function 160 | //**************************** 161 | void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { 162 | isrCallback = isr; 163 | TIMSK |= _BV(OCIE1A); 164 | } 165 | void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { 166 | if(microseconds > 0) setPeriod(microseconds); 167 | attachInterrupt(isr); 168 | } 169 | void detachInterrupt() __attribute__((always_inline)) { 170 | //TIMSK = 0; // Timer 0 and Timer 1 both use TIMSK register so setting it to 0 will override settings for Timer1 as well 171 | TIMSK &= ~_BV(OCIE1A); 172 | } 173 | static void (*isrCallback)(); 174 | static void isrDefaultUnused(); 175 | 176 | private: 177 | static unsigned short pwmPeriod; 178 | static unsigned char clockSelectBits; 179 | static const byte ratio = (F_CPU)/ ( 1000000 ); 180 | 181 | #elif defined(__AVR__) 182 | 183 | #if defined (__AVR_ATmega8__) 184 | //in some io definitions for older microcontrollers TIMSK is used instead of TIMSK1 185 | #define TIMSK1 TIMSK 186 | #endif 187 | 188 | public: 189 | //**************************** 190 | // Configuration 191 | //**************************** 192 | void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { 193 | TCCR1B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timer 194 | TCCR1A = 0; // clear control register A 195 | setPeriod(microseconds); 196 | } 197 | void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { 198 | const unsigned long cycles = ((F_CPU/100000 * microseconds) / 20); 199 | if (cycles < TIMER1_RESOLUTION) { 200 | clockSelectBits = _BV(CS10); 201 | pwmPeriod = cycles; 202 | } else 203 | if (cycles < TIMER1_RESOLUTION * 8) { 204 | clockSelectBits = _BV(CS11); 205 | pwmPeriod = cycles / 8; 206 | } else 207 | if (cycles < TIMER1_RESOLUTION * 64) { 208 | clockSelectBits = _BV(CS11) | _BV(CS10); 209 | pwmPeriod = cycles / 64; 210 | } else 211 | if (cycles < TIMER1_RESOLUTION * 256) { 212 | clockSelectBits = _BV(CS12); 213 | pwmPeriod = cycles / 256; 214 | } else 215 | if (cycles < TIMER1_RESOLUTION * 1024) { 216 | clockSelectBits = _BV(CS12) | _BV(CS10); 217 | pwmPeriod = cycles / 1024; 218 | } else { 219 | clockSelectBits = _BV(CS12) | _BV(CS10); 220 | pwmPeriod = TIMER1_RESOLUTION - 1; 221 | } 222 | ICR1 = pwmPeriod; 223 | TCCR1B = _BV(WGM13) | clockSelectBits; 224 | } 225 | 226 | //**************************** 227 | // Run Control 228 | //**************************** 229 | void start() __attribute__((always_inline)) { 230 | TCCR1B = 0; 231 | TCNT1 = 0; // TODO: does this cause an undesired interrupt? 232 | resume(); 233 | } 234 | void stop() __attribute__((always_inline)) { 235 | TCCR1B = _BV(WGM13); 236 | } 237 | void restart() __attribute__((always_inline)) { 238 | start(); 239 | } 240 | void resume() __attribute__((always_inline)) { 241 | TCCR1B = _BV(WGM13) | clockSelectBits; 242 | } 243 | 244 | //**************************** 245 | // PWM outputs 246 | //**************************** 247 | void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) { 248 | unsigned long dutyCycle = pwmPeriod; 249 | dutyCycle *= duty; 250 | dutyCycle >>= 10; 251 | if (pin == TIMER1_A_PIN) OCR1A = dutyCycle; 252 | #ifdef TIMER1_B_PIN 253 | else if (pin == TIMER1_B_PIN) OCR1B = dutyCycle; 254 | #endif 255 | #ifdef TIMER1_C_PIN 256 | else if (pin == TIMER1_C_PIN) OCR1C = dutyCycle; 257 | #endif 258 | } 259 | void pwm(char pin, unsigned int duty) __attribute__((always_inline)) { 260 | if (pin == TIMER1_A_PIN) { pinMode(TIMER1_A_PIN, OUTPUT); TCCR1A |= _BV(COM1A1); } 261 | #ifdef TIMER1_B_PIN 262 | else if (pin == TIMER1_B_PIN) { pinMode(TIMER1_B_PIN, OUTPUT); TCCR1A |= _BV(COM1B1); } 263 | #endif 264 | #ifdef TIMER1_C_PIN 265 | else if (pin == TIMER1_C_PIN) { pinMode(TIMER1_C_PIN, OUTPUT); TCCR1A |= _BV(COM1C1); } 266 | #endif 267 | setPwmDuty(pin, duty); 268 | TCCR1B = _BV(WGM13) | clockSelectBits; 269 | } 270 | void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) { 271 | if (microseconds > 0) setPeriod(microseconds); 272 | pwm(pin, duty); 273 | } 274 | void disablePwm(char pin) __attribute__((always_inline)) { 275 | if (pin == TIMER1_A_PIN) TCCR1A &= ~_BV(COM1A1); 276 | #ifdef TIMER1_B_PIN 277 | else if (pin == TIMER1_B_PIN) TCCR1A &= ~_BV(COM1B1); 278 | #endif 279 | #ifdef TIMER1_C_PIN 280 | else if (pin == TIMER1_C_PIN) TCCR1A &= ~_BV(COM1C1); 281 | #endif 282 | } 283 | 284 | //**************************** 285 | // Interrupt Function 286 | //**************************** 287 | 288 | void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { 289 | isrCallback = isr; 290 | TIMSK1 = _BV(TOIE1); 291 | } 292 | void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { 293 | if(microseconds > 0) setPeriod(microseconds); 294 | attachInterrupt(isr); 295 | } 296 | void detachInterrupt() __attribute__((always_inline)) { 297 | TIMSK1 = 0; 298 | } 299 | static void (*isrCallback)(); 300 | static void isrDefaultUnused(); 301 | 302 | private: 303 | // properties 304 | static unsigned short pwmPeriod; 305 | static unsigned char clockSelectBits; 306 | 307 | 308 | 309 | 310 | 311 | 312 | #elif defined(__arm__) && defined(TEENSYDUINO) && (defined(KINETISK) || defined(KINETISL)) 313 | 314 | #if defined(KINETISK) 315 | #define F_TIMER F_BUS 316 | #elif defined(KINETISL) 317 | #define F_TIMER (F_PLL/2) 318 | #endif 319 | 320 | // Use only 15 bit resolution. From K66 reference manual, 45.5.7 page 1200: 321 | // The CPWM pulse width (duty cycle) is determined by 2 x (CnV - CNTIN) and the 322 | // period is determined by 2 x (MOD - CNTIN). See the following figure. MOD must be 323 | // kept in the range of 0x0001 to 0x7FFF because values outside this range can produce 324 | // ambiguous results. 325 | #undef TIMER1_RESOLUTION 326 | #define TIMER1_RESOLUTION 32768 327 | 328 | public: 329 | //**************************** 330 | // Configuration 331 | //**************************** 332 | void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { 333 | setPeriod(microseconds); 334 | } 335 | void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { 336 | const unsigned long cycles = (F_TIMER / 2000000) * microseconds; 337 | // A much faster if-else 338 | // This is like a binary serch tree and no more than 3 conditions are evaluated. 339 | // I haven't checked if this becomes significantly longer ASM than the simple ladder. 340 | // It looks very similar to the ladder tho: same # of if's and else's 341 | 342 | /* 343 | // This code does not work properly in all cases :( 344 | // https://github.com/PaulStoffregen/TimerOne/issues/17 345 | if (cycles < TIMER1_RESOLUTION * 16) { 346 | if (cycles < TIMER1_RESOLUTION * 4) { 347 | if (cycles < TIMER1_RESOLUTION) { 348 | clockSelectBits = 0; 349 | pwmPeriod = cycles; 350 | }else{ 351 | clockSelectBits = 1; 352 | pwmPeriod = cycles >> 1; 353 | } 354 | }else{ 355 | if (cycles < TIMER1_RESOLUTION * 8) { 356 | clockSelectBits = 3; 357 | pwmPeriod = cycles >> 3; 358 | }else{ 359 | clockSelectBits = 4; 360 | pwmPeriod = cycles >> 4; 361 | } 362 | } 363 | }else{ 364 | if (cycles > TIMER1_RESOLUTION * 64) { 365 | if (cycles > TIMER1_RESOLUTION * 128) { 366 | clockSelectBits = 7; 367 | pwmPeriod = TIMER1_RESOLUTION - 1; 368 | }else{ 369 | clockSelectBits = 7; 370 | pwmPeriod = cycles >> 7; 371 | } 372 | } 373 | else{ 374 | if (cycles > TIMER1_RESOLUTION * 32) { 375 | clockSelectBits = 6; 376 | pwmPeriod = cycles >> 6; 377 | }else{ 378 | clockSelectBits = 5; 379 | pwmPeriod = cycles >> 5; 380 | } 381 | } 382 | } 383 | */ 384 | if (cycles < TIMER1_RESOLUTION) { 385 | clockSelectBits = 0; 386 | pwmPeriod = cycles; 387 | } else 388 | if (cycles < TIMER1_RESOLUTION * 2) { 389 | clockSelectBits = 1; 390 | pwmPeriod = cycles >> 1; 391 | } else 392 | if (cycles < TIMER1_RESOLUTION * 4) { 393 | clockSelectBits = 2; 394 | pwmPeriod = cycles >> 2; 395 | } else 396 | if (cycles < TIMER1_RESOLUTION * 8) { 397 | clockSelectBits = 3; 398 | pwmPeriod = cycles >> 3; 399 | } else 400 | if (cycles < TIMER1_RESOLUTION * 16) { 401 | clockSelectBits = 4; 402 | pwmPeriod = cycles >> 4; 403 | } else 404 | if (cycles < TIMER1_RESOLUTION * 32) { 405 | clockSelectBits = 5; 406 | pwmPeriod = cycles >> 5; 407 | } else 408 | if (cycles < TIMER1_RESOLUTION * 64) { 409 | clockSelectBits = 6; 410 | pwmPeriod = cycles >> 6; 411 | } else 412 | if (cycles < TIMER1_RESOLUTION * 128) { 413 | clockSelectBits = 7; 414 | pwmPeriod = cycles >> 7; 415 | } else { 416 | clockSelectBits = 7; 417 | pwmPeriod = TIMER1_RESOLUTION - 1; 418 | } 419 | 420 | uint32_t sc = FTM1_SC; 421 | FTM1_SC = 0; 422 | FTM1_MOD = pwmPeriod; 423 | FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_CPWMS | clockSelectBits | (sc & FTM_SC_TOIE); 424 | } 425 | 426 | //**************************** 427 | // Run Control 428 | //**************************** 429 | void start() __attribute__((always_inline)) { 430 | stop(); 431 | FTM1_CNT = 0; 432 | resume(); 433 | } 434 | void stop() __attribute__((always_inline)) { 435 | FTM1_SC = FTM1_SC & (FTM_SC_TOIE | FTM_SC_CPWMS | FTM_SC_PS(7)); 436 | } 437 | void restart() __attribute__((always_inline)) { 438 | start(); 439 | } 440 | void resume() __attribute__((always_inline)) { 441 | FTM1_SC = (FTM1_SC & (FTM_SC_TOIE | FTM_SC_PS(7))) | FTM_SC_CPWMS | FTM_SC_CLKS(1); 442 | } 443 | 444 | //**************************** 445 | // PWM outputs 446 | //**************************** 447 | void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) { 448 | unsigned long dutyCycle = pwmPeriod; 449 | dutyCycle *= duty; 450 | dutyCycle >>= 10; 451 | if (pin == TIMER1_A_PIN) { 452 | FTM1_C0V = dutyCycle; 453 | } else if (pin == TIMER1_B_PIN) { 454 | FTM1_C1V = dutyCycle; 455 | } 456 | } 457 | void pwm(char pin, unsigned int duty) __attribute__((always_inline)) { 458 | setPwmDuty(pin, duty); 459 | if (pin == TIMER1_A_PIN) { 460 | *portConfigRegister(TIMER1_A_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; 461 | } else if (pin == TIMER1_B_PIN) { 462 | *portConfigRegister(TIMER1_B_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; 463 | } 464 | } 465 | void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) { 466 | if (microseconds > 0) setPeriod(microseconds); 467 | pwm(pin, duty); 468 | } 469 | void disablePwm(char pin) __attribute__((always_inline)) { 470 | if (pin == TIMER1_A_PIN) { 471 | *portConfigRegister(TIMER1_A_PIN) = 0; 472 | } else if (pin == TIMER1_B_PIN) { 473 | *portConfigRegister(TIMER1_B_PIN) = 0; 474 | } 475 | } 476 | 477 | //**************************** 478 | // Interrupt Function 479 | //**************************** 480 | void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { 481 | isrCallback = isr; 482 | FTM1_SC |= FTM_SC_TOIE; 483 | NVIC_ENABLE_IRQ(IRQ_FTM1); 484 | } 485 | void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { 486 | if(microseconds > 0) setPeriod(microseconds); 487 | attachInterrupt(isr); 488 | } 489 | void detachInterrupt() __attribute__((always_inline)) { 490 | FTM1_SC &= ~FTM_SC_TOIE; 491 | NVIC_DISABLE_IRQ(IRQ_FTM1); 492 | } 493 | static void (*isrCallback)(); 494 | static void isrDefaultUnused(); 495 | 496 | private: 497 | // properties 498 | static unsigned short pwmPeriod; 499 | static unsigned char clockSelectBits; 500 | 501 | #undef F_TIMER 502 | 503 | #elif defined(__arm__) && defined(TEENSYDUINO) && defined(__IMXRT1062__) 504 | 505 | public: 506 | //**************************** 507 | // Configuration 508 | //**************************** 509 | void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { 510 | setPeriod(microseconds); 511 | } 512 | void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { 513 | uint32_t period = (float)F_BUS_ACTUAL * (float)microseconds * 0.0000005f; 514 | uint32_t prescale = 0; 515 | while (period > 32767) { 516 | period = period >> 1; 517 | if (++prescale > 7) { 518 | prescale = 7; // when F_BUS is 150 MHz, longest 519 | period = 32767; // period is 55922 us (~17.9 Hz) 520 | break; 521 | } 522 | } 523 | //Serial.printf("setPeriod, period=%u, prescale=%u\n", period, prescale); 524 | FLEXPWM1_FCTRL0 |= FLEXPWM_FCTRL0_FLVL(8); // logic high = fault 525 | FLEXPWM1_FSTS0 = 0x0008; // clear fault status 526 | FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8); 527 | FLEXPWM1_SM3CTRL2 = FLEXPWM_SMCTRL2_INDEP; 528 | FLEXPWM1_SM3CTRL = FLEXPWM_SMCTRL_HALF | FLEXPWM_SMCTRL_PRSC(prescale); 529 | FLEXPWM1_SM3INIT = -period; 530 | FLEXPWM1_SM3VAL0 = 0; 531 | FLEXPWM1_SM3VAL1 = period; 532 | FLEXPWM1_SM3VAL2 = 0; 533 | FLEXPWM1_SM3VAL3 = 0; 534 | FLEXPWM1_SM3VAL4 = 0; 535 | FLEXPWM1_SM3VAL5 = 0; 536 | FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8) | FLEXPWM_MCTRL_RUN(8); 537 | pwmPeriod = period; 538 | } 539 | //**************************** 540 | // Run Control 541 | //**************************** 542 | void start() __attribute__((always_inline)) { 543 | stop(); 544 | // TODO: how to force counter back to zero? 545 | resume(); 546 | } 547 | void stop() __attribute__((always_inline)) { 548 | FLEXPWM1_MCTRL &= ~FLEXPWM_MCTRL_RUN(8); 549 | } 550 | void restart() __attribute__((always_inline)) { 551 | start(); 552 | } 553 | void resume() __attribute__((always_inline)) { 554 | FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_RUN(8); 555 | } 556 | 557 | //**************************** 558 | // PWM outputs 559 | //**************************** 560 | void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) { 561 | if (duty > 1023) duty = 1023; 562 | int dutyCycle = (pwmPeriod * duty) >> 10; 563 | //Serial.printf("setPwmDuty, period=%u\n", dutyCycle); 564 | if (pin == TIMER1_A_PIN) { 565 | FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8); 566 | FLEXPWM1_SM3VAL5 = dutyCycle; 567 | FLEXPWM1_SM3VAL4 = -dutyCycle; 568 | FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8); 569 | } else if (pin == TIMER1_B_PIN) { 570 | FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8); 571 | FLEXPWM1_SM3VAL3 = dutyCycle; 572 | FLEXPWM1_SM3VAL2 = -dutyCycle; 573 | FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8); 574 | } 575 | } 576 | void pwm(char pin, unsigned int duty) __attribute__((always_inline)) { 577 | setPwmDuty(pin, duty); 578 | if (pin == TIMER1_A_PIN) { 579 | FLEXPWM1_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(8); 580 | IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_01 = 6; // pin 7 FLEXPWM1_PWM3_B 581 | } else if (pin == TIMER1_B_PIN) { 582 | FLEXPWM1_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(8); 583 | IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00 = 6; // pin 8 FLEXPWM1_PWM3_A 584 | } 585 | } 586 | void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) { 587 | if (microseconds > 0) setPeriod(microseconds); 588 | pwm(pin, duty); 589 | } 590 | void disablePwm(char pin) __attribute__((always_inline)) { 591 | if (pin == TIMER1_A_PIN) { 592 | IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_01 = 5; // pin 7 FLEXPWM1_PWM3_B 593 | FLEXPWM1_OUTEN &= ~FLEXPWM_OUTEN_PWMB_EN(8); 594 | } else if (pin == TIMER1_B_PIN) { 595 | IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00 = 5; // pin 8 FLEXPWM1_PWM3_A 596 | FLEXPWM1_OUTEN &= ~FLEXPWM_OUTEN_PWMA_EN(8); 597 | } 598 | } 599 | //**************************** 600 | // Interrupt Function 601 | //**************************** 602 | void attachInterrupt(void (*f)()) __attribute__((always_inline)) { 603 | isrCallback = f; 604 | attachInterruptVector(IRQ_FLEXPWM1_3, &isr); 605 | FLEXPWM1_SM3STS = FLEXPWM_SMSTS_RF; 606 | FLEXPWM1_SM3INTEN = FLEXPWM_SMINTEN_RIE; 607 | NVIC_ENABLE_IRQ(IRQ_FLEXPWM1_3); 608 | } 609 | void attachInterrupt(void (*f)(), unsigned long microseconds) __attribute__((always_inline)) { 610 | if(microseconds > 0) setPeriod(microseconds); 611 | attachInterrupt(f); 612 | } 613 | void detachInterrupt() __attribute__((always_inline)) { 614 | NVIC_DISABLE_IRQ(IRQ_FLEXPWM1_3); 615 | FLEXPWM1_SM3INTEN = 0; 616 | } 617 | static void isr(void); 618 | static void (*isrCallback)(); 619 | static void isrDefaultUnused(); 620 | 621 | private: 622 | // properties 623 | static unsigned short pwmPeriod; 624 | static unsigned char clockSelectBits; 625 | 626 | 627 | 628 | 629 | 630 | 631 | #elif defined(ESP32) 632 | 633 | // ESP32 code inspired by TimerInterrupt_Generic by Khoi Hoang 634 | // ESP32 has an operating system / SDK layer we need to use 635 | 636 | private: 637 | hw_timer_t* _timer; 638 | 639 | public: 640 | //**************************** 641 | // Configuration 642 | //**************************** 643 | void initialize(unsigned long microseconds = 1000000UL) __attribute__((always_inline)) { 644 | #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) 645 | uint32_t frequency = 1000000 / microseconds; 646 | _timer = timerBegin(frequency); 647 | #else 648 | // setup timer 1 with a divider of 80 for 1 MHz 649 | _timer = timerBegin(1, 80, true); // count up = true 650 | timerStart(_timer); 651 | setPeriod(microseconds); 652 | #endif 653 | } 654 | // Any method called from within the ISR must have IRAM_ATTR set! 655 | void IRAM_ATTR setPeriod(unsigned long microseconds) __attribute__((always_inline)) { 656 | #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) 657 | timerAlarm(_timer, microseconds, true, 0 /* repeat=unlimited */); 658 | timerRestart(_timer); //compatibility to Arduino: starts when setPeriod is called 659 | #else 660 | // number of counts is in microseconds, desired_freq = (1MHz / microseconds) 661 | // count == base_frequency / desired_freq == 1MHz / (1MHz / period) == period 662 | timerAlarmWrite(_timer, microseconds, true); // autoreload = true to run forever 663 | timerAlarmEnable(_timer); 664 | timerRestart(_timer); //compatibility to Arduino: starts when setPeriod is called 665 | #endif 666 | } 667 | //**************************** 668 | // Run Control 669 | //**************************** 670 | void start() __attribute__((always_inline)) { 671 | timerStart(_timer); 672 | } 673 | void stop() __attribute__((always_inline)) { 674 | timerStop(_timer); 675 | } 676 | void restart() __attribute__((always_inline)) { 677 | timerRestart(_timer); 678 | } 679 | void resume() __attribute__((always_inline)) { 680 | timerStart(_timer); 681 | } 682 | 683 | //**************************** 684 | // PWM outputs 685 | //**************************** 686 | // NOT IMPLEMENTED FOR ESP32 687 | 688 | //**************************** 689 | // Interrupt Function 690 | //**************************** 691 | void attachInterrupt(timer_callback isr) __attribute__((always_inline)) { 692 | #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) 693 | timerAttachInterrupt(_timer, isr); 694 | #else 695 | timerAttachInterrupt(_timer, isr, true); // Interrupt on EGDE = true 696 | #endif 697 | } 698 | void attachInterrupt(timer_callback isr, unsigned long microseconds) __attribute__((always_inline)) { 699 | #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) 700 | if(microseconds > 0) { 701 | setPeriod(microseconds); 702 | } 703 | timerAttachInterrupt(_timer, isr); 704 | #else 705 | timerAttachInterrupt(_timer, isr, true); // Interrupt on EGDE = true 706 | if(microseconds > 0) { 707 | setPeriod(microseconds); 708 | } 709 | #endif 710 | } 711 | void detachInterrupt() __attribute__((always_inline)) { 712 | timerDetachInterrupt(_timer); 713 | } 714 | 715 | #endif 716 | }; 717 | 718 | extern TimerOne Timer1; 719 | 720 | #endif 721 | 722 | --------------------------------------------------------------------------------