├── .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 ├── RPM_Measure │ └── RPM_Measure.ino ├── SwitchDebounce │ └── SwitchDebounce.ino └── TimerInterruptTest │ └── TimerInterruptTest.ino ├── keywords.txt ├── library.json ├── library.properties ├── platformio └── platformio.ini ├── src ├── ESP32_New_ISR_Timer-Impl.h ├── ESP32_New_ISR_Timer.h ├── ESP32_New_ISR_Timer.hpp ├── ESP32_New_TimerInterrupt.h └── 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 | * `ESP32` Core Version (e.g. ESP32 core v2.0.5) 40 | * `ESP32` Board type (e.g. ESP32_DEV Module, etc.) 41 | * `ESP32-S2` Board type (e.g. ESP32S2_DEV Module, ESP32_S2_Saola, etc.) 42 | * `ESP32_S3` Board type (e.g. ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, etc.) 43 | * `ESP32-C3` Board type (e.g. ESP32C3_DEV Module, LOLIN_C3_MINI, DFROBOT_BEETLE_ESP32_C3, ADAFRUIT_QTPY_ESP32C3, AirM2M_CORE_ESP32C3, XIAO_ESP32C3, etc.) 44 | * Contextual information (e.g. what you were trying to achieve) 45 | * Simplest possible steps to reproduce 46 | * Anything that might be relevant in your opinion, such as: 47 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 48 | * Network configuration 49 | 50 | 51 | ### Example 52 | 53 | ``` 54 | Arduino IDE version: 1.8.19 55 | ESP32 core v2.0.5 56 | ESP32S3_DEV Module 57 | OS: Ubuntu 20.04 LTS 58 | 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 59 | 60 | Context: 61 | I encountered a crash while using this library 62 | Steps to reproduce: 63 | 1. ... 64 | 2. ... 65 | 3. ... 66 | 4. ... 67 | ``` 68 | 69 | ### Additional context 70 | 71 | Add any other context about the problem here. 72 | 73 | --- 74 | 75 | ### Sending Feature Requests 76 | 77 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 78 | 79 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 80 | 81 | --- 82 | 83 | ### Sending Pull Requests 84 | 85 | Pull Requests with changes and fixes are also welcome! 86 | 87 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 88 | 89 | 1. Change directory to the library GitHub 90 | 91 | ``` 92 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESP32_New_TimerInterrupt_GitHub/ 93 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP32_New_TimerInterrupt_GitHub$ 94 | ``` 95 | 96 | 2. Issue astyle command 97 | 98 | ``` 99 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP32_New_TimerInterrupt_GitHub$ bash utils/restyle.sh 100 | ``` 101 | 102 | -------------------------------------------------------------------------------- /.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 | 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | ### Additional context 23 | 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /.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 ESP32_New_TimerInterrupt 2 | 3 | ### Reporting Bugs 4 | 5 | Please report bugs in [ESP32_New_TimerInterrupt Issues](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/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/ESP32_New_TimerInterrupt/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/ESP32_New_TimerInterrupt/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 | * `ESP32` Core Version (e.g. ESP32 core v2.0.5) 19 | * `ESP32` Board type (e.g. ESP32_DEV Module, etc.) 20 | * `ESP32-S2` Board type (e.g. ESP32S2_DEV Module, ESP32_S2_Saola, etc.) 21 | * `ESP32_S3` Board type (e.g. ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, etc.) 22 | * `ESP32-C3` Board type (e.g. ESP32C3_DEV Module, LOLIN_C3_MINI, DFROBOT_BEETLE_ESP32_C3, ADAFRUIT_QTPY_ESP32C3, AirM2M_CORE_ESP32C3, XIAO_ESP32C3, etc.) 23 | * Contextual information (e.g. what you were trying to achieve) 24 | * Simplest possible steps to reproduce 25 | * Anything that might be relevant in your opinion, such as: 26 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 27 | * Network configuration 28 | 29 | 30 | ### Example 31 | 32 | ``` 33 | Arduino IDE version: 1.8.19 34 | ESP32 core v2.0.5 35 | ESP32S3_DEV Module 36 | OS: Ubuntu 20.04 LTS 37 | 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 38 | 39 | Context: 40 | I encountered a crash while using this library 41 | Steps to reproduce: 42 | 1. ... 43 | 2. ... 44 | 3. ... 45 | 4. ... 46 | ``` 47 | 48 | ### Additional context 49 | 50 | Add any other context about the problem here. 51 | 52 | --- 53 | 54 | ### Sending Feature Requests 55 | 56 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 57 | 58 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 59 | 60 | --- 61 | 62 | ### Sending Pull Requests 63 | 64 | Pull Requests with changes and fixes are also welcome! 65 | 66 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 67 | 68 | 1. Change directory to the library GitHub 69 | 70 | ``` 71 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESP32_New_TimerInterrupt_GitHub/ 72 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP32_New_TimerInterrupt_GitHub$ 73 | ``` 74 | 75 | 2. Issue astyle command 76 | 77 | ``` 78 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP32_New_TimerInterrupt_GitHub$ bash utils/restyle.sh 79 | ``` 80 | 81 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32_New_TimerInterrupt Library 2 | 3 | [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP32_New_TimerInterrupt.svg?)](https://www.ardu-badge.com/ESP32_New_TimerInterrupt) 4 | [![GitHub release](https://img.shields.io/github/release/khoih-prog/ESP32_New_TimerInterrupt.svg)](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/releases) 5 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/blob/main/LICENSE) 6 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) 7 | [![GitHub issues](https://img.shields.io/github/issues/khoih-prog/ESP32_New_TimerInterrupt.svg)](http://github.com/khoih-prog/ESP32_New_TimerInterrupt/issues) 8 | 9 | 10 | Donate to my libraries using BuyMeACoffee 11 | 12 | 13 | --- 14 | --- 15 | 16 | ## Table of Contents 17 | 18 | * [Why do we need this ESP32_New_TimerInterrupt library](#why-do-we-need-this-esp32_new_timerinterrupt-library) 19 | * [Important Notes](#Important-Notes) 20 | * [Features](#features) 21 | * [Why using ISR-based Hardware Timer Interrupt is better](#why-using-isr-based-hardware-timer-interrupt-is-better) 22 | * [Currently supported Boards](#currently-supported-boards) 23 | * [Important Notes about ISR](#important-notes-about-isr) 24 | * [Changelog](changelog.md) 25 | * [Prerequisites](#prerequisites) 26 | * [Installation](#installation) 27 | * [Use Arduino Library Manager](#use-arduino-library-manager) 28 | * [Manual Install](#manual-install) 29 | * [VS Code & PlatformIO](#vs-code--platformio) 30 | * [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) 31 | * [HOWTO Use analogRead() with ESP32 running WiFi and/or BlueTooth (BT/BLE)](#howto-use-analogread-with-esp32-running-wifi-andor-bluetooth-btble) 32 | * [1. ESP32 has 2 ADCs, named ADC1 and ADC2](#1--esp32-has-2-adcs-named-adc1-and-adc2) 33 | * [2. ESP32 ADCs functions](#2-esp32-adcs-functions) 34 | * [3. ESP32 WiFi uses ADC2 for WiFi functions](#3-esp32-wifi-uses-adc2-for-wifi-functions) 35 | * [More useful Information](#more-useful-information) 36 | * [How to use](#how-to-use) 37 | * [Examples](#examples) 38 | * [ 1. Argument_None](examples/Argument_None) 39 | * [ 2. Change_Interval](examples/Change_Interval). 40 | * [ 3. RPM_Measure](examples/RPM_Measure) 41 | * [ 4. SwitchDebounce](examples/SwitchDebounce) 42 | * [ 5. TimerInterruptTest](examples/TimerInterruptTest) 43 | * [ 6. ISR_16_Timers_Array](examples/ISR_16_Timers_Array) 44 | * [ 7. ISR_16_Timers_Array_Complex](examples/ISR_16_Timers_Array_Complex) 45 | * [Example ISR_16_Timers_Array_Complex](#example-isr_16_timers_array_complex) 46 | * [Debug Terminal Output Samples](#debug-terminal-output-samples) 47 | * [1. TimerInterruptTest on ESP32C3_DEV](#1-timerinterrupttest-on-esp32c3_dev) 48 | * [2. Change_Interval on ESP32_DEV](#2-change_interval-on-esp32_dev) 49 | * [3. Argument_None on ESP32S2_DEV](#3-argument_none-on-esp32s2_dev) 50 | * [4. ISR_16_Timers_Array_Complex on ESP32C3_DEV](#4-isr_16_timers_array_complex-on-esp32c3_dev) 51 | * [5. ISR_16_Timers_Array on ESP32S2_DEV](#5-isr_16_timers_array-on-esp32s2_dev) 52 | * [6. ISR_16_Timers_Array_Complex on ESP32S3_DEV](#6-isr_16_timers_array_complex-on-ESP32S3_DEV) **New** 53 | * [Debug](#debug) 54 | * [Troubleshooting](#troubleshooting) 55 | * [Issues](#issues) 56 | * [TO DO](#to-do) 57 | * [DONE](#done) 58 | * [Contributions and Thanks](#contributions-and-thanks) 59 | * [Contributing](#contributing) 60 | * [License](#license) 61 | * [Copyright](#copyright) 62 | 63 | --- 64 | --- 65 | 66 | ### Why do we need this [ESP32_New_TimerInterrupt library](https://github.com/khoih-prog/ESP32_New_TimerInterrupt) 67 | 68 | ### Important Notes 69 | 70 | 1. Avoid using `PIN_D1 (GPIO1)` in your code due to issue with core v2.0.0 and v2.0.1. Check [ESP32 Core v2.0.1 / 2.0.1 RC1 crashes if using pinMode with GPIO1 #5868](https://github.com/espressif/arduino-esp32/issues/5868). Only OK with core v1.0.6- 71 | 2. Don't use `float` in `ISR` due to issue with core v2.0.0 and v2.0.1. Check [ESP32 Core v2.0.1 / 2.0.1 RC1 crashes if using float in ISR #5892](https://github.com/espressif/arduino-esp32/issues/5892). Only OK with core v1.0.6-. 72 | 73 | ### Features 74 | 75 | This library enables you to use Interrupt from Hardware Timers on an ESP32, ESP32_S2/S3/C3-based board. 76 | 77 | --- 78 | 79 | As **Hardware Timers are rare, and very precious assets** of any board, this library now enables you to use up to **16 ISR-based Timers, while consuming only 1 Hardware Timer**. Timers' interval is very long (**ulong millisecs**). 80 | 81 | Now with these new **16 ISR-based timers**, the maximum interval is **practically unlimited** (limited only by unsigned long milliseconds) while **the accuracy is nearly perfect** compared to software timers. 82 | 83 | The most important feature is they're ISR-based timers. Therefore, their executions are **not blocked by bad-behaving functions / tasks**. This important feature is absolutely necessary for mission-critical tasks. 84 | 85 | The [**ISR_Timer_Complex**](examples/ISR_Timer_Complex) example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs of each type of timers. 86 | 87 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet and Blynk services. You can also have many `(up to 16)` timers to use. 88 | 89 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 90 | 91 | You'll see blynkTimer Software is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task 92 | in loop(), using delay() function as an example. The elapsed time then is very unaccurate 93 | 94 | ### Why using ISR-based Hardware Timer Interrupt is better 95 | 96 | Imagine you have a system with a **mission-critical** function, measuring water level and control the sump pump or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is **blocking** the loop() or setup(). 97 | 98 | So your function **might not be executed, and the result would be disastrous.** 99 | 100 | You'd prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.). 101 | 102 | The correct choice is to use a Hardware Timer with **Interrupt** to call your function. 103 | 104 | 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. 105 | 106 | Functions using normal software timers, relying on loop() and calling millis(), won't work if the loop() or setup() is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services. 107 | 108 | The catch is **your function is now part of an ISR (Interrupt Service Routine), and must be lean / mean, and follow certain rules.** More to read on: 109 | 110 | [**HOWTO Attach Interrupt**](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/) 111 | 112 | --- 113 | 114 | ### Currently supported Boards 115 | 116 | 1. ESP32 boards, such as `ESP32_DEV`, etc. 117 | 2. ESP32_S2-based boards, such as `ESP32S2_DEV`, `ESP32_S2 Saola`, Adafruit QTPY_ESP32S2, ESP32S2 Native USB, UM FeatherS2 Neo, UM TinyS2, UM RMP, microS2, etc. 118 | 3. ESP32_C3-based boards, such as `ESP32C3_DEV`, LOLIN_C3_MINI, DFROBOT_BEETLE_ESP32_C3, ADAFRUIT_QTPY_ESP32C3, AirM2M_CORE_ESP32C3, XIAO_ESP32C3, etc. **New** 119 | 4. ESP32_S3-based boards, such as ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, FEATHER_ESP32S3_NOPSRAM, QTPY_ESP32S3_NOPSRAM, etc. **New** 120 | 121 | 122 | --- 123 | 124 | ### Important Notes about ISR 125 | 126 | 1. Inside the attached function, **delay() won’t work and the value returned by millis() will not increment.** Serial data received while in the function may be lost. You should declare as **volatile any variables that you modify within the attached function.** 127 | 128 | 2. Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile. 129 | 130 | 131 | --- 132 | --- 133 | 134 | ## Prerequisites 135 | 136 | 1. [`Arduino IDE 1.8.19+` for Arduino](https://github.com/arduino/Arduino). [![GitHub release](https://img.shields.io/github/release/arduino/Arduino.svg)](https://github.com/arduino/Arduino/releases/latest) 137 | 2. [`ESP32 Core 2.0.5+`](https://github.com/espressif/arduino-esp32) for ESP32-based boards (ESP32, ESP32_S2, ESP32_S3 and ESP32_C3). [![Latest release](https://img.shields.io/github/release/espressif/arduino-esp32.svg)](https://github.com/espressif/arduino-esp32/releases/latest/). 138 | 139 | --- 140 | --- 141 | 142 | ## Installation 143 | 144 | ### Use Arduino Library Manager 145 | 146 | The best and easiest way is to use `Arduino Library Manager`. Search for [**ESP32_New_TimerInterrupt**](https://github.com/khoih-prog/ESP32_New_TimerInterrupt), then select / install the latest version. 147 | You can also use this link [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP32_New_TimerInterrupt.svg?)](https://www.ardu-badge.com/ESP32_New_TimerInterrupt) for more detailed instructions. 148 | 149 | --- 150 | 151 | ### Manual Install 152 | 153 | Another way to install is to: 154 | 155 | 1. Navigate to [**ESP32_New_TimerInterrupt**](https://github.com/khoih-prog/ESP32_New_TimerInterrupt) page. 156 | 2. Download the latest release `ESP32_New_TimerInterrupt-main.zip`. 157 | 3. Extract the zip file to `ESP32_New_TimerInterrupt-main` directory 158 | 4. Copy whole `ESP32_New_TimerInterrupt-main` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. 159 | 160 | --- 161 | 162 | ### VS Code & PlatformIO 163 | 164 | 1. Install [VS Code](https://code.visualstudio.com/) 165 | 2. Install [PlatformIO](https://platformio.org/platformio-ide) 166 | 3. Install [**ESP32_New_TimerInterrupt** library](https://registry.platformio.org/libraries/khoih-prog/ESP32_New_TimerInterrupt) by using [Library Manager](https://registry.platformio.org/libraries/khoih-prog/ESP32_New_TimerInterrupt/installation). Search for **ESP32_New_TimerInterrupt** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) 167 | 4. Use included [platformio.ini](platformio/platformio.ini) file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at [Project Configuration File](https://docs.platformio.org/page/projectconf.html) 168 | 169 | 170 | --- 171 | --- 172 | 173 | 174 | ### HOWTO Fix `Multiple Definitions` Linker Error 175 | 176 | The current library implementation, using `xyz-Impl.h` instead of standard `xyz.cpp`, possibly creates certain `Multiple Definitions` Linker error in certain use cases. 177 | 178 | You can use 179 | 180 | ```cpp 181 | #include //https://github.com/khoih-prog/ESP32_New_TimerInterrupt 182 | ``` 183 | 184 | in many files. But be sure to use the following `#include ` **in just 1 `.h`, `.cpp` or `.ino` file**, which must **not be included in any other file**, to avoid `Multiple Definitions` Linker Error 185 | 186 | ```cpp 187 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 188 | #include //https://github.com/khoih-prog/ESP32_New_TimerInterrupt 189 | ``` 190 | 191 | --- 192 | --- 193 | 194 | ### HOWTO Use analogRead() with ESP32 running WiFi and/or BlueTooth (BT/BLE) 195 | 196 | Please have a look at [**ESP_WiFiManager Issue 39: Not able to read analog port when using the autoconnect example**](https://github.com/khoih-prog/ESP_WiFiManager/issues/39) to have more detailed description and solution of the issue. 197 | 198 | #### 1. ESP32 has 2 ADCs, named ADC1 and ADC2 199 | 200 | #### 2. ESP32 ADCs functions 201 | 202 | - `ADC1` controls ADC function for pins **GPIO32-GPIO39** 203 | - `ADC2` controls ADC function for pins **GPIO0, 2, 4, 12-15, 25-27** 204 | 205 | #### 3.. ESP32 WiFi uses ADC2 for WiFi functions 206 | 207 | Look in file [**adc_common.c**](https://github.com/espressif/esp-idf/blob/master/components/driver/adc_common.c) 208 | 209 | > In `ADC2`, there're two locks used for different cases: 210 | > 1. lock shared with app and Wi-Fi: 211 | > ESP32: 212 | > When Wi-Fi using the `ADC2`, we assume it will never stop, so app checks the lock and returns immediately if failed. 213 | > ESP32S2: 214 | > The controller's control over the ADC is determined by the arbiter. There is no need to control by lock. 215 | > 216 | > 2. lock shared between tasks: 217 | > when several tasks sharing the `ADC2`, we want to guarantee 218 | > all the requests will be handled. 219 | > Since conversions are short (about 31us), app returns the lock very soon, 220 | > we use a spinlock to stand there waiting to do conversions one by one. 221 | > 222 | > adc2_spinlock should be acquired first, then adc2_wifi_lock or rtc_spinlock. 223 | 224 | 225 | - In order to use `ADC2` for other functions, we have to **acquire complicated firmware locks and very difficult to do** 226 | - So, it's not advisable to use `ADC2` with WiFi/BlueTooth (BT/BLE). 227 | - Use `ADC1`, and pins `GPIO32-GPIO39` 228 | - If somehow it's a must to use those pins serviced by `ADC2` (**GPIO0, 2, 4, 12, 13, 14, 15, 25, 26 and 27**), use the **fix mentioned at the end** of [**ESP_WiFiManager Issue 39: Not able to read analog port when using the autoconnect example**](https://github.com/khoih-prog/ESP_WiFiManager/issues/39) to work with ESP32 WiFi/BlueTooth (BT/BLE). 229 | 230 | 231 | --- 232 | --- 233 | 234 | ## More useful Information 235 | 236 | The ESP32 has two timer groups, each one with two general purpose hardware timers. All the timers are based on 64 bits counters and 16 bit prescalers. 237 | 238 | The timer counters can be configured to count up or down and support automatic reload and software reload. 239 | 240 | They can also generate alarms when they reach a specific value, defined by the software. The value of the counter can be read by 241 | the software program. 242 | 243 | --- 244 | 245 | Now with these new `16 ISR-based timers` (while consuming only **1 hardware timer**), the maximum interval is practically unlimited (limited only by unsigned long milliseconds). The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers Therefore, their executions are not blocked by bad-behaving functions / tasks. 246 | This important feature is absolutely necessary for mission-critical tasks. 247 | 248 | The `ISR_Timer_Complex` example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs of each type of timers. 249 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet and Blynk services. You can also have many `(up to 16)` timers to use. 250 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 251 | You'll see blynkTimer Software is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task 252 | in loop(), using delay() function as an example. The elapsed time then is very unaccurate 253 | 254 | --- 255 | --- 256 | 257 | ## How to use 258 | 259 | Before using any Timer, you have to make sure the Timer has not been used by any other purpose. 260 | 261 | `Timer0, Timer1, Timer2 and Timer3` are supported for ESP32-S2. 262 | 263 | --- 264 | --- 265 | 266 | ### Examples: 267 | 268 | 1. [Argument_None](examples/Argument_None) 269 | 2. [RPM_Measure](examples/RPM_Measure) 270 | 3. [SwitchDebounce](examples/SwitchDebounce) 271 | 4. [TimerInterruptTest](examples/TimerInterruptTest) 272 | 5. [**Change_Interval**](examples/Change_Interval). 273 | 6. [**ISR_16_Timers_Array**](examples/ISR_16_Timers_Array) 274 | 7. [**ISR_16_Timers_Array_Complex**](examples/ISR_16_Timers_Array_Complex). 275 | 276 | --- 277 | --- 278 | 279 | ### Example [ISR_16_Timers_Array_Complex](examples/ISR_16_Timers_Array_Complex) 280 | 281 | https://github.com/khoih-prog/ESP32_New_TimerInterrupt/blob/eb11f04967974c3b19e9c0f7b7ba078af49526c9/examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino#L43-L383 282 | 283 | --- 284 | --- 285 | 286 | ### Debug Terminal Output Samples 287 | 288 | 289 | ### 1. TimerInterruptTest on ESP32C3_DEV 290 | 291 | The following is the sample terminal output when running example [TimerInterruptTest](examples/TimerInterruptTest) to demonstrate how to start/stop Hardware Timers. 292 | 293 | ``` 294 | Starting TimerInterruptTest on ESP32_DEV 295 | ESP32_New_TimerInterrupt v1.5.0 296 | CPU Frequency = 240 MHz 297 | [TISR] ESP32_TimerInterrupt: _timerNo = 0 , _fre = 1000000 298 | [TISR] TIMER_BASE_CLK = 80000000 , TIMER_DIVIDER = 80 299 | [TISR] _timerIndex = 0 , _timerGroup = 0 300 | [TISR] _count = 0 - 1000000 301 | [TISR] timer_set_alarm_value = 1000000.00 302 | Starting ITimer0 OK, millis() = 158 303 | [TISR] ESP32_TimerInterrupt: _timerNo = 1 , _fre = 1000000 304 | [TISR] TIMER_BASE_CLK = 80000000 , TIMER_DIVIDER = 80 305 | [TISR] _timerIndex = 1 , _timerGroup = 0 306 | [TISR] _count = 0 - 3000000 307 | [TISR] timer_set_alarm_value = 3000000.00 308 | Starting ITimer1 OK, millis() = 189 309 | Stop ITimer0, millis() = 5001 310 | Start ITimer0, millis() = 10002 311 | Stop ITimer1, millis() = 15001 312 | Stop ITimer0, millis() = 15003 313 | Start ITimer0, millis() = 20004 314 | Stop ITimer0, millis() = 25005 315 | Start ITimer1, millis() = 30002 316 | Start ITimer0, millis() = 30006 317 | ``` 318 | 319 | --- 320 | 321 | 322 | ### 2. Change_Interval on ESP32_DEV 323 | 324 | The following is the sample terminal output when running example [Change_Interval](examples/Change_Interval) to demonstrate how to change Timer Interval on-the-fly 325 | 326 | ``` 327 | Starting Change_Interval on ESP32_DEV 328 | ESP32_New_TimerInterrupt v1.5.0 329 | CPU Frequency = 240 MHz 330 | Starting ITimer0 OK, millis() = 136 331 | Starting ITimer1 OK, millis() = 147 332 | Time = 10001, Timer0Count = 5, Timer1Count = 2 333 | Time = 20002, Timer0Count = 10, Timer1Count = 4 334 | Changing Interval, Timer0 = 4000, Timer1 = 10000 335 | Time = 30003, Timer0Count = 12, Timer1Count = 5 336 | Time = 40004, Timer0Count = 15, Timer1Count = 6 337 | Changing Interval, Timer0 = 2000, Timer1 = 5000 338 | Time = 50005, Timer0Count = 20, Timer1Count = 8 339 | Time = 60006, Timer0Count = 25, Timer1Count = 10 340 | Changing Interval, Timer0 = 4000, Timer1 = 10000 341 | Time = 70007, Timer0Count = 27, Timer1Count = 11 342 | Time = 80008, Timer0Count = 30, Timer1Count = 12 343 | Changing Interval, Timer0 = 2000, Timer1 = 5000 344 | Time = 90009, Timer0Count = 35, Timer1Count = 14 345 | Time = 100010, Timer0Count = 40, Timer1Count = 16 346 | Changing Interval, Timer0 = 4000, Timer1 = 10000 347 | ``` 348 | 349 | --- 350 | 351 | ### 3. Argument_None on ESP32S2_DEV 352 | 353 | The following is the sample terminal output when running example [Argument_None](examples/Argument_None) 354 | 355 | ``` 356 | Starting Argument_None on ESP32S2_DEV 357 | ESP32_New_TimerInterrupt v1.5.0 358 | CPU Frequency = 240 MHz 359 | [TISR] ESP32_S2_TimerInterrupt: _timerNo = 0 , _fre = 1000000 360 | [TISR] TIMER_BASE_CLK = 80000000 , TIMER_DIVIDER = 80 361 | [TISR] _timerIndex = 0 , _timerGroup = 0 362 | [TISR] _count = 0 - 1000000 363 | [TISR] timer_set_alarm_value = 1000000.00 364 | ITimer0: millis() = 656 365 | Starting ITimer0 OK, millis() = 660 366 | [TISR] ESP32_S2_TimerInterrupt: _timerNo = 1 , _fre = 1000000 367 | [TISR] TIMER_BASE_CLK = 80000000 , TIMER_DIVIDER = 80 368 | [TISR] _timerIndex = 1 , _timerGroup = 0 369 | [TISR] _count = 0 - 5000000 370 | [TISR] timer_set_alarm_value = 5000000.00 371 | ITimer1: millis() = 682 372 | Starting ITimer1 OK, millis() = 685 373 | ITimer0: millis() = 1651 374 | ITimer0: millis() = 2651 375 | ITimer0: millis() = 3651 376 | ITimer0: millis() = 4651 377 | ITimer0: millis() = 5651 378 | ITimer1: millis() = 5676 379 | ITimer0: millis() = 6651 380 | ITimer0: millis() = 7651 381 | ITimer0: millis() = 8651 382 | ITimer0: millis() = 9651 383 | ITimer0: millis() = 10651 384 | ITimer1: millis() = 10676 385 | ITimer0: millis() = 11651 386 | ITimer0: millis() = 12651 387 | ITimer0: millis() = 13651 388 | ITimer0: millis() = 14651 389 | ITimer0: millis() = 15651 390 | ITimer1: millis() = 15676 391 | ITimer0: millis() = 16651 392 | ITimer0: millis() = 17651 393 | ITimer0: millis() = 18651 394 | ITimer0: millis() = 19651 395 | ITimer0: millis() = 20651 396 | ITimer1: millis() = 20676 397 | ITimer0: millis() = 21651 398 | ITimer0: millis() = 22651 399 | ``` 400 | 401 | --- 402 | 403 | ### 4. ISR_16_Timers_Array_Complex on ESP32C3_DEV 404 | 405 | The following is the sample terminal output when running example [ISR_16_Timers_Array_Complex](examples/ISR_16_Timers_Array_Complex) on **ESP32C3_DEV** to demonstrate of ISR Hardware Timer, especially when system is very busy or blocked. The 16 independent ISR timers are programmed to be activated repetitively after certain intervals, is activated exactly after that programmed interval !!! 406 | 407 | ``` 408 | Starting ISR_16_Timers_Array_Complex on ESP32C3_DEV 409 | ESP32_New_TimerInterrupt v1.5.0 410 | CPU Frequency = 160 MHz 411 | Starting ITimer OK, millis() = 2187 412 | SimpleTimer : 2, ms : 12193, Dms : 10004 413 | Timer : 0, programmed : 5000, actual : 5008 414 | Timer : 1, programmed : 10000, actual : 0 415 | Timer : 2, programmed : 15000, actual : 0 416 | Timer : 3, programmed : 20000, actual : 0 417 | Timer : 4, programmed : 25000, actual : 0 418 | Timer : 5, programmed : 30000, actual : 0 419 | Timer : 6, programmed : 35000, actual : 0 420 | Timer : 7, programmed : 40000, actual : 0 421 | Timer : 8, programmed : 45000, actual : 0 422 | Timer : 9, programmed : 50000, actual : 0 423 | Timer : 10, programmed : 55000, actual : 0 424 | Timer : 11, programmed : 60000, actual : 0 425 | Timer : 12, programmed : 65000, actual : 0 426 | Timer : 13, programmed : 70000, actual : 0 427 | Timer : 14, programmed : 75000, actual : 0 428 | Timer : 15, programmed : 80000, actual : 0 429 | SimpleTimer : 2, ms : 22251, Dms : 10058 430 | Timer : 0, programmed : 5000, actual : 5000 431 | Timer : 1, programmed : 10000, actual : 10000 432 | Timer : 2, programmed : 15000, actual : 15008 433 | Timer : 3, programmed : 20000, actual : 20008 434 | Timer : 4, programmed : 25000, actual : 0 435 | Timer : 5, programmed : 30000, actual : 0 436 | Timer : 6, programmed : 35000, actual : 0 437 | Timer : 7, programmed : 40000, actual : 0 438 | Timer : 8, programmed : 45000, actual : 0 439 | Timer : 9, programmed : 50000, actual : 0 440 | Timer : 10, programmed : 55000, actual : 0 441 | Timer : 11, programmed : 60000, actual : 0 442 | Timer : 12, programmed : 65000, actual : 0 443 | Timer : 13, programmed : 70000, actual : 0 444 | Timer : 14, programmed : 75000, actual : 0 445 | Timer : 15, programmed : 80000, actual : 0 446 | SimpleTimer : 2, ms : 32310, Dms : 10059 447 | Timer : 0, programmed : 5000, actual : 5000 448 | Timer : 1, programmed : 10000, actual : 10000 449 | Timer : 2, programmed : 15000, actual : 15000 450 | Timer : 3, programmed : 20000, actual : 20008 451 | Timer : 4, programmed : 25000, actual : 25008 452 | Timer : 5, programmed : 30000, actual : 30008 453 | Timer : 6, programmed : 35000, actual : 0 454 | Timer : 7, programmed : 40000, actual : 0 455 | Timer : 8, programmed : 45000, actual : 0 456 | Timer : 9, programmed : 50000, actual : 0 457 | Timer : 10, programmed : 55000, actual : 0 458 | Timer : 11, programmed : 60000, actual : 0 459 | Timer : 12, programmed : 65000, actual : 0 460 | Timer : 13, programmed : 70000, actual : 0 461 | Timer : 14, programmed : 75000, actual : 0 462 | Timer : 15, programmed : 80000, actual : 0 463 | SimpleTimer : 2, ms : 42369, Dms : 10059 464 | Timer : 0, programmed : 5000, actual : 5000 465 | Timer : 1, programmed : 10000, actual : 10000 466 | Timer : 2, programmed : 15000, actual : 15000 467 | Timer : 3, programmed : 20000, actual : 20000 468 | Timer : 4, programmed : 25000, actual : 25008 469 | Timer : 5, programmed : 30000, actual : 30008 470 | Timer : 6, programmed : 35000, actual : 35008 471 | Timer : 7, programmed : 40000, actual : 40008 472 | Timer : 8, programmed : 45000, actual : 0 473 | Timer : 9, programmed : 50000, actual : 0 474 | Timer : 10, programmed : 55000, actual : 0 475 | Timer : 11, programmed : 60000, actual : 0 476 | Timer : 12, programmed : 65000, actual : 0 477 | Timer : 13, programmed : 70000, actual : 0 478 | Timer : 14, programmed : 75000, actual : 0 479 | Timer : 15, programmed : 80000, actual : 0 480 | SimpleTimer : 2, ms : 52429, Dms : 10060 481 | Timer : 0, programmed : 5000, actual : 5000 482 | Timer : 1, programmed : 10000, actual : 10000 483 | Timer : 2, programmed : 15000, actual : 15000 484 | Timer : 3, programmed : 20000, actual : 20000 485 | Timer : 4, programmed : 25000, actual : 25000 486 | Timer : 5, programmed : 30000, actual : 30008 487 | Timer : 6, programmed : 35000, actual : 35008 488 | Timer : 7, programmed : 40000, actual : 40008 489 | Timer : 8, programmed : 45000, actual : 45008 490 | Timer : 9, programmed : 50000, actual : 50008 491 | Timer : 10, programmed : 55000, actual : 0 492 | Timer : 11, programmed : 60000, actual : 0 493 | Timer : 12, programmed : 65000, actual : 0 494 | Timer : 13, programmed : 70000, actual : 0 495 | Timer : 14, programmed : 75000, actual : 0 496 | Timer : 15, programmed : 80000, actual : 0 497 | SimpleTimer : 2, ms : 62490, Dms : 10061 498 | Timer : 0, programmed : 5000, actual : 5000 499 | Timer : 1, programmed : 10000, actual : 10000 500 | Timer : 2, programmed : 15000, actual : 15000 501 | Timer : 3, programmed : 20000, actual : 20000 502 | Timer : 4, programmed : 25000, actual : 25000 503 | Timer : 5, programmed : 30000, actual : 30000 504 | Timer : 6, programmed : 35000, actual : 35008 505 | Timer : 7, programmed : 40000, actual : 40008 506 | Timer : 8, programmed : 45000, actual : 45008 507 | Timer : 9, programmed : 50000, actual : 50008 508 | Timer : 10, programmed : 55000, actual : 55008 509 | Timer : 11, programmed : 60000, actual : 60008 510 | Timer : 12, programmed : 65000, actual : 0 511 | Timer : 13, programmed : 70000, actual : 0 512 | Timer : 14, programmed : 75000, actual : 0 513 | Timer : 15, programmed : 80000, actual : 0 514 | SimpleTimer : 2, ms : 72551, Dms : 10061 515 | Timer : 0, programmed : 5000, actual : 5000 516 | Timer : 1, programmed : 10000, actual : 10000 517 | Timer : 2, programmed : 15000, actual : 15000 518 | Timer : 3, programmed : 20000, actual : 20000 519 | Timer : 4, programmed : 25000, actual : 25000 520 | Timer : 5, programmed : 30000, actual : 30000 521 | Timer : 6, programmed : 35000, actual : 35000 522 | Timer : 7, programmed : 40000, actual : 40008 523 | Timer : 8, programmed : 45000, actual : 45008 524 | Timer : 9, programmed : 50000, actual : 50008 525 | Timer : 10, programmed : 55000, actual : 55008 526 | Timer : 11, programmed : 60000, actual : 60008 527 | Timer : 12, programmed : 65000, actual : 65008 528 | Timer : 13, programmed : 70000, actual : 70008 529 | Timer : 14, programmed : 75000, actual : 0 530 | Timer : 15, programmed : 80000, actual : 0 531 | SimpleTimer : 2, ms : 82613, Dms : 10062 532 | Timer : 0, programmed : 5000, actual : 5000 533 | Timer : 1, programmed : 10000, actual : 10000 534 | Timer : 2, programmed : 15000, actual : 15000 535 | Timer : 3, programmed : 20000, actual : 20000 536 | Timer : 4, programmed : 25000, actual : 25000 537 | Timer : 5, programmed : 30000, actual : 30000 538 | Timer : 6, programmed : 35000, actual : 35000 539 | Timer : 7, programmed : 40000, actual : 40000 540 | Timer : 8, programmed : 45000, actual : 45008 541 | Timer : 9, programmed : 50000, actual : 50008 542 | Timer : 10, programmed : 55000, actual : 55008 543 | Timer : 11, programmed : 60000, actual : 60008 544 | Timer : 12, programmed : 65000, actual : 65008 545 | Timer : 13, programmed : 70000, actual : 70008 546 | Timer : 14, programmed : 75000, actual : 75008 547 | Timer : 15, programmed : 80000, actual : 80008 548 | ``` 549 | 550 | --- 551 | 552 | ### 5. ISR_16_Timers_Array on ESP32S2_DEV 553 | 554 | The following is the sample terminal output when running example [ISR_16_Timers_Array](examples/ISR_16_Timers_Array) on **ESP32S2_DEV** to demonstrate of ISR Hardware Timer, especially when system is very busy or blocked. The 16 independent ISR timers are programmed to be activated repetitively after certain intervals, is activated exactly after that programmed interval !!! 555 | 556 | 557 | ``` 558 | Starting ISR_16_Timers_Array on ESP32S2_DEV 559 | ESP32_New_TimerInterrupt v1.5.0 560 | CPU Frequency = 240 MHz 561 | Starting ITimer OK, millis() = 2538 562 | simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10008 563 | simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10005 564 | ``` 565 | 566 | --- 567 | 568 | ### 6. ISR_16_Timers_Array_Complex on ESP32S3_DEV 569 | 570 | The following is the sample terminal output when running example [ISR_16_Timers_Array_Complex](examples/ISR_16_Timers_Array_Complex) on **ESP32S3_DEV** to demonstrate of ISR Hardware Timer, especially when system is very busy or blocked. The 16 independent ISR timers are programmed to be activated repetitively after certain intervals, is activated exactly after that programmed interval !!! 571 | 572 | ``` 573 | Starting ISR_16_Timers_Array_Complex on ESP32S3_DEV 574 | ESP32_New_TimerInterrupt v1.5.0 575 | CPU Frequency = 240 MHz 576 | [TISR] ESP32_S3_TimerInterrupt: _timerNo = 1 , _fre = 1000000 577 | [TISR] TIMER_BASE_CLK = 80000000 , TIMER_DIVIDER = 80 578 | [TISR] _timerIndex = 1 , _timerGroup = 0 579 | [TISR] _count = 0 - 10000 580 | [TISR] timer_set_alarm_value = 10000.00 581 | Starting ITimer OK, millis() = 2118 582 | SimpleTimer : 2, ms : 12117, Dms : 9999 583 | Timer : 0, programmed : 5000, actual : 5009 584 | Timer : 1, programmed : 10000, actual : 0 585 | Timer : 2, programmed : 15000, actual : 0 586 | Timer : 3, programmed : 20000, actual : 0 587 | Timer : 4, programmed : 25000, actual : 0 588 | Timer : 5, programmed : 30000, actual : 0 589 | Timer : 6, programmed : 35000, actual : 0 590 | Timer : 7, programmed : 40000, actual : 0 591 | Timer : 8, programmed : 45000, actual : 0 592 | Timer : 9, programmed : 50000, actual : 0 593 | Timer : 10, programmed : 55000, actual : 0 594 | Timer : 11, programmed : 60000, actual : 0 595 | Timer : 12, programmed : 65000, actual : 0 596 | Timer : 13, programmed : 70000, actual : 0 597 | Timer : 14, programmed : 75000, actual : 0 598 | Timer : 15, programmed : 80000, actual : 0 599 | SimpleTimer : 2, ms : 22180, Dms : 10063 600 | Timer : 0, programmed : 5000, actual : 5000 601 | Timer : 1, programmed : 10000, actual : 10000 602 | Timer : 2, programmed : 15000, actual : 15009 603 | Timer : 3, programmed : 20000, actual : 20009 604 | Timer : 4, programmed : 25000, actual : 0 605 | Timer : 5, programmed : 30000, actual : 0 606 | Timer : 6, programmed : 35000, actual : 0 607 | Timer : 7, programmed : 40000, actual : 0 608 | Timer : 8, programmed : 45000, actual : 0 609 | Timer : 9, programmed : 50000, actual : 0 610 | Timer : 10, programmed : 55000, actual : 0 611 | Timer : 11, programmed : 60000, actual : 0 612 | Timer : 12, programmed : 65000, actual : 0 613 | Timer : 13, programmed : 70000, actual : 0 614 | Timer : 14, programmed : 75000, actual : 0 615 | Timer : 15, programmed : 80000, actual : 0 616 | SimpleTimer : 2, ms : 32243, Dms : 10063 617 | Timer : 0, programmed : 5000, actual : 5000 618 | Timer : 1, programmed : 10000, actual : 10000 619 | Timer : 2, programmed : 15000, actual : 15000 620 | Timer : 3, programmed : 20000, actual : 20009 621 | Timer : 4, programmed : 25000, actual : 25009 622 | Timer : 5, programmed : 30000, actual : 30009 623 | Timer : 6, programmed : 35000, actual : 0 624 | Timer : 7, programmed : 40000, actual : 0 625 | Timer : 8, programmed : 45000, actual : 0 626 | Timer : 9, programmed : 50000, actual : 0 627 | Timer : 10, programmed : 55000, actual : 0 628 | Timer : 11, programmed : 60000, actual : 0 629 | Timer : 12, programmed : 65000, actual : 0 630 | Timer : 13, programmed : 70000, actual : 0 631 | Timer : 14, programmed : 75000, actual : 0 632 | Timer : 15, programmed : 80000, actual : 0 633 | SimpleTimer : 2, ms : 42306, Dms : 10063 634 | Timer : 0, programmed : 5000, actual : 5000 635 | Timer : 1, programmed : 10000, actual : 10000 636 | Timer : 2, programmed : 15000, actual : 15000 637 | Timer : 3, programmed : 20000, actual : 20000 638 | Timer : 4, programmed : 25000, actual : 25009 639 | Timer : 5, programmed : 30000, actual : 30009 640 | Timer : 6, programmed : 35000, actual : 35009 641 | Timer : 7, programmed : 40000, actual : 40009 642 | Timer : 8, programmed : 45000, actual : 0 643 | Timer : 9, programmed : 50000, actual : 0 644 | Timer : 10, programmed : 55000, actual : 0 645 | Timer : 11, programmed : 60000, actual : 0 646 | Timer : 12, programmed : 65000, actual : 0 647 | Timer : 13, programmed : 70000, actual : 0 648 | Timer : 14, programmed : 75000, actual : 0 649 | Timer : 15, programmed : 80000, actual : 0 650 | SimpleTimer : 2, ms : 52369, Dms : 10063 651 | Timer : 0, programmed : 5000, actual : 5000 652 | Timer : 1, programmed : 10000, actual : 10000 653 | Timer : 2, programmed : 15000, actual : 15000 654 | Timer : 3, programmed : 20000, actual : 20000 655 | Timer : 4, programmed : 25000, actual : 25000 656 | Timer : 5, programmed : 30000, actual : 30009 657 | Timer : 6, programmed : 35000, actual : 35009 658 | Timer : 7, programmed : 40000, actual : 40009 659 | Timer : 8, programmed : 45000, actual : 45009 660 | Timer : 9, programmed : 50000, actual : 50009 661 | Timer : 10, programmed : 55000, actual : 0 662 | Timer : 11, programmed : 60000, actual : 0 663 | Timer : 12, programmed : 65000, actual : 0 664 | Timer : 13, programmed : 70000, actual : 0 665 | Timer : 14, programmed : 75000, actual : 0 666 | Timer : 15, programmed : 80000, actual : 0 667 | SimpleTimer : 2, ms : 62432, Dms : 10063 668 | Timer : 0, programmed : 5000, actual : 5000 669 | Timer : 1, programmed : 10000, actual : 10000 670 | Timer : 2, programmed : 15000, actual : 15000 671 | Timer : 3, programmed : 20000, actual : 20000 672 | Timer : 4, programmed : 25000, actual : 25000 673 | Timer : 5, programmed : 30000, actual : 30000 674 | Timer : 6, programmed : 35000, actual : 35009 675 | Timer : 7, programmed : 40000, actual : 40009 676 | Timer : 8, programmed : 45000, actual : 45009 677 | Timer : 9, programmed : 50000, actual : 50009 678 | Timer : 10, programmed : 55000, actual : 55009 679 | Timer : 11, programmed : 60000, actual : 60009 680 | Timer : 12, programmed : 65000, actual : 0 681 | Timer : 13, programmed : 70000, actual : 0 682 | Timer : 14, programmed : 75000, actual : 0 683 | Timer : 15, programmed : 80000, actual : 0 684 | SimpleTimer : 2, ms : 72495, Dms : 10063 685 | Timer : 0, programmed : 5000, actual : 5000 686 | Timer : 1, programmed : 10000, actual : 10000 687 | Timer : 2, programmed : 15000, actual : 15000 688 | Timer : 3, programmed : 20000, actual : 20000 689 | Timer : 4, programmed : 25000, actual : 25000 690 | Timer : 5, programmed : 30000, actual : 30000 691 | Timer : 6, programmed : 35000, actual : 35000 692 | Timer : 7, programmed : 40000, actual : 40009 693 | Timer : 8, programmed : 45000, actual : 45009 694 | Timer : 9, programmed : 50000, actual : 50009 695 | Timer : 10, programmed : 55000, actual : 55009 696 | Timer : 11, programmed : 60000, actual : 60009 697 | Timer : 12, programmed : 65000, actual : 65009 698 | Timer : 13, programmed : 70000, actual : 70009 699 | Timer : 14, programmed : 75000, actual : 0 700 | Timer : 15, programmed : 80000, actual : 0 701 | SimpleTimer : 2, ms : 82558, Dms : 10063 702 | Timer : 0, programmed : 5000, actual : 5000 703 | Timer : 1, programmed : 10000, actual : 10000 704 | Timer : 2, programmed : 15000, actual : 15000 705 | Timer : 3, programmed : 20000, actual : 20000 706 | Timer : 4, programmed : 25000, actual : 25000 707 | Timer : 5, programmed : 30000, actual : 30000 708 | Timer : 6, programmed : 35000, actual : 35000 709 | Timer : 7, programmed : 40000, actual : 40000 710 | Timer : 8, programmed : 45000, actual : 45009 711 | Timer : 9, programmed : 50000, actual : 50009 712 | Timer : 10, programmed : 55000, actual : 55009 713 | Timer : 11, programmed : 60000, actual : 60009 714 | Timer : 12, programmed : 65000, actual : 65009 715 | Timer : 13, programmed : 70000, actual : 70009 716 | Timer : 14, programmed : 75000, actual : 75009 717 | Timer : 15, programmed : 80000, actual : 80009 718 | ``` 719 | 720 | --- 721 | --- 722 | 723 | ### Debug 724 | 725 | Debug is enabled by default on Serial. 726 | 727 | You can also change the debugging level (_TIMERINTERRUPT_LOGLEVEL_) from 0 to 4 728 | 729 | ```cpp 730 | // These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h" 731 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 732 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 733 | #define TIMER_INTERRUPT_DEBUG 0 734 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 735 | ``` 736 | 737 | --- 738 | 739 | ### Troubleshooting 740 | 741 | If you get compilation errors, more often than not, you may need to install a newer version of the core for Arduino boards. 742 | 743 | Sometimes, the library will only work if you update the board core to the latest version because I am using newly added functions. 744 | 745 | 746 | --- 747 | --- 748 | 749 | 750 | ### Issues 751 | 752 | Submit issues to: [ESP32_New_TimerInterrupt issues](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/issues) 753 | 754 | --- 755 | --- 756 | 757 | ## TO DO 758 | 759 | 1. Search for bug and improvement. 760 | 761 | 762 | 763 | ## DONE 764 | 765 | 1. Basic hardware timers for ESP32, ESP32-S2, ESP32-S3 and ESP32_C3 for [ESP32 core v2.0.0-rc1+](https://github.com/espressif/arduino-esp32/releases/tag/2.0.0-rc1) 766 | 2. More hardware-initiated software-enabled timers 767 | 3. Longer time interval 768 | 4. Similar features for remaining Arduino boards such as SAMD21, SAMD51, SAM-DUE, nRF52, ESP8266, STM32, etc. 769 | 5. Fix compiler errors due to conflict to some libraries. 770 | 6. Add complex examples. 771 | 7. Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1. 772 | 8. Avoid using float in ISR due to issue with core v2.0.0 and v2.0.1. 773 | 9. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories 774 | 10. Optimize library code by using `reference-passing` instead of `value-passing` 775 | 11. Add support to **ESP32-S3 (ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, etc.)** 776 | 12. Add support to many more boards, such as 777 | - ESP32_S2 : ESP32S2 Native USB, UM FeatherS2 Neo, UM TinyS2, UM RMP, microS2, LOLIN_S2_MINI, LOLIN_S2_PICO, ADAFRUIT_FEATHER_ESP32S2, ADAFRUIT_FEATHER_ESP32S2_TFT, ATMegaZero ESP32-S2, Deneyap Mini, FRANZININHO_WIFI, FRANZININHO_WIFI_MSC 778 | - ESP32_S3 : UM TinyS3, UM PROS3, UM FeatherS3, ESP32_S3_USB_OTG, ESP32S3_CAM_LCD, DFROBOT_FIREBEETLE_2_ESP32S3, ADAFRUIT_FEATHER_ESP32S3_TFT 779 | - ESP32_C3 : LOLIN_C3_MINI, DFROBOT_BEETLE_ESP32_C3, ADAFRUIT_QTPY_ESP32C3, AirM2M_CORE_ESP32C3, XIAO_ESP32C3 780 | 13. Use `allman astyle` and add `utils` 781 | 782 | 783 | 784 | --- 785 | --- 786 | 787 | ### Contributions and Thanks 788 | 789 | Many thanks for everyone for bug reporting, new feature suggesting, testing and contributing to the development of this library. 790 | 791 | 792 | --- 793 | 794 | ## Contributing 795 | 796 | If you want to contribute to this project: 797 | - Report bugs and errors 798 | - Ask for enhancements 799 | - Create issues and pull requests 800 | - Tell other people about this library 801 | 802 | --- 803 | 804 | ### License 805 | 806 | - The library is licensed under [MIT](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/blob/main/LICENSE) 807 | 808 | --- 809 | 810 | ## Copyright 811 | 812 | Copyright 2021- Khoi Hoang 813 | 814 | 815 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # ESP32_New_TimerInterrupt Library 2 | 3 | [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP32_New_TimerInterrupt.svg?)](https://www.ardu-badge.com/ESP32_New_TimerInterrupt) 4 | [![GitHub release](https://img.shields.io/github/release/khoih-prog/ESP32_New_TimerInterrupt.svg)](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/releases) 5 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/blob/main/LICENSE) 6 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) 7 | [![GitHub issues](https://img.shields.io/github/issues/khoih-prog/ESP32_New_TimerInterrupt.svg)](http://github.com/khoih-prog/ESP32_New_TimerInterrupt/issues) 8 | 9 | 10 | Donate to my libraries using BuyMeACoffee 11 | 12 | 13 | --- 14 | --- 15 | 16 | ## Table of Contents 17 | 18 | * [Changelog](#changelog) 19 | * [Releases v1.5.0](#releases-v150) 20 | * [Releases v1.4.0](#releases-v140) 21 | * [Releases v1.3.0](#releases-v130) 22 | * [Releases v1.2.1](#releases-v121) 23 | * [Releases v1.2.0](#releases-v120) 24 | * [Releases v1.1.0](#releases-v110) 25 | * [Releases v1.0.1](#releases-v101) 26 | * [Releases v1.0.0](#releases-v100) 27 | 28 | 29 | --- 30 | --- 31 | 32 | ## Changelog 33 | 34 | ### Releases v1.5.0 35 | 36 | 1. Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3. Check [Error in the value defined by TIMER0_INTERVAL_MS #28](https://github.com/khoih-prog/ESP32_New_TimerInterrupt/issues/28) 37 | 2. Modify examples to avoid using `LED_BUILTIN` / `GPIO2` as it can cause crash in some boards, such as `ESP32_C3` 38 | 3. Use `allman astyle` and add `utils` 39 | 40 | ### Releases v1.4.0 41 | 42 | 1. Suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 43 | 2. Add support to 44 | - ESP32_S2 : ESP32S2 Native USB, UM FeatherS2 Neo, UM TinyS2, UM RMP, microS2, LOLIN_S2_MINI, LOLIN_S2_PICO, ADAFRUIT_FEATHER_ESP32S2, ADAFRUIT_FEATHER_ESP32S2_TFT, ATMegaZero ESP32-S2, Deneyap Mini, FRANZININHO_WIFI, FRANZININHO_WIFI_MSC 45 | - ESP32_S3 : UM TinyS3, UM PROS3, UM FeatherS3, ESP32_S3_USB_OTG, ESP32S3_CAM_LCD, DFROBOT_FIREBEETLE_2_ESP32S3, ADAFRUIT_FEATHER_ESP32S3_TFT 46 | - ESP32_C3 : LOLIN_C3_MINI, DFROBOT_BEETLE_ESP32_C3, ADAFRUIT_QTPY_ESP32C3, AirM2M_CORE_ESP32C3, XIAO_ESP32C3 47 | 48 | ### Releases v1.3.0 49 | 50 | 1. Suppress errors and warnings for new ESP32 core v2.0.4+ 51 | 52 | ### Releases v1.2.1 53 | 54 | 1. Add support to new Adafruit boards such as QTPY_ESP32S2, FEATHER_ESP32S3_NOPSRAM and QTPY_ESP32S3_NOPSRAM 55 | 56 | ### Releases v1.2.0 57 | 58 | 1. Add support to new `ESP32-S3` 59 | 2. Optimize library code by using `reference-passing` instead of `value-passing` 60 | 61 | 62 | ### Releases v1.1.0 63 | 64 | 1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories 65 | 66 | ### Releases v1.0.1 67 | 68 | 1. Avoid using `PIN_D1 (GPIO1)` in your code due to issue with core v2.0.0 and v2.0.1. Check [ESP32 Core v2.0.1 / 2.0.1 RC1 crashes if using pinMode with GPIO1 #5868](https://github.com/espressif/arduino-esp32/issues/5868). Only OK with core v1.0.6- 69 | 2. Don't use `float` in `ISR` due to issue with core v2.0.0 and v2.0.1. Only OK with core v1.0.6-. 70 | 3. Delete Blynk-related examples 71 | 4. Add changelog.md 72 | 73 | ### Releases v1.0.0 74 | 75 | 1. Initial coding for ESP32, ESP32_S2, ESP32_C3 boards with [ESP32 core v2.0.0-rc1+](https://github.com/espressif/arduino-esp32/releases/tag/2.0.0-rc1) 76 | 77 | 78 | -------------------------------------------------------------------------------- /examples/Argument_None/Argument_None.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Argument_None.ino 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | *****************************************************************************************************************************/ 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 | 36 | #if !defined( ESP32 ) 37 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 38 | #endif 39 | 40 | // These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h" 41 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 42 | #define _TIMERINTERRUPT_LOGLEVEL_ 4 43 | 44 | #include "ESP32_New_TimerInterrupt.h" 45 | 46 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 47 | // Don't use PIN_D2 with ESP32_C3 (crash) 48 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32 49 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 50 | 51 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 52 | // and you can't use float calculation inside ISR 53 | // Only OK in core v1.0.6- 54 | bool IRAM_ATTR TimerHandler0(void * timerNo) 55 | { 56 | static bool toggle0 = false; 57 | 58 | //timer interrupt toggles pin PIN_D19 59 | digitalWrite(19, toggle0); 60 | toggle0 = !toggle0; 61 | 62 | return true; 63 | } 64 | 65 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 66 | // and you can't use float calculation inside ISR 67 | // Only OK in core v1.0.6- 68 | bool IRAM_ATTR TimerHandler1(void * timerNo) 69 | { 70 | ///////////////////////////////////////////////////////// 71 | 72 | static bool toggle1 = false; 73 | 74 | //timer interrupt toggles outputPin 75 | digitalWrite(PIN_D3, toggle1); 76 | toggle1 = !toggle1; 77 | 78 | return true; 79 | } 80 | 81 | #define TIMER0_INTERVAL_MS 100 //1000 82 | 83 | #define TIMER1_INTERVAL_MS 5000 84 | 85 | // Init ESP32 timer 0 and 1 86 | ESP32Timer ITimer0(0); 87 | ESP32Timer ITimer1(1); 88 | 89 | void setup() 90 | { 91 | pinMode(PIN_D19, OUTPUT); 92 | pinMode(PIN_D3, OUTPUT); 93 | 94 | Serial.begin(115200); 95 | 96 | while (!Serial && millis() < 5000); 97 | 98 | delay(500); 99 | 100 | Serial.print(F("\nStarting Argument_None on ")); 101 | Serial.println(ARDUINO_BOARD); 102 | Serial.println(ESP32_NEW_TIMERINTERRUPT_VERSION); 103 | Serial.print(F("CPU Frequency = ")); 104 | Serial.print(F_CPU / 1000000); 105 | Serial.println(F(" MHz")); 106 | 107 | // Using ESP32 => 80 / 160 / 240MHz CPU clock , 108 | // For 64-bit timer counter 109 | // For 16-bit timer prescaler up to 1024 110 | 111 | // Interval in microsecs 112 | if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 1000, TimerHandler0)) 113 | //if (ITimer0.attachInterrupt(1, TimerHandler0)) 114 | { 115 | Serial.print(F("Starting ITimer0 OK, millis() = ")); 116 | Serial.println(millis()); 117 | } 118 | else 119 | Serial.println(F("Can't set ITimer0. Select another Timer, freq. or timer")); 120 | 121 | 122 | // Interval in microsecs 123 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS * 1000, TimerHandler1)) 124 | //if (ITimer1.attachInterrupt(2, TimerHandler1)) 125 | { 126 | Serial.print(F("Starting ITimer1 OK, millis() = ")); 127 | Serial.println(millis()); 128 | } 129 | else 130 | Serial.println(F("Can't set ITimer1. Select another Timer, freq. or timer")); 131 | } 132 | 133 | void loop() 134 | { 135 | delay(1); 136 | } 137 | -------------------------------------------------------------------------------- /examples/Change_Interval/Change_Interval.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Change_Interval.ino 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | *****************************************************************************************************************************/ 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 | 36 | #if !defined( ESP32 ) 37 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 38 | #endif 39 | 40 | // These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h" 41 | #define _TIMERINTERRUPT_LOGLEVEL_ 1 42 | 43 | #include "ESP32_New_TimerInterrupt.h" 44 | 45 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 46 | // Don't use PIN_D2 with ESP32_C3 (crash) 47 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32 48 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 49 | 50 | volatile uint32_t Timer0Count = 0; 51 | volatile uint32_t Timer1Count = 0; 52 | 53 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 54 | // and you can't use float calculation inside ISR 55 | // Only OK in core v1.0.6- 56 | bool IRAM_ATTR TimerHandler0(void * timerNo) 57 | { 58 | static bool toggle0 = false; 59 | 60 | // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR 61 | Timer0Count++; 62 | 63 | //timer interrupt toggles pin PIN_D19 64 | digitalWrite(PIN_D19, toggle0); 65 | toggle0 = !toggle0; 66 | 67 | return true; 68 | } 69 | 70 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 71 | // and you can't use float calculation inside ISR 72 | // Only OK in core v1.0.6- 73 | bool IRAM_ATTR TimerHandler1(void * timerNo) 74 | { 75 | static bool toggle1 = false; 76 | 77 | // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR 78 | Timer1Count++; 79 | 80 | //timer interrupt toggles PIN_D3 81 | digitalWrite(PIN_D3, toggle1); 82 | toggle1 = !toggle1; 83 | 84 | return true; 85 | } 86 | 87 | void printResult(uint32_t currTime) 88 | { 89 | Serial.print(F("Time = ")); 90 | Serial.print(currTime); 91 | Serial.print(F(", Timer0Count = ")); 92 | Serial.print(Timer0Count); 93 | Serial.print(F(", Timer1Count = ")); 94 | Serial.println(Timer1Count); 95 | } 96 | 97 | #define TIMER0_INTERVAL_MS 2000 98 | 99 | #define TIMER1_INTERVAL_MS 5000 100 | 101 | // Init ESP32 timer 0 102 | ESP32Timer ITimer0(0); 103 | ESP32Timer ITimer1(1); 104 | 105 | void setup() 106 | { 107 | pinMode(PIN_D19, OUTPUT); 108 | pinMode(PIN_D3, OUTPUT); 109 | 110 | Serial.begin(115200); 111 | 112 | while (!Serial && millis() < 5000); 113 | 114 | delay(500); 115 | 116 | Serial.print(F("\nStarting Change_Interval on ")); 117 | Serial.println(ARDUINO_BOARD); 118 | Serial.println(ESP32_NEW_TIMERINTERRUPT_VERSION); 119 | Serial.print(F("CPU Frequency = ")); 120 | Serial.print(F_CPU / 1000000); 121 | Serial.println(F(" MHz")); 122 | 123 | // Interval in microsecs 124 | if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 1000, TimerHandler0)) 125 | { 126 | Serial.print(F("Starting ITimer0 OK, millis() = ")); 127 | Serial.println(millis()); 128 | } 129 | else 130 | Serial.println(F("Can't set ITimer0. Select another freq. or timer")); 131 | 132 | // Interval in microsecs 133 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS * 1000, TimerHandler1)) 134 | { 135 | Serial.print(F("Starting ITimer1 OK, millis() = ")); 136 | Serial.println(millis()); 137 | } 138 | else 139 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 140 | } 141 | 142 | #define CHECK_INTERVAL_MS 10000L 143 | #define CHANGE_INTERVAL_MS 20000L 144 | 145 | void loop() 146 | { 147 | static uint32_t lastTime = 0; 148 | static uint32_t lastChangeTime = 0; 149 | static uint32_t currTime; 150 | static uint32_t multFactor = 0; 151 | 152 | currTime = millis(); 153 | 154 | if (currTime - lastTime > CHECK_INTERVAL_MS) 155 | { 156 | printResult(currTime); 157 | lastTime = currTime; 158 | 159 | if (currTime - lastChangeTime > CHANGE_INTERVAL_MS) 160 | { 161 | //setInterval(unsigned long interval, timerCallback callback) 162 | multFactor = (multFactor + 1) % 2; 163 | 164 | ITimer0.setInterval(TIMER0_INTERVAL_MS * 1000 * (multFactor + 1), TimerHandler0); 165 | ITimer1.setInterval(TIMER1_INTERVAL_MS * 1000 * (multFactor + 1), TimerHandler1); 166 | 167 | Serial.print(F("Changing Interval, Timer0 = ")); 168 | Serial.print(TIMER0_INTERVAL_MS * (multFactor + 1)); 169 | Serial.print(F(", Timer1 = ")); 170 | Serial.println(TIMER1_INTERVAL_MS * (multFactor + 1)); 171 | 172 | lastChangeTime = currTime; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_16_Timers_Array.ino 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | *****************************************************************************************************************************/ 23 | /* 24 | Notes: 25 | Special design is necessary to share data between interrupt code and the rest of your program. 26 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 27 | variable can not spontaneously change. Because your function may change variables while your program is using them, 28 | the compiler needs this hint. But volatile alone is often not enough. 29 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 30 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 31 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 32 | or the entire sequence of your code which accesses the data. 33 | 34 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms 35 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor 36 | Asssuming LOW is active. 37 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing 38 | If the time between active state is less than 8ms => consider noise. 39 | RPM = 60000 / (rotation time in ms) 40 | 41 | We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state 42 | 43 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 44 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 45 | and Blynk services. You can also have many (up to 16) timers to use. 46 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 47 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 48 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 49 | written 50 | */ 51 | 52 | #if !defined( ESP32 ) 53 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 54 | #endif 55 | 56 | // These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h" 57 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 58 | 59 | #include "ESP32_New_TimerInterrupt.h" 60 | #include "ESP32_New_ISR_Timer.h" 61 | 62 | #include // https://github.com/jfturcot/SimpleTimer 63 | 64 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 65 | 66 | #ifndef LED_BLUE 67 | #define LED_BLUE 25 68 | #endif 69 | 70 | #ifndef LED_RED 71 | #define LED_RED 27 72 | #endif 73 | 74 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 75 | // Don't use PIN_D2 with ESP32_C3 (crash) 76 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32 77 | 78 | #define HW_TIMER_INTERVAL_MS 1L 79 | 80 | volatile uint32_t startMillis = 0; 81 | 82 | // Init ESP32 timer 1 83 | ESP32Timer ITimer(1); 84 | 85 | // Init ESP32_ISR_Timer 86 | ESP32_ISR_Timer ISR_Timer; 87 | 88 | #define LED_TOGGLE_INTERVAL_MS 2000L 89 | 90 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 91 | // and you can't use float calculation inside ISR 92 | // Only OK in core v1.0.6- 93 | bool IRAM_ATTR TimerHandler(void * timerNo) 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(PIN_D19, OUTPUT); 110 | } 111 | 112 | //timer interrupt toggles pin PIN_D19 113 | digitalWrite(PIN_D19, toggle); 114 | toggle = !toggle; 115 | } 116 | 117 | return true; 118 | } 119 | 120 | #define NUMBER_ISR_TIMERS 16 121 | 122 | // You can assign any interval for any timer here, in milliseconds 123 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] = 124 | { 125 | 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L, 126 | 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L 127 | }; 128 | 129 | typedef void (*irqCallback) (); 130 | 131 | 132 | // In ESP32, avoid doing something fancy in ISR, for example complex Serial.print with String() argument 133 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment 134 | // Or you can get this run-time error / crash 135 | void doingSomething0() 136 | { 137 | } 138 | 139 | void doingSomething1() 140 | { 141 | } 142 | 143 | void doingSomething2() 144 | { 145 | } 146 | 147 | void doingSomething3() 148 | { 149 | } 150 | 151 | void doingSomething4() 152 | { 153 | } 154 | 155 | void doingSomething5() 156 | { 157 | } 158 | 159 | void doingSomething6() 160 | { 161 | } 162 | 163 | void doingSomething7() 164 | { 165 | } 166 | 167 | void doingSomething8() 168 | { 169 | } 170 | 171 | void doingSomething9() 172 | { 173 | } 174 | 175 | void doingSomething10() 176 | { 177 | } 178 | 179 | void doingSomething11() 180 | { 181 | } 182 | 183 | void doingSomething12() 184 | { 185 | } 186 | 187 | void doingSomething13() 188 | { 189 | } 190 | 191 | void doingSomething14() 192 | { 193 | } 194 | 195 | void doingSomething15() 196 | { 197 | } 198 | 199 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = 200 | { 201 | doingSomething0, doingSomething1, doingSomething2, doingSomething3, 202 | doingSomething4, doingSomething5, doingSomething6, doingSomething7, 203 | doingSomething8, doingSomething9, doingSomething10, doingSomething11, 204 | doingSomething12, doingSomething13, doingSomething14, doingSomething15 205 | }; 206 | 207 | //////////////////////////////////////////////// 208 | 209 | 210 | #define SIMPLE_TIMER_MS 2000L 211 | 212 | // Init SimpleTimer 213 | SimpleTimer simpleTimer; 214 | 215 | // Here is software Timer, you can do somewhat fancy stuffs without many issues. 216 | // But always avoid 217 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead 218 | // 2. Very long "do", "while", "for" loops without predetermined exit time. 219 | void simpleTimerDoingSomething2s() 220 | { 221 | static unsigned long previousMillis = startMillis; 222 | 223 | Serial.print(F("simpleTimerDoingSomething2s: Delta programmed ms = ")); 224 | Serial.print(SIMPLE_TIMER_MS); 225 | Serial.print(F(", actual = ")); 226 | Serial.println(millis() - previousMillis); 227 | 228 | previousMillis = millis(); 229 | } 230 | 231 | void setup() 232 | { 233 | Serial.begin(115200); 234 | 235 | while (!Serial && millis() < 5000); 236 | 237 | delay(500); 238 | 239 | Serial.print(F("\nStarting ISR_16_Timers_Array on ")); 240 | Serial.println(ARDUINO_BOARD); 241 | Serial.println(ESP32_NEW_TIMERINTERRUPT_VERSION); 242 | Serial.print(F("CPU Frequency = ")); 243 | Serial.print(F_CPU / 1000000); 244 | Serial.println(F(" MHz")); 245 | 246 | // Interval in microsecs 247 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler)) 248 | { 249 | startMillis = millis(); 250 | Serial.print(F("Starting ITimer OK, millis() = ")); 251 | Serial.println(startMillis); 252 | } 253 | else 254 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 255 | 256 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 257 | // You can use up to 16 timer for each ISR_Timer 258 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 259 | { 260 | ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); 261 | } 262 | 263 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. 264 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); 265 | } 266 | 267 | #define BLOCKING_TIME_MS 10000L 268 | 269 | void loop() 270 | { 271 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer 272 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer 273 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) 274 | // While that of ISR_Timer is still prefect. 275 | delay(BLOCKING_TIME_MS); 276 | 277 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary 278 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. 279 | simpleTimer.run(); 280 | } 281 | -------------------------------------------------------------------------------- /examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_16_Timers_Array_Complex.ino 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | *****************************************************************************************************************************/ 23 | /* 24 | Notes: 25 | Special design is necessary to share data between interrupt code and the rest of your program. 26 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 27 | variable can not spontaneously change. Because your function may change variables while your program is using them, 28 | the compiler needs this hint. But volatile alone is often not enough. 29 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 30 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 31 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 32 | or the entire sequence of your code which accesses the data. 33 | 34 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 35 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 36 | and Blynk services. You can also have many (up to 16) timers to use. 37 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 38 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 39 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 40 | written 41 | */ 42 | 43 | #if !defined( ESP32 ) 44 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 45 | #endif 46 | 47 | // These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h" 48 | #define _TIMERINTERRUPT_LOGLEVEL_ 4 49 | 50 | #include "ESP32_New_TimerInterrupt.h" 51 | #include "ESP32_New_ISR_Timer.h" 52 | 53 | #include // https://github.com/jfturcot/SimpleTimer 54 | 55 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 56 | 57 | #ifndef LED_BLUE 58 | #define LED_BLUE 25 59 | #endif 60 | 61 | #ifndef LED_RED 62 | #define LED_RED 27 63 | #endif 64 | 65 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 66 | // Don't use PIN_D2 with ESP32_C3 (crash) 67 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32 68 | 69 | #define HW_TIMER_INTERVAL_US 10000L 70 | 71 | volatile uint32_t startMillis = 0; 72 | 73 | // Init ESP32 timer 1 74 | ESP32Timer ITimer(1); 75 | 76 | // Init ESP32_ISR_Timer 77 | ESP32_ISR_Timer ISR_Timer; 78 | 79 | #define LED_TOGGLE_INTERVAL_MS 2000L 80 | 81 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 82 | // and you can't use float calculation inside ISR 83 | // Only OK in core v1.0.6- 84 | bool IRAM_ATTR TimerHandler(void * timerNo) 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 PIN_D19 97 | digitalWrite(PIN_D19, toggle); 98 | toggle = !toggle; 99 | } 100 | 101 | return true; 102 | } 103 | 104 | ///////////////////////////////////////////////// 105 | 106 | #define NUMBER_ISR_TIMERS 16 107 | 108 | typedef void (*irqCallback) (); 109 | 110 | ///////////////////////////////////////////////// 111 | 112 | #define USE_COMPLEX_STRUCT true 113 | 114 | #if USE_COMPLEX_STRUCT 115 | 116 | typedef struct 117 | { 118 | irqCallback irqCallbackFunc; 119 | uint32_t TimerInterval; 120 | unsigned long deltaMillis; 121 | unsigned long previousMillis; 122 | } ISRTimerData; 123 | 124 | // In NRF52, avoid doing something fancy in ISR, for example Serial.print() 125 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment 126 | // Or you can get this run-time error / crash 127 | 128 | void doingSomething(int index); 129 | 130 | #else 131 | 132 | volatile unsigned long deltaMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 133 | volatile unsigned long previousMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 134 | 135 | // You can assign any interval for any timer here, in milliseconds 136 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] = 137 | { 138 | 5000L, 10000L, 15000L, 20000L, 25000L, 30000L, 35000L, 40000L, 139 | 45000L, 50000L, 55000L, 60000L, 65000L, 70000L, 75000L, 80000L 140 | }; 141 | 142 | void doingSomething(int index) 143 | { 144 | unsigned long currentMillis = millis(); 145 | 146 | deltaMillis[index] = currentMillis - previousMillis[index]; 147 | previousMillis[index] = currentMillis; 148 | } 149 | 150 | #endif 151 | 152 | //////////////////////////////////// 153 | // Shared 154 | //////////////////////////////////// 155 | 156 | void doingSomething0() 157 | { 158 | doingSomething(0); 159 | } 160 | 161 | void doingSomething1() 162 | { 163 | doingSomething(1); 164 | } 165 | 166 | void doingSomething2() 167 | { 168 | doingSomething(2); 169 | } 170 | 171 | void doingSomething3() 172 | { 173 | doingSomething(3); 174 | } 175 | 176 | void doingSomething4() 177 | { 178 | doingSomething(4); 179 | } 180 | 181 | void doingSomething5() 182 | { 183 | doingSomething(5); 184 | } 185 | 186 | void doingSomething6() 187 | { 188 | doingSomething(6); 189 | } 190 | 191 | void doingSomething7() 192 | { 193 | doingSomething(7); 194 | } 195 | 196 | void doingSomething8() 197 | { 198 | doingSomething(8); 199 | } 200 | 201 | void doingSomething9() 202 | { 203 | doingSomething(9); 204 | } 205 | 206 | void doingSomething10() 207 | { 208 | doingSomething(10); 209 | } 210 | 211 | void doingSomething11() 212 | { 213 | doingSomething(11); 214 | } 215 | 216 | void doingSomething12() 217 | { 218 | doingSomething(12); 219 | } 220 | 221 | void doingSomething13() 222 | { 223 | doingSomething(13); 224 | } 225 | 226 | void doingSomething14() 227 | { 228 | doingSomething(14); 229 | } 230 | 231 | void doingSomething15() 232 | { 233 | doingSomething(15); 234 | } 235 | 236 | #if USE_COMPLEX_STRUCT 237 | 238 | ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] = 239 | { 240 | //irqCallbackFunc, TimerInterval, deltaMillis, previousMillis 241 | { doingSomething0, 5000L, 0, 0 }, 242 | { doingSomething1, 10000L, 0, 0 }, 243 | { doingSomething2, 15000L, 0, 0 }, 244 | { doingSomething3, 20000L, 0, 0 }, 245 | { doingSomething4, 25000L, 0, 0 }, 246 | { doingSomething5, 30000L, 0, 0 }, 247 | { doingSomething6, 35000L, 0, 0 }, 248 | { doingSomething7, 40000L, 0, 0 }, 249 | { doingSomething8, 45000L, 0, 0 }, 250 | { doingSomething9, 50000L, 0, 0 }, 251 | { doingSomething10, 55000L, 0, 0 }, 252 | { doingSomething11, 60000L, 0, 0 }, 253 | { doingSomething12, 65000L, 0, 0 }, 254 | { doingSomething13, 70000L, 0, 0 }, 255 | { doingSomething14, 75000L, 0, 0 }, 256 | { doingSomething15, 80000L, 0, 0 } 257 | }; 258 | 259 | void doingSomething(int index) 260 | { 261 | unsigned long currentMillis = millis(); 262 | 263 | curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis; 264 | curISRTimerData[index].previousMillis = currentMillis; 265 | } 266 | 267 | #else 268 | 269 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = 270 | { 271 | doingSomething0, doingSomething1, doingSomething2, doingSomething3, 272 | doingSomething4, doingSomething5, doingSomething6, doingSomething7, 273 | doingSomething8, doingSomething9, doingSomething10, doingSomething11, 274 | doingSomething12, doingSomething13, doingSomething14, doingSomething15 275 | }; 276 | 277 | #endif 278 | /////////////////////////////////////////// 279 | 280 | #define SIMPLE_TIMER_MS 2000L 281 | 282 | // Init SimpleTimer 283 | SimpleTimer simpleTimer; 284 | 285 | // Here is software Timer, you can do somewhat fancy stuffs without many issues. 286 | // But always avoid 287 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead 288 | // 2. Very long "do", "while", "for" loops without predetermined exit time. 289 | void simpleTimerDoingSomething2s() 290 | { 291 | static unsigned long previousMillis = startMillis; 292 | 293 | unsigned long currMillis = millis(); 294 | 295 | Serial.print(F("SimpleTimer : ")); 296 | Serial.print(SIMPLE_TIMER_MS / 1000); 297 | Serial.print(F(", ms : ")); 298 | Serial.print(currMillis); 299 | Serial.print(F(", Dms : ")); 300 | Serial.println(currMillis - previousMillis); 301 | 302 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 303 | { 304 | #if USE_COMPLEX_STRUCT 305 | Serial.print(F("Timer : ")); 306 | Serial.print(i); 307 | Serial.print(F(", programmed : ")); 308 | Serial.print(curISRTimerData[i].TimerInterval); 309 | Serial.print(F(", actual : ")); 310 | Serial.println(curISRTimerData[i].deltaMillis); 311 | #else 312 | Serial.print(F("Timer : ")); 313 | Serial.print(i); 314 | Serial.print(F(", programmed : ")); 315 | Serial.print(TimerInterval[i]); 316 | Serial.print(F(", actual : ")); 317 | Serial.println(deltaMillis[i]); 318 | #endif 319 | } 320 | 321 | previousMillis = currMillis; 322 | } 323 | 324 | void setup() 325 | { 326 | pinMode(PIN_D19, OUTPUT); 327 | 328 | Serial.begin(115200); 329 | 330 | while (!Serial && millis() < 5000); 331 | 332 | delay(500); 333 | 334 | Serial.print(F("\nStarting ISR_16_Timers_Array_Complex on ")); 335 | Serial.println(ARDUINO_BOARD); 336 | Serial.println(ESP32_NEW_TIMERINTERRUPT_VERSION); 337 | Serial.print(F("CPU Frequency = ")); 338 | Serial.print(F_CPU / 1000000); 339 | Serial.println(F(" MHz")); 340 | 341 | // Interval in microsecs 342 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler)) 343 | { 344 | startMillis = millis(); 345 | Serial.print(F("Starting ITimer OK, millis() = ")); 346 | Serial.println(startMillis); 347 | } 348 | else 349 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 350 | 351 | startMillis = millis(); 352 | 353 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 354 | // You can use up to 16 timer for each ISR_Timer 355 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 356 | { 357 | #if USE_COMPLEX_STRUCT 358 | curISRTimerData[i].previousMillis = startMillis; 359 | ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc); 360 | #else 361 | previousMillis[i] = millis(); 362 | ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); 363 | #endif 364 | } 365 | 366 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. 367 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); 368 | } 369 | 370 | #define BLOCKING_TIME_MS 10000L 371 | 372 | void loop() 373 | { 374 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer 375 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer 376 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) 377 | // While that of ISR_Timer is still prefect. 378 | delay(BLOCKING_TIME_MS); 379 | 380 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary 381 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. 382 | simpleTimer.run(); 383 | } 384 | -------------------------------------------------------------------------------- /examples/RPM_Measure/RPM_Measure.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | RPM_Measure.ino 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | 23 | Based on SimpleTimer - A timer library for Arduino. 24 | Author: mromani@ottotecnica.com 25 | Copyright (c) 2010 OTTOTECNICA Italy 26 | *****************************************************************************************************************************/ 27 | /* 28 | Notes: 29 | Special design is necessary to share data between interrupt code and the rest of your program. 30 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 31 | variable can not spontaneously change. Because your function may change variables while your program is using them, 32 | the compiler needs this hint. But volatile alone is often not enough. 33 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 34 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 35 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 36 | or the entire sequence of your code which accesses the data. 37 | 38 | RPM Measuring uses high frequency hardware timer 1000Hz == 1ms) to measure the time from of one rotation, in ms 39 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor 40 | Asssuming LOW is active. 41 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing 42 | If the time between active state is less than 8ms => consider noise. 43 | RPM = 60000 / (rotation time in ms) 44 | 45 | You can also use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state 46 | */ 47 | 48 | #if !defined(ESP32) 49 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 50 | #elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \ 51 | defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) ) 52 | //#error ESP32_S3 is not supported yet 53 | #endif 54 | 55 | // These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h" 56 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 57 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 58 | 59 | #include "ESP32_New_TimerInterrupt.h" 60 | 61 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 62 | // Don't use PIN_D2 with ESP32_C3 (crash) 63 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32 64 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 65 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 66 | 67 | unsigned int SWPin = PIN_D4; 68 | 69 | #define TIMER0_INTERVAL_MS 1 70 | #define DEBOUNCING_INTERVAL_MS 80 71 | 72 | #define LOCAL_DEBUG 1 73 | 74 | // Init ESP32 timer 0 75 | ESP32Timer ITimer0(0); 76 | 77 | volatile unsigned long rotationTime = 0; 78 | 79 | // Not using float => using RPM = 100 * real RPM 80 | float RPM = 0; 81 | float avgRPM = 0; 82 | //uint32_t RPM = 0; 83 | //uint32_t avgRPM = 0; 84 | 85 | volatile int debounceCounter; 86 | 87 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 88 | // and you can't use float calculation inside ISR 89 | // Only OK in core v1.0.6- 90 | bool IRAM_ATTR TimerHandler0(void * timerNo) 91 | { 92 | if ( !digitalRead(SWPin) && (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER0_INTERVAL_MS ) ) 93 | { 94 | //min time between pulses has passed 95 | // Using float calculation / vars in core v2.0.0 and core v2.0.1 will cause crash 96 | // Not using float => using RPM = 100 * real RPM 97 | RPM = ( 6000000 / ( rotationTime * TIMER0_INTERVAL_MS ) ); 98 | 99 | avgRPM = ( 2 * avgRPM + RPM) / 3; 100 | 101 | rotationTime = 0; 102 | debounceCounter = 0; 103 | } 104 | else 105 | { 106 | debounceCounter++; 107 | } 108 | 109 | //if (rotationTime >= 5000) 110 | if (rotationTime >= 1000) 111 | { 112 | // If idle, set RPM to 0, don't increase rotationTime 113 | RPM = 0; 114 | 115 | avgRPM = ( avgRPM + 3 * RPM) / 4; 116 | 117 | rotationTime = 0; 118 | } 119 | else 120 | { 121 | rotationTime++; 122 | } 123 | 124 | return true; 125 | } 126 | 127 | void setup() 128 | { 129 | pinMode(SWPin, INPUT_PULLUP); 130 | 131 | Serial.begin(115200); 132 | 133 | while (!Serial && millis() < 5000); 134 | 135 | delay(500); 136 | 137 | Serial.print(F("\nStarting RPM_Measure on ")); 138 | Serial.println(ARDUINO_BOARD); 139 | Serial.println(ESP32_NEW_TIMERINTERRUPT_VERSION); 140 | Serial.print(F("CPU Frequency = ")); 141 | Serial.print(F_CPU / 1000000); 142 | Serial.println(F(" MHz")); 143 | 144 | // Using ESP32 => 80 / 160 / 240MHz CPU clock , 145 | // For 64-bit timer counter 146 | // For 16-bit timer prescaler up to 1024 147 | 148 | // Interval in microsecs 149 | if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 1000, TimerHandler0)) 150 | { 151 | Serial.print(F("Starting ITimer0 OK, millis() = ")); 152 | Serial.println(millis()); 153 | } 154 | else 155 | Serial.println(F("Can't set ITimer0. Select another freq. or timer")); 156 | 157 | Serial.flush(); 158 | } 159 | 160 | void loop() 161 | { 162 | if (avgRPM > 0) 163 | { 164 | Serial.print(F("RPM = ")); 165 | Serial.print((float) RPM / 100.f); 166 | Serial.print(F(", avgRPM = ")); 167 | Serial.println((float) avgRPM / 100.f); 168 | } 169 | 170 | delay(1000); 171 | } 172 | -------------------------------------------------------------------------------- /examples/SwitchDebounce/SwitchDebounce.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | SwitchDebounce.ino 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | *****************************************************************************************************************************/ 23 | /* 24 | Notes: 25 | Special design is necessary to share data between interrupt code and the rest of your program. 26 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 27 | variable can not spontaneously change. Because your function may change variables while your program is using them, 28 | the compiler needs this hint. But volatile alone is often not enough. 29 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 30 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 31 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 32 | or the entire sequence of your code which accesses the data. 33 | 34 | Switch Debouncing uses high frequency hardware timer 50Hz == 20ms) to measure the time from the SW is pressed, 35 | debouncing time is 100ms => SW is considered pressed if timer count is > 5, then call / flag SW is pressed 36 | When the SW is released, timer will count (debounce) until more than 50ms until consider SW is released. 37 | We can set to flag or call a function whenever SW is pressed more than certain predetermined time, even before 38 | SW is released. 39 | */ 40 | 41 | #if !defined( ESP32 ) 42 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 43 | #endif 44 | 45 | //These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h" 46 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. 47 | #define TIMER_INTERRUPT_DEBUG 1 48 | 49 | #include "ESP32_New_TimerInterrupt.h" 50 | 51 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 52 | // Don't use PIN_D2 with ESP32_C3 (crash) 53 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32 54 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 55 | 56 | unsigned int SWPin = PIN_D4; 57 | 58 | #define TIMER1_INTERVAL_MS 20 59 | #define DEBOUNCING_INTERVAL_MS 100 60 | #define LONG_PRESS_INTERVAL_MS 5000 61 | 62 | // Init ESP32 timer 1 63 | ESP32Timer ITimer1(1); 64 | 65 | volatile bool SWPressed = false; 66 | volatile bool SWLongPressed = false; 67 | 68 | volatile uint64_t lastSWPressedTime = 0; 69 | volatile uint64_t lastSWLongPressedTime = 0; 70 | 71 | volatile bool lastSWPressedNoted = true; 72 | volatile bool lastSWLongPressedNoted = true; 73 | 74 | void IRAM_ATTR lastSWPressedMS() 75 | { 76 | lastSWPressedTime = millis(); 77 | lastSWPressedNoted = false; 78 | } 79 | 80 | void IRAM_ATTR lastSWLongPressedMS() 81 | { 82 | lastSWLongPressedTime = millis(); 83 | lastSWLongPressedNoted = false; 84 | } 85 | 86 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 87 | // and you can't use float calculation inside ISR 88 | // Only OK in core v1.0.6- 89 | bool IRAM_ATTR TimerHandler1(void * timerNo) 90 | { 91 | static unsigned int debounceCountSWPressed = 0; 92 | static unsigned int debounceCountSWReleased = 0; 93 | 94 | if ( (!digitalRead(SWPin)) ) 95 | { 96 | // Start debouncing counting debounceCountSWPressed and clear debounceCountSWReleased 97 | debounceCountSWReleased = 0; 98 | 99 | if (++debounceCountSWPressed >= DEBOUNCING_INTERVAL_MS / TIMER1_INTERVAL_MS) 100 | { 101 | // Call and flag SWPressed 102 | if (!SWPressed) 103 | { 104 | SWPressed = true; 105 | // Do something for SWPressed here in ISR 106 | // But it's better to use outside software timer to do your job instead of inside ISR 107 | //Your_Response_To_Press(); 108 | lastSWPressedMS(); 109 | } 110 | 111 | if (debounceCountSWPressed >= LONG_PRESS_INTERVAL_MS / TIMER1_INTERVAL_MS) 112 | { 113 | // Call and flag SWLongPressed 114 | if (!SWLongPressed) 115 | { 116 | SWLongPressed = true; 117 | // Do something for SWLongPressed here in ISR 118 | // But it's better to use outside software timer to do your job instead of inside ISR 119 | //Your_Response_To_Long_Press(); 120 | lastSWLongPressedMS(); 121 | } 122 | } 123 | } 124 | } 125 | else 126 | { 127 | // Start debouncing counting debounceCountSWReleased and clear debounceCountSWPressed 128 | if ( SWPressed && (++debounceCountSWReleased >= DEBOUNCING_INTERVAL_MS / TIMER1_INTERVAL_MS)) 129 | { 130 | SWPressed = false; 131 | SWLongPressed = false; 132 | 133 | // Do something for !SWPressed here in ISR 134 | // But it's better to use outside software timer to do your job instead of inside ISR 135 | //Your_Response_To_Release(); 136 | 137 | // Call and flag SWPressed 138 | debounceCountSWPressed = 0; 139 | } 140 | } 141 | 142 | return true; 143 | } 144 | 145 | void setup() 146 | { 147 | pinMode(SWPin, INPUT_PULLUP); 148 | 149 | Serial.begin(115200); 150 | 151 | while (!Serial && millis() < 5000); 152 | 153 | delay(500); 154 | 155 | Serial.print(F("\nStarting SwitchDebounce on ")); 156 | Serial.println(ARDUINO_BOARD); 157 | Serial.println(ESP32_NEW_TIMERINTERRUPT_VERSION); 158 | Serial.print(F("CPU Frequency = ")); 159 | Serial.print(F_CPU / 1000000); 160 | Serial.println(F(" MHz")); 161 | 162 | // Using ESP32 => 80 / 160 / 240MHz CPU clock , 163 | // For 64-bit timer counter 164 | // For 16-bit timer prescaler up to 1024 165 | 166 | // Interval in microsecs 167 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS * 1000, TimerHandler1)) 168 | { 169 | Serial.print(F("Starting ITimer1 OK, millis() = ")); 170 | Serial.println(millis()); 171 | } 172 | else 173 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 174 | } 175 | 176 | void loop() 177 | { 178 | if (!lastSWPressedNoted) 179 | { 180 | lastSWPressedNoted = true; 181 | Serial.print(F("lastSWPressed @ millis() = ")); 182 | Serial.println(lastSWPressedTime); 183 | } 184 | 185 | if (!lastSWLongPressedNoted) 186 | { 187 | lastSWLongPressedNoted = true; 188 | Serial.print(F("lastSWLongPressed @ millis() = ")); 189 | Serial.println(lastSWLongPressedTime); 190 | } 191 | 192 | delay(500); 193 | } 194 | -------------------------------------------------------------------------------- /examples/TimerInterruptTest/TimerInterruptTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerInterruptTest.ino 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | *****************************************************************************************************************************/ 23 | /* 24 | Notes: 25 | Special design is necessary to share data between interrupt code and the rest of your program. 26 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 27 | variable can not spontaneously change. Because your function may change variables while your program is using them, 28 | the compiler needs this hint. But volatile alone is often not enough. 29 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 30 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 31 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 32 | or the entire sequence of your code which accesses the data. 33 | */ 34 | 35 | #if !defined( ESP32 ) 36 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 37 | #endif 38 | 39 | // These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h" 40 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 41 | #define _TIMERINTERRUPT_LOGLEVEL_ 4 42 | 43 | #include "ESP32_New_TimerInterrupt.h" 44 | 45 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 46 | // Don't use PIN_D2 with ESP32_C3 (crash) 47 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32 48 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 49 | 50 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 51 | // and you can't use float calculation inside ISR 52 | // Only OK in core v1.0.6- 53 | bool IRAM_ATTR TimerHandler0(void * timerNo) 54 | { 55 | static bool toggle0 = false; 56 | 57 | //timer interrupt toggles pin PIN_D19 58 | digitalWrite(PIN_D19, toggle0); 59 | toggle0 = !toggle0; 60 | 61 | return true; 62 | } 63 | 64 | bool IRAM_ATTR TimerHandler1(void * timerNo) 65 | { 66 | static bool toggle1 = false; 67 | 68 | //timer interrupt toggles outputPin 69 | digitalWrite(PIN_D3, toggle1); 70 | toggle1 = !toggle1; 71 | 72 | return true; 73 | } 74 | 75 | #define TIMER0_INTERVAL_MS 1000 76 | #define TIMER0_DURATION_MS 5000 77 | 78 | #define TIMER1_INTERVAL_MS 3000 79 | #define TIMER1_DURATION_MS 15000 80 | 81 | // Init ESP32 timer 0 and 1 82 | ESP32Timer ITimer0(0); 83 | ESP32Timer ITimer1(1); 84 | 85 | void setup() 86 | { 87 | pinMode(PIN_D19, OUTPUT); 88 | pinMode(PIN_D3, OUTPUT); 89 | 90 | Serial.begin(115200); 91 | 92 | while (!Serial && millis() < 5000); 93 | 94 | delay(500); 95 | 96 | Serial.print(F("\nStarting TimerInterruptTest on ")); 97 | Serial.println(ARDUINO_BOARD); 98 | Serial.println(ESP32_NEW_TIMERINTERRUPT_VERSION); 99 | Serial.print(F("CPU Frequency = ")); 100 | Serial.print(F_CPU / 1000000); 101 | Serial.println(F(" MHz")); 102 | 103 | // Using ESP32 => 80 / 160 / 240MHz CPU clock , 104 | // For 64-bit timer counter 105 | // For 16-bit timer prescaler up to 1024 106 | 107 | // Interval in microsecs 108 | if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 1000, TimerHandler0)) 109 | { 110 | Serial.print(F("Starting ITimer0 OK, millis() = ")); 111 | Serial.println(millis()); 112 | } 113 | else 114 | Serial.println(F("Can't set ITimer0. Select another freq. or timer")); 115 | 116 | // Interval in microsecs 117 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS * 1000, TimerHandler1)) 118 | { 119 | Serial.print(F("Starting ITimer1 OK, millis() = ")); 120 | Serial.println(millis()); 121 | } 122 | else 123 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 124 | 125 | Serial.flush(); 126 | } 127 | 128 | void loop() 129 | { 130 | static unsigned long lastTimer0 = 0; 131 | static unsigned long lastTimer1 = 0; 132 | 133 | static bool timer0Stopped = false; 134 | static bool timer1Stopped = false; 135 | 136 | if (millis() - lastTimer0 > TIMER0_DURATION_MS) 137 | { 138 | lastTimer0 = millis(); 139 | 140 | if (timer0Stopped) 141 | { 142 | Serial.print(F("Start ITimer0, millis() = ")); 143 | Serial.println(millis()); 144 | ITimer0.restartTimer(); 145 | } 146 | else 147 | { 148 | Serial.print(F("Stop ITimer0, millis() = ")); 149 | Serial.println(millis()); 150 | ITimer0.stopTimer(); 151 | } 152 | 153 | timer0Stopped = !timer0Stopped; 154 | } 155 | 156 | if (millis() - lastTimer1 > TIMER1_DURATION_MS) 157 | { 158 | lastTimer1 = millis(); 159 | 160 | if (timer1Stopped) 161 | { 162 | Serial.print(F("Start ITimer1, millis() = ")); 163 | Serial.println(millis()); 164 | ITimer1.restartTimer(); 165 | } 166 | else 167 | { 168 | Serial.print(F("Stop ITimer1, millis() = ")); 169 | Serial.println(millis()); 170 | ITimer1.stopTimer(); 171 | } 172 | 173 | timer1Stopped = !timer1Stopped; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Datatypes (KEYWORD1) 3 | ####################################### 4 | 5 | ESP32TimerInterrupt KEYWORD1 6 | ESP32Timer KEYWORD1 7 | ESP32_ISRTimer KEYWORD1 8 | 9 | ####################################### 10 | # Methods and Functions (KEYWORD2) 11 | ####################################### 12 | 13 | setFrequency KEYWORD2 14 | setInterval KEYWORD2 15 | attachInterrupt KEYWORD2 16 | attachInterruptInterval KEYWORD2 17 | detachInterrupt KEYWORD2 18 | disableTimer KEYWORD2 19 | reattachInterrupt KEYWORD2 20 | enableTimer KEYWORD2 21 | stopTimer KEYWORD2 22 | restartTimer KEYWORD2 23 | init KEYWORD2 24 | run KEYWORD2 25 | setTimeout KEYWORD2 26 | setTimer KEYWORD2 27 | changeInterval KEYWORD2 28 | deleteTimer KEYWORD2 29 | restartTimer KEYWORD2 30 | isEnabled KEYWORD2 31 | enable KEYWORD2 32 | disable KEYWORD2 33 | enableAll KEYWORD2 34 | disableAll KEYWORD2 35 | toggle KEYWORD2 36 | getNumTimers KEYWORD2 37 | getNumAvailableTimers KEYWORD2 38 | 39 | ####################################### 40 | # Constants (LITERAL1) 41 | ####################################### 42 | 43 | ESP32_NEW_TIMERINTERRUPT_VERSION LITERAL1 44 | ESP32_NEW_TIMERINTERRUPT_VERSION_MAJOR LITERAL1 45 | ESP32_NEW_TIMERINTERRUPT_VERSION_MINOR LITERAL1 46 | ESP32_NEW_TIMERINTERRUPT_VERSION_PATCH LITERAL1 47 | ESP32_NEW_TIMERINTERRUPT_VERSION_INT LITERAL1 48 | 49 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP32_New_TimerInterrupt", 3 | "version": "1.5.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, esp32, esp32-s2, esp32-c3, esp32-s3", 5 | "description": "This library enables you to use Interrupt from Hardware Timers on an ESP32, ESP32_S2, ESP32_S3 or ESP32_C3-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/ESP32_New_TimerInterrupt" 16 | }, 17 | "homepage": "https://github.com/khoih-prog/ESP32_New_TimerInterrupt", 18 | "export": { 19 | "exclude": [ 20 | "linux", 21 | "extras", 22 | "tests" 23 | ] 24 | }, 25 | "license": "MIT", 26 | "frameworks": "*", 27 | "platforms": "espressif32", 28 | "examples": "examples/*/*/*.ino", 29 | "headers": ["ESP32_New_TimerInterrupt.h", "ESP32_New_ISR_Timer.hpp", "ESP32_New_ISR_Timer.h"] 30 | } 31 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32_New_TimerInterrupt 2 | version=1.5.0 3 | author=Khoi Hoang 4 | maintainer=Khoi Hoang 5 | sentence=This library enables you to use Interrupt from Hardware Timers on an ESP32, ESP32_S2, ESP32_S3 or ESP32_C3-based board 6 | paragraph=These ESP32, ESP32_S2, ESP32_S3 or ESP32_C3 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 is 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 are 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/ESP32_New_TimerInterrupt 9 | architectures=esp32 10 | repository=https://github.com/khoih-prog/ESP32_New_TimerInterrupt 11 | license=MIT 12 | includes=ESP32_New_TimerInterrupt.h,ESP32_New_ISR_Timer.hpp,ESP32_New_ISR_Timer.h 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 | ; ESP32 15 | ; esp32s2 16 | ; esp32s3 17 | ; esp32c3 18 | 19 | ; ============================================================ 20 | default_envs = ESP32 21 | 22 | [env] 23 | ; ============================================================ 24 | ; Serial configuration 25 | ; choose upload speed, serial-monitor speed 26 | ; ============================================================ 27 | upload_speed = 921600 28 | ;upload_port = COM11 29 | ;monitor_speed = 9600 30 | ;monitor_port = COM11 31 | 32 | ; Checks for the compatibility with frameworks and dev/platforms 33 | lib_compat_mode = strict 34 | lib_ldf_mode = chain+ 35 | ;lib_ldf_mode = deep+ 36 | 37 | lib_deps = 38 | 39 | build_flags = 40 | ; set your debug output (default=Serial) 41 | -D DEBUG_ESP_PORT=Serial 42 | ; comment the following line to enable WiFi debugging 43 | -D NDEBUG 44 | 45 | 46 | ; ============================================================ 47 | ; ============================================================ 48 | 49 | [env:ESP32] 50 | platform = espressif32 51 | framework = arduino 52 | 53 | ; ============================================================ 54 | ; Board configuration 55 | ; choose your board by uncommenting one of the following lines 56 | ; ============================================================ 57 | ;board = esp32cam 58 | ;board = alksesp32 59 | ;board = featheresp32 60 | ;board = espea32 61 | ;board = bpi-bit 62 | ;board = d-duino-32 63 | board = esp32doit-devkit-v1 64 | ;board = pocket_32 65 | ;board = fm-devkit 66 | ;board = pico32 67 | ;board = esp32-evb 68 | ;board = esp32-gateway 69 | ;board = esp32-pro 70 | ;board = esp32-poe 71 | ;board = oroca_edubot 72 | ;board = onehorse32dev 73 | ;board = lopy 74 | ;board = lopy4 75 | ;board = wesp32 76 | ;board = esp32thing 77 | ;board = sparkfun_lora_gateway_1-channel 78 | ;board = ttgo-lora32-v1 79 | ;board = ttgo-t-beam 80 | ;board = turta_iot_node 81 | ;board = lolin_d32 82 | ;board = lolin_d32_pro 83 | ;board = lolin32 84 | ;board = wemosbat 85 | ;board = widora-air 86 | ;board = xinabox_cw02 87 | ;board = iotbusio 88 | ;board = iotbusproteus 89 | ;board = nina_w10 90 | 91 | ; ============================================================ 92 | 93 | [env:esp32s2] 94 | platform = espressif32 95 | framework = arduino 96 | 97 | ; toolchain download links see 98 | ; refer "name": "xtensa-esp32s2-elf-gcc","version": "gcc8_4_0-esp-2021r1" section of 99 | ; https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json 100 | ; e.g. Windows: https://github.com/espressif/crosstool-NG/releases/download/esp-2021r1/xtensa-esp32s2-elf-gcc8_4_0-esp-2021r1-win32.zip 101 | platform_packages = 102 | toolchain-xtensa32s2@file://C:\Users\Max\Downloads\xtensa-esp32s2-elf 103 | framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#a4118ea88987c28aac3a49bcb9cc5d6c0acc6f3f 104 | platformio/tool-esptoolpy @ ~1.30100 105 | framework = arduino 106 | board = esp32dev 107 | board_build.mcu = esp32s2 108 | board_build.partitions = huge_app.csv 109 | board_build.variant = esp32s2 110 | board_build.f_cpu = 240000000L 111 | board_build.f_flash = 80000000L 112 | board_build.flash_mode = qio 113 | board_build.arduino.ldscript = esp32s2_out.ld 114 | build_unflags = 115 | -DARDUINO_ESP32_DEV 116 | -DARDUINO_VARIANT="esp32" 117 | build_flags = 118 | -DARDUINO_ESP32S2_DEV 119 | -DARDUINO_VARIANT="esp32s2" 120 | 121 | ; ============================================================ 122 | 123 | [env:esp32s3] 124 | platform = espressif32 125 | framework = arduino 126 | 127 | board_build.mcu = esp32s3 128 | board_build.partitions = huge_app.csv 129 | board_build.variant = esp32s3 130 | board_build.f_cpu = 240000000L 131 | board_build.f_flash = 80000000L 132 | board_build.flash_mode = qio 133 | board_build.arduino.ldscript = esp32s3_out.ld 134 | build_unflags = 135 | -DARDUINO_ESP32_DEV 136 | -DARDUINO_VARIANT="esp32" 137 | build_flags = 138 | -DARDUINO_ESP32S3_DEV 139 | -DARDUINO_VARIANT="esp32s3" 140 | 141 | ; ============================================================ 142 | 143 | [env:esp32c3] 144 | platform = espressif32 145 | framework = arduino 146 | 147 | board_build.mcu = esp32c3 148 | board_build.partitions = huge_app.csv 149 | board_build.variant = esp32c3 150 | board_build.f_cpu = 160000000L 151 | board_build.f_flash = 80000000L 152 | board_build.flash_mode = qio 153 | board_build.arduino.ldscript = esp32c3_out.ld 154 | build_unflags = 155 | -DARDUINO_ESP32_DEV 156 | -DARDUINO_VARIANT="esp32" 157 | build_flags = 158 | -DARDUINO_ESP32S3_DEV 159 | -DARDUINO_VARIANT="esp32c3" 160 | 161 | ; ============================================================ 162 | -------------------------------------------------------------------------------- /src/ESP32_New_ISR_Timer-Impl.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_New_ISR_Timer-Impl.h 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | 23 | Based on SimpleTimer - A timer library for Arduino. 24 | Author: mromani@ottotecnica.com 25 | Copyright (c) 2010 OTTOTECNICA Italy 26 | 27 | Based on BlynkTimer.h 28 | Author: Volodymyr Shymanskyy 29 | 30 | Version: 1.5.0 31 | 32 | Version Modified By Date Comments 33 | ------- ----------- ---------- ----------- 34 | 1.0.0 K Hoang 15/08/2021 Initial coding for ESP32, ESP32_S2, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 35 | 1.0.1 K.Hoang 14/11/2021 Avoid using float and D1 in examples due to issue with core v2.0.0 and v2.0.1 36 | 1.1.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. 37 | 1.2.0 K Hoang 12/02/2022 Add support to new ESP32-S3 38 | 1.2.1 K Hoang 16/06/2022 Add support to new Adafruit boards 39 | 1.3.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 40 | 1.4.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 41 | 1.5.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 42 | *****************************************************************************************************************************/ 43 | 44 | #pragma once 45 | 46 | #ifndef ISR_TIMER_GENERIC_IMPL_H 47 | #define ISR_TIMER_GENERIC_IMPL_H 48 | 49 | #include 50 | 51 | ESP32_ISR_Timer::ESP32_ISR_Timer() 52 | : numTimers (-1) 53 | { 54 | } 55 | 56 | void ESP32_ISR_Timer::init() 57 | { 58 | unsigned long current_millis = millis(); //elapsed(); 59 | 60 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 61 | { 62 | memset((void*) &timer[i], 0, sizeof (timer_t)); 63 | timer[i].prev_millis = current_millis; 64 | } 65 | 66 | numTimers = 0; 67 | 68 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 69 | timerMux = portMUX_INITIALIZER_UNLOCKED; 70 | } 71 | 72 | void IRAM_ATTR ESP32_ISR_Timer::run() 73 | { 74 | uint8_t i; 75 | unsigned long current_millis; 76 | 77 | // get current time 78 | current_millis = millis(); //elapsed(); 79 | 80 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 81 | portENTER_CRITICAL_ISR(&timerMux); 82 | 83 | for (i = 0; i < MAX_NUMBER_TIMERS; i++) 84 | { 85 | 86 | timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN; 87 | 88 | // no callback == no timer, i.e. jump over empty slots 89 | if (timer[i].callback != NULL) 90 | { 91 | 92 | // is it time to process this timer ? 93 | // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592 94 | 95 | if ((current_millis - timer[i].prev_millis) >= timer[i].delay) 96 | { 97 | unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay; 98 | 99 | // update time 100 | timer[i].prev_millis += timer[i].delay * skipTimes; 101 | 102 | // check if the timer callback has to be executed 103 | if (timer[i].enabled) 104 | { 105 | 106 | // "run forever" timers must always be executed 107 | if (timer[i].maxNumRuns == TIMER_RUN_FOREVER) 108 | { 109 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; 110 | } 111 | // other timers get executed the specified number of times 112 | else if (timer[i].numRuns < timer[i].maxNumRuns) 113 | { 114 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; 115 | timer[i].numRuns++; 116 | 117 | // after the last run, delete the timer 118 | if (timer[i].numRuns >= timer[i].maxNumRuns) 119 | { 120 | timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL; 121 | } 122 | } 123 | } 124 | } 125 | } 126 | } 127 | 128 | for (i = 0; i < MAX_NUMBER_TIMERS; i++) 129 | { 130 | if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN) 131 | continue; 132 | 133 | if (timer[i].hasParam) 134 | (*(timer_callback_p)timer[i].callback)(timer[i].param); 135 | else 136 | (*(timer_callback)timer[i].callback)(); 137 | 138 | if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL) 139 | deleteTimer(i); 140 | } 141 | 142 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 143 | portEXIT_CRITICAL_ISR(&timerMux); 144 | 145 | } 146 | 147 | 148 | // find the first available slot 149 | // return -1 if none found 150 | int ESP32_ISR_Timer::findFirstFreeSlot() 151 | { 152 | // all slots are used 153 | if (numTimers >= MAX_NUMBER_TIMERS) 154 | { 155 | return -1; 156 | } 157 | 158 | // return the first slot with no callback (i.e. free) 159 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 160 | { 161 | if (timer[i].callback == NULL) 162 | { 163 | return i; 164 | } 165 | } 166 | 167 | // no free slots found 168 | return -1; 169 | } 170 | 171 | 172 | int ESP32_ISR_Timer::setupTimer(const unsigned long& delay, void* callback, void* param, bool hasParam, 173 | const uint32_t& numRuns) 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 (callback == NULL) 190 | { 191 | return -1; 192 | } 193 | 194 | timer[freeTimer].delay = delay; 195 | timer[freeTimer].callback = callback; 196 | timer[freeTimer].param = param; 197 | timer[freeTimer].hasParam = hasParam; 198 | timer[freeTimer].maxNumRuns = numRuns; 199 | timer[freeTimer].enabled = true; 200 | timer[freeTimer].prev_millis = millis(); 201 | 202 | numTimers++; 203 | 204 | return freeTimer; 205 | } 206 | 207 | 208 | int ESP32_ISR_Timer::setTimer(const unsigned long& delay, timer_callback callback, const uint32_t& numRuns) 209 | { 210 | return setupTimer(delay, (void *)callback, NULL, false, numRuns); 211 | } 212 | 213 | int ESP32_ISR_Timer::setTimer(const unsigned long& delay, timer_callback_p callback, void* param, 214 | const uint32_t& numRuns) 215 | { 216 | return setupTimer(delay, (void *)callback, param, true, numRuns); 217 | } 218 | 219 | int ESP32_ISR_Timer::setInterval(const unsigned long& delay, timer_callback callback) 220 | { 221 | return setupTimer(delay, (void *)callback, NULL, false, TIMER_RUN_FOREVER); 222 | } 223 | 224 | int ESP32_ISR_Timer::setInterval(const unsigned long& delay, timer_callback_p callback, void* param) 225 | { 226 | return setupTimer(delay, (void *)callback, param, true, TIMER_RUN_FOREVER); 227 | } 228 | 229 | int ESP32_ISR_Timer::setTimeout(const unsigned long& delay, timer_callback callback) 230 | { 231 | return setupTimer(delay, (void *)callback, NULL, false, TIMER_RUN_ONCE); 232 | } 233 | 234 | int ESP32_ISR_Timer::setTimeout(const unsigned long& delay, timer_callback_p callback, void* param) 235 | { 236 | return setupTimer(delay, (void *)callback, param, true, TIMER_RUN_ONCE); 237 | } 238 | 239 | bool IRAM_ATTR ESP32_ISR_Timer::changeInterval(const uint8_t& numTimer, const unsigned long& delay) 240 | { 241 | if (numTimer >= MAX_NUMBER_TIMERS) 242 | { 243 | return false; 244 | } 245 | 246 | // Updates interval of existing specified timer 247 | if (timer[numTimer].callback != NULL) 248 | { 249 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 250 | portENTER_CRITICAL(&timerMux); 251 | 252 | timer[numTimer].delay = delay; 253 | timer[numTimer].prev_millis = millis(); 254 | 255 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 256 | portEXIT_CRITICAL(&timerMux); 257 | 258 | return true; 259 | } 260 | 261 | // false return for non-used numTimer, no callback 262 | return false; 263 | } 264 | 265 | void ESP32_ISR_Timer::deleteTimer(const uint8_t& timerId) 266 | { 267 | // nothing to delete if no timers are in use 268 | if ( (timerId >= MAX_NUMBER_TIMERS) || (numTimers == 0) ) 269 | { 270 | return; 271 | } 272 | 273 | // don't decrease the number of timers if the specified slot is already empty 274 | if (timer[timerId].callback != NULL) 275 | { 276 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 277 | portENTER_CRITICAL(&timerMux); 278 | 279 | memset((void*) &timer[timerId], 0, sizeof (timer_t)); 280 | timer[timerId].prev_millis = millis(); 281 | 282 | // update number of timers 283 | numTimers--; 284 | 285 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 286 | portEXIT_CRITICAL(&timerMux); 287 | 288 | } 289 | } 290 | 291 | // function contributed by code@rowansimms.com 292 | void ESP32_ISR_Timer::restartTimer(const uint8_t& numTimer) 293 | { 294 | if (numTimer >= MAX_NUMBER_TIMERS) 295 | { 296 | return; 297 | } 298 | 299 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 300 | portENTER_CRITICAL(&timerMux); 301 | 302 | timer[numTimer].prev_millis = millis(); 303 | 304 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 305 | portEXIT_CRITICAL(&timerMux); 306 | } 307 | 308 | 309 | bool ESP32_ISR_Timer::isEnabled(const uint8_t& numTimer) 310 | { 311 | if (numTimer >= MAX_NUMBER_TIMERS) 312 | { 313 | return false; 314 | } 315 | 316 | return timer[numTimer].enabled; 317 | } 318 | 319 | 320 | void ESP32_ISR_Timer::enable(const uint8_t& numTimer) 321 | { 322 | if (numTimer >= MAX_NUMBER_TIMERS) 323 | { 324 | return; 325 | } 326 | 327 | timer[numTimer].enabled = true; 328 | } 329 | 330 | 331 | void ESP32_ISR_Timer::disable(const uint8_t& numTimer) 332 | { 333 | if (numTimer >= MAX_NUMBER_TIMERS) 334 | { 335 | return; 336 | } 337 | 338 | timer[numTimer].enabled = false; 339 | } 340 | 341 | void ESP32_ISR_Timer::enableAll() 342 | { 343 | // Enable all timers with a callback assigned (used) 344 | 345 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 346 | portENTER_CRITICAL(&timerMux); 347 | 348 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 349 | { 350 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) 351 | { 352 | timer[i].enabled = true; 353 | } 354 | } 355 | 356 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 357 | portEXIT_CRITICAL(&timerMux); 358 | } 359 | 360 | void ESP32_ISR_Timer::disableAll() 361 | { 362 | // Disable all timers with a callback assigned (used) 363 | 364 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 365 | portENTER_CRITICAL(&timerMux); 366 | 367 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 368 | { 369 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) 370 | { 371 | timer[i].enabled = false; 372 | } 373 | } 374 | 375 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 376 | portEXIT_CRITICAL(&timerMux); 377 | 378 | } 379 | 380 | void ESP32_ISR_Timer::toggle(const uint8_t& numTimer) 381 | { 382 | if (numTimer >= MAX_NUMBER_TIMERS) 383 | { 384 | return; 385 | } 386 | 387 | timer[numTimer].enabled = !timer[numTimer].enabled; 388 | } 389 | 390 | 391 | int8_t ESP32_ISR_Timer::getNumTimers() 392 | { 393 | return numTimers; 394 | } 395 | 396 | #endif // ISR_TIMER_GENERIC_IMPL_H 397 | 398 | -------------------------------------------------------------------------------- /src/ESP32_New_ISR_Timer.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_New_ISR_Timer.h 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | 23 | Based on SimpleTimer - A timer library for Arduino. 24 | Author: mromani@ottotecnica.com 25 | Copyright (c) 2010 OTTOTECNICA Italy 26 | 27 | Based on BlynkTimer.h 28 | Author: Volodymyr Shymanskyy 29 | 30 | Version: 1.5.0 31 | 32 | Version Modified By Date Comments 33 | ------- ----------- ---------- ----------- 34 | 1.0.0 K Hoang 15/08/2021 Initial coding for ESP32, ESP32_S2, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 35 | 1.0.1 K.Hoang 14/11/2021 Avoid using float and D1 in examples due to issue with core v2.0.0 and v2.0.1 36 | 1.1.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. 37 | 1.2.0 K Hoang 12/02/2022 Add support to new ESP32-S3 38 | 1.2.1 K Hoang 16/06/2022 Add support to new Adafruit boards 39 | 1.3.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 40 | 1.4.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 41 | 1.5.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 42 | *****************************************************************************************************************************/ 43 | 44 | #pragma once 45 | 46 | #ifndef ISR_TIMER_GENERIC_H 47 | #define ISR_TIMER_GENERIC_H 48 | 49 | #include "ESP32_New_ISR_Timer.hpp" 50 | #include "ESP32_New_ISR_Timer-Impl.h" 51 | 52 | 53 | #endif // ISR_TIMER_GENERIC_H 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/ESP32_New_ISR_Timer.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_New_ISR_Timer.hpp 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | 23 | Based on SimpleTimer - A timer library for Arduino. 24 | Author: mromani@ottotecnica.com 25 | Copyright (c) 2010 OTTOTECNICA Italy 26 | 27 | Based on BlynkTimer.h 28 | Author: Volodymyr Shymanskyy 29 | 30 | Version: 1.5.0 31 | 32 | Version Modified By Date Comments 33 | ------- ----------- ---------- ----------- 34 | 1.0.0 K Hoang 15/08/2021 Initial coding for ESP32, ESP32_S2, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 35 | 1.0.1 K.Hoang 14/11/2021 Avoid using float and D1 in examples due to issue with core v2.0.0 and v2.0.1 36 | 1.1.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. 37 | 1.2.0 K Hoang 12/02/2022 Add support to new ESP32-S3 38 | 1.2.1 K Hoang 16/06/2022 Add support to new Adafruit boards 39 | 1.3.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 40 | 1.4.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 41 | 1.5.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 42 | *****************************************************************************************************************************/ 43 | 44 | #pragma once 45 | 46 | #ifndef ISR_TIMER_GENERIC_HPP 47 | #define ISR_TIMER_GENERIC_HPP 48 | 49 | #if !defined( ESP32 ) 50 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 51 | #endif 52 | 53 | #ifndef ESP32_NEW_TIMERINTERRUPT_VERSION 54 | #define ESP32_NEW_TIMERINTERRUPT_VERSION "ESP32_New_TimerInterrupt v1.5.0" 55 | 56 | #define ESP32_NEW_TIMERINTERRUPT_VERSION_MAJOR 1 57 | #define ESP32_NEW_TIMERINTERRUPT_VERSION_MINOR 5 58 | #define ESP32_NEW_TIMERINTERRUPT_VERSION_PATCH 0 59 | 60 | #define ESP32_NEW_TIMERINTERRUPT_VERSION_INT 1005000 61 | #endif 62 | 63 | #include "TimerInterrupt_Generic_Debug.h" 64 | 65 | #define CONFIG_ESP32_APPTRACE_ENABLE 66 | 67 | #if 0 68 | #ifndef configMINIMAL_STACK_SIZE 69 | #define configMINIMAL_STACK_SIZE 2048 70 | #else 71 | #undef configMINIMAL_STACK_SIZE 72 | #define configMINIMAL_STACK_SIZE 2048 73 | #endif 74 | #endif 75 | 76 | #include 77 | 78 | #include 79 | 80 | #if defined(ARDUINO) 81 | #if ARDUINO >= 100 82 | #include 83 | #else 84 | #include 85 | #endif 86 | #endif 87 | 88 | #define ESP32_ISR_Timer ESP32_ISRTimer 89 | 90 | typedef void (*timer_callback)(); 91 | typedef void (*timer_callback_p)(void *); 92 | 93 | class ESP32_ISR_Timer 94 | { 95 | 96 | public: 97 | // maximum number of timers 98 | #define MAX_NUMBER_TIMERS 16 99 | #define TIMER_RUN_FOREVER 0 100 | #define TIMER_RUN_ONCE 1 101 | 102 | // constructor 103 | ESP32_ISR_Timer(); 104 | 105 | void init(); 106 | 107 | // this function must be called inside loop() 108 | void IRAM_ATTR run(); 109 | 110 | // Timer will call function 'callback' every 'delay' milliseconds forever 111 | // returns the timer number (numTimer) on success or 112 | // -1 on failure (callback == NULL) or no free timers 113 | int setInterval(const unsigned long& delay, timer_callback callback); 114 | 115 | // Timer will call function 'callback' with parameter 'param' every 'delay' milliseconds forever 116 | // returns the timer number (numTimer) on success or 117 | // -1 on failure (callback == NULL) or no free timers 118 | int setInterval(const unsigned long& delay, timer_callback_p callback, void* param); 119 | 120 | // Timer will call function 'callback' after 'delay' milliseconds one time 121 | // returns the timer number (numTimer) on success or 122 | // -1 on failure (callback == NULL) or no free timers 123 | int setTimeout(const unsigned long& delay, timer_callback callback); 124 | 125 | // Timer will call function 'callback' with parameter 'param' after 'delay' milliseconds one time 126 | // returns the timer number (numTimer) on success or 127 | // -1 on failure (callback == NULL) or no free timers 128 | int setTimeout(const unsigned long& delay, timer_callback_p callback, void* param); 129 | 130 | // Timer will call function 'callback' every 'delay' milliseconds 'numRuns' times 131 | // returns the timer number (numTimer) on success or 132 | // -1 on failure (callback == NULL) or no free timers 133 | int setTimer(const unsigned long& delay, timer_callback callback, const uint32_t& numRuns); 134 | 135 | // Timer will call function 'callback' with parameter 'param' every 'delay' milliseconds 'numRuns' times 136 | // returns the timer number (numTimer) on success or 137 | // -1 on failure (callback == NULL) or no free timers 138 | int setTimer(const unsigned long& delay, timer_callback_p callback, void* param, const uint32_t& numRuns); 139 | 140 | // updates interval of the specified timer 141 | bool changeInterval(const uint8_t& numTimer, const unsigned long& delay); 142 | 143 | // destroy the specified timer 144 | void deleteTimer(const uint8_t& numTimer); 145 | 146 | // restart the specified timer 147 | void restartTimer(const uint8_t& numTimer); 148 | 149 | // returns true if the specified timer is enabled 150 | bool isEnabled(const uint8_t& numTimer); 151 | 152 | // enables the specified timer 153 | void enable(const uint8_t& numTimer); 154 | 155 | // disables the specified timer 156 | void disable(const uint8_t& numTimer); 157 | 158 | // enables all timers 159 | void enableAll(); 160 | 161 | // disables all timers 162 | void disableAll(); 163 | 164 | // enables the specified timer if it's currently disabled, and vice-versa 165 | void toggle(const uint8_t& numTimer); 166 | 167 | // returns the number of used timers 168 | int8_t getNumTimers(); 169 | 170 | // returns the number of available timers 171 | uint8_t getNumAvailableTimers() __attribute__((always_inline)) 172 | { 173 | if (numTimers <= 0) 174 | return MAX_NUMBER_TIMERS; 175 | else 176 | return MAX_NUMBER_TIMERS - numTimers; 177 | }; 178 | 179 | private: 180 | // deferred call constants 181 | #define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function 182 | #define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer 183 | #define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer 184 | 185 | // low level function to initialize and enable a new timer 186 | // returns the timer number (numTimer) on success or 187 | // -1 on failure (f == NULL) or no free timers 188 | int setupTimer(const unsigned long& delay, void* callback, void* param, bool hasParam, const uint32_t& numRuns) ; 189 | 190 | // find the first available slot 191 | int findFirstFreeSlot(); 192 | 193 | typedef struct 194 | { 195 | unsigned long prev_millis; // value returned by the millis() function in the previous run() call 196 | void* callback; // pointer to the callback function 197 | void* param; // function parameter 198 | bool hasParam; // true if callback takes a parameter 199 | unsigned long delay; // delay value 200 | uint32_t maxNumRuns; // number of runs to be executed 201 | uint32_t numRuns; // number of executed runs 202 | bool enabled; // true if enabled 203 | unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run() 204 | } timer_t; 205 | 206 | volatile timer_t timer[MAX_NUMBER_TIMERS]; 207 | 208 | // actual number of timers in use (-1 means uninitialized) 209 | volatile int8_t numTimers; 210 | 211 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 212 | portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; 213 | }; 214 | 215 | #endif // ISR_TIMER_GENERIC_HPP 216 | 217 | 218 | -------------------------------------------------------------------------------- /src/ESP32_New_TimerInterrupt.h: -------------------------------------------------------------------------------- 1 | 2 | /**************************************************************************************************************************** 3 | ESP32_New_TimerInterrupt.h 4 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 5 | Written by Khoi Hoang 6 | 7 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 15 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 16 | the software. 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: 1.5.0 32 | 33 | Version Modified By Date Comments 34 | ------- ----------- ---------- ----------- 35 | 1.0.0 K Hoang 15/08/2021 Initial coding for ESP32, ESP32_S2, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 36 | 1.0.1 K.Hoang 14/11/2021 Avoid using float and D1 in examples due to issue with core v2.0.0 and v2.0.1 37 | 1.1.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. 38 | 1.2.0 K Hoang 12/02/2022 Add support to new ESP32-S3 39 | 1.2.1 K Hoang 16/06/2022 Add support to new Adafruit boards 40 | 1.3.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 41 | 1.4.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 42 | 1.5.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 43 | *****************************************************************************************************************************/ 44 | 45 | #pragma once 46 | 47 | #ifndef ESP32_NEW_TIMERINTERRUPT_H 48 | #define ESP32_NEW_TIMERINTERRUPT_H 49 | 50 | //////////////////////////////////////// 51 | 52 | #if ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \ 53 | ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \ 54 | ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM || ARDUINO_ADAFRUIT_QTPY_ESP32S2 || ARDUINO_ESP32S2_USB || \ 55 | ARDUINO_FEATHERS2NEO || ARDUINO_TINYS2 || ARDUINO_RMP || ARDUINO_LOLIN_S2_MINI || ARDUINO_LOLIN_S2_PICO || \ 56 | ARDUINO_ADAFRUIT_FEATHER_ESP32S2 || ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT || ARDUINO_atmegazero_esp32s2 || \ 57 | ARDUINO_DYM || ARDUINO_FRANZININHO_WIFI || ARDUINO_FRANZININHO_WIFI_MSC ) 58 | #define USING_ESP32_S2_NEW_TIMERINTERRUPT true 59 | 60 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3) 61 | #warning USING_ESP32_S2_NEW_TIMERINTERRUPT 62 | #endif 63 | 64 | //////////////////////////////////////// 65 | 66 | #elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \ 67 | defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_NOPSRAM) || \ 68 | defined(ARDUINO_ADAFRUIT_QTPY_ESP32S3_NOPSRAM) || defined(ARDUINO_ESP32S3_CAM_LCD) || \ 69 | defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_TFT) || \ 70 | defined(ARDUINO_ESP32_S3_USB_OTG) ) 71 | #define USING_ESP32_S3_NEW_TIMERINTERRUPT true 72 | 73 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3) 74 | #warning USING_ESP32_S3_NEW_TIMERINTERRUPT 75 | #endif 76 | 77 | //////////////////////////////////////// 78 | 79 | #elif ( defined(ARDUINO_ESP32C3_DEV) || defined(ARDUINO_LOLIN_C3_MINI) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) || \ 80 | defined(ARDUINO_AirM2M_CORE_ESP32C3) || defined(ARDUINO_XIAO_ESP32C3) ) 81 | #define USING_ESP32_C3_NEW_TIMERINTERRUPT true 82 | 83 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3) 84 | #warning USING_ESP32_C3_NEW_TIMERINTERRUPT 85 | #endif 86 | #elif defined(ESP32) 87 | #define USING_ESP32_NEW_TIMERINTERRUPT true 88 | 89 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3) 90 | #warning USING_ESP32_NEW_TIMERINTERRUPT 91 | #endif 92 | #else 93 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 94 | #endif 95 | 96 | #ifndef ESP32_NEW_TIMERINTERRUPT_VERSION 97 | #define ESP32_NEW_TIMERINTERRUPT_VERSION "ESP32_New_TimerInterrupt v1.5.0" 98 | 99 | #define ESP32_NEW_TIMERINTERRUPT_VERSION_MAJOR 1 100 | #define ESP32_NEW_TIMERINTERRUPT_VERSION_MINOR 5 101 | #define ESP32_NEW_TIMERINTERRUPT_VERSION_PATCH 0 102 | 103 | #define ESP32_NEW_TIMERINTERRUPT_VERSION_INT 1005000 104 | #endif 105 | 106 | #ifndef TIMER_INTERRUPT_DEBUG 107 | #define TIMER_INTERRUPT_DEBUG 0 108 | #endif 109 | 110 | #include "TimerInterrupt_Generic_Debug.h" 111 | 112 | #include 113 | 114 | /* 115 | //ESP32 core v1.0.6, hw_timer_t defined in esp32/tools/sdk/include/driver/driver/timer.h: 116 | 117 | #define TIMER_BASE_CLK (APB_CLK_FREQ) //Frequency of the clock on the input of the timer groups 118 | 119 | 120 | //@brief Selects a Timer-Group out of 2 available groups 121 | 122 | typedef enum 123 | { 124 | TIMER_GROUP_0 = 0, // Hw timer group 0 125 | TIMER_GROUP_1 = 1, // Hw timer group 1 126 | TIMER_GROUP_MAX, 127 | } timer_group_t; 128 | 129 | 130 | //@brief Select a hardware timer from timer groups 131 | 132 | typedef enum 133 | { 134 | TIMER_0 = 0, // Select timer0 of GROUPx 135 | TIMER_1 = 1, // Select timer1 of GROUPx 136 | TIMER_MAX, 137 | } timer_idx_t; 138 | 139 | 140 | //@brief Decides the direction of counter 141 | 142 | typedef enum 143 | { 144 | TIMER_COUNT_DOWN = 0, //Descending Count from cnt.high|cnt.low 145 | TIMER_COUNT_UP = 1, //Ascending Count from Zero 146 | TIMER_COUNT_MAX 147 | } timer_count_dir_t; 148 | 149 | 150 | //@brief Decides whether timer is on or paused 151 | 152 | typedef enum 153 | { 154 | TIMER_PAUSE = 0, //Pause timer counter 155 | TIMER_START = 1, //Start timer counter 156 | } timer_start_t; 157 | 158 | 159 | //@brief Decides whether to enable alarm mode 160 | 161 | typedef enum 162 | { 163 | TIMER_ALARM_DIS = 0, //Disable timer alarm 164 | TIMER_ALARM_EN = 1, //Enable timer alarm 165 | TIMER_ALARM_MAX 166 | } timer_alarm_t; 167 | 168 | 169 | //@brief Select interrupt type if running in alarm mode. 170 | 171 | typedef enum 172 | { 173 | TIMER_INTR_LEVEL = 0, //Interrupt mode: level mode 174 | //TIMER_INTR_EDGE = 1, //Interrupt mode: edge mode, Not supported Now 175 | TIMER_INTR_MAX 176 | } timer_intr_mode_t; 177 | 178 | 179 | //@brief Select if Alarm needs to be loaded by software or automatically reload by hardware. 180 | 181 | typedef enum 182 | { 183 | TIMER_AUTORELOAD_DIS = 0, //Disable auto-reload: hardware will not load counter value after an alarm event 184 | TIMER_AUTORELOAD_EN = 1, //Enable auto-reload: hardware will load counter value after an alarm event 185 | TIMER_AUTORELOAD_MAX, 186 | } timer_autoreload_t; 187 | 188 | 189 | //@brief Data structure with timer's configuration settings 190 | 191 | typedef struct 192 | { 193 | bool alarm_en; //Timer alarm enable 194 | bool counter_en; //Counter enable 195 | timer_intr_mode_t intr_type; //Interrupt mode 196 | timer_count_dir_t counter_dir; //Counter direction 197 | bool auto_reload; //Timer auto-reload 198 | uint32_t divider; //Counter clock divider. The divider's range is from from 2 to 65536. 199 | } timer_config_t; 200 | 201 | */ 202 | 203 | /* 204 | //ESP32 core v2.0.4, timer_config_t defined in tools/sdk/esp32/include/hal/include/hal/timer_types.h: 205 | #if SOC_TIMER_GROUP_SUPPORT_XTAL 206 | typedef enum { 207 | TIMER_SRC_CLK_APB = 0, // Select APB as the source clock 208 | TIMER_SRC_CLK_XTAL = 1, // Select XTAL as the source clock 209 | } timer_src_clk_t; 210 | #endif 211 | typedef struct { 212 | timer_alarm_t alarm_en; // Timer alarm enable 213 | timer_start_t counter_en; // Counter enable 214 | timer_intr_mode_t intr_type; // Interrupt mode 215 | timer_count_dir_t counter_dir; // Counter direction 216 | timer_autoreload_t auto_reload; // Timer auto-reload 217 | uint32_t divider; // Counter clock divider. The divider's range is from from 2 to 65536 218 | #if SOC_TIMER_GROUP_SUPPORT_XTAL 219 | timer_src_clk_t clk_src; // Use XTAL as source clock 220 | #endif 221 | } timer_config_t; 222 | 223 | */ 224 | 225 | class ESP32TimerInterrupt; 226 | 227 | typedef ESP32TimerInterrupt ESP32Timer; 228 | 229 | #if USING_ESP32_C3_NEW_TIMERINTERRUPT 230 | #define MAX_ESP32_NUM_TIMERS 2 231 | #else 232 | #define MAX_ESP32_NUM_TIMERS 4 233 | #endif 234 | 235 | #define TIMER_DIVIDER 80 // Hardware timer clock divider 236 | // TIMER_BASE_CLK = APB_CLK_FREQ = Frequency of the clock on the input of the timer groups 237 | #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds 238 | 239 | 240 | // In esp32/1.0.6/tools/sdk/esp32s2/include/driver/include/driver/timer.h 241 | // typedef bool (*timer_isr_t)(void *); 242 | //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); 243 | //esp_err_t timer_isr_callback_remove(timer_group_t group_num, timer_idx_t timer_num); 244 | //timer_deinit(timer_group_t group_num, timer_idx_t timer_num); 245 | //esp_err_t timer_group_intr_enable(timer_group_t group_num, timer_intr_t intr_mask); 246 | //esp_err_t timer_group_intr_disable(timer_group_t group_num, timer_intr_t intr_mask); 247 | 248 | typedef bool (*esp32_timer_callback) (void *); 249 | 250 | // For ESP32_C3, TIMER_MAX == 1 251 | // For ESP32, ESP32_S2 and ESP32_S3, TIMER_MAX == 2 252 | 253 | typedef struct 254 | { 255 | timer_idx_t timer_idx; 256 | timer_group_t timer_group; 257 | //int alarm_interval; 258 | //timer_autoreload_t auto_reload; 259 | } timer_info_t; 260 | 261 | // Warning: TIMER_SRC_CLK_XTAL only good for ESP32 262 | // Use TIMER_SRC_CLK_APB for ESP32_C3, ESP32_S2 and ESP32_S3 263 | 264 | class ESP32TimerInterrupt 265 | { 266 | private: 267 | 268 | timer_config_t stdConfig = 269 | { 270 | .alarm_en = TIMER_ALARM_EN, //enable timer alarm 271 | .counter_en = TIMER_START, //starts counting counter once timer_init called 272 | .intr_type = TIMER_INTR_MAX, 273 | .counter_dir = TIMER_COUNT_UP, //counts from 0 to counter value 274 | .auto_reload = TIMER_AUTORELOAD_EN, //reloads counter automatically 275 | .divider = TIMER_DIVIDER, 276 | #if (SOC_TIMER_GROUP_SUPPORT_XTAL) 277 | #if (USING_ESP32_TIMERINTERRUPT) 278 | .clk_src = TIMER_SRC_CLK_XTAL //Use XTAL as source clock 279 | #else 280 | .clk_src = TIMER_SRC_CLK_APB //Use APB as source clock 281 | #endif 282 | #endif 283 | }; 284 | 285 | timer_idx_t _timerIndex; 286 | timer_group_t _timerGroup; 287 | uint32_t interruptFlag; // either TIMER_INTR_T0 or TIMER_INTR_T1 288 | 289 | uint8_t _timerNo; 290 | 291 | esp32_timer_callback _callback; // pointer to the callback function 292 | float _frequency; // Timer frequency 293 | uint64_t _timerCount; // count to activate timer 294 | 295 | //xQueueHandle s_timer_queue; 296 | 297 | public: 298 | 299 | ESP32TimerInterrupt(uint8_t timerNo) 300 | { 301 | _callback = NULL; 302 | 303 | if (timerNo < MAX_ESP32_NUM_TIMERS) 304 | { 305 | _timerNo = timerNo; 306 | 307 | #if USING_ESP32_C3_NEW_TIMERINTERRUPT 308 | 309 | // Always using TIMER_INTR_T0 310 | _timerIndex = (timer_idx_t) ( (uint32_t) 0 ); 311 | 312 | // timerNo == 0 => Group 0, timerNo == 1 => Group 1 313 | _timerGroup = (timer_group_t) ( (uint32_t) timerNo); 314 | 315 | #else 316 | 317 | _timerIndex = (timer_idx_t) (_timerNo % TIMER_MAX); 318 | 319 | _timerGroup = (timer_group_t) (_timerNo / TIMER_MAX); 320 | 321 | #endif 322 | } 323 | else 324 | { 325 | _timerNo = MAX_ESP32_NUM_TIMERS; 326 | } 327 | }; 328 | 329 | // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 330 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c 331 | bool setFrequency(const float& frequency, esp32_timer_callback callback) 332 | { 333 | if (_timerNo < MAX_ESP32_NUM_TIMERS) 334 | { 335 | // select timer frequency is 1MHz for better accuracy. We don't use 16-bit prescaler for now. 336 | // Will use later if very low frequency is needed. 337 | _frequency = TIMER_BASE_CLK / TIMER_DIVIDER; //1000000; 338 | _timerCount = (uint64_t) _frequency / frequency; 339 | // count up 340 | 341 | #if USING_ESP32_S2_NEW_TIMERINTERRUPT 342 | TISR_LOGWARN3(F("ESP32_S2_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 343 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 344 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 345 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32), F("-"), (uint32_t) (_timerCount)); 346 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 347 | #elif USING_ESP32_S3_NEW_TIMERINTERRUPT 348 | // ESP32-S3 is embedded with four 54-bit general-purpose timers, which are based on 16-bit prescalers 349 | // and 54-bit auto-reload-capable up/down-timers 350 | TISR_LOGWARN3(F("ESP32_S3_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 351 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 352 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 353 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32), F("-"), (uint32_t) (_timerCount)); 354 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 355 | #else 356 | TISR_LOGWARN3(F("ESP32_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 357 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 358 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 359 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32), F("-"), (uint32_t) (_timerCount)); 360 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 361 | #endif 362 | 363 | timer_init(_timerGroup, _timerIndex, &stdConfig); 364 | 365 | // Counter value to 0 => counting up to alarm value as .counter_dir == TIMER_COUNT_UP 366 | timer_set_counter_value(_timerGroup, _timerIndex, 0x00000000ULL); 367 | 368 | timer_set_alarm_value(_timerGroup, _timerIndex, TIMER_SCALE / frequency); 369 | 370 | // enable interrupts for _timerGroup, _timerIndex 371 | timer_enable_intr(_timerGroup, _timerIndex); 372 | 373 | _callback = callback; 374 | 375 | // Register the ISR handler 376 | // If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set, the handler function must be declared with IRAM_ATTR attribute 377 | // and can only call functions in IRAM or ROM. It cannot call other timer APIs. 378 | //timer_isr_register(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, ESP_INTR_FLAG_IRAM, NULL); 379 | timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0); 380 | 381 | timer_start(_timerGroup, _timerIndex); 382 | 383 | return true; 384 | } 385 | else 386 | { 387 | #if USING_ESP32_C3_NEW_TIMERINTERRUPT 388 | TISR_LOGERROR(F("Error. Timer must be 0-1")); 389 | #else 390 | TISR_LOGERROR(F("Error. Timer must be 0-3")); 391 | #endif 392 | 393 | return false; 394 | } 395 | } 396 | 397 | // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 398 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c 399 | bool setInterval(const unsigned long& interval, esp32_timer_callback callback) 400 | { 401 | return setFrequency((float) (1000000.0f / interval), callback); 402 | } 403 | 404 | bool attachInterrupt(const float& frequency, esp32_timer_callback callback) 405 | { 406 | return setFrequency(frequency, callback); 407 | } 408 | 409 | // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 410 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c 411 | bool attachInterruptInterval(const unsigned long& interval, esp32_timer_callback callback) 412 | { 413 | return setFrequency( (float) ( 1000000.0f / interval), callback); 414 | } 415 | 416 | void detachInterrupt() 417 | { 418 | #if USING_ESP32_C3_NEW_TIMERINTERRUPT 419 | timer_group_intr_disable(_timerGroup, TIMER_INTR_T0); 420 | #else 421 | timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 422 | #endif 423 | } 424 | 425 | void disableTimer() 426 | { 427 | #if USING_ESP32_C3_NEW_TIMERINTERRUPT 428 | timer_group_intr_disable(_timerGroup, TIMER_INTR_T0); 429 | #else 430 | timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 431 | #endif 432 | } 433 | 434 | // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely 435 | void reattachInterrupt() 436 | { 437 | #if USING_ESP32_C3_NEW_TIMERINTERRUPT 438 | timer_group_intr_enable(_timerGroup, TIMER_INTR_T0); 439 | #else 440 | timer_group_intr_enable(_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 enableTimer() 446 | { 447 | #if USING_ESP32_C3_NEW_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 | // Just stop clock source, clear the count 455 | void stopTimer() 456 | { 457 | timer_pause(_timerGroup, _timerIndex); 458 | } 459 | 460 | // Just reconnect clock source, start current count from 0 461 | void restartTimer() 462 | { 463 | timer_set_counter_value(_timerGroup, _timerIndex, 0x00000000ULL); 464 | timer_start(_timerGroup, _timerIndex); 465 | } 466 | 467 | int8_t getTimer() __attribute__((always_inline)) 468 | { 469 | return _timerIndex; 470 | }; 471 | 472 | int8_t getTimerGroup() __attribute__((always_inline)) 473 | { 474 | return _timerGroup; 475 | }; 476 | 477 | }; // class ESP32TimerInterrupt 478 | 479 | #endif // ESP32_NEW_TIMERINTERRUPT_H 480 | 481 | -------------------------------------------------------------------------------- /src/TimerInterrupt_Generic_Debug.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerInterrupt_Generic_Debug.h 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_TimerInterrupt 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 bits counters and 16 bit prescalers. The timer counters can be configured to count up or down 14 | and support automatic reload and software reload. They can also generate alarms when they reach a specific value, defined by 15 | the software. The value of the counter can be read by the software program. 16 | 17 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 18 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 19 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 20 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 21 | This important feature is absolutely necessary for mission-critical tasks. 22 | 23 | Based on SimpleTimer - A timer library for Arduino. 24 | Author: mromani@ottotecnica.com 25 | Copyright (c) 2010 OTTOTECNICA Italy 26 | 27 | Based on BlynkTimer.h 28 | Author: Volodymyr Shymanskyy 29 | 30 | Version: 1.5.0 31 | 32 | Version Modified By Date Comments 33 | ------- ----------- ---------- ----------- 34 | 1.0.0 K Hoang 15/08/2021 Initial coding for ESP32, ESP32_S2, ESP32_C3 boards with ESP32 core v2.0.0-rc1+ 35 | 1.0.1 K.Hoang 14/11/2021 Avoid using float and D1 in examples due to issue with core v2.0.0 and v2.0.1 36 | 1.1.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. 37 | 1.2.0 K Hoang 12/02/2022 Add support to new ESP32-S3 38 | 1.2.1 K Hoang 16/06/2022 Add support to new Adafruit boards 39 | 1.3.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 40 | 1.4.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 41 | 1.5.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 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 | ///////////////////////////////////////////////////////// 67 | 68 | const char TISR_MARK[] = "[TISR] "; 69 | 70 | #define TISR_PRINT_MARK TISR_PRINT(TISR_MARK) 71 | #define TISR_PRINT_SP TISR_DBG_PORT.print(" ") 72 | 73 | #define TISR_PRINT TISR_DBG_PORT.print 74 | #define TISR_PRINTLN TISR_DBG_PORT.println 75 | 76 | ///////////////////////////////////////////////////////// 77 | 78 | #define TISR_LOGERROR(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_PRINT_MARK; TISR_PRINTLN(x); } 79 | #define TISR_LOGERROR0(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_PRINT(x); } 80 | #define TISR_LOGERROR1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINTLN(y); } 81 | #define TISR_LOGERROR2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINT(y); TISR_PRINT_SP; TISR_PRINTLN(z); } 82 | #define TISR_LOGERROR3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINT(y); TISR_PRINT_SP; TISR_PRINT(z); TISR_PRINT_SP; TISR_PRINTLN(w); } 83 | 84 | ///////////////////////////////////////////////////////// 85 | 86 | #define TISR_LOGWARN(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_PRINT_MARK; TISR_PRINTLN(x); } 87 | #define TISR_LOGWARN0(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_PRINT(x); } 88 | #define TISR_LOGWARN1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINTLN(y); } 89 | #define TISR_LOGWARN2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINT(y); TISR_PRINT_SP; TISR_PRINTLN(z); } 90 | #define TISR_LOGWARN3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINT(y); TISR_PRINT_SP; TISR_PRINT(z); TISR_PRINT_SP; TISR_PRINTLN(w); } 91 | 92 | ///////////////////////////////////////////////////////// 93 | 94 | #define TISR_LOGINFO(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_PRINT_MARK; TISR_PRINTLN(x); } 95 | #define TISR_LOGINFO0(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_PRINT(x); } 96 | #define TISR_LOGINFO1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINTLN(y); } 97 | #define TISR_LOGINFO2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINT(y); TISR_PRINT_SP; TISR_PRINTLN(z); } 98 | #define TISR_LOGINFO3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINT(y); TISR_PRINT_SP; TISR_PRINT(z); TISR_PRINT_SP; TISR_PRINTLN(w); } 99 | 100 | ///////////////////////////////////////////////////////// 101 | 102 | #define TISR_LOGDEBUG(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_PRINT_MARK; TISR_PRINTLN(x); } 103 | #define TISR_LOGDEBUG0(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_PRINT(x); } 104 | #define TISR_LOGDEBUG1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINTLN(y); } 105 | #define TISR_LOGDEBUG2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINT(y); TISR_PRINT_SP; TISR_PRINTLN(z); } 106 | #define TISR_LOGDEBUG3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINT(y); TISR_PRINT_SP; TISR_PRINT(z); TISR_PRINT_SP; TISR_PRINTLN(w); } 107 | 108 | ///////////////////////////////////////////////////////// 109 | 110 | 111 | #endif //TIMERINTERRUPT_GENERIC_DEBUG_H 112 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------