|
| 1 | +/**************************************************************************************************************************** |
| 2 | + ESP8266TimerInterrupt.h |
| 3 | + For ESP8266 boards |
| 4 | + Written by Khoi Hoang |
| 5 | +
|
| 6 | + Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt |
| 7 | + Licensed under MIT license |
| 8 | + Version: 1.0.3 |
| 9 | +
|
| 10 | + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. |
| 11 | + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. |
| 12 | + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. |
| 13 | + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! |
| 14 | +
|
| 15 | + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) |
| 16 | + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers |
| 17 | + Therefore, their executions are not blocked by bad-behaving functions / tasks. |
| 18 | + This important feature is absolutely necessary for mission-critical tasks. |
| 19 | +
|
| 20 | + Based on SimpleTimer - A timer library for Arduino. |
| 21 | + Author: mromani@ottotecnica.com |
| 22 | + Copyright (c) 2010 OTTOTECNICA Italy |
| 23 | +
|
| 24 | + Based on BlynkTimer.h |
| 25 | + Author: Volodymyr Shymanskyy |
| 26 | +
|
| 27 | + Version Modified By Date Comments |
| 28 | + ------- ----------- ---------- ----------- |
| 29 | + 1.0.0 K Hoang 23/11/2019 Initial coding |
| 30 | + 1.0.1 K Hoang 25/11/2019 New release fixing compiler error |
| 31 | + 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked |
| 32 | + 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. |
| 33 | +*****************************************************************************************************************************/ |
| 34 | + |
| 35 | +#ifndef ESP8266TimerInterrupt_h |
| 36 | +#define ESP8266TimerInterrupt_h |
| 37 | + |
| 38 | +#if !defined(ESP8266) |
| 39 | +#error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. |
| 40 | +#endif |
| 41 | + |
| 42 | +#ifndef TIMER_INTERRUPT_DEBUG |
| 43 | +#define TIMER_INTERRUPT_DEBUG 0 |
| 44 | +#endif |
| 45 | + |
| 46 | +#ifndef ESP8266 |
| 47 | +#error This code is designed to run on ESP8266 platform, not Arduino nor ESP32! Please check your Tools->Board setting. |
| 48 | +#endif |
| 49 | + |
| 50 | +/* From /arduino-1.8.10/hardware/esp8266com/esp8266/cores/esp8266/esp8266_peri.h |
| 51 | +
|
| 52 | + #define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr))) |
| 53 | + #define ESP8266_DREG(addr) *((volatile uint32_t *)(0x3FF00000+(addr))) |
| 54 | + #define ESP8266_CLOCK 80000000UL |
| 55 | +
|
| 56 | + //CPU Register |
| 57 | + #define CPU2X ESP8266_DREG(0x14) //when bit 0 is set, F_CPU = 160MHz |
| 58 | +*/ |
| 59 | + |
| 60 | +/* From /arduino-1.8.10/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h |
| 61 | +
|
| 62 | + //timer dividers |
| 63 | + enum TIM_DIV_ENUM { |
| 64 | + TIM_DIV1 = 0, // 80 / 160 MHz (80 / 160 ticks/us - 104857.588 us max) |
| 65 | + TIM_DIV16 = 1, // 5 / 10 MHz (5 / 10 ticks/us - 1677721.4 us max) |
| 66 | + TIM_DIV256 = 3 // 312.5 / 625 Khz (1 tick = 3.2 / 1.6 us - 26843542.4 us max) |
| 67 | + }; |
| 68 | +
|
| 69 | + //timer int_types |
| 70 | + #define TIM_EDGE 0 |
| 71 | + #define TIM_LEVEL 1 |
| 72 | + //timer reload values |
| 73 | + #define TIM_SINGLE 0 //on interrupt routine you need to write a new value to start the timer again |
| 74 | + #define TIM_LOOP 1 //on interrupt the counter will start with the same value again |
| 75 | +
|
| 76 | +*/ |
| 77 | + |
| 78 | +// ESP8266 only has one usable timer1, max count is only 8,388,607. So to get longer time, we use max available 256 divider |
| 79 | +class ESP8266TimerInterrupt; |
| 80 | + |
| 81 | +typedef ESP8266TimerInterrupt ESP8266Timer; |
| 82 | + |
| 83 | +#define MAX_ESP8266_NUM_TIMERS 1 |
| 84 | +#define MAX_ESP8266_COUNT 8388607 |
| 85 | + |
| 86 | +typedef void (*timer_callback) (void); |
| 87 | + |
| 88 | + |
| 89 | +class ESP8266TimerInterrupt |
| 90 | +{ |
| 91 | + private: |
| 92 | + timer_callback _callback; // pointer to the callback function |
| 93 | + float _frequency; // Timer frequency |
| 94 | + uint32_t _timerCount; // count to activate timer |
| 95 | + |
| 96 | + public: |
| 97 | + |
| 98 | + ESP8266TimerInterrupt() |
| 99 | + { |
| 100 | + _frequency = 0; |
| 101 | + _timerCount = 0; |
| 102 | + _callback = NULL; |
| 103 | + }; |
| 104 | + |
| 105 | + // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely |
| 106 | + // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c |
| 107 | + bool setFrequency(float frequency, timer_callback callback) |
| 108 | + { |
| 109 | +#if (TIMER_INTERRUPT_DEBUG > 0) |
| 110 | + Serial.println("setFrequency : freq " + String(frequency, 5)); |
| 111 | +#endif |
| 112 | + bool isOKFlag = true; |
| 113 | + |
| 114 | + // ESP8266 only has one usable timer1, max count is only 8,388,607. So to get longer time, we use max available 256 divider |
| 115 | + // Will use later if very low frequency is needed. |
| 116 | + _frequency = 80000000 / 256; |
| 117 | +#if (TIMER_INTERRUPT_DEBUG > 0) |
| 118 | + Serial.println("setFrequency : const freq " + String(_frequency, 5)); |
| 119 | +#endif |
| 120 | + _timerCount = (uint32_t) _frequency / frequency; |
| 121 | +#if (TIMER_INTERRUPT_DEBUG > 0) |
| 122 | + Serial.println("setFrequency : timercount " + String(_timerCount)); |
| 123 | +#endif |
| 124 | + _callback = callback; |
| 125 | + |
| 126 | + if ( _timerCount > MAX_ESP8266_COUNT) |
| 127 | + { |
| 128 | + _timerCount = MAX_ESP8266_COUNT; |
| 129 | + // Flag error |
| 130 | + isOKFlag = false; |
| 131 | + } |
| 132 | + |
| 133 | + // count up |
| 134 | +#if (TIMER_INTERRUPT_DEBUG > 0) |
| 135 | + Serial.println("ESP8266TimerInterrupt: _fre = " + String(_frequency) + ", _count = " + String(_timerCount)); |
| 136 | +#endif |
| 137 | + |
| 138 | + // Clock to timer (prescaler) is always 80MHz, even F_CPU is 160 MHz |
| 139 | + |
| 140 | + timer1_attachInterrupt(callback); |
| 141 | + |
| 142 | + timer1_write(_timerCount); |
| 143 | + |
| 144 | + // Interrupt on EGDE, autoloop |
| 145 | + timer1_enable(TIM_DIV256, TIM_EDGE, TIM_LOOP); |
| 146 | + |
| 147 | + return isOKFlag; |
| 148 | + } |
| 149 | + |
| 150 | + // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely |
| 151 | + // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c |
| 152 | + bool setInterval(unsigned long interval, timer_callback callback) |
| 153 | + { |
| 154 | + return setFrequency((float) (1000000.0f / interval), callback); |
| 155 | + } |
| 156 | + |
| 157 | + bool attachInterrupt(float frequency, timer_callback callback) |
| 158 | + { |
| 159 | + return setFrequency(frequency, callback); |
| 160 | + } |
| 161 | + |
| 162 | + // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely |
| 163 | + // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c |
| 164 | + bool attachInterruptInterval(unsigned long interval, timer_callback callback) |
| 165 | + { |
| 166 | + return setFrequency( (float) ( 1000000.0f / interval), callback); |
| 167 | + } |
| 168 | + |
| 169 | + void detachInterrupt() |
| 170 | + { |
| 171 | + timer1_disable(); |
| 172 | + } |
| 173 | + |
| 174 | + void disableTimer(void) |
| 175 | + { |
| 176 | + timer1_disable(); |
| 177 | + } |
| 178 | + |
| 179 | + // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely |
| 180 | + void reattachInterrupt() |
| 181 | + { |
| 182 | + if ( (_frequency != 0) && (_timerCount != 0) && (_callback != NULL) ) |
| 183 | + setFrequency(_frequency, _callback); |
| 184 | + } |
| 185 | + |
| 186 | + // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely |
| 187 | + void enableTimer(void) |
| 188 | + { |
| 189 | + reattachInterrupt(); |
| 190 | + } |
| 191 | + |
| 192 | + // Just stop clock source, clear the count |
| 193 | + void stopTimer(void) |
| 194 | + { |
| 195 | + timer1_disable(); |
| 196 | + } |
| 197 | + |
| 198 | + // Just reconnect clock source, start current count from 0 |
| 199 | + void restartTimer(void) |
| 200 | + { |
| 201 | + enableTimer(); |
| 202 | + } |
| 203 | +}; // class ESP8266TimerInterrupt |
| 204 | + |
| 205 | +#endif //#ifndef ESP8266TimerInterrupt_h |
0 commit comments