├── .codespellrc
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yml
├── stale.yml
└── workflows
│ ├── auto-github-actions.yml
│ ├── report-size-deltas.yml
│ └── spell-check.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── changelog.md
├── examples
├── Argument_None
│ └── Argument_None.ino
├── Change_Interval
│ └── Change_Interval.ino
├── ISR_16_Timers_Array
│ └── ISR_16_Timers_Array.ino
├── ISR_16_Timers_Array_Complex
│ └── ISR_16_Timers_Array_Complex.ino
├── ISR_16_Timers_Array_Complex_OneShot
│ └── ISR_16_Timers_Array_Complex_OneShot.ino
├── ISR_16_Timers_Array_OneShot
│ └── ISR_16_Timers_Array_OneShot.ino
├── ISR_RPM_Measure
│ └── ISR_RPM_Measure.ino
├── RPM_Measure
│ └── RPM_Measure.ino
├── SwitchDebounce
│ └── SwitchDebounce.ino
├── TimerInterruptTest
│ └── TimerInterruptTest.ino
└── multiFileProject
│ ├── multiFileProject.cpp
│ ├── multiFileProject.h
│ └── multiFileProject.ino
├── keywords.txt
├── library.json
├── library.properties
├── platformio
└── platformio.ini
├── src
├── ESP32TimerInterrupt.h
├── ESP32TimerInterrupt.hpp
├── ESP32_ISR_Timer-Impl.h
├── ESP32_ISR_Timer.h
├── ESP32_ISR_Timer.hpp
├── ESP8266TimerInterrupt.h
├── ESP8266_ISR_Timer-Impl.h
├── ESP8266_ISR_Timer.h
├── ESP8266_ISR_Timer.hpp
└── TimerInterrupt_Generic_Debug.h
└── utils
├── astyle_library.conf
└── restyle.sh
/.codespellrc:
--------------------------------------------------------------------------------
1 | # See: https://github.com/codespell-project/codespell#using-a-config-file
2 | [codespell]
3 | # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here:
4 | ignore-words-list = ,
5 | check-filenames =
6 | check-hidden =
7 | skip = ./.git,./src,./examples
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ### Describe the bug
11 |
12 | A clear and concise description of what the bug is.
13 |
14 | ### Steps to Reproduce
15 |
16 | Steps to reproduce the behavior. Including the [MRE](https://stackoverflow.com/help/minimal-reproducible-example) sketches
17 |
18 | ### Expected behavior
19 |
20 | A clear and concise description of what you expected to happen.
21 |
22 | ### Actual behavior
23 |
24 | A clear and concise description of what you expected to happen.
25 |
26 | ### Debug and AT-command log (if applicable)
27 |
28 | A clear and concise description of what you expected to happen.
29 |
30 | ### Screenshots
31 |
32 | If applicable, add screenshots to help explain your problem.
33 |
34 | ### Information
35 |
36 | Please ensure to specify the following:
37 |
38 | * Arduino IDE version (e.g. 1.8.19) or Platform.io version
39 | * `ESP8266` Core Version (e.g. ESP8266 core v3.0.2)
40 | * Contextual information (e.g. what you were trying to achieve)
41 | * Simplest possible steps to reproduce
42 | * Anything that might be relevant in your opinion, such as:
43 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a`
44 | * Network configuration
45 |
46 |
47 | ### Example
48 |
49 | ```
50 | Arduino IDE version: v1.8.19
51 | ESP8266 Core Version v3.0.2
52 | ESP8266_NODEMCU
53 | OS: Ubuntu 20.04 LTS
54 | Linux xy-Inspiron-3593 5.15.0-52-generic #58~20.04.1-Ubuntu SMP Thu Oct 13 13:09:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
55 |
56 | Context:
57 | I encountered a crash while trying to use the library
58 |
59 | Steps to reproduce:
60 | 1. ...
61 | 2. ...
62 | 3. ...
63 | 4. ...
64 | ```
65 |
66 | ---
67 |
68 | ### Sending Feature Requests
69 |
70 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful.
71 |
72 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them.
73 |
74 | ---
75 |
76 | ### Sending Pull Requests
77 |
78 | Pull Requests with changes and fixes are also welcome!
79 |
80 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux)
81 |
82 | 1. Change directory to the library GitHub
83 |
84 | ```
85 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESP8266TimerInterrupt_GitHub/
86 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP8266TimerInterrupt_GitHub$
87 | ```
88 |
89 | 2. Issue astyle command
90 |
91 | ```
92 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP8266TimerInterrupt_GitHub$ bash utils/restyle.sh
93 | ```
94 |
95 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ### Is your feature request related to a problem? Please describe.
11 |
12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
13 |
14 | ### Describe the solution you'd like
15 |
16 | A clear and concise description of what you want to happen.
17 |
18 | ### Describe alternatives you've considered
19 | A clear and concise description of any alternative solutions or features you've considered.
20 |
21 | ### Additional context
22 |
23 | Add any other context or screenshots about the feature request here.
24 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file
2 | version: 2
3 |
4 | updates:
5 | # Configure check for outdated GitHub Actions actions in workflows.
6 | # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot
7 | - package-ecosystem: github-actions
8 | directory: / # Check the repository's workflows under /.github/workflows/
9 | schedule:
10 | interval: daily
11 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-stale - https://github.com/probot/stale
2 |
3 | daysUntilStale: 60
4 | daysUntilClose: 14
5 | limitPerRun: 30
6 | staleLabel: stale
7 | exemptLabels:
8 | - pinned
9 | - security
10 | - "to be implemented"
11 | - "for reference"
12 | - "move to PR"
13 | - "enhancement"
14 |
15 | only: issues
16 | onlyLabels: []
17 | exemptProjects: false
18 | exemptMilestones: false
19 | exemptAssignees: false
20 |
21 | markComment: >
22 | [STALE_SET] This issue has been automatically marked as stale because it has not had
23 | recent activity. It will be closed in 14 days if no further activity occurs. Thank you
24 | for your contributions.
25 |
26 | unmarkComment: >
27 | [STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it opening the future.
28 |
29 | closeComment: >
30 | [STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.
31 |
32 |
--------------------------------------------------------------------------------
/.github/workflows/auto-github-actions.yml:
--------------------------------------------------------------------------------
1 | name: auto-github-actions
2 | on: [push]
3 | jobs:
4 | check-bats-version:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v3
8 | - uses: actions/setup-node@v3
9 | with:
10 | node-version: '14'
11 | - run: npm install -g bats
12 | - run: bats -v
13 |
--------------------------------------------------------------------------------
/.github/workflows/report-size-deltas.yml:
--------------------------------------------------------------------------------
1 | name: Report Size Deltas
2 |
3 | on:
4 | schedule:
5 | - cron: '*/5 * * * *'
6 |
7 | jobs:
8 | report:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Comment size deltas reports to PRs
13 | uses: arduino/report-size-deltas@v1
14 | with:
15 | # The name of the workflow artifact created by the "Compile Examples" workflow
16 | sketches-reports-source: sketches-reports
17 |
--------------------------------------------------------------------------------
/.github/workflows/spell-check.yml:
--------------------------------------------------------------------------------
1 | name: Spell Check
2 |
3 | on:
4 | pull_request:
5 | push:
6 | schedule:
7 | # run every Tuesday at 3 AM UTC
8 | - cron: "0 3 * * 2"
9 | workflow_dispatch:
10 | repository_dispatch:
11 |
12 | jobs:
13 | spellcheck:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v3
19 |
20 | # See: https://github.com/codespell-project/actions-codespell/blob/master/README.md
21 | - name: Spell check
22 | uses: codespell-project/actions-codespell@master
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | # Compiled Static libraries
24 | *.lai
25 | *.la
26 | *.a
27 | *.lib
28 |
29 | # Executables
30 | *.exe
31 | *.out
32 | *.app
33 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing to ESP8266TimerInterrupt
2 |
3 | ### Reporting Bugs
4 |
5 | Please report bugs in [ESP8266TimerInterrupt Issues](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues) if you find them.
6 |
7 | However, before reporting a bug please check through the following:
8 |
9 | * [Existing Open Issues](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues) - someone might have already encountered this.
10 |
11 | If you don't find anything, please [open a new issue](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues/new).
12 |
13 | ### How to submit a bug report
14 |
15 | Please ensure to specify the following:
16 |
17 | * Arduino IDE version (e.g. 1.8.19) or Platform.io version
18 | * `ESP8266` Core Version (e.g. ESP8266 core v3.0.2)
19 | * Contextual information (e.g. what you were trying to achieve)
20 | * Simplest possible steps to reproduce
21 | * Anything that might be relevant in your opinion, such as:
22 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a`
23 | * Network configuration
24 |
25 |
26 | ### Example
27 |
28 | ```
29 | Arduino IDE version: v1.8.19
30 | ESP8266 Core Version v3.0.2
31 | ESP8266_NODEMCU
32 | OS: Ubuntu 20.04 LTS
33 | Linux xy-Inspiron-3593 5.15.0-52-generic #58~20.04.1-Ubuntu SMP Thu Oct 13 13:09:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
34 |
35 | Context:
36 | I encountered a crash while trying to use the library
37 |
38 | Steps to reproduce:
39 | 1. ...
40 | 2. ...
41 | 3. ...
42 | 4. ...
43 | ```
44 |
45 | ---
46 |
47 | ### Sending Feature Requests
48 |
49 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful.
50 |
51 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them.
52 |
53 | ---
54 |
55 | ### Sending Pull Requests
56 |
57 | Pull Requests with changes and fixes are also welcome!
58 |
59 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux)
60 |
61 | 1. Change directory to the library GitHub
62 |
63 | ```
64 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESP8266TimerInterrupt_GitHub/
65 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP8266TimerInterrupt_GitHub$
66 | ```
67 |
68 | 2. Issue astyle command
69 |
70 | ```
71 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP8266TimerInterrupt_GitHub$ bash utils/restyle.sh
72 | ```
73 |
74 |
75 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Khoi Hoang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # ESP8266TimerInterrupt Library
2 |
3 | [](https://www.ardu-badge.com/ESP8266TimerInterrupt)
4 | [](https://github.com/khoih-prog/ESP8266TimerInterrupt/releases)
5 | [](https://github.com/khoih-prog/ESP8266TimerInterrupt/blob/master/LICENSE)
6 | [](#Contributing)
7 | [](http://github.com/khoih-prog/ESP8266TimerInterrupt/issues)
8 |
9 |
10 |
11 |
12 |
13 | ---
14 | ---
15 |
16 | * [Changelog](#changelog)
17 | * [Releases v1.6.0](#releases-v160)
18 | * [Releases v1.5.0](#releases-v150)
19 | * [Releases v1.4.1](#releases-v141)
20 | * [Releases v1.4.0](#releases-v140)
21 | * [Releases v1.3.0](#releases-v130)
22 | * [Releases v1.2.0](#releases-v120)
23 | * [Releases v1.1.1](#releases-v111)
24 | * [Releases v1.1.0](#releases-v110)
25 | * [Releases v1.0.3](#releases-v103)
26 | * [Releases v1.0.2](#releases-v102)
27 |
28 | ---
29 | ---
30 |
31 | ## Changelog
32 |
33 | ### Releases v1.6.0
34 |
35 | 1. Add example [ISR_16_Timers_Array_OneShot](examples/ISR_16_Timers_Array_OneShot) to demo how to use `one-shot ISR-based timer`
36 | 2. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project
37 | 3. Optimize code by using passing by `reference` instead of by `value`
38 |
39 | ### Releases v1.5.0
40 |
41 | 1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories
42 | 2. Add feature to select among highest, medium or lowest accuracy for Timers for shortest, medium or longest time
43 | 3. Fix reattachInterrupt() bug. Check [bugfix: reattachInterrupt() pass wrong frequency value to setFrequency() #19](https://github.com/khoih-prog/ESP8266TimerInterrupt/pull/19)
44 | 4. Update examples
45 |
46 | ### Releases v1.4.1
47 |
48 | 1. Examples modified and tested with core v3.0.2
49 | 2. Add instructions in `README.md`
50 | 3. Add `changelog.md`
51 | 4. Delete Blynk-related examples
52 |
53 | ### Releases v1.4.0
54 |
55 | 1. Fix compiler errors due to conflict to some libraries.
56 | 2. Add complex examples.
57 |
58 | ### Releases v1.3.0
59 |
60 | 1. Update to match new ESP8266 core v3.0.0
61 |
62 | ### Releases v1.2.0
63 |
64 | 1. Add better debug feature.
65 | 2. Optimize code and examples to reduce RAM usage
66 |
67 | ### Releases v1.1.1
68 |
69 | 1. Add [**Change_Interval**](examples/Change_Interval) example to show how to change TimerInterval on-the-fly
70 | 2. Add Version String
71 |
72 | ### Releases v1.1.0
73 |
74 | 1. Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
75 | 2. Update examples.
76 | 3. Enhance README.
77 |
78 | ### Releases v1.0.3
79 |
80 | 1. Restructure code.
81 | 2. Fix example.
82 | 3. Enhance README.
83 |
84 | ### Releases v1.0.2
85 |
86 | 1. Basic hardware timers for ESP8266.
87 | 2. Fix compatibility issue causing compiler error while using Arduino IDEs before 1.8.10 and ESP8266 cores 2.5.2 and before
88 | 3. More hardware-initiated software-enabled timers
89 | 4. Longer time interval
90 |
91 |
--------------------------------------------------------------------------------
/examples/Argument_None/Argument_None.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | Argument_None.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 |
20 | /* Notes:
21 | Special design is necessary to share data between interrupt code and the rest of your program.
22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
23 | variable can not spontaneously change. Because your function may change variables while your program is using them,
24 | the compiler needs this hint. But volatile alone is often not enough.
25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
28 | or the entire sequence of your code which accesses the data.
29 | */
30 |
31 | #if !defined(ESP8266)
32 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
33 | #endif
34 |
35 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
36 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
37 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
38 | #define TIMER_INTERRUPT_DEBUG 0
39 | #define _TIMERINTERRUPT_LOGLEVEL_ 0
40 |
41 | // Select a Timer Clock
42 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
43 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
44 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
45 |
46 | #include "ESP8266TimerInterrupt.h"
47 |
48 | #ifndef LED_BUILTIN
49 | #define LED_BUILTIN 2 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED
50 | #endif
51 |
52 | volatile uint32_t lastMillis = 0;
53 |
54 | void IRAM_ATTR TimerHandler()
55 | {
56 | static bool toggle = false;
57 | static bool started = false;
58 |
59 | if (!started)
60 | {
61 | started = true;
62 | pinMode(LED_BUILTIN, OUTPUT);
63 | }
64 |
65 | #if (TIMER_INTERRUPT_DEBUG > 0)
66 | Serial.print("Delta ms = ");
67 | Serial.println(millis() - lastMillis);
68 | lastMillis = millis();
69 | #endif
70 |
71 | //timer interrupt toggles pin LED_BUILTIN
72 | digitalWrite(LED_BUILTIN, toggle);
73 | toggle = !toggle;
74 | }
75 |
76 | #define TIMER_INTERVAL_MS 1000
77 |
78 | // Init ESP8266 timer 1
79 | ESP8266Timer ITimer;
80 |
81 | void setup()
82 | {
83 | Serial.begin(115200);
84 |
85 | while (!Serial && millis() < 5000);
86 |
87 | delay(500);
88 |
89 | Serial.print(F("\nStarting Argument_None on "));
90 | Serial.println(ARDUINO_BOARD);
91 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
92 | Serial.print(F("CPU Frequency = "));
93 | Serial.print(F_CPU / 1000000);
94 | Serial.println(F(" MHz"));
95 |
96 | // Interval in microsecs
97 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler))
98 | {
99 | lastMillis = millis();
100 | Serial.print(F("Starting ITimer OK, millis() = "));
101 | Serial.println(lastMillis);
102 | }
103 | else
104 | Serial.println(F("Can't set ITimer correctly. Select another freq. or interval"));
105 | }
106 |
107 | void loop()
108 | {
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/examples/Change_Interval/Change_Interval.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | Change_Interval.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 |
20 | /*
21 | Notes:
22 | Special design is necessary to share data between interrupt code and the rest of your program.
23 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
24 | variable can not spontaneously change. Because your function may change variables while your program is using them,
25 | the compiler needs this hint. But volatile alone is often not enough.
26 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
27 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
28 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
29 | or the entire sequence of your code which accesses the data.
30 | */
31 |
32 | #if !defined(ESP8266)
33 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
34 | #endif
35 |
36 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
37 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
38 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
39 | #define TIMER_INTERRUPT_DEBUG 0
40 | #define _TIMERINTERRUPT_LOGLEVEL_ 0
41 |
42 | // Select a Timer Clock
43 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
44 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
45 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
46 |
47 | #include "ESP8266TimerInterrupt.h"
48 |
49 | #ifndef LED_BUILTIN
50 | #define LED_BUILTIN D4 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED
51 | #endif
52 |
53 |
54 | #define TIMER_INTERVAL_MS 500 //1000
55 |
56 | volatile uint32_t TimerCount = 0;
57 |
58 | // Init ESP8266 timer 1
59 | ESP8266Timer ITimer;
60 |
61 | void printResult(uint32_t currTime)
62 | {
63 | Serial.print(F("Time = "));
64 | Serial.print(currTime);
65 | Serial.print(F(", TimerCount = "));
66 | Serial.println(TimerCount);
67 | }
68 |
69 | void TimerHandler()
70 | {
71 | static bool toggle = false;
72 |
73 | // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR
74 | TimerCount++;
75 |
76 | //timer interrupt toggles pin LED_BUILTIN
77 | digitalWrite(LED_BUILTIN, toggle);
78 | toggle = !toggle;
79 | }
80 |
81 | void setup()
82 | {
83 | pinMode(LED_BUILTIN, OUTPUT);
84 |
85 | Serial.begin(115200);
86 |
87 | while (!Serial && millis() < 5000);
88 |
89 | delay(500);
90 |
91 | Serial.print(F("\nStarting Change_Interval on "));
92 | Serial.println(ARDUINO_BOARD);
93 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
94 | Serial.print(F("CPU Frequency = "));
95 | Serial.print(F_CPU / 1000000);
96 | Serial.println(F(" MHz"));
97 |
98 | // Interval in microsecs
99 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler))
100 | {
101 | Serial.print(F("Starting ITimer OK, millis() = "));
102 | Serial.println(millis());
103 | }
104 | else
105 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
106 | }
107 |
108 | #define CHECK_INTERVAL_MS 10000L
109 | #define CHANGE_INTERVAL_MS 20000L
110 |
111 | void loop()
112 | {
113 | static uint32_t lastTime = 0;
114 | static uint32_t lastChangeTime = 0;
115 | static uint32_t currTime;
116 | static uint32_t multFactor = 0;
117 |
118 | currTime = millis();
119 |
120 | if (currTime - lastTime > CHECK_INTERVAL_MS)
121 | {
122 | printResult(currTime);
123 | lastTime = currTime;
124 |
125 | if (currTime - lastChangeTime > CHANGE_INTERVAL_MS)
126 | {
127 | //setInterval(unsigned long interval, timerCallback callback)
128 | multFactor = (multFactor + 1) % 2;
129 |
130 | ITimer.setInterval(TIMER_INTERVAL_MS * 1000 * (multFactor + 1), TimerHandler);
131 |
132 | Serial.print(F("Changing Interval, Timer = "));
133 | Serial.println(TIMER_INTERVAL_MS * (multFactor + 1));
134 |
135 | lastChangeTime = currTime;
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ISR_16_Timers_Array.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 | /*
20 | Notes:
21 | Special design is necessary to share data between interrupt code and the rest of your program.
22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
23 | variable can not spontaneously change. Because your function may change variables while your program is using them,
24 | the compiler needs this hint. But volatile alone is often not enough.
25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
28 | or the entire sequence of your code which accesses the data.
29 |
30 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms
31 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor
32 | Assuming LOW is active.
33 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing
34 | If the time between active state is less than 8ms => consider noise.
35 | RPM = 60000 / (rotation time in ms)
36 |
37 | We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state
38 |
39 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs.
40 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet
41 | and Blynk services. You can also have many (up to 16) timers to use.
42 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks.
43 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate
44 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is
45 | written
46 | */
47 |
48 | #if !defined(ESP8266)
49 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
50 | #endif
51 |
52 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
53 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
54 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
55 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system.
56 | #define TIMER_INTERRUPT_DEBUG 2
57 | #define _TIMERINTERRUPT_LOGLEVEL_ 0
58 |
59 | // Select a Timer Clock
60 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
61 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
62 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
63 |
64 | #include "ESP8266TimerInterrupt.h"
65 | #include "ESP8266_ISR_Timer.h"
66 |
67 | #include // https://github.com/jfturcot/SimpleTimer
68 |
69 | #ifndef LED_BUILTIN
70 | #define LED_BUILTIN 2
71 | #endif
72 |
73 | #ifndef LED_BLUE
74 | #define LED_BLUE 25
75 | #endif
76 |
77 | #ifndef LED_RED
78 | #define LED_RED 27
79 | #endif
80 |
81 | #define HW_TIMER_INTERVAL_MS 1L
82 |
83 | volatile uint32_t startMillis = 0;
84 |
85 | // Init ESP8266 timer 1
86 | ESP8266Timer ITimer;
87 |
88 | // Init BlynkTimer
89 | ESP8266_ISR_Timer ISR_Timer;
90 |
91 | #define LED_TOGGLE_INTERVAL_MS 2000L
92 |
93 | void IRAM_ATTR TimerHandler()
94 | {
95 | static bool toggle = false;
96 | static bool started = false;
97 | static int timeRun = 0;
98 |
99 | ISR_Timer.run();
100 |
101 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s
102 | if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS) )
103 | {
104 | timeRun = 0;
105 |
106 | if (!started)
107 | {
108 | started = true;
109 | pinMode(LED_BUILTIN, OUTPUT);
110 | }
111 |
112 | //timer interrupt toggles pin LED_BUILTIN
113 | digitalWrite(LED_BUILTIN, toggle);
114 | toggle = !toggle;
115 | }
116 | }
117 |
118 | #define NUMBER_ISR_TIMERS 16
119 |
120 | // You can assign any interval for any timer here, in milliseconds
121 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] =
122 | {
123 | 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L,
124 | 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L
125 | };
126 |
127 | typedef void (*irqCallback) ();
128 |
129 | #if (TIMER_INTERRUPT_DEBUG > 0)
130 | void printStatus(uint16_t index, unsigned long deltaMillis, unsigned long currentMillis)
131 | {
132 | Serial.print(TimerInterval[index] / 1000);
133 | Serial.print("s: Delta ms = ");
134 | Serial.print(deltaMillis);
135 | Serial.print(", ms = ");
136 | Serial.println(currentMillis);
137 | }
138 | #endif
139 |
140 | // In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument
141 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment
142 | // Or you can get this run-time error / crash
143 | void doingSomething0()
144 | {
145 | #if (TIMER_INTERRUPT_DEBUG > 0)
146 | static unsigned long previousMillis = startMillis;
147 |
148 | unsigned long currentMillis = millis();
149 | unsigned long deltaMillis = currentMillis - previousMillis;
150 |
151 | printStatus(0, deltaMillis, currentMillis);
152 |
153 | previousMillis = currentMillis;
154 | #endif
155 | }
156 |
157 | void doingSomething1()
158 | {
159 | #if (TIMER_INTERRUPT_DEBUG > 1)
160 | static unsigned long previousMillis = startMillis;
161 |
162 | unsigned long currentMillis = millis();
163 | unsigned long deltaMillis = currentMillis - previousMillis;
164 |
165 | printStatus(1, deltaMillis, currentMillis);
166 |
167 | previousMillis = currentMillis;
168 | #endif
169 | }
170 |
171 | void doingSomething2()
172 | {
173 | #if (TIMER_INTERRUPT_DEBUG > 1)
174 | static unsigned long previousMillis = startMillis;
175 |
176 | unsigned long currentMillis = millis();
177 | unsigned long deltaMillis = currentMillis - previousMillis;
178 |
179 | printStatus(2, deltaMillis, currentMillis);
180 |
181 | previousMillis = currentMillis;
182 | #endif
183 | }
184 |
185 | void doingSomething3()
186 | {
187 | #if (TIMER_INTERRUPT_DEBUG > 1)
188 | static unsigned long previousMillis = startMillis;
189 |
190 | unsigned long currentMillis = millis();
191 | unsigned long deltaMillis = currentMillis - previousMillis;
192 |
193 | printStatus(3, deltaMillis, currentMillis);
194 |
195 | previousMillis = currentMillis;
196 | #endif
197 | }
198 |
199 | void doingSomething4()
200 | {
201 | #if (TIMER_INTERRUPT_DEBUG > 1)
202 | static unsigned long previousMillis = startMillis;
203 |
204 | unsigned long currentMillis = millis();
205 | unsigned long deltaMillis = currentMillis - previousMillis;
206 |
207 | printStatus(4, deltaMillis, currentMillis);
208 |
209 | previousMillis = currentMillis;
210 | #endif
211 | }
212 |
213 | void doingSomething5()
214 | {
215 | #if (TIMER_INTERRUPT_DEBUG > 1)
216 | static unsigned long previousMillis = startMillis;
217 |
218 | unsigned long currentMillis = millis();
219 | unsigned long deltaMillis = currentMillis - previousMillis;
220 |
221 | printStatus(5, deltaMillis, currentMillis);
222 |
223 | previousMillis = currentMillis;
224 | #endif
225 | }
226 |
227 | void doingSomething6()
228 | {
229 | #if (TIMER_INTERRUPT_DEBUG > 1)
230 | static unsigned long previousMillis = startMillis;
231 |
232 | unsigned long currentMillis = millis();
233 | unsigned long deltaMillis = currentMillis - previousMillis;
234 |
235 | printStatus(6, deltaMillis, currentMillis);
236 |
237 | previousMillis = currentMillis;
238 | #endif
239 | }
240 |
241 | void doingSomething7()
242 | {
243 | #if (TIMER_INTERRUPT_DEBUG > 1)
244 | static unsigned long previousMillis = startMillis;
245 |
246 | unsigned long currentMillis = millis();
247 | unsigned long deltaMillis = currentMillis - previousMillis;
248 |
249 | printStatus(7, deltaMillis, currentMillis);
250 |
251 | previousMillis = currentMillis;
252 | #endif
253 | }
254 |
255 | void doingSomething8()
256 | {
257 | #if (TIMER_INTERRUPT_DEBUG > 1)
258 | static unsigned long previousMillis = startMillis;
259 |
260 | unsigned long currentMillis = millis();
261 | unsigned long deltaMillis = currentMillis - previousMillis;
262 |
263 | printStatus(8, deltaMillis, currentMillis);
264 |
265 | previousMillis = currentMillis;
266 | #endif
267 | }
268 |
269 | void doingSomething9()
270 | {
271 | #if (TIMER_INTERRUPT_DEBUG > 1)
272 | static unsigned long previousMillis = startMillis;
273 |
274 | unsigned long currentMillis = millis();
275 | unsigned long deltaMillis = currentMillis - previousMillis;
276 |
277 | printStatus(9, deltaMillis, currentMillis);
278 |
279 | previousMillis = currentMillis;
280 | #endif
281 | }
282 |
283 | void doingSomething10()
284 | {
285 | #if (TIMER_INTERRUPT_DEBUG > 1)
286 | static unsigned long previousMillis = startMillis;
287 |
288 | unsigned long currentMillis = millis();
289 | unsigned long deltaMillis = currentMillis - previousMillis;
290 |
291 | printStatus(10, deltaMillis, currentMillis);
292 |
293 | previousMillis = currentMillis;
294 | #endif
295 | }
296 |
297 | void doingSomething11()
298 | {
299 | #if (TIMER_INTERRUPT_DEBUG > 1)
300 | static unsigned long previousMillis = startMillis;
301 |
302 | unsigned long currentMillis = millis();
303 | unsigned long deltaMillis = currentMillis - previousMillis;
304 |
305 | printStatus(11, deltaMillis, currentMillis);
306 |
307 | previousMillis = currentMillis;
308 | #endif
309 | }
310 |
311 | void doingSomething12()
312 | {
313 | #if (TIMER_INTERRUPT_DEBUG > 1)
314 | static unsigned long previousMillis = startMillis;
315 |
316 | unsigned long currentMillis = millis();
317 | unsigned long deltaMillis = currentMillis - previousMillis;
318 |
319 | printStatus(12, deltaMillis, currentMillis);
320 |
321 | previousMillis = currentMillis;
322 | #endif
323 | }
324 |
325 | void doingSomething13()
326 | {
327 | #if (TIMER_INTERRUPT_DEBUG > 1)
328 | static unsigned long previousMillis = startMillis;
329 |
330 | unsigned long currentMillis = millis();
331 | unsigned long deltaMillis = currentMillis - previousMillis;
332 |
333 | printStatus(13, deltaMillis, currentMillis);
334 |
335 | previousMillis = currentMillis;
336 | #endif
337 | }
338 |
339 | void doingSomething14()
340 | {
341 | #if (TIMER_INTERRUPT_DEBUG > 1)
342 | static unsigned long previousMillis = startMillis;
343 |
344 | unsigned long currentMillis = millis();
345 | unsigned long deltaMillis = currentMillis - previousMillis;
346 |
347 | printStatus(14, deltaMillis, currentMillis);
348 |
349 | previousMillis = currentMillis;
350 | #endif
351 | }
352 |
353 | void doingSomething15()
354 | {
355 | #if (TIMER_INTERRUPT_DEBUG > 1)
356 | static unsigned long previousMillis = startMillis;
357 |
358 | unsigned long currentMillis = millis();
359 | unsigned long deltaMillis = currentMillis - previousMillis;
360 |
361 | printStatus(15, deltaMillis, currentMillis);
362 |
363 | previousMillis = currentMillis;
364 | #endif
365 | }
366 |
367 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] =
368 | {
369 | doingSomething0, doingSomething1, doingSomething2, doingSomething3,
370 | doingSomething4, doingSomething5, doingSomething6, doingSomething7,
371 | doingSomething8, doingSomething9, doingSomething10, doingSomething11,
372 | doingSomething12, doingSomething13, doingSomething14, doingSomething15
373 | };
374 |
375 | ////////////////////////////////////////////////
376 |
377 |
378 | #define SIMPLE_TIMER_MS 2000L
379 |
380 | // Init SimpleTimer
381 | SimpleTimer simpleTimer;
382 |
383 | // Here is software Timer, you can do somewhat fancy stuffs without many issues.
384 | // But always avoid
385 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead
386 | // 2. Very long "do", "while", "for" loops without predetermined exit time.
387 | void simpleTimerDoingSomething2s()
388 | {
389 | static unsigned long previousMillis = startMillis;
390 |
391 | Serial.print(F("simpleTimerDoingSomething2s: Delta programmed ms = "));
392 | Serial.print(SIMPLE_TIMER_MS);
393 | Serial.print(F(", actual = "));
394 | Serial.println(millis() - previousMillis);
395 |
396 | previousMillis = millis();
397 | }
398 |
399 | void setup()
400 | {
401 | Serial.begin(115200);
402 |
403 | while (!Serial && millis() < 5000);
404 |
405 | delay(500);
406 |
407 | Serial.print(F("\nStarting ISR_16_Timers_Array on "));
408 | Serial.println(ARDUINO_BOARD);
409 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
410 | Serial.print(F("CPU Frequency = "));
411 | Serial.print(F_CPU / 1000000);
412 | Serial.println(F(" MHz"));
413 |
414 | // Interval in microsecs
415 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler))
416 | {
417 | startMillis = millis();
418 | Serial.print(F("Starting ITimer OK, millis() = "));
419 | Serial.println(startMillis);
420 | }
421 | else
422 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
423 |
424 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary
425 | // You can use up to 16 timer for each SAMD_ISR_Timer
426 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
427 | {
428 | ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]);
429 | }
430 |
431 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary.
432 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s);
433 | }
434 |
435 | #define BLOCKING_TIME_MS 10000L
436 |
437 | void loop()
438 | {
439 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer
440 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer
441 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS)
442 | // While that of ISR_Timer is still prefect.
443 | delay(BLOCKING_TIME_MS);
444 |
445 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary
446 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer.
447 | simpleTimer.run();
448 | }
449 |
--------------------------------------------------------------------------------
/examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ISR_16_Timers_Array_Complex.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 | /*
20 | Notes:
21 | Special design is necessary to share data between interrupt code and the rest of your program.
22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
23 | variable can not spontaneously change. Because your function may change variables while your program is using them,
24 | the compiler needs this hint. But volatile alone is often not enough.
25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
28 | or the entire sequence of your code which accesses the data.
29 |
30 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs.
31 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet
32 | and Blynk services. You can also have many (up to 16) timers to use.
33 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks.
34 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate
35 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is
36 | written
37 | */
38 |
39 | #if !defined(ESP8266)
40 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
41 | #endif
42 |
43 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
44 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
45 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
46 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system.
47 | #define TIMER_INTERRUPT_DEBUG 0
48 | #define _TIMERINTERRUPT_LOGLEVEL_ 4
49 |
50 | // Select a Timer Clock
51 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
52 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
53 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
54 |
55 | #include "ESP8266TimerInterrupt.h"
56 | #include "ESP8266_ISR_Timer.h"
57 |
58 | #include // https://github.com/jfturcot/SimpleTimer
59 |
60 | #ifndef LED_BUILTIN
61 | #define LED_BUILTIN 2
62 | #endif
63 |
64 | #ifndef LED_BLUE
65 | #define LED_BLUE 25
66 | #endif
67 |
68 | #ifndef LED_RED
69 | #define LED_RED 27
70 | #endif
71 |
72 | #define HW_TIMER_INTERVAL_US 10000L
73 |
74 | volatile uint32_t startMillis = 0;
75 |
76 | // Init ESP8266 timer 1
77 | ESP8266Timer ITimer;
78 |
79 | // Init ESP8266_ISR_Timer
80 | ESP8266_ISR_Timer ISR_Timer;
81 |
82 | #define LED_TOGGLE_INTERVAL_MS 2000L
83 |
84 | void IRAM_ATTR TimerHandler()
85 | {
86 | static bool toggle = false;
87 | static int timeRun = 0;
88 |
89 | ISR_Timer.run();
90 |
91 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s
92 | if (++timeRun == ((LED_TOGGLE_INTERVAL_MS * 1000) / HW_TIMER_INTERVAL_US) )
93 | {
94 | timeRun = 0;
95 |
96 | //timer interrupt toggles pin LED_BUILTIN
97 | digitalWrite(LED_BUILTIN, toggle);
98 | toggle = !toggle;
99 | }
100 | }
101 |
102 | /////////////////////////////////////////////////
103 |
104 | #define NUMBER_ISR_TIMERS 16
105 |
106 | typedef void (*irqCallback) ();
107 |
108 | /////////////////////////////////////////////////
109 |
110 | #define USE_COMPLEX_STRUCT true
111 |
112 | #if USE_COMPLEX_STRUCT
113 |
114 | typedef struct
115 | {
116 | irqCallback irqCallbackFunc;
117 | uint32_t TimerInterval;
118 | unsigned long deltaMillis;
119 | unsigned long previousMillis;
120 | } ISRTimerData;
121 |
122 | // In ESP8266, avoid doing something fancy in ISR, for example Serial.print()
123 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment
124 | // Or you can get this run-time error / crash
125 |
126 | void doingSomething(int index);
127 |
128 | #else
129 |
130 | volatile unsigned long deltaMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
131 | volatile unsigned long previousMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
132 |
133 | // You can assign any interval for any timer here, in milliseconds
134 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] =
135 | {
136 | 5000L, 10000L, 15000L, 20000L, 25000L, 30000L, 35000L, 40000L,
137 | 45000L, 50000L, 55000L, 60000L, 65000L, 70000L, 75000L, 80000L
138 | };
139 |
140 | void doingSomething(int index)
141 | {
142 | unsigned long currentMillis = millis();
143 |
144 | deltaMillis[index] = currentMillis - previousMillis[index];
145 | previousMillis[index] = currentMillis;
146 | }
147 |
148 | #endif
149 |
150 | ////////////////////////////////////
151 | // Shared
152 | ////////////////////////////////////
153 |
154 | void doingSomething0()
155 | {
156 | doingSomething(0);
157 | }
158 |
159 | void doingSomething1()
160 | {
161 | doingSomething(1);
162 | }
163 |
164 | void doingSomething2()
165 | {
166 | doingSomething(2);
167 | }
168 |
169 | void doingSomething3()
170 | {
171 | doingSomething(3);
172 | }
173 |
174 | void doingSomething4()
175 | {
176 | doingSomething(4);
177 | }
178 |
179 | void doingSomething5()
180 | {
181 | doingSomething(5);
182 | }
183 |
184 | void doingSomething6()
185 | {
186 | doingSomething(6);
187 | }
188 |
189 | void doingSomething7()
190 | {
191 | doingSomething(7);
192 | }
193 |
194 | void doingSomething8()
195 | {
196 | doingSomething(8);
197 | }
198 |
199 | void doingSomething9()
200 | {
201 | doingSomething(9);
202 | }
203 |
204 | void doingSomething10()
205 | {
206 | doingSomething(10);
207 | }
208 |
209 | void doingSomething11()
210 | {
211 | doingSomething(11);
212 | }
213 |
214 | void doingSomething12()
215 | {
216 | doingSomething(12);
217 | }
218 |
219 | void doingSomething13()
220 | {
221 | doingSomething(13);
222 | }
223 |
224 | void doingSomething14()
225 | {
226 | doingSomething(14);
227 | }
228 |
229 | void doingSomething15()
230 | {
231 | doingSomething(15);
232 | }
233 |
234 | #if USE_COMPLEX_STRUCT
235 |
236 | ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] =
237 | {
238 | //irqCallbackFunc, TimerInterval, deltaMillis, previousMillis
239 | { doingSomething0, 5000L, 0, 0 },
240 | { doingSomething1, 10000L, 0, 0 },
241 | { doingSomething2, 15000L, 0, 0 },
242 | { doingSomething3, 20000L, 0, 0 },
243 | { doingSomething4, 25000L, 0, 0 },
244 | { doingSomething5, 30000L, 0, 0 },
245 | { doingSomething6, 35000L, 0, 0 },
246 | { doingSomething7, 40000L, 0, 0 },
247 | { doingSomething8, 45000L, 0, 0 },
248 | { doingSomething9, 50000L, 0, 0 },
249 | { doingSomething10, 55000L, 0, 0 },
250 | { doingSomething11, 60000L, 0, 0 },
251 | { doingSomething12, 65000L, 0, 0 },
252 | { doingSomething13, 70000L, 0, 0 },
253 | { doingSomething14, 75000L, 0, 0 },
254 | { doingSomething15, 80000L, 0, 0 }
255 | };
256 |
257 | void doingSomething(int index)
258 | {
259 | unsigned long currentMillis = millis();
260 |
261 | curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis;
262 | curISRTimerData[index].previousMillis = currentMillis;
263 | }
264 |
265 | #else
266 |
267 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] =
268 | {
269 | doingSomething0, doingSomething1, doingSomething2, doingSomething3,
270 | doingSomething4, doingSomething5, doingSomething6, doingSomething7,
271 | doingSomething8, doingSomething9, doingSomething10, doingSomething11,
272 | doingSomething12, doingSomething13, doingSomething14, doingSomething15
273 | };
274 |
275 | #endif
276 | ///////////////////////////////////////////
277 |
278 | #define SIMPLE_TIMER_MS 2000L
279 |
280 | // Init SimpleTimer
281 | SimpleTimer simpleTimer;
282 |
283 | // Here is software Timer, you can do somewhat fancy stuffs without many issues.
284 | // But always avoid
285 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead
286 | // 2. Very long "do", "while", "for" loops without predetermined exit time.
287 | void simpleTimerDoingSomething2s()
288 | {
289 | static unsigned long previousMillis = startMillis;
290 |
291 | unsigned long currMillis = millis();
292 |
293 | Serial.print(F("SimpleTimer : "));
294 | Serial.print(SIMPLE_TIMER_MS / 1000);
295 | Serial.print(F(", ms : "));
296 | Serial.print(currMillis);
297 | Serial.print(F(", Dms : "));
298 | Serial.println(currMillis - previousMillis);
299 |
300 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
301 | {
302 | #if USE_COMPLEX_STRUCT
303 | Serial.print(F("Timer : "));
304 | Serial.print(i);
305 | Serial.print(F(", programmed : "));
306 | Serial.print(curISRTimerData[i].TimerInterval);
307 | Serial.print(F(", actual : "));
308 | Serial.println(curISRTimerData[i].deltaMillis);
309 | #else
310 | Serial.print(F("Timer : "));
311 | Serial.print(i);
312 | Serial.print(F(", programmed : "));
313 | Serial.print(TimerInterval[i]);
314 | Serial.print(F(", actual : "));
315 | Serial.println(deltaMillis[i]);
316 | #endif
317 | }
318 |
319 | previousMillis = currMillis;
320 | }
321 |
322 | void setup()
323 | {
324 | pinMode(LED_BUILTIN, OUTPUT);
325 |
326 | Serial.begin(115200);
327 |
328 | while (!Serial && millis() < 5000);
329 |
330 | delay(500);
331 |
332 | Serial.print(F("\nStarting ISR_16_Timers_Array_Complex on "));
333 | Serial.println(ARDUINO_BOARD);
334 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
335 | Serial.print(F("CPU Frequency = "));
336 | Serial.print(F_CPU / 1000000);
337 | Serial.println(F(" MHz"));
338 |
339 | // Interval in microsecs
340 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler))
341 | {
342 | startMillis = millis();
343 | Serial.print(F("Starting ITimer OK, millis() = "));
344 | Serial.println(startMillis);
345 | }
346 | else
347 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
348 |
349 | startMillis = millis();
350 |
351 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary
352 | // You can use up to 16 timer for each ISR_Timer
353 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
354 | {
355 | #if USE_COMPLEX_STRUCT
356 | curISRTimerData[i].previousMillis = startMillis;
357 | ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc);
358 | #else
359 | previousMillis[i] = millis();
360 | ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]);
361 | #endif
362 | }
363 |
364 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary.
365 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s);
366 | }
367 |
368 | #define BLOCKING_TIME_MS 10000L
369 |
370 | void loop()
371 | {
372 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer
373 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer
374 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS)
375 | // While that of ISR_Timer is still prefect.
376 | delay(BLOCKING_TIME_MS);
377 |
378 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary
379 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer.
380 | simpleTimer.run();
381 | }
382 |
--------------------------------------------------------------------------------
/examples/ISR_16_Timers_Array_Complex_OneShot/ISR_16_Timers_Array_Complex_OneShot.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ISR_16_Timers_Array_Complex_OneShot.ino
3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
10 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
11 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
12 |
13 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
14 | The timer counters can be configured to count up or down and support automatic reload and software reload.
15 | They can also generate alarms when they reach a specific value, defined by the software.
16 | The value of the counter can be read by the software program.
17 |
18 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
19 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
20 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
21 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
22 | This important feature is absolutely necessary for mission-critical tasks.
23 | *****************************************************************************************************************************/
24 | /*
25 | Notes:
26 | Special design is necessary to share data between interrupt code and the rest of your program.
27 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
28 | variable can not spontaneously change. Because your function may change variables while your program is using them,
29 | the compiler needs this hint. But volatile alone is often not enough.
30 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
31 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
32 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
33 | or the entire sequence of your code which accesses the data.
34 |
35 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs.
36 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet
37 | and Blynk services. You can also have many (up to 16) timers to use.
38 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks.
39 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate
40 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is
41 | written
42 | */
43 |
44 | #if !defined( ESP32 )
45 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
46 | #endif
47 |
48 | // These define's must be placed at the beginning before #include "ESP32TimerInterrupt.h"
49 | #define _TIMERINTERRUPT_LOGLEVEL_ 4
50 |
51 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
52 | #include "ESP32TimerInterrupt.h"
53 |
54 | #include // https://github.com/jfturcot/SimpleTimer
55 |
56 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868
57 |
58 | #ifndef LED_BLUE
59 | #define LED_BLUE 25
60 | #endif
61 |
62 | #ifndef LED_RED
63 | #define LED_RED 27
64 | #endif
65 |
66 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868
67 | // Don't use PIN_D2 with ESP32_C3 (crash)
68 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32
69 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32
70 |
71 | #define HW_TIMER_INTERVAL_US 10000L
72 |
73 | volatile uint32_t startMillis = 0;
74 |
75 | // Init ESP32 timer 1
76 | ESP32Timer ITimer(1);
77 |
78 | // Init ESP32_ISR_Timer
79 | ESP32_ISR_Timer ISR_Timer;
80 |
81 | #define LED_TOGGLE_INTERVAL_MS 2000L
82 |
83 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash.
84 | // and you can't use float calculation inside ISR
85 | // Only OK in core v1.0.6-
86 | bool IRAM_ATTR TimerHandler(void * timerNo)
87 | {
88 | static bool toggle = false;
89 | static int timeRun = 0;
90 |
91 | ISR_Timer.run();
92 |
93 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s
94 | if (++timeRun == ((LED_TOGGLE_INTERVAL_MS * 1000) / HW_TIMER_INTERVAL_US) )
95 | {
96 | timeRun = 0;
97 |
98 | //timer interrupt toggles pin PIN_D19
99 | digitalWrite(PIN_D19, toggle);
100 | toggle = !toggle;
101 | }
102 |
103 | return true;
104 | }
105 |
106 | /////////////////////////////////////////////////
107 |
108 | #define NUMBER_ISR_TIMERS 16
109 |
110 | typedef void (*irqCallback) ();
111 |
112 | /////////////////////////////////////////////////
113 |
114 | #define USE_COMPLEX_STRUCT true
115 |
116 | #if USE_COMPLEX_STRUCT
117 |
118 | typedef struct
119 | {
120 | irqCallback irqCallbackFunc;
121 | uint32_t TimerInterval;
122 | unsigned long deltaMillis;
123 | unsigned long previousMillis;
124 | } ISRTimerData;
125 |
126 | // In ESP32, avoid doing something fancy in ISR, for example Serial.print()
127 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment
128 | // Or you can get this run-time error / crash
129 |
130 | void doingSomething(int index);
131 |
132 | #else
133 |
134 | volatile unsigned long deltaMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
135 | volatile unsigned long previousMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
136 |
137 | // You can assign any interval for any timer here, in milliseconds
138 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] =
139 | {
140 | 5000L, 10000L, 15000L, 20000L, 25000L, 30000L, 35000L, 40000L,
141 | 45000L, 50000L, 55000L, 60000L, 65000L, 70000L, 75000L, 80000L
142 | };
143 |
144 | void doingSomething(int index)
145 | {
146 | unsigned long currentMillis = millis();
147 |
148 | deltaMillis[index] = currentMillis - previousMillis[index];
149 | previousMillis[index] = currentMillis;
150 | }
151 |
152 | #endif
153 |
154 | ////////////////////////////////////
155 | // Shared
156 | ////////////////////////////////////
157 |
158 | void doingSomething0()
159 | {
160 | doingSomething(0);
161 | }
162 |
163 | void doingSomething1()
164 | {
165 | doingSomething(1);
166 | }
167 |
168 | void doingSomething2()
169 | {
170 | doingSomething(2);
171 | }
172 |
173 | void doingSomething3()
174 | {
175 | doingSomething(3);
176 | }
177 |
178 | void doingSomething4()
179 | {
180 | doingSomething(4);
181 | }
182 |
183 | void doingSomething5()
184 | {
185 | doingSomething(5);
186 | }
187 |
188 | void doingSomething6()
189 | {
190 | doingSomething(6);
191 | }
192 |
193 | void doingSomething7()
194 | {
195 | doingSomething(7);
196 | }
197 |
198 | void doingSomething8()
199 | {
200 | doingSomething(8);
201 | }
202 |
203 | void doingSomething9()
204 | {
205 | doingSomething(9);
206 | }
207 |
208 | void doingSomething10()
209 | {
210 | doingSomething(10);
211 | }
212 |
213 | void doingSomething11()
214 | {
215 | doingSomething(11);
216 | }
217 |
218 | void doingSomething12()
219 | {
220 | doingSomething(12);
221 | }
222 |
223 | void doingSomething13()
224 | {
225 | doingSomething(13);
226 | }
227 |
228 | void doingSomething14()
229 | {
230 | doingSomething(14);
231 | }
232 |
233 | void doingSomething15()
234 | {
235 | doingSomething(15);
236 | }
237 |
238 | #if USE_COMPLEX_STRUCT
239 |
240 | ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] =
241 | {
242 | //irqCallbackFunc, TimerInterval, deltaMillis, previousMillis
243 | { doingSomething0, 5000L, 0, 0 },
244 | { doingSomething1, 10000L, 0, 0 },
245 | { doingSomething2, 15000L, 0, 0 },
246 | { doingSomething3, 20000L, 0, 0 },
247 | { doingSomething4, 25000L, 0, 0 },
248 | { doingSomething5, 30000L, 0, 0 },
249 | { doingSomething6, 35000L, 0, 0 },
250 | { doingSomething7, 40000L, 0, 0 },
251 | { doingSomething8, 45000L, 0, 0 },
252 | { doingSomething9, 50000L, 0, 0 },
253 | { doingSomething10, 55000L, 0, 0 },
254 | { doingSomething11, 60000L, 0, 0 },
255 | { doingSomething12, 65000L, 0, 0 },
256 | { doingSomething13, 70000L, 0, 0 },
257 | { doingSomething14, 75000L, 0, 0 },
258 | { doingSomething15, 80000L, 0, 0 }
259 | };
260 |
261 | void doingSomething(int index)
262 | {
263 | unsigned long currentMillis = millis();
264 |
265 | curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis;
266 | curISRTimerData[index].previousMillis = currentMillis;
267 | }
268 |
269 | #else
270 |
271 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] =
272 | {
273 | doingSomething0, doingSomething1, doingSomething2, doingSomething3,
274 | doingSomething4, doingSomething5, doingSomething6, doingSomething7,
275 | doingSomething8, doingSomething9, doingSomething10, doingSomething11,
276 | doingSomething12, doingSomething13, doingSomething14, doingSomething15
277 | };
278 |
279 | #endif
280 | ///////////////////////////////////////////
281 |
282 | #define SIMPLE_TIMER_MS 2000L
283 |
284 | // Init SimpleTimer
285 | SimpleTimer simpleTimer;
286 |
287 | // Here is software Timer, you can do somewhat fancy stuffs without many issues.
288 | // But always avoid
289 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead
290 | // 2. Very long "do", "while", "for" loops without predetermined exit time.
291 | void simpleTimerDoingSomething2s()
292 | {
293 | static unsigned long previousMillis = startMillis;
294 |
295 | unsigned long currMillis = millis();
296 |
297 | Serial.print(F("SimpleTimer : "));
298 | Serial.print(SIMPLE_TIMER_MS / 1000);
299 | Serial.print(F(", ms : "));
300 | Serial.print(currMillis);
301 | Serial.print(F(", Dms : "));
302 | Serial.println(currMillis - previousMillis);
303 |
304 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
305 | {
306 | #if USE_COMPLEX_STRUCT
307 | Serial.print(F("Timer : "));
308 | Serial.print(i);
309 | Serial.print(F(", programmed : "));
310 | Serial.print(curISRTimerData[i].TimerInterval);
311 | Serial.print(F(", actual : "));
312 | Serial.println(curISRTimerData[i].deltaMillis);
313 | // reset to 0 to be sure the timer is running one-shot or permanent
314 | curISRTimerData[i].deltaMillis = 0;
315 | #else
316 | Serial.print(F("Timer : "));
317 | Serial.print(i);
318 | Serial.print(F(", programmed : "));
319 | Serial.print(TimerInterval[i]);
320 | Serial.print(F(", actual : "));
321 | Serial.println(deltaMillis[i]);
322 | // reset to 0 to be sure the timer is running one-shot or permanent
323 | deltaMillis[i] = 0;
324 | #endif
325 | }
326 |
327 | previousMillis = currMillis;
328 | }
329 |
330 | void setup()
331 | {
332 | pinMode(PIN_D19, OUTPUT);
333 |
334 | Serial.begin(115200);
335 |
336 | while (!Serial && millis() < 5000);
337 |
338 | delay(500);
339 |
340 | Serial.print(F("\nStarting ISR_16_Timers_Array_Complex_OneShot on "));
341 | Serial.println(ARDUINO_BOARD);
342 | Serial.println(ESP32_TIMER_INTERRUPT_VERSION);
343 | Serial.print(F("CPU Frequency = "));
344 | Serial.print(F_CPU / 1000000);
345 | Serial.println(F(" MHz"));
346 |
347 | // Interval in microsecs
348 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler))
349 | {
350 | startMillis = millis();
351 | Serial.print(F("Starting ITimer OK, millis() = "));
352 | Serial.println(startMillis);
353 | }
354 | else
355 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
356 |
357 | startMillis = millis();
358 |
359 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary
360 | // You can use up to 16 timer for each ISR_Timer
361 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
362 | {
363 | #if USE_COMPLEX_STRUCT
364 | curISRTimerData[i].previousMillis = startMillis;
365 |
366 | // Set even timer one shot, odd timers run forever
367 | if ( i % 2 )
368 | ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc);
369 | else
370 | ISR_Timer.setTimeout(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc);
371 |
372 | #else
373 | previousMillis[i] = millis();
374 |
375 | // Set even timer one shot, odd timers run forever
376 | if ( i % 2 )
377 | ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]);
378 | else
379 | ISR_Timer.setTimeout(TimerInterval[i], irqCallbackFunc[i]);
380 |
381 | #endif
382 | }
383 |
384 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary.
385 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s);
386 | }
387 |
388 | #define BLOCKING_TIME_MS 10000L
389 |
390 | void loop()
391 | {
392 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer
393 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer
394 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS)
395 | // While that of ISR_Timer is still prefect.
396 | delay(BLOCKING_TIME_MS);
397 |
398 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary
399 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer.
400 | simpleTimer.run();
401 | }
402 |
--------------------------------------------------------------------------------
/examples/ISR_16_Timers_Array_OneShot/ISR_16_Timers_Array_OneShot.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ISR_16_Timers_Array_OneShot.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 | /*
20 | Notes:
21 | Special design is necessary to share data between interrupt code and the rest of your program.
22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
23 | variable can not spontaneously change. Because your function may change variables while your program is using them,
24 | the compiler needs this hint. But volatile alone is often not enough.
25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
28 | or the entire sequence of your code which accesses the data.
29 |
30 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms
31 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor
32 | Assuming LOW is active.
33 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing
34 | If the time between active state is less than 8ms => consider noise.
35 | RPM = 60000 / (rotation time in ms)
36 |
37 | We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state
38 |
39 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs.
40 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet
41 | and Blynk services. You can also have many (up to 16) timers to use.
42 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks.
43 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate
44 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is
45 | written
46 | */
47 |
48 | #if !defined(ESP8266)
49 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
50 | #endif
51 |
52 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
53 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
54 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
55 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system.
56 | #define TIMER_INTERRUPT_DEBUG 2
57 | #define _TIMERINTERRUPT_LOGLEVEL_ 0
58 |
59 | // Select a Timer Clock
60 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
61 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
62 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
63 |
64 | #include "ESP8266TimerInterrupt.h"
65 | #include "ESP8266_ISR_Timer.h"
66 |
67 | #include // https://github.com/jfturcot/SimpleTimer
68 |
69 | #ifndef LED_BUILTIN
70 | #define LED_BUILTIN 2
71 | #endif
72 |
73 | #ifndef LED_BLUE
74 | #define LED_BLUE 25
75 | #endif
76 |
77 | #ifndef LED_RED
78 | #define LED_RED 27
79 | #endif
80 |
81 | #define HW_TIMER_INTERVAL_MS 1L
82 |
83 | volatile uint32_t startMillis = 0;
84 |
85 | // Init ESP8266 timer 1
86 | ESP8266Timer ITimer;
87 |
88 | // Init BlynkTimer
89 | ESP8266_ISR_Timer ISR_Timer;
90 |
91 | #define LED_TOGGLE_INTERVAL_MS 2000L
92 |
93 | void IRAM_ATTR TimerHandler()
94 | {
95 | static bool toggle = false;
96 | static bool started = false;
97 | static int timeRun = 0;
98 |
99 | ISR_Timer.run();
100 |
101 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s
102 | if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS) )
103 | {
104 | timeRun = 0;
105 |
106 | if (!started)
107 | {
108 | started = true;
109 | pinMode(LED_BUILTIN, OUTPUT);
110 | }
111 |
112 | //timer interrupt toggles pin LED_BUILTIN
113 | digitalWrite(LED_BUILTIN, toggle);
114 | toggle = !toggle;
115 | }
116 | }
117 |
118 | #define NUMBER_ISR_TIMERS 16
119 |
120 | // You can assign any interval for any timer here, in milliseconds
121 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] =
122 | {
123 | 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L,
124 | 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L
125 | };
126 |
127 | typedef void (*irqCallback) ();
128 |
129 | #if (TIMER_INTERRUPT_DEBUG > 0)
130 | void printStatus(uint16_t index, unsigned long deltaMillis, unsigned long currentMillis)
131 | {
132 | Serial.print(TimerInterval[index] / 1000);
133 | Serial.print("s: Delta ms = ");
134 | Serial.print(deltaMillis);
135 | Serial.print(", ms = ");
136 | Serial.println(currentMillis);
137 | }
138 | #endif
139 |
140 | // In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument
141 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment
142 | // Or you can get this run-time error / crash
143 | void doingSomething0()
144 | {
145 | #if (TIMER_INTERRUPT_DEBUG > 0)
146 | static unsigned long previousMillis = startMillis;
147 |
148 | unsigned long currentMillis = millis();
149 | unsigned long deltaMillis = currentMillis - previousMillis;
150 |
151 | printStatus(0, deltaMillis, currentMillis);
152 |
153 | previousMillis = currentMillis;
154 | #endif
155 | }
156 |
157 | void doingSomething1()
158 | {
159 | #if (TIMER_INTERRUPT_DEBUG > 1)
160 | static unsigned long previousMillis = startMillis;
161 |
162 | unsigned long currentMillis = millis();
163 | unsigned long deltaMillis = currentMillis - previousMillis;
164 |
165 | printStatus(1, deltaMillis, currentMillis);
166 |
167 | previousMillis = currentMillis;
168 | #endif
169 | }
170 |
171 | void doingSomething2()
172 | {
173 | #if (TIMER_INTERRUPT_DEBUG > 1)
174 | static unsigned long previousMillis = startMillis;
175 |
176 | unsigned long currentMillis = millis();
177 | unsigned long deltaMillis = currentMillis - previousMillis;
178 |
179 | printStatus(2, deltaMillis, currentMillis);
180 |
181 | previousMillis = currentMillis;
182 | #endif
183 | }
184 |
185 | void doingSomething3()
186 | {
187 | #if (TIMER_INTERRUPT_DEBUG > 1)
188 | static unsigned long previousMillis = startMillis;
189 |
190 | unsigned long currentMillis = millis();
191 | unsigned long deltaMillis = currentMillis - previousMillis;
192 |
193 | printStatus(3, deltaMillis, currentMillis);
194 |
195 | previousMillis = currentMillis;
196 | #endif
197 | }
198 |
199 | void doingSomething4()
200 | {
201 | #if (TIMER_INTERRUPT_DEBUG > 1)
202 | static unsigned long previousMillis = startMillis;
203 |
204 | unsigned long currentMillis = millis();
205 | unsigned long deltaMillis = currentMillis - previousMillis;
206 |
207 | printStatus(4, deltaMillis, currentMillis);
208 |
209 | previousMillis = currentMillis;
210 | #endif
211 | }
212 |
213 | void doingSomething5()
214 | {
215 | #if (TIMER_INTERRUPT_DEBUG > 1)
216 | static unsigned long previousMillis = startMillis;
217 |
218 | unsigned long currentMillis = millis();
219 | unsigned long deltaMillis = currentMillis - previousMillis;
220 |
221 | printStatus(5, deltaMillis, currentMillis);
222 |
223 | previousMillis = currentMillis;
224 | #endif
225 | }
226 |
227 | void doingSomething6()
228 | {
229 | #if (TIMER_INTERRUPT_DEBUG > 1)
230 | static unsigned long previousMillis = startMillis;
231 |
232 | unsigned long currentMillis = millis();
233 | unsigned long deltaMillis = currentMillis - previousMillis;
234 |
235 | printStatus(6, deltaMillis, currentMillis);
236 |
237 | previousMillis = currentMillis;
238 | #endif
239 | }
240 |
241 | void doingSomething7()
242 | {
243 | #if (TIMER_INTERRUPT_DEBUG > 1)
244 | static unsigned long previousMillis = startMillis;
245 |
246 | unsigned long currentMillis = millis();
247 | unsigned long deltaMillis = currentMillis - previousMillis;
248 |
249 | printStatus(7, deltaMillis, currentMillis);
250 |
251 | previousMillis = currentMillis;
252 | #endif
253 | }
254 |
255 | void doingSomething8()
256 | {
257 | #if (TIMER_INTERRUPT_DEBUG > 1)
258 | static unsigned long previousMillis = startMillis;
259 |
260 | unsigned long currentMillis = millis();
261 | unsigned long deltaMillis = currentMillis - previousMillis;
262 |
263 | printStatus(8, deltaMillis, currentMillis);
264 |
265 | previousMillis = currentMillis;
266 | #endif
267 | }
268 |
269 | void doingSomething9()
270 | {
271 | #if (TIMER_INTERRUPT_DEBUG > 1)
272 | static unsigned long previousMillis = startMillis;
273 |
274 | unsigned long currentMillis = millis();
275 | unsigned long deltaMillis = currentMillis - previousMillis;
276 |
277 | printStatus(9, deltaMillis, currentMillis);
278 |
279 | previousMillis = currentMillis;
280 | #endif
281 | }
282 |
283 | void doingSomething10()
284 | {
285 | #if (TIMER_INTERRUPT_DEBUG > 1)
286 | static unsigned long previousMillis = startMillis;
287 |
288 | unsigned long currentMillis = millis();
289 | unsigned long deltaMillis = currentMillis - previousMillis;
290 |
291 | printStatus(10, deltaMillis, currentMillis);
292 |
293 | previousMillis = currentMillis;
294 | #endif
295 | }
296 |
297 | void doingSomething11()
298 | {
299 | #if (TIMER_INTERRUPT_DEBUG > 1)
300 | static unsigned long previousMillis = startMillis;
301 |
302 | unsigned long currentMillis = millis();
303 | unsigned long deltaMillis = currentMillis - previousMillis;
304 |
305 | printStatus(11, deltaMillis, currentMillis);
306 |
307 | previousMillis = currentMillis;
308 | #endif
309 | }
310 |
311 | void doingSomething12()
312 | {
313 | #if (TIMER_INTERRUPT_DEBUG > 1)
314 | static unsigned long previousMillis = startMillis;
315 |
316 | unsigned long currentMillis = millis();
317 | unsigned long deltaMillis = currentMillis - previousMillis;
318 |
319 | printStatus(12, deltaMillis, currentMillis);
320 |
321 | previousMillis = currentMillis;
322 | #endif
323 | }
324 |
325 | void doingSomething13()
326 | {
327 | #if (TIMER_INTERRUPT_DEBUG > 1)
328 | static unsigned long previousMillis = startMillis;
329 |
330 | unsigned long currentMillis = millis();
331 | unsigned long deltaMillis = currentMillis - previousMillis;
332 |
333 | printStatus(13, deltaMillis, currentMillis);
334 |
335 | previousMillis = currentMillis;
336 | #endif
337 | }
338 |
339 | void doingSomething14()
340 | {
341 | #if (TIMER_INTERRUPT_DEBUG > 1)
342 | static unsigned long previousMillis = startMillis;
343 |
344 | unsigned long currentMillis = millis();
345 | unsigned long deltaMillis = currentMillis - previousMillis;
346 |
347 | printStatus(14, deltaMillis, currentMillis);
348 |
349 | previousMillis = currentMillis;
350 | #endif
351 | }
352 |
353 | void doingSomething15()
354 | {
355 | #if (TIMER_INTERRUPT_DEBUG > 1)
356 | static unsigned long previousMillis = startMillis;
357 |
358 | unsigned long currentMillis = millis();
359 | unsigned long deltaMillis = currentMillis - previousMillis;
360 |
361 | printStatus(15, deltaMillis, currentMillis);
362 |
363 | previousMillis = currentMillis;
364 | #endif
365 | }
366 |
367 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] =
368 | {
369 | doingSomething0, doingSomething1, doingSomething2, doingSomething3,
370 | doingSomething4, doingSomething5, doingSomething6, doingSomething7,
371 | doingSomething8, doingSomething9, doingSomething10, doingSomething11,
372 | doingSomething12, doingSomething13, doingSomething14, doingSomething15
373 | };
374 |
375 | ////////////////////////////////////////////////
376 |
377 |
378 | #define SIMPLE_TIMER_MS 2000L
379 |
380 | // Init SimpleTimer
381 | SimpleTimer simpleTimer;
382 |
383 | // Here is software Timer, you can do somewhat fancy stuffs without many issues.
384 | // But always avoid
385 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead
386 | // 2. Very long "do", "while", "for" loops without predetermined exit time.
387 | void simpleTimerDoingSomething2s()
388 | {
389 | static unsigned long previousMillis = startMillis;
390 |
391 | Serial.print(F("simpleTimerDoingSomething2s: Delta programmed ms = "));
392 | Serial.print(SIMPLE_TIMER_MS);
393 | Serial.print(F(", actual = "));
394 | Serial.println(millis() - previousMillis);
395 |
396 | previousMillis = millis();
397 | }
398 |
399 | void setup()
400 | {
401 | Serial.begin(115200);
402 |
403 | while (!Serial && millis() < 5000);
404 |
405 | delay(500);
406 |
407 | Serial.print(F("\nStarting ISR_16_Timers_Array_OneShot on "));
408 | Serial.println(ARDUINO_BOARD);
409 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
410 | Serial.print(F("CPU Frequency = "));
411 | Serial.print(F_CPU / 1000000);
412 | Serial.println(F(" MHz"));
413 |
414 | // Interval in microsecs
415 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler))
416 | {
417 | startMillis = millis();
418 | Serial.print(F("Starting ITimer OK, millis() = "));
419 | Serial.println(startMillis);
420 | }
421 | else
422 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
423 |
424 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary
425 | // You can use up to 16 timer for each SAMD_ISR_Timer
426 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
427 | {
428 | //ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]);
429 | // Use this for one shot ISR TImer
430 | ISR_Timer.setTimeout(TimerInterval[i], irqCallbackFunc[i]);
431 | }
432 |
433 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary.
434 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s);
435 | }
436 |
437 | #define BLOCKING_TIME_MS 10000L
438 |
439 | void loop()
440 | {
441 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer
442 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer
443 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS)
444 | // While that of ISR_Timer is still prefect.
445 | delay(BLOCKING_TIME_MS);
446 |
447 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary
448 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer.
449 | simpleTimer.run();
450 | }
451 |
--------------------------------------------------------------------------------
/examples/ISR_RPM_Measure/ISR_RPM_Measure.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ISR_RPM_Measure.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 |
20 | /* Notes:
21 | Special design is necessary to share data between interrupt code and the rest of your program.
22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
23 | variable can not spontaneously change. Because your function may change variables while your program is using them,
24 | the compiler needs this hint. But volatile alone is often not enough.
25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
28 | or the entire sequence of your code which accesses the data.
29 |
30 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms
31 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor
32 | Assuming LOW is active.
33 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing
34 | If the time between active state is less than 8ms => consider noise.
35 | RPM = 60000 / (rotation time in ms)
36 |
37 | We use interrupt to detect whenever the SW is active, set a flag
38 | then use timer to count the time between active state
39 | */
40 |
41 | #if !defined(ESP8266)
42 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
43 | #endif
44 |
45 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
46 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
47 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
48 | #define TIMER_INTERRUPT_DEBUG 2
49 | #define _TIMERINTERRUPT_LOGLEVEL_ 0
50 |
51 | // Select a Timer Clock
52 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
53 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
54 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
55 |
56 | #include "ESP8266TimerInterrupt.h"
57 |
58 | #define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266
59 |
60 | unsigned int interruptPin = PIN_D1;
61 |
62 | #define TIMER_INTERVAL_MS 1
63 | #define DEBOUNCING_INTERVAL_MS 80
64 |
65 | #define LOCAL_DEBUG 1
66 |
67 | // Init ESP8266 timer 1
68 | ESP8266Timer ITimer;
69 |
70 | volatile unsigned long rotationTime = 0;
71 | float RPM = 0.00;
72 | float avgRPM = 0.00;
73 |
74 | volatile int debounceCounter;
75 |
76 | volatile bool activeState = false;
77 |
78 | void IRAM_ATTR detectRotation()
79 | {
80 | activeState = true;
81 | }
82 |
83 | void IRAM_ATTR TimerHandler()
84 | {
85 | if ( activeState )
86 | {
87 | // Reset to prepare for next round of interrupt
88 | activeState = false;
89 |
90 | if (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS )
91 | {
92 |
93 | //min time between pulses has passed
94 | RPM = (float) ( 60000.0f / ( rotationTime * TIMER_INTERVAL_MS ) );
95 |
96 | avgRPM = ( 2 * avgRPM + RPM) / 3,
97 |
98 | #if (TIMER_INTERRUPT_DEBUG > 1)
99 | Serial.print("RPM = ");
100 | Serial.print(avgRPM);
101 | Serial.print(", rotationTime ms = ");
102 | Serial.println(rotationTime * TIMER_INTERVAL_MS);
103 | #endif
104 |
105 | rotationTime = 0;
106 | debounceCounter = 0;
107 | }
108 | else
109 | debounceCounter++;
110 | }
111 | else
112 | {
113 | debounceCounter++;
114 | }
115 |
116 | if (rotationTime >= 5000)
117 | {
118 | // If idle, set RPM to 0, don't increase rotationTime
119 | RPM = 0;
120 |
121 | #if (TIMER_INTERRUPT_DEBUG > 1)
122 | Serial.print("RPM = ");
123 | Serial.print(RPM);
124 | Serial.print(", rotationTime = ");
125 | Serial.println(rotationTime);
126 | #endif
127 |
128 | rotationTime = 0;
129 | }
130 | else
131 | {
132 | rotationTime++;
133 | }
134 | }
135 |
136 | void setup()
137 | {
138 | pinMode(interruptPin, INPUT_PULLUP);
139 |
140 | Serial.begin(115200);
141 |
142 | while (!Serial && millis() < 5000);
143 |
144 | delay(500);
145 |
146 | Serial.print(F("\nStarting ISR_RPM_Measure on "));
147 | Serial.println(ARDUINO_BOARD);
148 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
149 | Serial.print(F("CPU Frequency = "));
150 | Serial.print(F_CPU / 1000000);
151 | Serial.println(F(" MHz"));
152 |
153 | // Interval in microsecs
154 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler))
155 | {
156 | Serial.print(F("Starting ITimer OK, millis() = "));
157 | Serial.println(millis());
158 | }
159 | else
160 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
161 |
162 | // Assumming the interruptPin will go LOW
163 | attachInterrupt(digitalPinToInterrupt(interruptPin), detectRotation, FALLING);
164 | }
165 |
166 | void loop()
167 | {
168 |
169 | }
170 |
--------------------------------------------------------------------------------
/examples/RPM_Measure/RPM_Measure.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | RPM_Measure.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 |
20 | /* Notes:
21 | Special design is necessary to share data between interrupt code and the rest of your program.
22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
23 | variable can not spontaneously change. Because your function may change variables while your program is using them,
24 | the compiler needs this hint. But volatile alone is often not enough.
25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
28 | or the entire sequence of your code which accesses the data.
29 |
30 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms
31 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor
32 | Assuming LOW is active.
33 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing
34 | If the time between active state is less than 8ms => consider noise.
35 | RPM = 60000 / (rotation time in ms)
36 |
37 | You can also use interrupt to detect whenever the SW is active, set a flag
38 | then use timer to count the time between active state
39 | */
40 | #if !defined(ESP8266)
41 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
42 | #endif
43 |
44 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
45 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
46 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
47 | #define TIMER_INTERRUPT_DEBUG 2
48 | #define _TIMERINTERRUPT_LOGLEVEL_ 0
49 |
50 | // Select a Timer Clock
51 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
52 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
53 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
54 |
55 | #include "ESP8266TimerInterrupt.h"
56 |
57 | #define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266
58 |
59 | unsigned int SWPin = PIN_D1;
60 |
61 | #define TIMER_INTERVAL_MS 1
62 | #define DEBOUNCING_INTERVAL_MS 80
63 |
64 | #define LOCAL_DEBUG 1
65 |
66 | // Init ESP8266 timer 1
67 | ESP8266Timer ITimer;
68 |
69 | volatile unsigned long rotationTime = 0;
70 |
71 | float RPM = 0.00;
72 | float avgRPM = 0.00;
73 |
74 | volatile int debounceCounter;
75 |
76 | void IRAM_ATTR TimerHandler()
77 | {
78 | static bool started = false;
79 |
80 | if (!started)
81 | {
82 | started = true;
83 | pinMode(SWPin, INPUT_PULLUP);
84 | }
85 |
86 | if ( !digitalRead(SWPin) && (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS ) )
87 | {
88 | //min time between pulses has passed
89 | RPM = (float) ( 60000.0f / ( rotationTime * TIMER_INTERVAL_MS ) );
90 |
91 | avgRPM = ( 2 * avgRPM + RPM) / 3,
92 |
93 | #if (LOCAL_DEBUG > 0)
94 | Serial.print("RPM = ");
95 | Serial.print(avgRPM);
96 | Serial.print(", rotationTime ms = ");
97 | Serial.println(rotationTime * TIMER_INTERVAL_MS);
98 | #endif
99 |
100 | rotationTime = 0;
101 | debounceCounter = 0;
102 | }
103 | else
104 | {
105 | debounceCounter++;
106 | }
107 |
108 | if (rotationTime >= 5000)
109 | {
110 | // If idle, set RPM to 0, don't increase rotationTime
111 | RPM = 0;
112 |
113 | #if (LOCAL_DEBUG > 0)
114 | Serial.print("RPM = ");
115 | Serial.print(RPM);
116 | Serial.print(", rotationTime = ");
117 | Serial.println(rotationTime);
118 | #endif
119 |
120 | rotationTime = 0;
121 | }
122 | else
123 | {
124 | rotationTime++;
125 | }
126 | }
127 |
128 | void setup()
129 | {
130 | Serial.begin(115200);
131 |
132 | while (!Serial && millis() < 5000);
133 |
134 | delay(500);
135 |
136 | Serial.print(F("\nStarting RPM_Measure on "));
137 | Serial.println(ARDUINO_BOARD);
138 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
139 | Serial.print(F("CPU Frequency = "));
140 | Serial.print(F_CPU / 1000000);
141 | Serial.println(F(" MHz"));
142 |
143 | // Interval in microsecs
144 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler))
145 | {
146 | Serial.print(F("Starting ITimer OK, millis() = "));
147 | Serial.println(millis());
148 | }
149 | else
150 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
151 | }
152 |
153 | void loop()
154 | {
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/examples/SwitchDebounce/SwitchDebounce.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | SwitchDebounce.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 | /* Notes:
20 | Special design is necessary to share data between interrupt code and the rest of your program.
21 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
22 | variable can not spontaneously change. Because your function may change variables while your program is using them,
23 | the compiler needs this hint. But volatile alone is often not enough.
24 |
25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
28 | or the entire sequence of your code which accesses the data.
29 |
30 | Switch Debouncing uses high frequency hardware timer 50Hz == 20ms) to measure the time from the SW is pressed,
31 | debouncing time is 100ms => SW is considered pressed if timer count is > 5, then call / flag SW is pressed
32 | When the SW is released, timer will count (debounce) until more than 50ms until consider SW is released.
33 | We can set to flag or call a function whenever SW is pressed more than certain predetermined time, even before
34 | SW is released.
35 | */
36 | #if !defined(ESP8266)
37 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
38 | #endif
39 |
40 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
41 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
42 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
43 | #define TIMER_INTERRUPT_DEBUG 1
44 | #define _TIMERINTERRUPT_LOGLEVEL_ 0
45 |
46 | // Select a Timer Clock
47 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
48 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
49 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
50 |
51 | #include "ESP8266TimerInterrupt.h"
52 |
53 | #define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266
54 |
55 | unsigned int SWPin = PIN_D1;
56 |
57 | #define TIMER_INTERVAL_MS 20
58 | #define DEBOUNCING_INTERVAL_MS 100
59 | #define LONG_PRESS_INTERVAL_MS 5000
60 |
61 | //#define LOCAL_DEBUG 1
62 |
63 | // Init ESP8266 timer 1
64 | ESP8266Timer ITimer;
65 |
66 | volatile bool SWPressed = false;
67 | volatile bool SWLongPressed = false;
68 |
69 | void IRAM_ATTR TimerHandler()
70 | {
71 | static unsigned int debounceCountSWPressed = 0;
72 | static unsigned int debounceCountSWReleased = 0;
73 |
74 | #if (TIMER_INTERRUPT_DEBUG > 0)
75 | static unsigned long SWPressedTime;
76 | static unsigned long SWReleasedTime;
77 |
78 | static unsigned long currentMillis;
79 | #endif
80 |
81 | currentMillis = millis();
82 |
83 | if ( (!digitalRead(SWPin)) )
84 | {
85 | // Start debouncing counting debounceCountSWPressed and clear debounceCountSWReleased
86 | debounceCountSWReleased = 0;
87 |
88 | if (++debounceCountSWPressed >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS)
89 | {
90 | // Call and flag SWPressed
91 | if (!SWPressed)
92 | {
93 | #if (TIMER_INTERRUPT_DEBUG > 0)
94 | SWPressedTime = currentMillis;
95 |
96 | Serial.print("SW Press, from millis() = ");
97 | Serial.println(SWPressedTime - DEBOUNCING_INTERVAL_MS);
98 | #endif
99 |
100 | SWPressed = true;
101 | // Do something for SWPressed here in ISR
102 | // But it's better to use outside software timer to do your job instead of inside ISR
103 | //Your_Response_To_Press();
104 | }
105 |
106 | if (debounceCountSWPressed >= LONG_PRESS_INTERVAL_MS / TIMER_INTERVAL_MS)
107 | {
108 | // Call and flag SWLongPressed
109 | if (!SWLongPressed)
110 | {
111 | #if (TIMER_INTERRUPT_DEBUG > 0)
112 | Serial.print("SW Long Pressed, total time ms = ");
113 | Serial.print(currentMillis);
114 | Serial.print(" - ");
115 | Serial.print(SWPressedTime - DEBOUNCING_INTERVAL_MS);
116 | Serial.print(" = ");
117 | Serial.println(currentMillis - SWPressedTime + DEBOUNCING_INTERVAL_MS);
118 | #endif
119 |
120 | SWLongPressed = true;
121 | // Do something for SWLongPressed here in ISR
122 | // But it's better to use outside software timer to do your job instead of inside ISR
123 | //Your_Response_To_Long_Press();
124 | }
125 | }
126 | }
127 | }
128 | else
129 | {
130 | // Start debouncing counting debounceCountSWReleased and clear debounceCountSWPressed
131 | if ( SWPressed && (++debounceCountSWReleased >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS))
132 | {
133 | // Call and flag SWPressed
134 | #if (TIMER_INTERRUPT_DEBUGEBUG > 0)
135 | SWReleasedTime = millis();
136 |
137 | // Call and flag SWPressed
138 | Serial.print("SW Released, from millis() = ");
139 | Serial.println(millis());
140 | #endif
141 |
142 | SWPressed = false;
143 | SWLongPressed = false;
144 |
145 | // Do something for !SWPressed here in ISR
146 | // But it's better to use outside software timer to do your job instead of inside ISR
147 | //Your_Response_To_Release();
148 |
149 | // Call and flag SWPressed
150 | #if (TIMER_INTERRUPT_DEBUG > 0)
151 | Serial.print("SW Pressed total time ms = ");
152 | Serial.println(millis() - SWPressedTime);
153 | #endif
154 |
155 | debounceCountSWPressed = 0;
156 | }
157 | }
158 | }
159 |
160 | void setup()
161 | {
162 | pinMode(SWPin, INPUT_PULLUP);
163 |
164 | Serial.begin(115200);
165 |
166 | while (!Serial && millis() < 5000);
167 |
168 | delay(500);
169 |
170 | Serial.print(F("\nStarting SwitchDebounce on "));
171 | Serial.println(ARDUINO_BOARD);
172 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
173 | Serial.print(F("CPU Frequency = "));
174 | Serial.print(F_CPU / 1000000);
175 | Serial.println(F(" MHz"));
176 |
177 | // Interval in microsecs
178 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler))
179 | {
180 | Serial.print(F("Starting ITimer OK, millis() = "));
181 | Serial.println(millis());
182 | }
183 | else
184 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
185 | }
186 |
187 | void loop()
188 | {
189 |
190 | }
191 |
--------------------------------------------------------------------------------
/examples/TimerInterruptTest/TimerInterruptTest.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | TimerInterruptTest.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 | *****************************************************************************************************************************/
19 | /* Notes:
20 | Special design is necessary to share data between interrupt code and the rest of your program.
21 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
22 | variable can not spontaneously change. Because your function may change variables while your program is using them,
23 | the compiler needs this hint. But volatile alone is often not enough.
24 |
25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile,
26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
28 | or the entire sequence of your code which accesses the data.
29 | */
30 |
31 | #if !defined(ESP8266)
32 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
33 | #endif
34 |
35 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
36 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
37 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
38 | #define TIMER_INTERRUPT_DEBUG 1
39 | #define _TIMERINTERRUPT_LOGLEVEL_ 1
40 |
41 | // Select a Timer Clock
42 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
43 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
44 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
45 |
46 | #include "ESP8266TimerInterrupt.h"
47 |
48 | #define BUILTIN_LED 2 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED
49 |
50 | volatile bool statusLed = false;
51 | volatile uint32_t lastMillis = 0;
52 |
53 | #define TIMER_INTERVAL_MS 1000
54 |
55 | // Init ESP8266 timer 1
56 | ESP8266Timer ITimer;
57 |
58 | //=======================================================================
59 | void IRAM_ATTR TimerHandler()
60 | {
61 | static bool started = false;
62 |
63 | if (!started)
64 | {
65 | started = true;
66 | pinMode(BUILTIN_LED, OUTPUT);
67 | }
68 |
69 | digitalWrite(BUILTIN_LED, statusLed); //Toggle LED Pin
70 | statusLed = !statusLed;
71 |
72 | #if (TIMER_INTERRUPT_DEBUG > 0)
73 | Serial.println("Delta ms = " + String(millis() - lastMillis));
74 | lastMillis = millis();
75 | #endif
76 | }
77 | //=======================================================================
78 | // Setup
79 | //=======================================================================
80 | void setup()
81 | {
82 | Serial.begin(115200);
83 |
84 | while (!Serial && millis() < 5000);
85 |
86 | delay(500);
87 |
88 | Serial.print(F("\nStarting TimerInterruptTest on "));
89 | Serial.println(ARDUINO_BOARD);
90 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
91 | Serial.print(F("CPU Frequency = "));
92 | Serial.print(F_CPU / 1000000);
93 | Serial.println(F(" MHz"));
94 |
95 | // Interval in microsecs
96 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler))
97 | {
98 | lastMillis = millis();
99 | Serial.print(F("Starting ITimer OK, millis() = "));
100 | Serial.println(lastMillis);
101 | }
102 | else
103 | Serial.println(F("Can't set ITimer correctly. Select another freq. or interval"));
104 |
105 | }
106 | //=======================================================================
107 | // MAIN LOOP
108 | //=======================================================================
109 | void loop()
110 | {
111 | }
112 |
--------------------------------------------------------------------------------
/examples/multiFileProject/multiFileProject.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | multiFileProject.cpp
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 | *****************************************************************************************************************************/
9 |
10 | // To demo how to include files in multi-file Projects
11 |
12 | #include "multiFileProject.h"
13 |
14 | ESP8266Timer ITimer;
15 | ESP8266_ISR_Timer ISR_Timer;
16 |
17 | void IRAM_ATTR TimerHandler()
18 | {
19 | ISR_Timer.run();
20 | Serial.println("TimerHandler triggered");
21 | }
22 |
23 | void doingSomething0()
24 | {
25 | Serial.println("doingSomething0 triggered");
26 | }
27 |
28 | void setupISR()
29 | {
30 | if (ITimer.attachInterruptInterval(1000 * 1000, TimerHandler))
31 | {
32 | Serial.print(F("Starting ITimer OK"));
33 | }
34 | else
35 | Serial.println(F("Can't set ITimer. Select another freq. or timer"));
36 |
37 | ISR_Timer.setInterval(3000, doingSomething0);
38 | }
39 |
--------------------------------------------------------------------------------
/examples/multiFileProject/multiFileProject.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | multiFileProject.h
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 | *****************************************************************************************************************************/
9 |
10 | // To demo how to include files in multi-file Projects
11 |
12 | #pragma once
13 |
14 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
15 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
16 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
17 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system.
18 | #define TIMER_INTERRUPT_DEBUG 2
19 | #define _TIMERINTERRUPT_LOGLEVEL_ 0
20 |
21 | // Select a Timer Clock
22 | #define USING_TIM_DIV1 false // for shortest and most accurate timer
23 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer
24 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default
25 |
26 | // Can be included as many times as necessary, without `Multiple Definitions` Linker Error
27 | #include "ESP8266TimerInterrupt.h"
28 | #include "ESP8266_ISR_Timer.hpp"
29 |
30 | extern ESP8266_ISR_Timer ISR_Timer; // declaration of the global variable ISRTimer
31 |
32 | void setupISR();
33 |
--------------------------------------------------------------------------------
/examples/multiFileProject/multiFileProject.ino:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | multiFileProject.ino
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 | *****************************************************************************************************************************/
9 |
10 | // To demo how to include files in multi-file Projects
11 |
12 | #ifndef ESP8266
13 | #error This code is designed to run on ESP8266 platform! Please check your Tools->Board setting.
14 | #endif
15 |
16 | #define ESP8266_TIMER_INTERRUPT_VERSION_MIN_TARGET "ESP8266TimerInterrupt v1.6.0"
17 | #define ESP8266_TIMER_INTERRUPT_VERSION_MIN 1006000
18 |
19 | #include "multiFileProject.h"
20 |
21 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
22 | #include "ESP8266TimerInterrupt.h"
23 | #include "ESP8266_ISR_Timer.h"
24 |
25 | void doingSomething1()
26 | {
27 | Serial.println("doingSomething1 triggered");
28 | }
29 |
30 | void setup()
31 | {
32 | Serial.begin(115200);
33 |
34 | while (!Serial && millis() < 5000);
35 |
36 | delay(500);
37 |
38 | Serial.println("\nStart multiFileProject");
39 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION);
40 |
41 | #if defined(ESP8266_TIMER_INTERRUPT_VERSION_MIN)
42 |
43 | if (ESP8266_TIMER_INTERRUPT_VERSION_INT < ESP8266_TIMER_INTERRUPT_VERSION_MIN)
44 | {
45 | Serial.print("Warning. Must use this example on Version equal or later than : ");
46 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION_MIN_TARGET);
47 | }
48 |
49 | #endif
50 |
51 | setupISR(); // in multifileProject.cpp
52 |
53 | ISR_Timer.setTimeout(5000, doingSomething1);
54 | }
55 |
56 | void loop()
57 | {
58 | // put your main code here, to run repeatedly:
59 | }
60 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | #######################################
2 | # Datatypes (KEYWORD1)
3 | #######################################
4 |
5 | ESP8266TimerInterrupt KEYWORD1
6 | ESP8266Timer KEYWORD1
7 | ESP8266_ISR_Timer KEYWORD1
8 | ISRTimer KEYWORD1
9 |
10 | #######################################
11 | # Methods and Functions (KEYWORD2)
12 | #######################################
13 |
14 | setFrequency KEYWORD2
15 | setInterval KEYWORD2
16 | attachInterrupt KEYWORD2
17 | attachInterruptInterval KEYWORD2
18 | detachInterrupt KEYWORD2
19 | disableTimer KEYWORD2
20 | reattachInterrupt KEYWORD2
21 | enableTimer KEYWORD2
22 | stopTimer KEYWORD2
23 | restartTimer KEYWORD2
24 | init KEYWORD2
25 | run KEYWORD2
26 | setTimeout KEYWORD2
27 | setTimer KEYWORD2
28 | changeInterval KEYWORD2
29 | deleteTimer KEYWORD2
30 | restartTimer KEYWORD2
31 | isEnabled KEYWORD2
32 | enable KEYWORD2
33 | disable KEYWORD2
34 | enableAll KEYWORD2
35 | disableAll KEYWORD2
36 | toggle KEYWORD2
37 | getNumTimers KEYWORD2
38 | getNumAvailableTimers KEYWORD2
39 |
40 | #######################################
41 | # Constants (LITERAL1)
42 | #######################################
43 |
44 | ESP8266_TIMER_INTERRUPT_VERSION LITERAL1
45 | ESP8266_TIMER_INTERRUPT_VERSION_MAJOR LITERAL1
46 | ESP8266_TIMER_INTERRUPT_VERSION_MINOR LITERAL1
47 | ESP8266_TIMER_INTERRUPT_VERSION_PATCH LITERAL1
48 | ESP8266_TIMER_INTERRUPT_VERSION_INT LITERAL1
49 |
50 | MAX_ESP8266_NUM_TIMERS LITERAL1
51 | MAX_ESP8266_COUNT LITERAL1
52 |
53 | TIM_DIV1_CLOCK LITERAL1
54 | TIM_DIV16_CLOCK LITERAL1
55 | TIM_DIV256_CLOCK LITERAL1
56 |
57 | TIM_CLOCK_FREQ LITERAL1
58 | TIM_DIV LITERAL1
59 |
60 | USING_TIM_DIV1 LITERAL1
61 | USING_TIM_DIV16 LITERAL1
62 | USING_TIM_DIV256 LITERAL1
63 |
64 |
--------------------------------------------------------------------------------
/library.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ESP8266TimerInterrupt",
3 | "version": "1.6.0",
4 | "keywords": "timing, device, control, timer, interrupt, timer-interrupt, hardware, isr, isr-based, hardware-timer, isr-timer, isr-based-timer, mission-critical, accuracy, precise, non-blocking, long-timer, esp8266, multi-isr-based-timers",
5 | "description": "This library enables you to use Interrupt from Hardware Timers on an ESP8266-based board. It now supports 16 ISR-based timers, while consuming only 1 Hardware Timer. Timers' interval is very long (ulong millisecs). The most important feature is they're ISR-based timers. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware timers, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy.",
6 | "authors":
7 | {
8 | "name": "Khoi Hoang",
9 | "url": "https://github.com/khoih-prog",
10 | "maintainer": true
11 | },
12 | "repository":
13 | {
14 | "type": "git",
15 | "url": "https://github.com/khoih-prog/ESP8266TimerInterrupt"
16 | },
17 | "homepage": "https://github.com/khoih-prog/ESP8266TimerInterrupt",
18 | "export": {
19 | "exclude": [
20 | "linux",
21 | "extras",
22 | "tests"
23 | ]
24 | },
25 | "license": "MIT",
26 | "frameworks": "*",
27 | "platforms": "espressif8266",
28 | "examples": "examples/*/*/*.ino",
29 | "headers": ["ESP8266TimerInterrupt.h", "ESP8266_ISR_Timer.h", "ESP8266_ISR_Timer.hpp"]
30 | }
31 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=ESP8266TimerInterrupt
2 | version=1.6.0
3 | author=Khoi Hoang
4 | maintainer=Khoi Hoang
5 | sentence=This library enables you to use Interrupt from Hardware Timers on an ESP8266-based board.
6 | paragraph=These ESP8266 Hardware Timers, using Interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's mandatory if you need to measure some data requiring better accuracy. It now supports 16 ISR-based Timers, while consuming only 1 Hardware Timer. Timers' interval is very long (ulong millisecs). The most important feature is they're ISR-based Timers. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks.
7 | category=Device Control
8 | url=https://github.com/khoih-prog/ESP8266TimerInterrupt
9 | architectures=esp8266
10 | repository=https://github.com/khoih-prog/ESP8266TimerInterrupt
11 | license=MIT
12 | includes=ESP8266TimerInterrupt.h,ESP8266_ISR_Timer.h,ESP8266_ISR_Timer.hpp
13 |
--------------------------------------------------------------------------------
/platformio/platformio.ini:
--------------------------------------------------------------------------------
1 | ;PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [platformio]
12 | ; ============================================================
13 | ; chose environment:
14 | ; ESP8266
15 | ; ============================================================
16 | default_envs = ESP8266
17 |
18 | [env]
19 | ; ============================================================
20 | ; Serial configuration
21 | ; choose upload speed, serial-monitor speed
22 | ; ============================================================
23 | upload_speed = 921600
24 | ;upload_port = COM11
25 | ;monitor_speed = 9600
26 | ;monitor_port = COM11
27 |
28 | ; Checks for the compatibility with frameworks and dev/platforms
29 | lib_compat_mode = strict
30 | lib_ldf_mode = chain+
31 | ;lib_ldf_mode = deep+
32 |
33 | lib_deps =
34 |
35 |
36 | build_flags =
37 | ; set your debug output (default=Serial)
38 | -D DEBUG_ESP_PORT=Serial
39 | ; comment the following line to enable WiFi debugging
40 | -D NDEBUG
41 |
42 | [env:ESP8266]
43 | platform = espressif8266
44 | framework = arduino
45 | ; ============================================================
46 | ; Board configuration
47 | ; choose your board by uncommenting one of the following lines
48 | ; ============================================================
49 | ;board = gen4iod
50 | ;board = huzzah
51 | ;board = oak
52 | ;board = esp_wroom_02
53 | ;board = espduino
54 | ;board = espectro
55 | ;board = espino
56 | ;board = espresso_lite_v1
57 | ;board = espresso_lite_v2
58 | ;board = esp12e
59 | ;board = esp01_1m
60 | ;board = esp01
61 | ;board = esp07
62 | ;board = esp8285
63 | ;board = heltec_wifi_kit_8
64 | ;board = inventone
65 | ;board = nodemcu
66 | board = nodemcuv2
67 | ;board = modwifi
68 | ;board = phoenix_v1
69 | ;board = phoenix_v2
70 | ;board = sparkfunBlynk
71 | ;board = thing
72 | ;board = thingdev
73 | ;board = esp210
74 | ;board = espinotee
75 | ;board = d1
76 | ;board = d1_mini
77 | ;board = d1_mini_lite
78 | ;board = d1_mini_pro
79 | ;board = wifi_slot
80 | ;board = wifiduino
81 | ;board = wifinfo
82 | ;board = wio_link
83 | ;board = wio_node
84 | ;board = xinabox_cw01
85 | ;board = esp32doit-devkit-v1
86 |
87 |
--------------------------------------------------------------------------------
/src/ESP32TimerInterrupt.h:
--------------------------------------------------------------------------------
1 |
2 | /****************************************************************************************************************************
3 | ESP32TimerInterrupt.h
4 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
5 | Written by Khoi Hoang
6 |
7 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
8 | Licensed under MIT license
9 |
10 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
11 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
12 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
13 |
14 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
15 | The timer counters can be configured to count up or down and support automatic reload and software reload.
16 | They can also generate alarms when they reach a specific value, defined by the software.
17 | The value of the counter can be read by the software program.
18 |
19 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
20 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
21 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
22 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
23 | This important feature is absolutely necessary for mission-critical tasks.
24 |
25 | Based on SimpleTimer - A timer library for Arduino.
26 | Author: mromani@ottotecnica.com
27 | Copyright (c) 2010 OTTOTECNICA Italy
28 |
29 | Based on BlynkTimer.h
30 | Author: Volodymyr Shymanskyy
31 |
32 | Version: 2.3.0
33 |
34 | Version Modified By Date Comments
35 | ------- ----------- ---------- -----------
36 | 1.0.0 K Hoang 23/11/2019 Initial coding
37 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library
38 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
39 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README.
40 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
41 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
42 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
43 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2
44 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
45 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1
46 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error
47 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library.
48 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
49 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards
50 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core
51 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards
52 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3
53 | *****************************************************************************************************************************/
54 |
55 | #pragma once
56 |
57 | #ifndef ESP32TIMERINTERRUPT_H
58 | #define ESP32TIMERINTERRUPT_H
59 |
60 | #include "ESP32TimerInterrupt.hpp"
61 | #include "ESP32_ISR_Timer.h"
62 |
63 | #endif // ESP32TIMERINTERRUPT_H
64 |
65 |
--------------------------------------------------------------------------------
/src/ESP32TimerInterrupt.hpp:
--------------------------------------------------------------------------------
1 |
2 | /****************************************************************************************************************************
3 | ESP32TimerInterrupt.hpp
4 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
5 | Written by Khoi Hoang
6 |
7 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
8 | Licensed under MIT license
9 |
10 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
11 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
12 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
13 |
14 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
15 | The timer counters can be configured to count up or down and support automatic reload and software reload.
16 | They can also generate alarms when they reach a specific value, defined by the software.
17 | The value of the counter can be read by the software program.
18 |
19 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
20 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
21 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
22 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
23 | This important feature is absolutely necessary for mission-critical tasks.
24 |
25 | Based on SimpleTimer - A timer library for Arduino.
26 | Author: mromani@ottotecnica.com
27 | Copyright (c) 2010 OTTOTECNICA Italy
28 |
29 | Based on BlynkTimer.h
30 | Author: Volodymyr Shymanskyy
31 |
32 | Version: 2.3.0
33 |
34 | Version Modified By Date Comments
35 | ------- ----------- ---------- -----------
36 | 1.0.0 K Hoang 23/11/2019 Initial coding
37 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library
38 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
39 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README.
40 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
41 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
42 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
43 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2
44 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
45 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1
46 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error
47 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library.
48 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
49 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards
50 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core
51 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards
52 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3
53 | *****************************************************************************************************************************/
54 |
55 | #pragma once
56 |
57 | #ifndef ESP32TIMERINTERRUPT_HPP
58 | #define ESP32TIMERINTERRUPT_HPP
59 |
60 | #if ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \
61 | ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \
62 | ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM || ARDUINO_ADAFRUIT_QTPY_ESP32S2 || ARDUINO_ESP32S2_USB || \
63 | ARDUINO_FEATHERS2NEO || ARDUINO_TINYS2 || ARDUINO_RMP || ARDUINO_LOLIN_S2_MINI || ARDUINO_LOLIN_S2_PICO || \
64 | ARDUINO_ADAFRUIT_FEATHER_ESP32S2 || ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT || ARDUINO_atmegazero_esp32s2 || \
65 | ARDUINO_DYM || ARDUINO_FRANZININHO_WIFI || ARDUINO_FRANZININHO_WIFI_MSC )
66 | #define USING_ESP32_S2_TIMERINTERRUPT true
67 |
68 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3)
69 | #warning USING_ESP32_S2_TIMERINTERRUPT
70 | #endif
71 | #elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \
72 | defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_NOPSRAM) || \
73 | defined(ARDUINO_ADAFRUIT_QTPY_ESP32S3_NOPSRAM) || defined(ARDUINO_ESP32S3_CAM_LCD) || \
74 | defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_TFT) || \
75 | defined(ARDUINO_ESP32_S3_USB_OTG) )
76 | #define USING_ESP32_S3_TIMERINTERRUPT true
77 |
78 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3)
79 | #warning USING_ESP32_S3_TIMERINTERRUPT
80 | #endif
81 | #elif ( defined(ARDUINO_ESP32C3_DEV) || defined(ARDUINO_LOLIN_C3_MINI) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) || \
82 | defined(ARDUINO_AirM2M_CORE_ESP32C3) || defined(ARDUINO_XIAO_ESP32C3) )
83 | #define USING_ESP32_C3_TIMERINTERRUPT true
84 |
85 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3)
86 | #warning USING_ESP32_C3_TIMERINTERRUPT
87 | #endif
88 | #elif defined(ESP32)
89 | #define USING_ESP32_TIMERINTERRUPT true
90 |
91 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3)
92 | #warning USING_ESP32_TIMERINTERRUPT
93 | #endif
94 | #else
95 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
96 | #endif
97 |
98 | #ifndef ESP32_TIMER_INTERRUPT_VERSION
99 | #define ESP32_TIMER_INTERRUPT_VERSION "ESP32TimerInterrupt v2.3.0"
100 |
101 | #define ESP32_TIMER_INTERRUPT_VERSION_MAJOR 2
102 | #define ESP32_TIMER_INTERRUPT_VERSION_MINOR 3
103 | #define ESP32_TIMER_INTERRUPT_VERSION_PATCH 0
104 |
105 | #define ESP32_TIMER_INTERRUPT_VERSION_INT 2003000
106 | #endif
107 |
108 | #ifndef TIMER_INTERRUPT_DEBUG
109 | #define TIMER_INTERRUPT_DEBUG 0
110 | #endif
111 |
112 | #if defined(ARDUINO)
113 | #if ARDUINO >= 100
114 | #include
115 | #else
116 | #include
117 | #endif
118 | #endif
119 |
120 | #include "TimerInterrupt_Generic_Debug.h"
121 |
122 | #include
123 |
124 | /*
125 | //ESP32 core v1.0.6, hw_timer_t defined in esp32/tools/sdk/include/driver/driver/timer.h:
126 |
127 | #define TIMER_BASE_CLK (APB_CLK_FREQ) //Frequency of the clock on the input of the timer groups
128 |
129 |
130 | //@brief Selects a Timer-Group out of 2 available groups
131 |
132 | typedef enum
133 | {
134 | TIMER_GROUP_0 = 0, // Hw timer group 0
135 | TIMER_GROUP_1 = 1, // Hw timer group 1
136 | TIMER_GROUP_MAX,
137 | } timer_group_t;
138 |
139 |
140 | //@brief Select a hardware timer from timer groups
141 |
142 | typedef enum
143 | {
144 | TIMER_0 = 0, // Select timer0 of GROUPx
145 | TIMER_1 = 1, // Select timer1 of GROUPx
146 | TIMER_MAX,
147 | } timer_idx_t;
148 |
149 |
150 | //@brief Decides the direction of counter
151 |
152 | typedef enum
153 | {
154 | TIMER_COUNT_DOWN = 0, //Descending Count from cnt.high|cnt.low
155 | TIMER_COUNT_UP = 1, //Ascending Count from Zero
156 | TIMER_COUNT_MAX
157 | } timer_count_dir_t;
158 |
159 |
160 | //@brief Decides whether timer is on or paused
161 |
162 | typedef enum
163 | {
164 | TIMER_PAUSE = 0, //Pause timer counter
165 | TIMER_START = 1, //Start timer counter
166 | } timer_start_t;
167 |
168 |
169 | //@brief Decides whether to enable alarm mode
170 |
171 | typedef enum
172 | {
173 | TIMER_ALARM_DIS = 0, //Disable timer alarm
174 | TIMER_ALARM_EN = 1, //Enable timer alarm
175 | TIMER_ALARM_MAX
176 | } timer_alarm_t;
177 |
178 |
179 | //@brief Select interrupt type if running in alarm mode.
180 |
181 | typedef enum
182 | {
183 | TIMER_INTR_LEVEL = 0, //Interrupt mode: level mode
184 | //TIMER_INTR_EDGE = 1, //Interrupt mode: edge mode, Not supported Now
185 | TIMER_INTR_MAX
186 | } timer_intr_mode_t;
187 |
188 |
189 | //@brief Select if Alarm needs to be loaded by software or automatically reload by hardware.
190 |
191 | typedef enum
192 | {
193 | TIMER_AUTORELOAD_DIS = 0, //Disable auto-reload: hardware will not load counter value after an alarm event
194 | TIMER_AUTORELOAD_EN = 1, //Enable auto-reload: hardware will load counter value after an alarm event
195 | TIMER_AUTORELOAD_MAX,
196 | } timer_autoreload_t;
197 |
198 |
199 | //@brief Data structure with timer's configuration settings
200 |
201 | typedef struct
202 | {
203 | bool alarm_en; //Timer alarm enable
204 | bool counter_en; //Counter enable
205 | timer_intr_mode_t intr_type; //Interrupt mode
206 | timer_count_dir_t counter_dir; //Counter direction
207 | bool auto_reload; //Timer auto-reload
208 | uint32_t divider; //Counter clock divider. The divider's range is from from 2 to 65536.
209 | } timer_config_t;
210 |
211 | */
212 |
213 | /*
214 | //ESP32 core v2.0.4, timer_config_t defined in tools/sdk/esp32/include/hal/include/hal/timer_types.h:
215 | #if SOC_TIMER_GROUP_SUPPORT_XTAL
216 | typedef enum {
217 | TIMER_SRC_CLK_APB = 0, // Select APB as the source clock
218 | TIMER_SRC_CLK_XTAL = 1, // Select XTAL as the source clock
219 | } timer_src_clk_t;
220 | #endif
221 | typedef struct {
222 | timer_alarm_t alarm_en; // Timer alarm enable
223 | timer_start_t counter_en; // Counter enable
224 | timer_intr_mode_t intr_type; // Interrupt mode
225 | timer_count_dir_t counter_dir; // Counter direction
226 | timer_autoreload_t auto_reload; // Timer auto-reload
227 | uint32_t divider; // Counter clock divider. The divider's range is from from 2 to 65536
228 | #if SOC_TIMER_GROUP_SUPPORT_XTAL
229 | timer_src_clk_t clk_src; // Use XTAL as source clock
230 | #endif
231 | } timer_config_t;
232 |
233 | */
234 |
235 | class ESP32TimerInterrupt;
236 |
237 | typedef ESP32TimerInterrupt ESP32Timer;
238 |
239 | #if USING_ESP32_C3_TIMERINTERRUPT
240 | #define MAX_ESP32_NUM_TIMERS 2
241 | #else
242 | #define MAX_ESP32_NUM_TIMERS 4
243 | #endif
244 |
245 | #define TIMER_DIVIDER 80 // Hardware timer clock divider
246 | // TIMER_BASE_CLK = APB_CLK_FREQ = Frequency of the clock on the input of the timer groups
247 | #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds
248 |
249 |
250 | // In esp32/1.0.6/tools/sdk/esp32s2/include/driver/include/driver/timer.h
251 | // typedef bool (*timer_isr_t)(void *);
252 | //esp_err_t timer_isr_callback_add(timer_group_t group_num, timer_idx_t timer_num, timer_isr_t isr_handler, void *arg, int intr_alloc_flags);
253 | //esp_err_t timer_isr_callback_remove(timer_group_t group_num, timer_idx_t timer_num);
254 | //timer_deinit(timer_group_t group_num, timer_idx_t timer_num);
255 | //esp_err_t timer_group_intr_enable(timer_group_t group_num, timer_intr_t intr_mask);
256 | //esp_err_t timer_group_intr_disable(timer_group_t group_num, timer_intr_t intr_mask);
257 |
258 | typedef bool (*esp32_timer_callback) (void *);
259 |
260 | // For ESP32_C3, TIMER_MAX == 1
261 | // For ESP32 and ESP32_S2, TIMER_MAX == 2
262 |
263 | typedef struct
264 | {
265 | timer_idx_t timer_idx;
266 | timer_group_t timer_group;
267 | //int alarm_interval;
268 | //timer_autoreload_t auto_reload;
269 | } timer_info_t;
270 |
271 | // Warning: TIMER_SRC_CLK_XTAL only good for ESP32
272 | // Use TIMER_SRC_CLK_APB for ESP32_C3, ESP32_S2 and ESP32_S3
273 |
274 | class ESP32TimerInterrupt
275 | {
276 | private:
277 |
278 | timer_config_t stdConfig =
279 | {
280 | .alarm_en = TIMER_ALARM_EN, //enable timer alarm
281 | .counter_en = TIMER_START, //starts counting counter once timer_init called
282 | .intr_type = TIMER_INTR_MAX,
283 | .counter_dir = TIMER_COUNT_UP, //counts from 0 to counter value
284 | .auto_reload = TIMER_AUTORELOAD_EN, //reloads counter automatically
285 | .divider = TIMER_DIVIDER,
286 | #if (SOC_TIMER_GROUP_SUPPORT_XTAL)
287 | #if (USING_ESP32_TIMERINTERRUPT)
288 | .clk_src = TIMER_SRC_CLK_XTAL //Use XTAL as source clock
289 | #else
290 | .clk_src = TIMER_SRC_CLK_APB //Use APB as source clock
291 | #endif
292 | #endif
293 | };
294 |
295 | timer_idx_t _timerIndex;
296 | timer_group_t _timerGroup;
297 | uint32_t interruptFlag; // either TIMER_INTR_T0 or TIMER_INTR_T1
298 |
299 | uint8_t _timerNo;
300 |
301 | esp32_timer_callback _callback; // pointer to the callback function
302 | float _frequency; // Timer frequency
303 | uint64_t _timerCount; // count to activate timer
304 |
305 | //xQueueHandle s_timer_queue;
306 |
307 | public:
308 |
309 | ESP32TimerInterrupt(const uint8_t& timerNo)
310 | {
311 | _callback = NULL;
312 |
313 | if (timerNo < MAX_ESP32_NUM_TIMERS)
314 | {
315 | _timerNo = timerNo;
316 |
317 | #if USING_ESP32_C3_TIMERINTERRUPT
318 |
319 | // Always using TIMER_INTR_T0
320 | _timerIndex = (timer_idx_t) ( (uint32_t) 0 );
321 |
322 | // timerNo == 0 => Group 0, timerNo == 1 => Group 1
323 | _timerGroup = (timer_group_t) ( (uint32_t) timerNo);
324 |
325 | #else
326 |
327 | _timerIndex = (timer_idx_t) (_timerNo % TIMER_MAX);
328 |
329 | _timerGroup = (timer_group_t) (_timerNo / TIMER_MAX);
330 |
331 | #endif
332 | }
333 | else
334 | {
335 | _timerNo = MAX_ESP32_NUM_TIMERS;
336 | }
337 | };
338 |
339 | // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
340 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
341 | bool setFrequency(const float& frequency, const esp32_timer_callback& callback)
342 | {
343 | if (_timerNo < MAX_ESP32_NUM_TIMERS)
344 | {
345 | // select timer frequency is 1MHz for better accuracy. We don't use 16-bit prescaler for now.
346 | // Will use later if very low frequency is needed.
347 | _frequency = TIMER_BASE_CLK / TIMER_DIVIDER; //1000000;
348 | _timerCount = (uint64_t) _frequency / frequency;
349 | // count up
350 |
351 | #if USING_ESP32_S2_TIMERINTERRUPT
352 | TISR_LOGWARN3(F("ESP32_S2_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
353 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
354 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
355 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
356 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
357 | #elif USING_ESP32_S3_TIMERINTERRUPT
358 | // ESP32-S3 is embedded with four 54-bit general-purpose timers, which are based on 16-bit prescalers
359 | // and 54-bit auto-reload-capable up/down-timers
360 | TISR_LOGWARN3(F("ESP32_S3_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
361 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
362 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
363 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
364 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
365 | #else
366 | TISR_LOGWARN3(F("ESP32_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
367 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
368 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
369 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
370 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
371 | #endif
372 |
373 | timer_init(_timerGroup, _timerIndex, &stdConfig);
374 |
375 | // Counter value to 0 => counting up to alarm value as .counter_dir == TIMER_COUNT_UP
376 | timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL);
377 |
378 | timer_set_alarm_value(_timerGroup, _timerIndex, TIMER_SCALE / frequency);
379 |
380 | // enable interrupts for _timerGroup, _timerIndex
381 | timer_enable_intr(_timerGroup, _timerIndex);
382 |
383 | _callback = callback;
384 |
385 | // Register the ISR handler
386 | // If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set, the handler function must be declared with IRAM_ATTR attribute
387 | // and can only call functions in IRAM or ROM. It cannot call other timer APIs.
388 | //timer_isr_register(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, ESP_INTR_FLAG_IRAM, NULL);
389 | timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0);
390 |
391 | timer_start(_timerGroup, _timerIndex);
392 |
393 | return true;
394 | }
395 | else
396 | {
397 | #if USING_ESP32_C3_TIMERINTERRUPT
398 | TISR_LOGERROR(F("Error. Timer must be 0-1"));
399 | #else
400 | TISR_LOGERROR(F("Error. Timer must be 0-3"));
401 | #endif
402 |
403 | return false;
404 | }
405 | }
406 |
407 | // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
408 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
409 | bool setInterval(const unsigned long& interval, const esp32_timer_callback& callback)
410 | {
411 | return setFrequency((float) (1000000.0f / interval), callback);
412 | }
413 |
414 | bool attachInterrupt(const float& frequency, const esp32_timer_callback& callback)
415 | {
416 | return setFrequency(frequency, callback);
417 | }
418 |
419 | // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
420 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
421 | bool attachInterruptInterval(const unsigned long& interval, const esp32_timer_callback& callback)
422 | {
423 | return setFrequency( (float) ( 1000000.0f / interval), callback);
424 | }
425 |
426 | void detachInterrupt()
427 | {
428 | #if USING_ESP32_C3_TIMERINTERRUPT
429 | timer_group_intr_disable(_timerGroup, TIMER_INTR_T0);
430 | #else
431 | timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
432 | #endif
433 | }
434 |
435 | void disableTimer()
436 | {
437 | #if USING_ESP32_C3_TIMERINTERRUPT
438 | timer_group_intr_disable(_timerGroup, TIMER_INTR_T0);
439 | #else
440 | timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
441 | #endif
442 | }
443 |
444 | // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely
445 | void reattachInterrupt()
446 | {
447 | #if USING_ESP32_C3_TIMERINTERRUPT
448 | timer_group_intr_enable(_timerGroup, TIMER_INTR_T0);
449 | #else
450 | timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
451 | #endif
452 | }
453 |
454 | // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely
455 | void enableTimer()
456 | {
457 | #if USING_ESP32_C3_TIMERINTERRUPT
458 | timer_group_intr_enable(_timerGroup, TIMER_INTR_T0);
459 | #else
460 | timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
461 | #endif
462 | }
463 |
464 | // Just stop clock source, clear the count
465 | void stopTimer()
466 | {
467 | timer_pause(_timerGroup, _timerIndex);
468 | }
469 |
470 | // Just reconnect clock source, start current count from 0
471 | void restartTimer()
472 | {
473 | timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL);
474 | timer_start(_timerGroup, _timerIndex);
475 | }
476 |
477 | int8_t getTimer() __attribute__((always_inline))
478 | {
479 | return _timerIndex;
480 | };
481 |
482 | int8_t getTimerGroup() __attribute__((always_inline))
483 | {
484 | return _timerGroup;
485 | };
486 |
487 | }; // class ESP32TimerInterrupt
488 |
489 | #include "ESP32_ISR_Timer.hpp"
490 |
491 | #endif // ESP32TIMERINTERRUPT_HPP
492 |
493 |
--------------------------------------------------------------------------------
/src/ESP32_ISR_Timer-Impl.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ESP32_ISR_Timer-Impl.h
3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
10 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
11 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
12 |
13 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
14 | The timer counters can be configured to count up or down and support automatic reload and software reload.
15 | They can also generate alarms when they reach a specific value, defined by the software.
16 | The value of the counter can be read by the software program.
17 |
18 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
19 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
20 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
21 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
22 | This important feature is absolutely necessary for mission-critical tasks.
23 |
24 | Based on SimpleTimer - A timer library for Arduino.
25 | Author: mromani@ottotecnica.com
26 | Copyright (c) 2010 OTTOTECNICA Italy
27 |
28 | Based on BlynkTimer.h
29 | Author: Volodymyr Shymanskyy
30 |
31 | Version: 2.3.0
32 |
33 | Version Modified By Date Comments
34 | ------- ----------- ---------- -----------
35 | 1.0.0 K Hoang 23/11/2019 Initial coding
36 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library
37 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
38 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README.
39 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
40 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
41 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
42 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2
43 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
44 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1
45 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error
46 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library.
47 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
48 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards
49 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core
50 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards
51 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3
52 | *****************************************************************************************************************************/
53 |
54 | #pragma once
55 |
56 | #ifndef ISR_TIMER_GENERIC_IMPL_H
57 | #define ISR_TIMER_GENERIC_IMPL_H
58 |
59 | #include
60 |
61 | ESP32_ISR_Timer::ESP32_ISR_Timer()
62 | : numTimers (-1)
63 | {
64 | }
65 |
66 | void ESP32_ISR_Timer::init()
67 | {
68 | unsigned long current_millis = millis(); //elapsed();
69 |
70 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++)
71 | {
72 | memset((void*) &timer[i], 0, sizeof (timer_t));
73 | timer[i].prev_millis = current_millis;
74 | }
75 |
76 | numTimers = 0;
77 |
78 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR
79 | timerMux = portMUX_INITIALIZER_UNLOCKED;
80 | }
81 |
82 | void IRAM_ATTR ESP32_ISR_Timer::run()
83 | {
84 | uint8_t i;
85 | unsigned long current_millis;
86 |
87 | // get current time
88 | current_millis = millis(); //elapsed();
89 |
90 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR
91 | portENTER_CRITICAL_ISR(&timerMux);
92 |
93 | for (i = 0; i < MAX_NUMBER_TIMERS; i++)
94 | {
95 |
96 | timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN;
97 |
98 | // no callback == no timer, i.e. jump over empty slots
99 | if (timer[i].callback != NULL)
100 | {
101 |
102 | // is it time to process this timer ?
103 | // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592
104 |
105 | if ((current_millis - timer[i].prev_millis) >= timer[i].delay)
106 | {
107 | unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay;
108 |
109 | // update time
110 | timer[i].prev_millis += timer[i].delay * skipTimes;
111 |
112 | // check if the timer callback has to be executed
113 | if (timer[i].enabled)
114 | {
115 |
116 | // "run forever" timers must always be executed
117 | if (timer[i].maxNumRuns == TIMER_RUN_FOREVER)
118 | {
119 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY;
120 | }
121 | // other timers get executed the specified number of times
122 | else if (timer[i].numRuns < timer[i].maxNumRuns)
123 | {
124 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY;
125 | timer[i].numRuns++;
126 |
127 | // after the last run, delete the timer
128 | if (timer[i].numRuns >= timer[i].maxNumRuns)
129 | {
130 | timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL;
131 | }
132 | }
133 | }
134 | }
135 | }
136 | }
137 |
138 | for (i = 0; i < MAX_NUMBER_TIMERS; i++)
139 | {
140 | if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN)
141 | continue;
142 |
143 | if (timer[i].hasParam)
144 | (*(timer_callback_p)timer[i].callback)(timer[i].param);
145 | else
146 | (*(timer_callback)timer[i].callback)();
147 |
148 | if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL)
149 | deleteTimer(i);
150 | }
151 |
152 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR
153 | portEXIT_CRITICAL_ISR(&timerMux);
154 |
155 | }
156 |
157 |
158 | // find the first available slot
159 | // return -1 if none found
160 | int8_t ESP32_ISR_Timer::findFirstFreeSlot()
161 | {
162 | // all slots are used
163 | if (numTimers >= MAX_NUMBER_TIMERS)
164 | {
165 | return -1;
166 | }
167 |
168 | // return the first slot with no callback (i.e. free)
169 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++)
170 | {
171 | if (timer[i].callback == NULL)
172 | {
173 | return i;
174 | }
175 | }
176 |
177 | // no free slots found
178 | return -1;
179 | }
180 |
181 |
182 | int8_t ESP32_ISR_Timer::setupTimer(const unsigned long& delay, void* callback, void* param, bool hasParam,
183 | const uint32_t& numRuns)
184 | {
185 | int freeTimer;
186 |
187 | if (numTimers < 0)
188 | {
189 | init();
190 | }
191 |
192 | freeTimer = findFirstFreeSlot();
193 |
194 | if (freeTimer < 0)
195 | {
196 | return -1;
197 | }
198 |
199 | if (callback == NULL)
200 | {
201 | return -1;
202 | }
203 |
204 | timer[freeTimer].delay = delay;
205 | timer[freeTimer].callback = callback;
206 | timer[freeTimer].param = param;
207 | timer[freeTimer].hasParam = hasParam;
208 | timer[freeTimer].maxNumRuns = numRuns;
209 | timer[freeTimer].enabled = true;
210 | timer[freeTimer].prev_millis = millis();
211 |
212 | numTimers++;
213 |
214 | return freeTimer;
215 | }
216 |
217 |
218 | int ESP32_ISR_Timer::setTimer(const unsigned long& delay, const timer_callback& callback, const uint32_t& numRuns)
219 | {
220 | return setupTimer(delay, (void *)callback, NULL, false, numRuns);
221 | }
222 |
223 | int ESP32_ISR_Timer::setTimer(const unsigned long& delay, const timer_callback_p& callback, void* param,
224 | const uint32_t& numRuns)
225 | {
226 | return setupTimer(delay, (void *)callback, param, true, numRuns);
227 | }
228 |
229 | int ESP32_ISR_Timer::setInterval(const unsigned long& delay, const timer_callback& callback)
230 | {
231 | return setupTimer(delay, (void *)callback, NULL, false, TIMER_RUN_FOREVER);
232 | }
233 |
234 | int ESP32_ISR_Timer::setInterval(const unsigned long& delay, const timer_callback_p& callback, void* param)
235 | {
236 | return setupTimer(delay, (void *)callback, param, true, TIMER_RUN_FOREVER);
237 | }
238 |
239 | int ESP32_ISR_Timer::setTimeout(const unsigned long& delay, const timer_callback& callback)
240 | {
241 | return setupTimer(delay, (void *)callback, NULL, false, TIMER_RUN_ONCE);
242 | }
243 |
244 | int ESP32_ISR_Timer::setTimeout(const unsigned long& delay, const timer_callback_p& callback, void* param)
245 | {
246 | return setupTimer(delay, (void *)callback, param, true, TIMER_RUN_ONCE);
247 | }
248 |
249 | bool IRAM_ATTR ESP32_ISR_Timer::changeInterval(const uint8_t& numTimer, const unsigned long& delay)
250 | {
251 | if (numTimer >= MAX_NUMBER_TIMERS)
252 | {
253 | return false;
254 | }
255 |
256 | // Updates interval of existing specified timer
257 | if (timer[numTimer].callback != NULL)
258 | {
259 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
260 | portENTER_CRITICAL(&timerMux);
261 |
262 | timer[numTimer].delay = delay;
263 | timer[numTimer].prev_millis = millis();
264 |
265 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
266 | portEXIT_CRITICAL(&timerMux);
267 |
268 | return true;
269 | }
270 |
271 | // false return for non-used numTimer, no callback
272 | return false;
273 | }
274 |
275 | void ESP32_ISR_Timer::deleteTimer(const uint8_t& timerId)
276 | {
277 | // nothing to delete if no timers are in use
278 | if ( (timerId >= MAX_NUMBER_TIMERS) || (numTimers == 0) )
279 | {
280 | return;
281 | }
282 |
283 | // don't decrease the number of timers if the specified slot is already empty
284 | if (timer[timerId].callback != NULL)
285 | {
286 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
287 | portENTER_CRITICAL(&timerMux);
288 |
289 | memset((void*) &timer[timerId], 0, sizeof (timer_t));
290 | timer[timerId].prev_millis = millis();
291 |
292 | // update number of timers
293 | numTimers--;
294 |
295 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
296 | portEXIT_CRITICAL(&timerMux);
297 |
298 | }
299 | }
300 |
301 | // function contributed by code@rowansimms.com
302 | void ESP32_ISR_Timer::restartTimer(const uint8_t& numTimer)
303 | {
304 | if (numTimer >= MAX_NUMBER_TIMERS)
305 | {
306 | return;
307 | }
308 |
309 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
310 | portENTER_CRITICAL(&timerMux);
311 |
312 | timer[numTimer].prev_millis = millis();
313 |
314 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
315 | portEXIT_CRITICAL(&timerMux);
316 | }
317 |
318 |
319 | bool ESP32_ISR_Timer::isEnabled(const uint8_t& numTimer)
320 | {
321 | if (numTimer >= MAX_NUMBER_TIMERS)
322 | {
323 | return false;
324 | }
325 |
326 | return timer[numTimer].enabled;
327 | }
328 |
329 |
330 | void ESP32_ISR_Timer::enable(const uint8_t& numTimer)
331 | {
332 | if (numTimer >= MAX_NUMBER_TIMERS)
333 | {
334 | return;
335 | }
336 |
337 | timer[numTimer].enabled = true;
338 | }
339 |
340 |
341 | void ESP32_ISR_Timer::disable(const uint8_t& numTimer)
342 | {
343 | if (numTimer >= MAX_NUMBER_TIMERS)
344 | {
345 | return;
346 | }
347 |
348 | timer[numTimer].enabled = false;
349 | }
350 |
351 | void ESP32_ISR_Timer::enableAll()
352 | {
353 | // Enable all timers with a callback assigned (used)
354 |
355 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
356 | portENTER_CRITICAL(&timerMux);
357 |
358 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++)
359 | {
360 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER)
361 | {
362 | timer[i].enabled = true;
363 | }
364 | }
365 |
366 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
367 | portEXIT_CRITICAL(&timerMux);
368 | }
369 |
370 | void ESP32_ISR_Timer::disableAll()
371 | {
372 | // Disable all timers with a callback assigned (used)
373 |
374 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
375 | portENTER_CRITICAL(&timerMux);
376 |
377 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++)
378 | {
379 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER)
380 | {
381 | timer[i].enabled = false;
382 | }
383 | }
384 |
385 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars
386 | portEXIT_CRITICAL(&timerMux);
387 |
388 | }
389 |
390 | void ESP32_ISR_Timer::toggle(const uint8_t& numTimer)
391 | {
392 | if (numTimer >= MAX_NUMBER_TIMERS)
393 | {
394 | return;
395 | }
396 |
397 | timer[numTimer].enabled = !timer[numTimer].enabled;
398 | }
399 |
400 |
401 | int8_t ESP32_ISR_Timer::getNumTimers()
402 | {
403 | return numTimers;
404 | }
405 |
406 | #endif // ISR_TIMER_GENERIC_IMPL_H
407 |
408 |
--------------------------------------------------------------------------------
/src/ESP32_ISR_Timer.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ESP32_ISR_Timer.h
3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
10 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
11 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
12 |
13 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
14 | The timer counters can be configured to count up or down and support automatic reload and software reload.
15 | They can also generate alarms when they reach a specific value, defined by the software.
16 | The value of the counter can be read by the software program.
17 |
18 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
19 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
20 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
21 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
22 | This important feature is absolutely necessary for mission-critical tasks.
23 |
24 | Based on SimpleTimer - A timer library for Arduino.
25 | Author: mromani@ottotecnica.com
26 | Copyright (c) 2010 OTTOTECNICA Italy
27 |
28 | Based on BlynkTimer.h
29 | Author: Volodymyr Shymanskyy
30 |
31 | Version: 2.3.0
32 |
33 | Version Modified By Date Comments
34 | ------- ----------- ---------- -----------
35 | 1.0.0 K Hoang 23/11/2019 Initial coding
36 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library
37 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
38 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README.
39 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
40 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
41 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
42 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2
43 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
44 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1
45 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error
46 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library.
47 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
48 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards
49 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core
50 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards
51 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3
52 | *****************************************************************************************************************************/
53 |
54 | #pragma once
55 |
56 | #ifndef ISR_TIMER_GENERIC_H
57 | #define ISR_TIMER_GENERIC_H
58 |
59 | #include "ESP32_ISR_Timer.hpp"
60 | #include "ESP32_ISR_Timer-Impl.h"
61 |
62 |
63 | #endif // ISR_TIMER_GENERIC_H
64 |
65 |
66 |
--------------------------------------------------------------------------------
/src/ESP32_ISR_Timer.hpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ESP32_ISR_Timer.hpp
3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
10 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
11 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
12 |
13 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
14 | The timer counters can be configured to count up or down and support automatic reload and software reload.
15 | They can also generate alarms when they reach a specific value, defined by the software.
16 | The value of the counter can be read by the software program.
17 |
18 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
19 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
20 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
21 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
22 | This important feature is absolutely necessary for mission-critical tasks.
23 |
24 | Based on SimpleTimer - A timer library for Arduino.
25 | Author: mromani@ottotecnica.com
26 | Copyright (c) 2010 OTTOTECNICA Italy
27 |
28 | Based on BlynkTimer.h
29 | Author: Volodymyr Shymanskyy
30 |
31 | Version: 2.3.0
32 |
33 | Version Modified By Date Comments
34 | ------- ----------- ---------- -----------
35 | 1.0.0 K Hoang 23/11/2019 Initial coding
36 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library
37 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
38 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README.
39 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
40 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
41 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
42 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2
43 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
44 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1
45 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error
46 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library.
47 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
48 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards
49 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core
50 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards
51 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3
52 | *****************************************************************************************************************************/
53 |
54 | #pragma once
55 |
56 | #ifndef ISR_TIMER_GENERIC_HPP
57 | #define ISR_TIMER_GENERIC_HPP
58 |
59 | #if !defined( ESP32 )
60 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
61 | #endif
62 |
63 | #ifndef ESP32_TIMER_INTERRUPT_VERSION
64 | #define ESP32_TIMER_INTERRUPT_VERSION "ESP32TimerInterrupt v2.2.0"
65 |
66 | #define ESP32_TIMER_INTERRUPT_VERSION_MAJOR 2
67 | #define ESP32_TIMER_INTERRUPT_VERSION_MINOR 2
68 | #define ESP32_TIMER_INTERRUPT_VERSION_PATCH 0
69 |
70 | #define ESP32_TIMER_INTERRUPT_VERSION_INT 2002000
71 | #endif
72 |
73 | #include "TimerInterrupt_Generic_Debug.h"
74 |
75 | #define CONFIG_ESP32_APPTRACE_ENABLE
76 |
77 | #if 0
78 | #ifndef configMINIMAL_STACK_SIZE
79 | #define configMINIMAL_STACK_SIZE 2048
80 | #else
81 | #undef configMINIMAL_STACK_SIZE
82 | #define configMINIMAL_STACK_SIZE 2048
83 | #endif
84 | #endif
85 |
86 | #include
87 |
88 | #include
89 |
90 | #if defined(ARDUINO)
91 | #if ARDUINO >= 100
92 | #include
93 | #else
94 | #include
95 | #endif
96 | #endif
97 |
98 | #define ESP32_ISR_Timer ESP32_ISRTimer
99 |
100 | typedef void (*timer_callback)();
101 | typedef void (*timer_callback_p)(void *);
102 |
103 | class ESP32_ISR_Timer
104 | {
105 |
106 | public:
107 | // maximum number of timers
108 | #define MAX_NUMBER_TIMERS 16
109 | #define TIMER_RUN_FOREVER 0
110 | #define TIMER_RUN_ONCE 1
111 |
112 | // constructor
113 | ESP32_ISR_Timer();
114 |
115 | void init();
116 |
117 | // this function must be called inside loop()
118 | void IRAM_ATTR run();
119 |
120 | // Timer will call function 'callback' every 'delay' milliseconds forever
121 | // returns the timer number (numTimer) on success or
122 | // -1 on failure (callback == NULL) or no free timers
123 | int setInterval(const unsigned long& delay, const timer_callback& callback);
124 |
125 | // Timer will call function 'callback' with parameter 'param' every 'delay' milliseconds forever
126 | // returns the timer number (numTimer) on success or
127 | // -1 on failure (callback == NULL) or no free timers
128 | int setInterval(const unsigned long& delay, const timer_callback_p& callback, void* param);
129 |
130 | // Timer will call function 'callback' after 'delay' milliseconds one time
131 | // returns the timer number (numTimer) on success or
132 | // -1 on failure (callback == NULL) or no free timers
133 | int setTimeout(const unsigned long& delay, const timer_callback& callback);
134 |
135 | // Timer will call function 'callback' with parameter 'param' after 'delay' milliseconds one time
136 | // returns the timer number (numTimer) on success or
137 | // -1 on failure (callback == NULL) or no free timers
138 | int setTimeout(const unsigned long& delay, const timer_callback_p& callback, void* param);
139 |
140 | // Timer will call function 'callback' every 'delay' milliseconds 'numRuns' times
141 | // returns the timer number (numTimer) on success or
142 | // -1 on failure (callback == NULL) or no free timers
143 | int setTimer(const unsigned long& delay, const timer_callback& callback, const uint32_t& numRuns);
144 |
145 | // Timer will call function 'callback' with parameter 'param' every 'delay' milliseconds 'numRuns' times
146 | // returns the timer number (numTimer) on success or
147 | // -1 on failure (callback == NULL) or no free timers
148 | int setTimer(const unsigned long& delay, const timer_callback_p& callback, void* param, const uint32_t& numRuns);
149 |
150 | // updates interval of the specified timer
151 | bool changeInterval(const uint8_t& numTimer, const unsigned long& delay);
152 |
153 | // destroy the specified timer
154 | void deleteTimer(const uint8_t& numTimer);
155 |
156 | // restart the specified timer
157 | void restartTimer(const uint8_t& numTimer);
158 |
159 | // returns true if the specified timer is enabled
160 | bool isEnabled(const uint8_t& numTimer);
161 |
162 | // enables the specified timer
163 | void enable(const uint8_t& numTimer);
164 |
165 | // disables the specified timer
166 | void disable(const uint8_t& numTimer);
167 |
168 | // enables all timers
169 | void enableAll();
170 |
171 | // disables all timers
172 | void disableAll();
173 |
174 | // enables the specified timer if it's currently disabled, and vice-versa
175 | void toggle(const uint8_t& numTimer);
176 |
177 | // returns the number of used timers
178 | int8_t getNumTimers();
179 |
180 | // returns the number of available timers
181 | uint8_t getNumAvailableTimers() __attribute__((always_inline))
182 | {
183 | if (numTimers <= 0)
184 | return MAX_NUMBER_TIMERS;
185 | else
186 | return MAX_NUMBER_TIMERS - numTimers;
187 | };
188 |
189 | private:
190 | // deferred call constants
191 | #define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function
192 | #define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer
193 | #define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer
194 |
195 | // low level function to initialize and enable a new timer
196 | // returns the timer number (numTimer) on success or
197 | // -1 on failure (f == NULL) or no free timers
198 | int8_t setupTimer(const unsigned long& delay, void* callback, void* param, bool hasParam, const uint32_t& numRuns) ;
199 |
200 | // find the first available slot
201 | int8_t findFirstFreeSlot();
202 |
203 | typedef struct
204 | {
205 | unsigned long prev_millis; // value returned by the millis() function in the previous run() call
206 | void* callback; // pointer to the callback function
207 | void* param; // function parameter
208 | bool hasParam; // true if callback takes a parameter
209 | unsigned long delay; // delay value
210 | uint32_t maxNumRuns; // number of runs to be executed
211 | uint32_t numRuns; // number of executed runs
212 | bool enabled; // true if enabled
213 | unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run()
214 | } timer_t;
215 |
216 | volatile timer_t timer[MAX_NUMBER_TIMERS];
217 |
218 | // actual number of timers in use (-1 means uninitialized)
219 | volatile int8_t numTimers;
220 |
221 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR
222 | portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
223 | };
224 |
225 | #endif // ISR_TIMER_GENERIC_HPP
226 |
227 |
228 |
--------------------------------------------------------------------------------
/src/ESP8266TimerInterrupt.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ESP8266TimerInterrupt.h
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 |
19 | Based on SimpleTimer - A timer library for Arduino.
20 | Author: mromani@ottotecnica.com
21 | Copyright (c) 2010 OTTOTECNICA Italy
22 |
23 | Based on BlynkTimer.h
24 | Author: Volodymyr Shymanskyy
25 |
26 | Version: 1.6.0
27 |
28 | Version Modified By Date Comments
29 | ------- ----------- ---------- -----------
30 | 1.0.0 K Hoang 23/11/2019 Initial coding
31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error
32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README.
34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0
38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md
40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer
41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
42 | *****************************************************************************************************************************/
43 |
44 | #pragma once
45 |
46 | #ifndef ESP8266TIMERINTERRUPT_H
47 | #define ESP8266TIMERINTERRUPT_H
48 |
49 | #if !defined(ESP8266)
50 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
51 | #endif
52 |
53 | #ifndef ESP8266_TIMER_INTERRUPT_VERSION
54 | #define ESP8266_TIMER_INTERRUPT_VERSION "ESP8266TimerInterrupt v1.6.0"
55 |
56 |
57 | #define ESP8266_TIMER_INTERRUPT_VERSION_MAJOR 1
58 | #define ESP8266_TIMER_INTERRUPT_VERSION_MINOR 6
59 | #define ESP8266_TIMER_INTERRUPT_VERSION_PATCH 0
60 |
61 | #define ESP8266_TIMER_INTERRUPT_VERSION_INT 1006000
62 |
63 | #endif
64 |
65 | #ifndef TIMER_INTERRUPT_DEBUG
66 | #define TIMER_INTERRUPT_DEBUG 0
67 | #endif
68 |
69 | #if defined(ARDUINO)
70 | #if ARDUINO >= 100
71 | #include
72 | #else
73 | #include
74 | #endif
75 | #endif
76 |
77 | #include "TimerInterrupt_Generic_Debug.h"
78 |
79 | /* From /arduino-1.8.10/hardware/esp8266com/esp8266/cores/esp8266/esp8266_peri.h
80 |
81 | #define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr)))
82 | #define ESP8266_DREG(addr) *((volatile uint32_t *)(0x3FF00000+(addr)))
83 | #define ESP8266_CLOCK 80000000UL
84 |
85 | //CPU Register
86 | #define CPU2X ESP8266_DREG(0x14) //when bit 0 is set, F_CPU = 160MHz
87 | */
88 |
89 | /* From /arduino-1.8.10/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h
90 |
91 | //timer dividers
92 | enum TIM_DIV_ENUM {
93 | TIM_DIV1 = 0, // 80 / 160 MHz (80 / 160 ticks/us - 104857.588 us max)
94 | TIM_DIV16 = 1, // 5 / 10 MHz (5 / 10 ticks/us - 1677721.4 us max)
95 | TIM_DIV256 = 3 // 312.5 / 625 Khz (1 tick = 3.2 / 1.6 us - 26843542.4 us max)
96 | };
97 |
98 | //timer int_types
99 | #define TIM_EDGE 0
100 | #define TIM_LEVEL 1
101 | //timer reload values
102 | #define TIM_SINGLE 0 //on interrupt routine you need to write a new value to start the timer again
103 | #define TIM_LOOP 1 //on interrupt the counter will start with the same value again
104 |
105 | */
106 |
107 | // ESP8266 only has one usable timer1, max count is only 8,388,607. So to get longer time, we use max available 256 divider
108 | class ESP8266TimerInterrupt;
109 |
110 | typedef ESP8266TimerInterrupt ESP8266Timer;
111 |
112 | #define MAX_ESP8266_NUM_TIMERS 1
113 | #define MAX_ESP8266_COUNT 8388607
114 |
115 | typedef void (*timer_callback) ();
116 |
117 | #define TIM_DIV1_CLOCK (80000000UL) // 80000000 / 1 = 80.0 MHz
118 | #define TIM_DIV16_CLOCK (5000000UL) // 80000000 / 16 = 5.0 MHz
119 | #define TIM_DIV256_CLOCK (312500UL) // 80000000 / 256 = 312.5 KHz
120 |
121 | #if ( defined(USING_TIM_DIV1) && USING_TIM_DIV1 )
122 | #warning Using TIM_DIV1_CLOCK for shortest and most accurate timer
123 | #define TIM_CLOCK_FREQ TIM_DIV1_CLOCK
124 | #define TIM_DIV TIM_DIV1
125 | #elif ( defined(USING_TIM_DIV16) && USING_TIM_DIV16 )
126 | #warning Using TIM_DIV16_CLOCK for medium time and medium accurate timer
127 | #define TIM_CLOCK_FREQ TIM_DIV16_CLOCK
128 | #define TIM_DIV TIM_DIV16
129 | #elif ( defined(USING_TIM_DIV256) && USING_TIM_DIV256 )
130 | #warning Using TIM_DIV256_CLOCK for longest timer but least accurate
131 | #define TIM_CLOCK_FREQ TIM_DIV256_CLOCK
132 | #define TIM_DIV TIM_DIV256
133 | #else
134 | #warning Default to using TIM_DIV256_CLOCK for longest timer but least accurate
135 | #define TIM_CLOCK_FREQ TIM_DIV256_CLOCK
136 | #define TIM_DIV TIM_DIV256
137 | #endif
138 |
139 | class ESP8266TimerInterrupt
140 | {
141 | private:
142 | timer_callback _callback; // pointer to the callback function
143 | float _frequency; // Timer frequency
144 | uint32_t _timerCount; // count to activate timer
145 |
146 | public:
147 |
148 | ESP8266TimerInterrupt()
149 | {
150 | _frequency = 0;
151 | _timerCount = 0;
152 | _callback = NULL;
153 | };
154 |
155 | // frequency (in hertz)
156 | bool setFrequency(const float& frequency, const timer_callback& callback)
157 | {
158 | bool isOKFlag = true;
159 | float minFreq = (float) TIM_CLOCK_FREQ / MAX_ESP8266_COUNT;
160 |
161 | // ESP8266 only has one usable timer1, max count is only 8,388,607. So to get longer time, we use max available 256 divider
162 | // Will use later if very low frequency is needed.
163 |
164 | if (frequency < minFreq)
165 | {
166 | TISR_LOGERROR3(F("ESP8266TimerInterrupt: Too long Timer, smallest frequency ="), minFreq, F(" for TIM_CLOCK_FREQ ="),
167 | TIM_CLOCK_FREQ);
168 |
169 | return false;
170 | }
171 |
172 | _frequency = frequency;
173 | _timerCount = (uint32_t) (TIM_CLOCK_FREQ / frequency);
174 | _callback = callback;
175 |
176 | if ( _timerCount > MAX_ESP8266_COUNT)
177 | {
178 | _timerCount = MAX_ESP8266_COUNT;
179 | // Flag error
180 | isOKFlag = false;
181 | }
182 |
183 | // count up
184 | TISR_LOGWARN3(F("ESP8266TimerInterrupt: Timer _fre ="), _frequency, F(", _count ="), _timerCount);
185 |
186 | // Clock to timer (prescaler) is always 80MHz, even F_CPU is 160 MHz
187 |
188 | timer1_attachInterrupt(callback);
189 |
190 | timer1_write(_timerCount);
191 |
192 | // Interrupt on EDGE, autoloop
193 | //timer1_enable(TIM_DIV256, TIM_EDGE, TIM_LOOP);
194 | timer1_enable(TIM_DIV, TIM_EDGE, TIM_LOOP);
195 |
196 | return isOKFlag;
197 | }
198 |
199 | // interval (in microseconds)
200 | bool setInterval(const unsigned long& interval, const timer_callback& callback)
201 | {
202 | return setFrequency((float) (1000000.0f / interval), callback);
203 | }
204 |
205 | bool attachInterrupt(const float& frequency, const timer_callback& callback)
206 | {
207 | return setFrequency(frequency, callback);
208 | }
209 |
210 | // interval (in microseconds)
211 | bool attachInterruptInterval(const unsigned long& interval, const timer_callback& callback)
212 | {
213 | return setFrequency( (float) ( 1000000.0f / interval), callback);
214 | }
215 |
216 | void detachInterrupt()
217 | {
218 | timer1_disable();
219 | }
220 |
221 | void disableTimer()
222 | {
223 | timer1_disable();
224 | }
225 |
226 | void reattachInterrupt()
227 | {
228 | if ( (_frequency > 0) && (_timerCount > 0) && (_callback != NULL) )
229 | setFrequency(_frequency, _callback);
230 | }
231 |
232 | void enableTimer()
233 | {
234 | reattachInterrupt();
235 | }
236 |
237 | // Just stop clock source, clear the count
238 | void stopTimer()
239 | {
240 | timer1_disable();
241 | }
242 |
243 | // Just reconnect clock source, start current count from 0
244 | void restartTimer()
245 | {
246 | enableTimer();
247 | }
248 | }; // class ESP8266TimerInterrupt
249 |
250 | #endif // ESP8266TIMERINTERRUPT_H
251 |
--------------------------------------------------------------------------------
/src/ESP8266_ISR_Timer-Impl.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ESP8266_ISR_Timer-Impl.h
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 |
19 | Based on SimpleTimer - A timer library for Arduino.
20 | Author: mromani@ottotecnica.com
21 | Copyright (c) 2010 OTTOTECNICA Italy
22 |
23 | Based on BlynkTimer.h
24 | Author: Volodymyr Shymanskyy
25 |
26 | Version: 1.6.0
27 |
28 | Version Modified By Date Comments
29 | ------- ----------- ---------- -----------
30 | 1.0.0 K Hoang 23/11/2019 Initial coding
31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error
32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README.
34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0
38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md
40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer
41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
42 | *****************************************************************************************************************************/
43 |
44 | #pragma once
45 |
46 | #if !defined(ESP8266)
47 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
48 | #endif
49 |
50 | #ifndef ISR_TIMER_GENERIC_IMPL_H
51 | #define ISR_TIMER_GENERIC_IMPL_H
52 |
53 | //#include "ESP8266_ISR_Timer.h"
54 | #include
55 |
56 | // Select time function:
57 | //static inline unsigned long elapsed() { return micros(); }
58 | static inline unsigned long elapsed()
59 | {
60 | return millis();
61 | }
62 |
63 |
64 | ESP8266_ISR_Timer::ESP8266_ISR_Timer()
65 | : numTimers (-1)
66 | {
67 | }
68 |
69 | void IRAM_ATTR ESP8266_ISR_Timer::init()
70 | {
71 | unsigned long current_millis = millis(); //elapsed();
72 |
73 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++)
74 | {
75 | memset((void*) &timer[i], 0, sizeof (timer_t));
76 | timer[i].prev_millis = current_millis;
77 | }
78 |
79 | numTimers = 0;
80 | }
81 |
82 |
83 | void IRAM_ATTR ESP8266_ISR_Timer::run()
84 | {
85 | uint8_t i;
86 | unsigned long current_millis;
87 |
88 | // get current time
89 | current_millis = millis(); //elapsed();
90 |
91 | for (i = 0; i < MAX_NUMBER_TIMERS; i++)
92 | {
93 |
94 | timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN;
95 |
96 | // no callback == no timer, i.e. jump over empty slots
97 | if (timer[i].callback != NULL)
98 | {
99 | // is it time to process this timer ?
100 | // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592
101 |
102 | if ((current_millis - timer[i].prev_millis) >= timer[i].delay)
103 | {
104 | unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay;
105 | // update time
106 | timer[i].prev_millis += timer[i].delay * skipTimes;
107 |
108 | // check if the timer callback has to be executed
109 | if (timer[i].enabled)
110 | {
111 | // "run forever" timers must always be executed
112 | if (timer[i].maxNumRuns == TIMER_RUN_FOREVER)
113 | {
114 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY;
115 | }
116 | // other timers get executed the specified number of times
117 | else if (timer[i].numRuns < timer[i].maxNumRuns)
118 | {
119 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY;
120 | timer[i].numRuns++;
121 |
122 | // after the last run, delete the timer
123 | if (timer[i].numRuns >= timer[i].maxNumRuns)
124 | {
125 | timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL;
126 | }
127 | }
128 | }
129 | }
130 | }
131 | }
132 |
133 | for (i = 0; i < MAX_NUMBER_TIMERS; i++)
134 | {
135 | if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN)
136 | continue;
137 |
138 | if (timer[i].hasParam)
139 | (*(timer_callback_p)timer[i].callback)(timer[i].param);
140 | else
141 | (*(timer_callback)timer[i].callback)();
142 |
143 | if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL)
144 | deleteTimer(i);
145 | }
146 | }
147 |
148 |
149 | // find the first available slot
150 | // return -1 if none found
151 | int8_t IRAM_ATTR ESP8266_ISR_Timer::findFirstFreeSlot()
152 | {
153 | // all slots are used
154 | if (numTimers >= MAX_NUMBER_TIMERS)
155 | {
156 | return -1;
157 | }
158 |
159 | // return the first slot with no callback (i.e. free)
160 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++)
161 | {
162 | if (timer[i].callback == NULL)
163 | {
164 | return i;
165 | }
166 | }
167 |
168 | // no free slots found
169 | return -1;
170 | }
171 |
172 |
173 | int8_t IRAM_ATTR ESP8266_ISR_Timer::setupTimer(const unsigned long& d, void* f, void* p, bool h, const unsigned& n)
174 | {
175 | int freeTimer;
176 |
177 | if (numTimers < 0)
178 | {
179 | init();
180 | }
181 |
182 | freeTimer = findFirstFreeSlot();
183 |
184 | if (freeTimer < 0)
185 | {
186 | return -1;
187 | }
188 |
189 | if (f == NULL)
190 | {
191 | return -1;
192 | }
193 |
194 | timer[freeTimer].delay = d;
195 | timer[freeTimer].callback = f;
196 | timer[freeTimer].param = p;
197 | timer[freeTimer].hasParam = h;
198 | timer[freeTimer].maxNumRuns = n;
199 | timer[freeTimer].enabled = true;
200 | timer[freeTimer].prev_millis = elapsed();
201 |
202 | numTimers++;
203 |
204 | return freeTimer;
205 | }
206 |
207 |
208 | int IRAM_ATTR ESP8266_ISR_Timer::setTimer(const unsigned long& d, const timer_callback& f, const unsigned& n)
209 | {
210 | return setupTimer(d, (void *)f, NULL, false, n);
211 | }
212 |
213 | int IRAM_ATTR ESP8266_ISR_Timer::setTimer(const unsigned long& d, const timer_callback_p& f, void* p, const unsigned& n)
214 | {
215 | return setupTimer(d, (void *)f, p, true, n);
216 | }
217 |
218 | int IRAM_ATTR ESP8266_ISR_Timer::setInterval(const unsigned long& d, const timer_callback& f)
219 | {
220 | return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_FOREVER);
221 | }
222 |
223 | int IRAM_ATTR ESP8266_ISR_Timer::setInterval(const unsigned long& d, const timer_callback_p& f, void* p)
224 | {
225 | return setupTimer(d, (void *)f, p, true, TIMER_RUN_FOREVER);
226 | }
227 |
228 | int IRAM_ATTR ESP8266_ISR_Timer::setTimeout(const unsigned long& d, const timer_callback& f)
229 | {
230 | return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_ONCE);
231 | }
232 |
233 | int IRAM_ATTR ESP8266_ISR_Timer::setTimeout(const unsigned long& d, const timer_callback_p& f, void* p)
234 | {
235 | return setupTimer(d, (void *)f, p, true, TIMER_RUN_ONCE);
236 | }
237 |
238 | bool IRAM_ATTR ESP8266_ISR_Timer::changeInterval(const unsigned& numTimer, const unsigned long& d)
239 | {
240 | if (numTimer >= MAX_NUMBER_TIMERS)
241 | {
242 | return false;
243 | }
244 |
245 | // Updates interval of existing specified timer
246 | if (timer[numTimer].callback != NULL)
247 | {
248 | timer[numTimer].delay = d;
249 | timer[numTimer].prev_millis = elapsed();
250 | return true;
251 | }
252 |
253 | // false return for non-used numTimer, no callback
254 | return false;
255 | }
256 |
257 | void IRAM_ATTR ESP8266_ISR_Timer::deleteTimer(const unsigned& timerId)
258 | {
259 | if (timerId >= MAX_NUMBER_TIMERS)
260 | {
261 | return;
262 | }
263 |
264 | // nothing to delete if no timers are in use
265 | if (numTimers == 0)
266 | {
267 | return;
268 | }
269 |
270 | // don't decrease the number of timers if the
271 | // specified slot is already empty
272 | if (timer[timerId].callback != NULL)
273 | {
274 | memset((void*) &timer[timerId], 0, sizeof (timer_t));
275 | timer[timerId].prev_millis = elapsed();
276 |
277 | // update number of timers
278 | numTimers--;
279 | }
280 | }
281 |
282 |
283 | // function contributed by code@rowansimms.com
284 | void IRAM_ATTR ESP8266_ISR_Timer::restartTimer(const unsigned& numTimer)
285 | {
286 | if (numTimer >= MAX_NUMBER_TIMERS)
287 | {
288 | return;
289 | }
290 |
291 | timer[numTimer].prev_millis = elapsed();
292 | }
293 |
294 |
295 | bool IRAM_ATTR ESP8266_ISR_Timer::isEnabled(const unsigned& numTimer)
296 | {
297 | if (numTimer >= MAX_NUMBER_TIMERS)
298 | {
299 | return false;
300 | }
301 |
302 | return timer[numTimer].enabled;
303 | }
304 |
305 |
306 | void IRAM_ATTR ESP8266_ISR_Timer::enable(const unsigned& numTimer)
307 | {
308 | if (numTimer >= MAX_NUMBER_TIMERS)
309 | {
310 | return;
311 | }
312 |
313 | timer[numTimer].enabled = true;
314 | }
315 |
316 |
317 | void IRAM_ATTR ESP8266_ISR_Timer::disable(const unsigned& numTimer)
318 | {
319 | if (numTimer >= MAX_NUMBER_TIMERS)
320 | {
321 | return;
322 | }
323 |
324 | timer[numTimer].enabled = false;
325 | }
326 |
327 | void IRAM_ATTR ESP8266_ISR_Timer::enableAll()
328 | {
329 | // Enable all timers with a callback assigned (used)
330 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++)
331 | {
332 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER)
333 | {
334 | timer[i].enabled = true;
335 | }
336 | }
337 | }
338 |
339 | void IRAM_ATTR ESP8266_ISR_Timer::disableAll()
340 | {
341 | // Disable all timers with a callback assigned (used)
342 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++)
343 | {
344 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER)
345 | {
346 | timer[i].enabled = false;
347 | }
348 | }
349 | }
350 |
351 | void IRAM_ATTR ESP8266_ISR_Timer::toggle(const unsigned& numTimer)
352 | {
353 | if (numTimer >= MAX_NUMBER_TIMERS)
354 | {
355 | return;
356 | }
357 |
358 | timer[numTimer].enabled = !timer[numTimer].enabled;
359 | }
360 |
361 |
362 | int8_t IRAM_ATTR ESP8266_ISR_Timer::getNumTimers()
363 | {
364 | return numTimers;
365 | }
366 |
367 | #endif // ISR_TIMER_GENERIC_IMPL_H
368 |
--------------------------------------------------------------------------------
/src/ESP8266_ISR_Timer.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ESP8266_ISR_Timer.h
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 |
19 | Based on SimpleTimer - A timer library for Arduino.
20 | Author: mromani@ottotecnica.com
21 | Copyright (c) 2010 OTTOTECNICA Italy
22 |
23 | Based on BlynkTimer.h
24 | Author: Volodymyr Shymanskyy
25 |
26 | Version: 1.6.0
27 |
28 | Version Modified By Date Comments
29 | ------- ----------- ---------- -----------
30 | 1.0.0 K Hoang 23/11/2019 Initial coding
31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error
32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README.
34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0
38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md
40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer
41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
42 | *****************************************************************************************************************************/
43 |
44 | #pragma once
45 |
46 | #ifndef ISR_TIMER_GENERIC_H
47 | #define ISR_TIMER_GENERIC_H
48 |
49 | #include "ESP8266_ISR_Timer.hpp"
50 | #include "ESP8266_ISR_Timer-Impl.h"
51 |
52 | #endif // ISR_TIMER_GENERIC_H
53 |
54 |
--------------------------------------------------------------------------------
/src/ESP8266_ISR_Timer.hpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | ESP8266_ISR_Timer.hpp
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 |
19 | Based on SimpleTimer - A timer library for Arduino.
20 | Author: mromani@ottotecnica.com
21 | Copyright (c) 2010 OTTOTECNICA Italy
22 |
23 | Based on BlynkTimer.h
24 | Author: Volodymyr Shymanskyy
25 |
26 | Version: 1.6.0
27 |
28 | Version Modified By Date Comments
29 | ------- ----------- ---------- -----------
30 | 1.0.0 K Hoang 23/11/2019 Initial coding
31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error
32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README.
34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0
38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md
40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer
41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
42 | *****************************************************************************************************************************/
43 |
44 | #pragma once
45 |
46 | #ifndef ISR_TIMER_GENERIC_HPP
47 | #define ISR_TIMER_GENERIC_HPP
48 |
49 | #if !defined(ESP8266)
50 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting.
51 | #endif
52 |
53 | #ifndef ESP8266_TIMER_INTERRUPT_VERSION
54 | #define ESP8266_TIMER_INTERRUPT_VERSION "ESP8266TimerInterrupt v1.6.0"
55 |
56 |
57 | #define ESP8266_TIMER_INTERRUPT_VERSION_MAJOR 1
58 | #define ESP8266_TIMER_INTERRUPT_VERSION_MINOR 6
59 | #define ESP8266_TIMER_INTERRUPT_VERSION_PATCH 0
60 |
61 | #define ESP8266_TIMER_INTERRUPT_VERSION_INT 1006000
62 |
63 | #endif
64 |
65 | #include "TimerInterrupt_Generic_Debug.h"
66 |
67 | #include
68 |
69 | #ifdef ESP8266
70 | extern "C"
71 | {
72 | #include "ets_sys.h"
73 | #include "os_type.h"
74 | #include "mem.h"
75 | }
76 | #else
77 | #include
78 | #endif
79 |
80 | #if defined(ARDUINO)
81 | #if ARDUINO >= 100
82 | #include
83 | #else
84 | #include
85 | #endif
86 | #endif
87 |
88 | #define ESP8266_ISR_Timer ISRTimer
89 |
90 | typedef void (*timer_callback)();
91 | typedef void (*timer_callback_p)(void *);
92 |
93 | class ESP8266_ISR_Timer
94 | {
95 |
96 | public:
97 |
98 | // maximum number of timers
99 | #define MAX_NUMBER_TIMERS 16
100 |
101 | // setTimer() constants
102 | #define TIMER_RUN_FOREVER 0
103 | #define TIMER_RUN_ONCE 1
104 |
105 | // constructor
106 | ESP8266_ISR_Timer();
107 |
108 | void IRAM_ATTR init();
109 |
110 | // this function must be called inside loop()
111 | void IRAM_ATTR run();
112 |
113 | // Timer will call function 'f' every 'd' milliseconds forever
114 | // returns the timer number (numTimer) on success or
115 | // -1 on failure (f == NULL) or no free timers
116 | int IRAM_ATTR setInterval(const unsigned long& d, const timer_callback& f);
117 |
118 | // Timer will call function 'f' with parameter 'p' every 'd' milliseconds forever
119 | // returns the timer number (numTimer) on success or
120 | // -1 on failure (f == NULL) or no free timers
121 | int IRAM_ATTR setInterval(const unsigned long& d, const timer_callback_p& f, void* p);
122 |
123 | // Timer will call function 'f' after 'd' milliseconds one time
124 | // returns the timer number (numTimer) on success or
125 | // -1 on failure (f == NULL) or no free timers
126 | int IRAM_ATTR setTimeout(const unsigned long& d, const timer_callback& f);
127 |
128 | // Timer will call function 'f' with parameter 'p' after 'd' milliseconds one time
129 | // returns the timer number (numTimer) on success or
130 | // -1 on failure (f == NULL) or no free timers
131 | int IRAM_ATTR setTimeout(const unsigned long& d, const timer_callback_p& f, void* p);
132 |
133 | // Timer will call function 'f' every 'd' milliseconds 'n' times
134 | // returns the timer number (numTimer) on success or
135 | // -1 on failure (f == NULL) or no free timers
136 | int IRAM_ATTR setTimer(const unsigned long& d, const timer_callback& f, const unsigned& n);
137 |
138 | // Timer will call function 'f' with parameter 'p' every 'd' milliseconds 'n' times
139 | // returns the timer number (numTimer) on success or
140 | // -1 on failure (f == NULL) or no free timers
141 | int IRAM_ATTR setTimer(const unsigned long& d, const timer_callback_p& f, void* p, const unsigned& n);
142 |
143 | // updates interval of the specified timer
144 | bool IRAM_ATTR changeInterval(const unsigned& numTimer, const unsigned long& d);
145 |
146 | // destroy the specified timer
147 | void IRAM_ATTR deleteTimer(const unsigned& timerId);
148 |
149 | // restart the specified timer
150 | void IRAM_ATTR restartTimer(const unsigned& numTimer);
151 |
152 | // returns true if the specified timer is enabled
153 | bool IRAM_ATTR isEnabled(const unsigned& numTimer);
154 |
155 | // enables the specified timer
156 | void IRAM_ATTR enable(const unsigned& numTimer);
157 |
158 | // disables the specified timer
159 | void IRAM_ATTR disable(const unsigned& numTimer);
160 |
161 | // enables all timers
162 | void IRAM_ATTR enableAll();
163 |
164 | // disables all timers
165 | void IRAM_ATTR disableAll();
166 |
167 | // enables the specified timer if it's currently disabled,
168 | // and vice-versa
169 | void IRAM_ATTR toggle(const unsigned& numTimer);
170 |
171 | // returns the number of used timers
172 | int8_t IRAM_ATTR getNumTimers();
173 |
174 | // returns the number of available timers
175 | int8_t IRAM_ATTR getNumAvailableTimers()
176 | {
177 | if (numTimers <= 0)
178 | return MAX_NUMBER_TIMERS;
179 | else
180 | return MAX_NUMBER_TIMERS - numTimers;
181 | };
182 |
183 | private:
184 | // deferred call constants
185 | #define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function
186 | #define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer
187 | #define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer
188 | // low level function to initialize and enable a new timer
189 | // returns the timer number (numTimer) on success or
190 | // -1 on failure (f == NULL) or no free timers
191 | int8_t IRAM_ATTR setupTimer(const unsigned long& d, void* f, void* p, bool h, const unsigned& n);
192 |
193 | // find the first available slot
194 | int8_t IRAM_ATTR findFirstFreeSlot();
195 |
196 | typedef struct
197 | {
198 | unsigned long prev_millis; // value returned by the millis() function in the previous run() call
199 | void* callback; // pointer to the callback function
200 | void* param; // function parameter
201 | bool hasParam; // true if callback takes a parameter
202 | unsigned long delay; // delay value
203 | unsigned maxNumRuns; // number of runs to be executed
204 | unsigned numRuns; // number of executed runs
205 | bool enabled; // true if enabled
206 | unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run()
207 | } timer_t;
208 |
209 | volatile timer_t timer[MAX_NUMBER_TIMERS];
210 |
211 | // actual number of timers in use (-1 means uninitialized)
212 | volatile int8_t numTimers;
213 | };
214 |
215 | #endif // ISR_TIMER_GENERIC_HPP
216 |
217 |
--------------------------------------------------------------------------------
/src/TimerInterrupt_Generic_Debug.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************************************************************
2 | TimerInterrupt_Generic_Debug.h
3 | For ESP8266 boards
4 | Written by Khoi Hoang
5 |
6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt
7 | Licensed under MIT license
8 |
9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega.
10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available.
11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short.
12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!!
13 |
14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds)
15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
16 | Therefore, their executions are not blocked by bad-behaving functions / tasks.
17 | This important feature is absolutely necessary for mission-critical tasks.
18 |
19 | Based on SimpleTimer - A timer library for Arduino.
20 | Author: mromani@ottotecnica.com
21 | Copyright (c) 2010 OTTOTECNICA Italy
22 |
23 | Based on BlynkTimer.h
24 | Author: Volodymyr Shymanskyy
25 |
26 | Version: 1.6.0
27 |
28 | Version Modified By Date Comments
29 | ------- ----------- ---------- -----------
30 | 1.0.0 K Hoang 23/11/2019 Initial coding
31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error
32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README.
34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0
38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md
40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer
41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
42 | *****************************************************************************************************************************/
43 |
44 | #pragma once
45 |
46 | #ifndef TIMERINTERRUPT_GENERIC_DEBUG_H
47 | #define TIMERINTERRUPT_GENERIC_DEBUG_H
48 |
49 | #ifdef TIMERINTERRUPT_DEBUG_PORT
50 | #define TISR_DBG_PORT TIMERINTERRUPT_DEBUG_PORT
51 | #else
52 | #define TISR_DBG_PORT Serial
53 | #endif
54 |
55 | // Change _TIMERINTERRUPT_LOGLEVEL_ to set tracing and logging verbosity
56 | // 0: DISABLED: no logging
57 | // 1: ERROR: errors
58 | // 2: WARN: errors and warnings
59 | // 3: INFO: errors, warnings and informational (default)
60 | // 4: DEBUG: errors, warnings, informational and debug
61 |
62 | #ifndef _TIMERINTERRUPT_LOGLEVEL_
63 | #define _TIMERINTERRUPT_LOGLEVEL_ 1
64 | #endif
65 |
66 | #define TISR_LOGERROR(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); }
67 | #define TISR_LOGERROR0(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print(x); }
68 | #define TISR_LOGERROR1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(y); }
69 | #define TISR_LOGERROR2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(z); }
70 | #define TISR_LOGERROR3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(z); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(w); }
71 |
72 | #define TISR_LOGWARN(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); }
73 | #define TISR_LOGWARN0(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print(x); }
74 | #define TISR_LOGWARN1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(y); }
75 | #define TISR_LOGWARN2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(z); }
76 | #define TISR_LOGWARN3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(z); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(w); }
77 |
78 | #define TISR_LOGINFO(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); }
79 | #define TISR_LOGINFO0(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print(x); }
80 | #define TISR_LOGINFO1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(y); }
81 | #define TISR_LOGINFO2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(z); }
82 | #define TISR_LOGINFO3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(z); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(w); }
83 |
84 | #define TISR_LOGDEBUG(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); }
85 | #define TISR_LOGDEBUG0(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print(x); }
86 | #define TISR_LOGDEBUG1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(y); }
87 | #define TISR_LOGDEBUG2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(z); }
88 | #define TISR_LOGDEBUG3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(z); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(w); }
89 |
90 | #endif //TIMERINTERRUPT_GENERIC_DEBUG_H
91 |
--------------------------------------------------------------------------------
/utils/astyle_library.conf:
--------------------------------------------------------------------------------
1 | # Code formatting rules for Arduino libraries, modified from for KH libraries:
2 | #
3 | # https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf
4 | #
5 |
6 | # astyle --style=allman -s2 -t2 -C -S -xW -Y -M120 -f -p -xg -H -xb -c --xC120 -xL *.h *.cpp *.ino
7 |
8 | --mode=c
9 | --lineend=linux
10 | --style=allman
11 |
12 | # -r or -R
13 | #--recursive
14 |
15 | # -c => Converts tabs into spaces
16 | convert-tabs
17 |
18 | # -s2 => 2 spaces indentation
19 | --indent=spaces=2
20 |
21 | # -t2 => tab =2 spaces
22 | #--indent=tab=2
23 |
24 | # -C
25 | --indent-classes
26 |
27 | # -S
28 | --indent-switches
29 |
30 | # -xW
31 | --indent-preproc-block
32 |
33 | # -Y => indent classes, switches (and cases), comments starting at column 1
34 | --indent-col1-comments
35 |
36 | # -M120 => maximum of 120 spaces to indent a continuation line
37 | --max-continuation-indent=120
38 |
39 | # -xC120 => max‑code‑length will break a line if the code exceeds # characters
40 | --max-code-length=120
41 |
42 | # -f =>
43 | --break-blocks
44 |
45 | # -p => put a space around operators
46 | --pad-oper
47 |
48 | # -xg => Insert space padding after commas
49 | --pad-comma
50 |
51 | # -H => put a space after if/for/while
52 | pad-header
53 |
54 | # -xb => Break one line headers (e.g. if/for/while)
55 | --break-one-line-headers
56 |
57 | # -c => Converts tabs into spaces
58 | #--convert-tabs
59 |
60 | # if you like one-liners, keep them
61 | #keep-one-line-statements
62 |
63 | # -xV
64 | --attach-closing-while
65 |
66 | #unpad-paren
67 |
68 | # -xp
69 | remove-comment-prefix
70 |
71 |
--------------------------------------------------------------------------------
/utils/restyle.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | for dir in . ; do
4 | find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.ino" \) -exec astyle --suffix=none --options=./utils/astyle_library.conf \{\} \;
5 | done
6 |
7 |
--------------------------------------------------------------------------------