├── utils ├── restyle.sh └── astyle_library.conf ├── .codespellrc ├── .github ├── workflows │ ├── auto-github-actions.yml │ ├── report-size-deltas.yml │ ├── spell-check.yml │ └── check-arduino.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── stale.yml ├── .gitignore ├── library.properties ├── LICENSE ├── library.json ├── keywords.txt ├── CONTRIBUTING.md ├── examples ├── Argument_Complex_Multi │ ├── Argument_Complex_Multi.cpp │ ├── Argument_Complex_Multi.h │ └── Argument_Complex_Multi.ino ├── Argument_None │ └── Argument_None.ino ├── Argument_Complex │ └── Argument_Complex.ino ├── RPM_Measure │ └── RPM_Measure.ino ├── TimerDuration │ └── TimerDuration.ino ├── Argument_Simple │ └── Argument_Simple.ino ├── ISR_RPM_Measure │ └── ISR_RPM_Measure.ino ├── ISR_Timers_Array_Simple │ └── ISR_Timers_Array_Simple.ino ├── SwitchDebounce │ └── SwitchDebounce.ino ├── Change_Interval_HF │ └── Change_Interval_HF.ino ├── TimerInterruptTest │ └── TimerInterruptTest.ino ├── Change_Interval │ └── Change_Interval.ino ├── ISR_16_Timers_Array_Complex │ └── ISR_16_Timers_Array_Complex.ino └── FakeAnalogWrite │ └── FakeAnalogWrite.ino ├── src ├── ISR_Timer.h ├── TimerInterrupt.h ├── TimerInterrupt_Generic_Debug.h ├── ISR_Timer.hpp ├── ISR_Timer-Impl.h └── TimerInterrupt.hpp ├── platformio └── platformio.ini └── changelog.md /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 | -------------------------------------------------------------------------------- /.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,./Packages_Patches,./LibraryPatches 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/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/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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Is your feature request related to a problem? Please describe. 11 | 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | ### Describe the solution you'd like 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | ### Describe alternatives you've considered 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | ### Additional context 22 | 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /.github/workflows/check-arduino.yml: -------------------------------------------------------------------------------- 1 | name: Check Arduino 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v3 20 | 21 | - name: Arduino Lint 22 | uses: arduino/arduino-lint-action@v1 23 | with: 24 | compliance: specification 25 | library-manager: update 26 | # Always use this setting for official repositories. Remove for 3rd party projects. 27 | official: true 28 | project-type: library 29 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=TimerInterrupt 2 | version=1.8.0 3 | author=Khoi Hoang 4 | maintainer=Khoi Hoang 5 | sentence=This library enables you to use Interrupt from Hardware Timers on an Arduino, Adafruit or Sparkfun AVR board, such as Nano, UNO, Mega, Leonardo, YUN, Teensy, Feather_32u4, Feather_328P, Pro Micro, etc. 6 | paragraph=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/TimerInterrupt 9 | architectures=avr,teensy 10 | repository=https://github.com/khoih-prog/TimerInterrupt 11 | license=MIT 12 | includes=TimerInterrupt.h,TimerInterrupt.hpp,ISR_Timer.h,ISR_Timer.hpp 13 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TimerInterrupt", 3 | "version": "1.8.0", 4 | "keywords": "timer, interrupt, timer-interrupt, hardware, isr, isr-based, hardware-timer, mission-critical, accuracy, precise, atmega, atmega2560, atmega1280, atmega16u4, atmega32u4, 32u4, atmega328p, nano, uno, leonardo, yun, yun-mini, esplora, gemma, mega, feather, adafruit, 16u4, atmega128, atmega168, atmegang, atmega8, attiny85", 5 | "description": "This library enables you to use Interrupt from Hardware Timers on an Arduino, Adafruit or Sparkfun AVR board, such as Nano, UNO, Mega, Leonardo, YUN, Teensy, Feather_32u4, Feather_328P, Pro Micro, etc. 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.", 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/TimerInterrupt" 16 | }, 17 | "homepage": "https://github.com/khoih-prog/TimerInterrupt", 18 | "export": { 19 | "exclude": [ 20 | "linux", 21 | "extras", 22 | "tests" 23 | ] 24 | }, 25 | "license": "MIT", 26 | "frameworks": "arduino", 27 | "platforms": 28 | [ 29 | "atmelavr", 30 | "teensy" 31 | ], 32 | "examples": "examples/*/*/*.ino", 33 | "headers": ["TimerInterrupt.h", "TimerInterrupt.hpp", "ISR_Timer.h", "ISR_Timer.hpp"] 34 | } 35 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Datatypes (KEYWORD1) 3 | ####################################### 4 | 5 | TimerInterrupt KEYWORD1 6 | 7 | ITimer1 KEYWORD1 8 | ITimer2 KEYWORD1 9 | ITimer3 KEYWORD1 10 | ITimer4 KEYWORD1 11 | ITimer5 KEYWORD1 12 | 13 | ISR_Timer KEYWORD1 14 | 15 | ####################################### 16 | # Methods and Functions (KEYWORD2) 17 | ####################################### 18 | 19 | init KEYWORD2 20 | set_OCR KEYWORD2 21 | callback KEYWORD2 22 | setFrequency KEYWORD2 23 | setInterval KEYWORD2 24 | attachInterrupt KEYWORD2 25 | attachInterruptInterval KEYWORD2 26 | detachInterrupt KEYWORD2 27 | disableTimer KEYWORD2 28 | reattachInterrupt KEYWORD2 29 | enableTimer KEYWORD2 30 | pauseTimer KEYWORD2 31 | resumeTimer KEYWORD2 32 | stopTimer KEYWORD2 33 | restartTimer KEYWORD2 34 | getTimer KEYWORD2 35 | getCount KEYWORD2 36 | setCount KEYWORD2 37 | get_OCRValue KEYWORD2 38 | get_OCRValueRemaining KEYWORD2 39 | adjust_OCRValue KEYWORD2 40 | reload_OCRValue KEYWORD2 41 | checkTimerDone KEYWORD2 42 | run KEYWORD2 43 | setTimeout KEYWORD2 44 | setTimer KEYWORD2 45 | changeInterval KEYWORD2 46 | deleteTimer KEYWORD2 47 | isEnabled KEYWORD2 48 | enable KEYWORD2 49 | disable KEYWORD2 50 | enableAll KEYWORD2 51 | disableAll KEYWORD2 52 | toggle KEYWORD2 53 | getNumTimers KEYWORD2 54 | getNumAvailableTimers KEYWORD2 55 | 56 | ####################################### 57 | # Constants (LITERAL1) 58 | ####################################### 59 | 60 | TIMER_INTERRUPT_VERSION LITERAL1 61 | TIMER_INTERRUPT_VERSION_MAJOR LITERAL1 62 | TIMER_INTERRUPT_VERSION_MINOR LITERAL1 63 | TIMER_INTERRUPT_VERSION_PATCH LITERAL1 64 | TIMER_INTERRUPT_VERSION_INT LITERAL1 65 | 66 | 67 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to TimerInterrupt 2 | 3 | ### Reporting Bugs 4 | 5 | Please report bugs in TimerInterrupt 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/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/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 | * `Arduino AVR` or `Adafruit AVR` Core Version (e.g. Arduino AVR core v1.8.6 or Adafruit AVR Core v1.4.15) 19 | * Contextual information (e.g. what you were trying to achieve) 20 | * Simplest possible steps to reproduce 21 | * Anything that might be relevant in your opinion, such as: 22 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 23 | * Network configuration 24 | 25 | 26 | ### Example 27 | 28 | ``` 29 | Arduino IDE version: 1.8.19 30 | Arduino AVR Core Version 1.8.6 31 | OS: Ubuntu 20.04 LTS 32 | Linux xy-Inspiron-3593 5.15.0-53-generic #59~20.04.1-Ubuntu SMP Thu Oct 20 15:10:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux 33 | 34 | Context: 35 | I encountered a crash while using this library 36 | Steps to reproduce: 37 | 1. ... 38 | 2. ... 39 | 3. ... 40 | 4. ... 41 | ``` 42 | 43 | ### Additional context 44 | 45 | Add any other context about the problem here. 46 | 47 | --- 48 | 49 | ### Sending Feature Requests 50 | 51 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 52 | 53 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 54 | 55 | --- 56 | 57 | ### Sending Pull Requests 58 | 59 | Pull Requests with changes and fixes are also welcome! 60 | 61 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 62 | 63 | 1. Change directory to the library GitHub 64 | 65 | ``` 66 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/TimerInterrupt_GitHub/ 67 | xy@xy-Inspiron-3593:~/Arduino/xy/TimerInterrupt_GitHub$ 68 | ``` 69 | 70 | 2. Issue astyle command 71 | 72 | ``` 73 | xy@xy-Inspiron-3593:~/Arduino/xy/TimerInterrupt_GitHub$ bash utils/restyle.sh 74 | ``` 75 | 76 | 77 | -------------------------------------------------------------------------------- /.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 | * `Arduino AVR` or `Adafruit AVR` Core Version (e.g. Arduino AVR core v1.8.6 or Adafruit AVR Core v1.4.15) 40 | * Contextual information (e.g. what you were trying to achieve) 41 | * Simplest possible steps to reproduce 42 | * Anything that might be relevant in your opinion, such as: 43 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 44 | * Network configuration 45 | 46 | 47 | ### Example 48 | 49 | ``` 50 | Arduino IDE version: 1.8.19 51 | Arduino AVR Core Version 1.8.6 52 | OS: Ubuntu 20.04 LTS 53 | Linux xy-Inspiron-3593 5.15.0-53-generic #59~20.04.1-Ubuntu SMP Thu Oct 20 15:10:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux 54 | 55 | Context: 56 | I encountered a crash while using this library 57 | Steps to reproduce: 58 | 1. ... 59 | 2. ... 60 | 3. ... 61 | 4. ... 62 | ``` 63 | 64 | ### Additional context 65 | 66 | Add any other context about the problem here. 67 | 68 | --- 69 | 70 | ### Sending Feature Requests 71 | 72 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 73 | 74 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 75 | 76 | --- 77 | 78 | ### Sending Pull Requests 79 | 80 | Pull Requests with changes and fixes are also welcome! 81 | 82 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 83 | 84 | 1. Change directory to the library GitHub 85 | 86 | ``` 87 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/TimerInterrupt_GitHub/ 88 | xy@xy-Inspiron-3593:~/Arduino/xy/TimerInterrupt_GitHub$ 89 | ``` 90 | 91 | 2. Issue astyle command 92 | 93 | ``` 94 | xy@xy-Inspiron-3593:~/Arduino/xy/TimerInterrupt_GitHub$ bash utils/restyle.sh 95 | ``` 96 | 97 | -------------------------------------------------------------------------------- /examples/Argument_Complex_Multi/Argument_Complex_Multi.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Argument_Complex_Multi.cpp 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // To demonstrate the usage of complex multiple files to avoid `multi definition linker` error 27 | // by using TimerInterrupt.hpp in multiple files but TimerInterrupt.h in only main file 28 | 29 | #include "Argument_Complex_Multi.h" 30 | 31 | void TimerHandler(unsigned int outputPinsAddress) 32 | { 33 | static bool toggle = false; 34 | 35 | //timer interrupt toggles pins 36 | #if (TIMER_INTERRUPT_DEBUG > 1) 37 | Serial.print("Toggle pin1 = "); Serial.println( ((pinStruct *) outputPinsAddress)->Pin1 ); 38 | #endif 39 | 40 | digitalWrite(((pinStruct *) outputPinsAddress)->Pin1, toggle); 41 | 42 | #if (TIMER_INTERRUPT_DEBUG > 1) 43 | Serial.print("Read pin2 A0 ("); Serial.print(((pinStruct *) outputPinsAddress)->Pin2 ); 44 | Serial.print(") = "); 45 | Serial.println(digitalRead(((pinStruct *) outputPinsAddress)->Pin2) ? "HIGH" : "LOW" ); 46 | 47 | Serial.print("Read pin3 A1 ("); Serial.print(((pinStruct *) outputPinsAddress)->Pin3 ); 48 | Serial.print(") = "); 49 | Serial.println(digitalRead(((pinStruct *) outputPinsAddress)->Pin3) ? "HIGH" : "LOW" ); 50 | #endif 51 | 52 | toggle = !toggle; 53 | } 54 | -------------------------------------------------------------------------------- /src/ISR_Timer.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_Timer.h 3 | For Arduino boards (UNO, Nano, Mega, etc. ) 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | TCNTx - Timer/Counter Register. The actual timer value is stored here. 10 | OCRx - Output Compare Register 11 | ICRx - Input Capture Register (only for 16bit timer) 12 | TIMSKx - Timer/Counter Interrupt Mask Register. To enable/disable timer interrupts. 13 | TIFRx - Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt. 14 | 15 | Now with we can use these new 16 ISR-based timers, while consuming only 1 hwarware Timer. 16 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 17 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 18 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 19 | This important feature is absolutely necessary for mission-critical tasks. 20 | 21 | Version: 1.8.0 22 | 23 | Version Modified By Date Comments 24 | ------- ----------- ---------- ----------- 25 | 1.0.0 K Hoang 23/11/2019 Initial coding 26 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 27 | 1.0.2 K.Hoang 28/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 28 | 1.0.3 K.Hoang 01/12/2020 Add complex examples ISR_16_Timers_Array_Complex and ISR_16_Timers_Array_Complex 29 | 1.1.1 K.Hoang 06/12/2020 Add example Change_Interval. Bump up version to sync with other TimerInterrupt Libraries 30 | 1.1.2 K.Hoang 05/01/2021 Fix warnings. Optimize examples to reduce memory usage 31 | 1.2.0 K.Hoang 07/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 32 | 1.3.0 K.Hoang 25/02/2021 Add support to AVR ATMEGA_32U4 such as Leonardo, YUN, ESPLORA, etc. 33 | 1.4.0 K.Hoang 01/04/2021 Add support to Adafruit 32U4 and 328(P) such as FEATHER32U4, FEATHER328P, etc. 34 | 1.4.1 K.Hoang 02/04/2021 Add support to Sparkfun 32U4, 328(P), 128RFA1 such as AVR_PROMICRO, REDBOT, etc. 35 | 1.5.0 K.Hoang 08/05/2021 Add Timer 3 and 4 to 32U4. Add Timer auto-selection to examples. 36 | 1.6.0 K.Hoang 15/11/2021 Fix bug resulting half frequency when using high frequencies. 37 | 1.7.0 K.Hoang 19/11/2021 Fix bug resulting wrong frequency for some frequencies. 38 | 1.8.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 39 | *****************************************************************************************************************************/ 40 | 41 | #pragma once 42 | 43 | #ifndef ISR_TIMER_H 44 | #define ISR_TIMER_H 45 | 46 | #include "ISR_Timer.hpp" 47 | #include "ISR_Timer-Impl.h" 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/TimerInterrupt.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerInterrupt.h 3 | For Arduino boards (UNO, Nano, Mega, etc. ) 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | TCNTx - Timer/Counter Register. The actual timer value is stored here. 10 | OCRx - Output Compare Register 11 | ICRx - Input Capture Register (only for 16bit timer) 12 | TIMSKx - Timer/Counter Interrupt Mask Register. To enable/disable timer interrupts. 13 | TIFRx - Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt. 14 | 15 | Now with we can use these new 16 ISR-based timers, while consuming only 1 hwarware Timer. 16 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 17 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 18 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 19 | This important feature is absolutely necessary for mission-critical tasks. 20 | 21 | Version: 1.8.0 22 | 23 | Version Modified By Date Comments 24 | ------- ----------- ---------- ----------- 25 | 1.0.0 K Hoang 23/11/2019 Initial coding 26 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 27 | 1.0.2 K.Hoang 28/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 28 | 1.0.3 K.Hoang 01/12/2020 Add complex examples ISR_16_Timers_Array_Complex and ISR_16_Timers_Array_Complex 29 | 1.1.1 K.Hoang 06/12/2020 Add example Change_Interval. Bump up version to sync with other TimerInterrupt Libraries 30 | 1.1.2 K.Hoang 05/01/2021 Fix warnings. Optimize examples to reduce memory usage 31 | 1.2.0 K.Hoang 07/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 32 | 1.3.0 K.Hoang 25/02/2021 Add support to AVR ATMEGA_32U4 such as Leonardo, YUN, ESPLORA, etc. 33 | 1.4.0 K.Hoang 01/04/2021 Add support to Adafruit 32U4 and 328(P) such as FEATHER32U4, FEATHER328P, etc. 34 | 1.4.1 K.Hoang 02/04/2021 Add support to Sparkfun 32U4, 328(P), 128RFA1 such as AVR_PROMICRO, REDBOT, etc. 35 | 1.5.0 K.Hoang 08/05/2021 Add Timer 3 and 4 to 32U4. Add Timer auto-selection to examples. 36 | 1.6.0 K.Hoang 15/11/2021 Fix bug resulting half frequency when using high frequencies. 37 | 1.7.0 K.Hoang 19/11/2021 Fix bug resulting wrong frequency for some frequencies. 38 | 1.8.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 39 | ****************************************************************************************************************************/ 40 | 41 | #pragma once 42 | 43 | #ifndef TimerInterrupt_h 44 | #define TimerInterrupt_h 45 | 46 | #include "TimerInterrupt.hpp" 47 | #include "TimerInterrupt-Impl.h" 48 | 49 | #endif //#ifndef TimerInterrupt_h 50 | -------------------------------------------------------------------------------- /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 | ; MEGAAVR 15 | ; AVR 16 | ; TEENSY 17 | 18 | ; ============================================================ 19 | ;default_envs = MEGAAVR 20 | default_envs = AVR 21 | ;default_envs = TEENSY 22 | 23 | [env] 24 | ; ============================================================ 25 | ; Serial configuration 26 | ; choose upload speed, serial-monitor speed 27 | ; ============================================================ 28 | upload_speed = 921600 29 | ;upload_port = COM11 30 | ;monitor_speed = 9600 31 | ;monitor_port = COM11 32 | 33 | ; Checks for the compatibility with frameworks and dev/platforms 34 | lib_compat_mode = strict 35 | lib_ldf_mode = chain+ 36 | ;lib_ldf_mode = deep+ 37 | 38 | lib_deps = ${common.lib_deps} 39 | 40 | build_flags = 41 | 42 | [env:MEGAAVR] 43 | platform = atmelmegaavr 44 | framework = arduino 45 | 46 | ; ============================================================ 47 | ; Choose your board by uncommenting one of the following lines 48 | ; ============================================================ 49 | 50 | ; ============================================================ 51 | ; Board configuration MEGAAVR 52 | ; ============================================================ 53 | 54 | ;board = uno2018 55 | ;board = nona4809 56 | ;board = uno_wifi_rev2 57 | ;board = nano_every 58 | 59 | [env:AVR] 60 | platform = atmelavr 61 | framework = arduino 62 | 63 | ; ============================================================ 64 | ; Board configuration AVR 65 | ; ============================================================ 66 | 67 | ;board = yun 68 | ;board = uno 69 | ;board = diecimila 70 | ;board = nano 71 | ;board = nanoatmega328 72 | ;board = mega 73 | board = megaatmega2560 74 | ;board = megaADK 75 | ;board = leonardo 76 | ;board = leonardoeth 77 | ;board = micro 78 | ;board = esplora 79 | ;board = mini 80 | ;board = ethernet 81 | ;board = fio 82 | ;board = bt 83 | ;board = LilyPadUSB 84 | ;board = lilypad 85 | ;board = pro 86 | ;board = atmegang 87 | ;board = robotControl 88 | ;board = robotMotor 89 | ;board = gemma 90 | ;board = circuitplay32u4cat 91 | ;board = yunmini 92 | ;board = chiwawa 93 | ;board = one 94 | ;board = unowifi 95 | 96 | [env:TEENSY] 97 | platform = teensy 98 | framework = arduino 99 | 100 | ; ============================================================ 101 | ; Choose your board by uncommenting one of the following lines 102 | ; ============================================================ 103 | 104 | ; ============================================================ 105 | ; Board configuration MEGAAVR 106 | ; ============================================================ 107 | 108 | ;board = teensy2 109 | ;board = teensy2pp 110 | ;board = teensy30 111 | ;board = teensy31 112 | ;board = teensylc 113 | ;board = teensy35 114 | ;board = teensy36 115 | ;board = teensy40 116 | ;board = teensy41 117 | -------------------------------------------------------------------------------- /examples/Argument_Complex_Multi/Argument_Complex_Multi.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Argument_Complex_Multi.h 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // To demonstrate the usage of complex multiple files to avoid `multi definition linker` error 27 | // by using TimerInterrupt.hpp in multiple files but TimerInterrupt.h in only main file 28 | 29 | #ifndef Argument_Complex_Multi_h 30 | #define Argument_Complex_Multi_h 31 | 32 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 33 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 34 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 35 | #define TIMER_INTERRUPT_DEBUG 0 36 | #define _TIMERINTERRUPT_LOGLEVEL_ 3 37 | 38 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 39 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 40 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 41 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 42 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 43 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 44 | #define USE_TIMER_1 true 45 | #warning Using Timer1 46 | #else 47 | #define USE_TIMER_3 true 48 | #warning Using Timer3 49 | #endif 50 | 51 | // Can be included in many files without `Multiple Definitions` Linker Error 52 | #include "TimerInterrupt.hpp" 53 | 54 | #if !defined(LED_BUILTIN) 55 | #define LED_BUILTIN 13 56 | #endif 57 | 58 | struct pinStruct 59 | { 60 | unsigned int Pin1; 61 | unsigned int Pin2; 62 | unsigned int Pin3; 63 | }; 64 | 65 | #define TIMER_INTERVAL_MS 1000 66 | 67 | void TimerHandler(unsigned int outputPinsAddress); 68 | 69 | #endif // Argument_Complex_Multi_h 70 | -------------------------------------------------------------------------------- /examples/Argument_Complex_Multi/Argument_Complex_Multi.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Argument_Complex_Multi.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // To demonstrate the usage of complex multiple files to avoid `multi definition linker` error 27 | // by using TimerInterrupt.hpp in multiple files but TimerInterrupt.h in only main file 28 | 29 | // These definitions must be placed before #include 30 | #include "Argument_Complex_Multi.h" 31 | 32 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 33 | #include "TimerInterrupt.h" 34 | 35 | volatile pinStruct myOutputPins = { LED_BUILTIN, A0, A1 }; 36 | 37 | #define TIMER_INTERVAL_MS 1000 38 | 39 | void setup() 40 | { 41 | pinMode(myOutputPins.Pin1, OUTPUT); 42 | pinMode(myOutputPins.Pin2, OUTPUT); 43 | pinMode(myOutputPins.Pin3, OUTPUT); 44 | 45 | Serial.begin(115200); 46 | while (!Serial); 47 | 48 | Serial.print(F("\nStarting Argument_Complex on ")); 49 | Serial.println(BOARD_TYPE); 50 | Serial.println(TIMER_INTERRUPT_VERSION); 51 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 52 | 53 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 54 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 55 | // Timer 2 is 8-bit timer, only for higher frequency 56 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 57 | 58 | // Using ATmega328 used in UNO => 16MHz CPU clock , 59 | // For 16-bit timer 1, 3, 4 and 5, set frequency from 0.2385 to some KHz 60 | // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz 61 | 62 | #if USE_TIMER_1 63 | 64 | ITimer1.init(); 65 | 66 | // Using ATmega328 used in UNO => 16MHz CPU clock , 67 | 68 | if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, (unsigned int) &myOutputPins)) 69 | { 70 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 71 | } 72 | else 73 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 74 | 75 | #elif USE_TIMER_3 76 | 77 | ITimer3.init(); 78 | 79 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, (unsigned int) &myOutputPins)) 80 | { 81 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 82 | } 83 | else 84 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 85 | 86 | #endif 87 | } 88 | 89 | void loop() 90 | { 91 | } 92 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## TimerInterrupt Library 2 | 3 | [![arduino-library-badge](https://www.ardu-badge.com/badge/TimerInterrupt.svg?)](https://www.ardu-badge.com/TimerInterrupt) 4 | [![GitHub release](https://img.shields.io/github/release/khoih-prog/TimerInterrupt.svg)](https://github.com/khoih-prog/TimerInterrupt/releases) 5 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/TimerInterrupt/blob/master/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/TimerInterrupt.svg)](http://github.com/khoih-prog/TimerInterrupt/issues) 8 | 9 | 10 | Donate to my libraries using BuyMeACoffee 11 | 12 | 13 | 14 | --- 15 | --- 16 | 17 | ## Table of Contents 18 | 19 | * [Changelog](#changelog) 20 | * [Releases v1.8.0](#releases-v180) 21 | * [Releases v1.7.0](#releases-v170) 22 | * [Releases v1.6.0](#releases-v160) 23 | * [Releases v1.5.0](#releases-v150) 24 | * [Releases v1.4.1](#releases-v141) 25 | * [Releases v1.4.0](#releases-v140) 26 | * [Releases v1.3.0](#releases-v130) 27 | * [Releases v1.2.0](#releases-v120) 28 | * [Releases v1.1.2](#releases-v112) 29 | * [Releases v1.1.1](#releases-v111) 30 | * [Releases v1.0.3](#releases-v103) 31 | * [Releases v1.0.2](#releases-v102) 32 | 33 | --- 34 | --- 35 | 36 | ## Changelog 37 | 38 | ### Releases v1.8.0 39 | 40 | 1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories 41 | 2. Add example `Argument_Complex_Multi` to demonstrate how to avoid `multiple-definitions` linker error in multiple-file projects 42 | 43 | ### Releases v1.7.0 44 | 45 | 1. Fix bug resulting wrong frequency for some low frequencies. 46 | 47 | ### Releases v1.6.0 48 | 49 | 1. Fix bug resulting half frequency when using high frequencies. Check [Frequencies are half of what I expect #22](https://github.com/khoih-prog/TimerInterrupt/discussions/22) 50 | 2. Add example to demo High Frequency 51 | 3. Delete Blynk-related examples 52 | 4. Add changelog.md 53 | 54 | ### Releases v1.5.0 55 | 56 | 1. Add **Timer3 and Timer4 support to ATmega32U4 and ATmega16U4.** 57 | 2. Add Timer auto-selection to examples. 58 | 59 | ### Releases v1.4.1 60 | 61 | 1. Add support to **Generic or Sparkfun AVR ATmega_32U4** such as **AVR_MAKEYMAKEY, AVR_PROMICRO, etc.** 62 | 2. Add support to **Generic or Sparkfun AVR ATmega_328(P)** such as **ARDUINO_REDBOT, ARDUINO_AVR_DIGITAL_SANDBOX, etc.** 63 | 3. Add support to **Generic or Sparkfun AVR ATmega128RFA1** such as **ATMEGA128RFA1_DEV_BOARD, etc.** 64 | 65 | ### Releases v1.4.0 66 | 67 | 1. Add support to **Adafruit AVR ATMEGA_32U4** such as **AVR_FLORA8, AVR_FEATHER32U4, etc.** 68 | 2. Add support to **Adafruit AVR ATMEGA_328(P)** such as **AVR_FEATHER328P, AVR_METRO, etc.** 69 | 3. Update examples 70 | 71 | ### Releases v1.3.0 72 | 73 | 1. Add support to **AVR ATMEGA_16U4, ATMEGA_32U4** such as **Leonardo, YUN, ESPLORA, etc.** 74 | 2. Update examples 75 | 76 | ### Releases v1.2.0 77 | 78 | 1. Add better debug feature. 79 | 2. Optimize code and examples to reduce RAM usage 80 | 81 | 82 | ### Releases v1.1.2 83 | 84 | 1. Clean-up all compiler warnings possible. 85 | 2. Optimize examples to reduce memory usage by using Flash String whenever possible. 86 | 3. Add Table of Contents 87 | 88 | ### Releases v1.1.1 89 | 90 | 1. Add example [**Change_Interval**](examples/Change_Interval) 91 | 2. Bump up version to sync with other TimerInterrupt Libraries. Modify Version String. 92 | 3. Add new h-only code besides conventional h/cpp code 93 | 94 | ### Releases v1.0.3 95 | 96 | 1. Add example [**ISR_16_Timers_Array_Complex**](examples/ISR_16_Timers_Array_Complex) and optimize example [**ISR_Timers_Array_Simple**](examples/ISR_Timers_Array_Simple) to demonstrate the usage of **16 ISR-based timers** 97 | 98 | ### Releases v1.0.2 99 | 100 | 1. Initial releases. 101 | 102 | 103 | -------------------------------------------------------------------------------- /examples/Argument_None/Argument_None.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Argument_None.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 27 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 28 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 29 | #define TIMER_INTERRUPT_DEBUG 0 30 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 31 | 32 | #define USE_TIMER_1 true 33 | 34 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 35 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 36 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 37 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 38 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 39 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 40 | #define USE_TIMER_2 true 41 | #warning Using Timer1, Timer2 42 | #else 43 | #define USE_TIMER_3 true 44 | #warning Using Timer1, Timer3 45 | #endif 46 | 47 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 48 | #include "TimerInterrupt.h" 49 | 50 | #define TIMER1_INTERVAL_MS 1000 51 | #define TIMER_INTERVAL_MS 2000 52 | 53 | #ifndef LED_BUILTIN 54 | #define LED_BUILTIN 13 55 | #endif 56 | 57 | void TimerHandler1(void) 58 | { 59 | static bool toggle1 = false; 60 | 61 | //timer interrupt toggles pin LED_BUILTIN 62 | digitalWrite(LED_BUILTIN, toggle1); 63 | toggle1 = !toggle1; 64 | } 65 | 66 | void TimerHandler(void) 67 | { 68 | static bool toggle = false; 69 | 70 | //timer interrupt toggles outputPin 71 | digitalWrite(A0, toggle); 72 | toggle = !toggle; 73 | } 74 | 75 | void setup() 76 | { 77 | pinMode(LED_BUILTIN, OUTPUT); 78 | pinMode(A0, OUTPUT); 79 | 80 | Serial.begin(115200); 81 | while (!Serial); 82 | 83 | Serial.print(F("\nStarting Argument_None on ")); 84 | Serial.println(BOARD_TYPE); 85 | Serial.println(TIMER_INTERRUPT_VERSION); 86 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 87 | 88 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 89 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 90 | // Timer 2 is 8-bit timer, only for higher frequency 91 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 92 | 93 | ITimer1.init(); 94 | 95 | // Using ATmega328 used in UNO => 16MHz CPU clock , 96 | // For 16-bit timer 1, 3, 4 and 5, set frequency from 0.2385 to some KHz 97 | // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz 98 | 99 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler1)) 100 | { 101 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 102 | } 103 | else 104 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 105 | 106 | #if USE_TIMER_2 107 | 108 | // Select Timer 1-2 for UNO, 0-5 for MEGA, 1,3,4 for 32u4 109 | // Timer 2 is 8-bit timer, only for higher frequency 110 | ITimer2.init(); 111 | 112 | if (ITimer2.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 113 | { 114 | Serial.print(F("Starting ITimer2 OK, millis() = ")); Serial.println(millis()); 115 | } 116 | else 117 | Serial.println(F("Can't set ITimer2. Select another freq. or timer")); 118 | 119 | #elif USE_TIMER_3 120 | 121 | // Select Timer 1-2 for UNO, 0-5 for MEGA, 1,3,4 for 32u4 122 | // Timer 3 is 16-bit timer 123 | ITimer3.init(); 124 | 125 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 126 | { 127 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 128 | } 129 | else 130 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 131 | 132 | #endif 133 | } 134 | 135 | void loop() 136 | { 137 | 138 | } 139 | -------------------------------------------------------------------------------- /examples/Argument_Complex/Argument_Complex.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Argument_Complex.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 27 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 28 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 29 | #define TIMER_INTERRUPT_DEBUG 0 30 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 31 | 32 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 33 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 34 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 35 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 36 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 37 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 38 | #define USE_TIMER_1 true 39 | #warning Using Timer1 40 | #else 41 | #define USE_TIMER_3 true 42 | #warning Using Timer3 43 | #endif 44 | 45 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 46 | #include "TimerInterrupt.h" 47 | 48 | #if !defined(LED_BUILTIN) 49 | #define LED_BUILTIN 13 50 | #endif 51 | 52 | struct pinStruct 53 | { 54 | unsigned int Pin1; 55 | unsigned int Pin2; 56 | unsigned int Pin3; 57 | }; 58 | 59 | volatile pinStruct myOutputPins = { LED_BUILTIN, A0, A1 }; 60 | 61 | void TimerHandler(unsigned int outputPinsAddress) 62 | { 63 | static bool toggle = false; 64 | 65 | //timer interrupt toggles pins 66 | #if (TIMER_INTERRUPT_DEBUG > 1) 67 | Serial.print("Toggle pin1 = "); Serial.println( ((pinStruct *) outputPinsAddress)->Pin1 ); 68 | #endif 69 | 70 | digitalWrite(((pinStruct *) outputPinsAddress)->Pin1, toggle); 71 | 72 | #if (TIMER_INTERRUPT_DEBUG > 1) 73 | Serial.print("Read pin2 A0 ("); Serial.print(((pinStruct *) outputPinsAddress)->Pin2 ); 74 | Serial.print(") = "); 75 | Serial.println(digitalRead(((pinStruct *) outputPinsAddress)->Pin2) ? "HIGH" : "LOW" ); 76 | 77 | Serial.print("Read pin3 A1 ("); Serial.print(((pinStruct *) outputPinsAddress)->Pin3 ); 78 | Serial.print(") = "); 79 | Serial.println(digitalRead(((pinStruct *) outputPinsAddress)->Pin3) ? "HIGH" : "LOW" ); 80 | #endif 81 | 82 | toggle = !toggle; 83 | } 84 | 85 | #define TIMER_INTERVAL_MS 1000 86 | 87 | void setup() 88 | { 89 | pinMode(myOutputPins.Pin1, OUTPUT); 90 | pinMode(myOutputPins.Pin2, OUTPUT); 91 | pinMode(myOutputPins.Pin3, OUTPUT); 92 | 93 | Serial.begin(115200); 94 | while (!Serial); 95 | 96 | Serial.print(F("\nStarting Argument_Complex on ")); 97 | Serial.println(BOARD_TYPE); 98 | Serial.println(TIMER_INTERRUPT_VERSION); 99 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 100 | 101 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 102 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 103 | // Timer 2 is 8-bit timer, only for higher frequency 104 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 105 | 106 | // Using ATmega328 used in UNO => 16MHz CPU clock , 107 | // For 16-bit timer 1, 3, 4 and 5, set frequency from 0.2385 to some KHz 108 | // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz 109 | 110 | #if USE_TIMER_1 111 | 112 | ITimer1.init(); 113 | 114 | // Using ATmega328 used in UNO => 16MHz CPU clock , 115 | 116 | if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, (unsigned int) &myOutputPins)) 117 | { 118 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 119 | } 120 | else 121 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 122 | 123 | #elif USE_TIMER_3 124 | 125 | ITimer3.init(); 126 | 127 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, (unsigned int) &myOutputPins)) 128 | { 129 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 130 | } 131 | else 132 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 133 | 134 | #endif 135 | } 136 | 137 | void loop() 138 | { 139 | } 140 | -------------------------------------------------------------------------------- /examples/RPM_Measure/RPM_Measure.ino: -------------------------------------------------------------------------------- 1 | /************************************************ 2 | RPM_Measure.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | /* RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms 26 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor 27 | Asssuming LOW is active. 28 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing 29 | If the time between active state is less than 8ms => consider noise. 30 | RPM = 60000 / (rotation time in ms) 31 | 32 | You can also use interrupt to detect whenever the SW is active, set a flag 33 | then use timer to count the time between active state 34 | */ 35 | 36 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 37 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 38 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 39 | #define TIMER_INTERRUPT_DEBUG 0 40 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 41 | 42 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 43 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 44 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 45 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 46 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 47 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 48 | #define USE_TIMER_1 true 49 | #warning Using Timer1 50 | #else 51 | #define USE_TIMER_3 true 52 | #warning Using Timer3 53 | #endif 54 | 55 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 56 | #include "TimerInterrupt.h" 57 | 58 | unsigned int SWPin = A0; 59 | 60 | #define TIMER_INTERVAL_MS 1 61 | #define DEBOUNCING_INTERVAL_MS 80 62 | 63 | #define LOCAL_DEBUG 1 64 | 65 | volatile unsigned long rotationTime = 0; 66 | float RPM = 0.00; 67 | float avgRPM = 0.00; 68 | 69 | volatile int debounceCounter; 70 | 71 | #define KAVG 100 72 | 73 | void TimerHandler() 74 | { 75 | if ( !digitalRead(SWPin) && (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS ) ) 76 | { 77 | //min time between pulses has passed 78 | RPM = (float) ( 60000.0f / ( rotationTime * TIMER_INTERVAL_MS ) ); 79 | 80 | avgRPM = ( 2 * avgRPM + RPM) / 3, 81 | 82 | #if (TIMER_INTERRUPT_DEBUG > 1) 83 | Serial.print("RPM = "); Serial.print(avgRPM); 84 | Serial.print(", rotationTime ms = "); Serial.println(rotationTime * TIMER_INTERVAL_MS); 85 | #endif 86 | 87 | rotationTime = 0; 88 | debounceCounter = 0; 89 | } 90 | else 91 | { 92 | debounceCounter++; 93 | } 94 | 95 | if (rotationTime >= 5000) 96 | { 97 | // If idle, set RPM to 0, don't increase rotationTime 98 | RPM = 0; 99 | 100 | #if (TIMER_INTERRUPT_DEBUG > 1) 101 | Serial.print("RPM = "); Serial.print(RPM); Serial.print(", rotationTime = "); Serial.println(rotationTime); 102 | #endif 103 | 104 | rotationTime = 0; 105 | } 106 | else 107 | { 108 | rotationTime++; 109 | } 110 | } 111 | 112 | void setup() 113 | { 114 | pinMode(SWPin, INPUT_PULLUP); 115 | 116 | Serial.begin(115200); 117 | while (!Serial); 118 | 119 | Serial.print(F("\nStarting RPM_Measure on ")); 120 | Serial.println(BOARD_TYPE); 121 | Serial.println(TIMER_INTERRUPT_VERSION); 122 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 123 | 124 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 125 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 126 | // Timer 2 is 8-bit timer, only for higher frequency 127 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 128 | 129 | #if USE_TIMER_1 130 | 131 | ITimer1.init(); 132 | 133 | // Using ATmega328 used in UNO => 16MHz CPU clock , 134 | 135 | if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 136 | { 137 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 138 | } 139 | else 140 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 141 | 142 | #elif USE_TIMER_3 143 | 144 | ITimer3.init(); 145 | 146 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 147 | { 148 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 149 | } 150 | else 151 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 152 | 153 | #endif 154 | } 155 | 156 | void loop() 157 | { 158 | 159 | } 160 | -------------------------------------------------------------------------------- /examples/TimerDuration/TimerDuration.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerDuration.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 27 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 28 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 29 | #define TIMER_INTERRUPT_DEBUG 2 30 | #define _TIMERINTERRUPT_LOGLEVEL_ 4 31 | 32 | #define USE_TIMER_1 true 33 | 34 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 35 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 36 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 37 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 38 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 39 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 40 | #define USE_TIMER_2 true 41 | #warning Using Timer1 42 | #else 43 | #define USE_TIMER_3 true 44 | #warning Using Timer3 45 | #endif 46 | 47 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 48 | #include "TimerInterrupt.h" 49 | 50 | #if !defined(LED_BUILTIN) 51 | #define LED_BUILTIN 13 52 | #endif 53 | 54 | #if USE_TIMER_1 55 | 56 | void TimerHandler1(unsigned int outputPin = LED_BUILTIN) 57 | { 58 | static bool toggle1 = false; 59 | 60 | #if (TIMER_INTERRUPT_DEBUG > 1) 61 | Serial.print("ITimer1 called, millis() = "); Serial.println(millis()); 62 | #endif 63 | 64 | //timer interrupt toggles pin LED_BUILTIN 65 | digitalWrite(outputPin, toggle1); 66 | toggle1 = !toggle1; 67 | } 68 | 69 | #endif 70 | 71 | #if (USE_TIMER_2 || USE_TIMER_3) 72 | 73 | void TimerHandler(unsigned int outputPin = LED_BUILTIN) 74 | { 75 | static bool toggle = false; 76 | 77 | #if (TIMER_INTERRUPT_DEBUG > 1) 78 | #if USE_TIMER_2 79 | Serial.print("ITimer2 called, millis() = "); 80 | #elif USE_TIMER_3 81 | Serial.print("ITimer3 called, millis() = "); 82 | #endif 83 | 84 | Serial.println(millis()); 85 | #endif 86 | 87 | //timer interrupt toggles outputPin 88 | digitalWrite(outputPin, toggle); 89 | toggle = !toggle; 90 | } 91 | 92 | #endif 93 | 94 | unsigned int outputPin1 = LED_BUILTIN; 95 | unsigned int outputPin = A0; 96 | 97 | #define TIMER1_INTERVAL_MS 10000 98 | #define TIMER1_FREQUENCY (float) (1000.0f / TIMER1_INTERVAL_MS) 99 | #define TIMER1_DURATION_MS 0 //(10 * TIMER1_INTERVAL_MS) 100 | 101 | #define TIMER_INTERVAL_MS 13000 102 | #define TIMER_FREQUENCY (float) (1000.0f / TIMER_INTERVAL_MS) 103 | #define TIMER_DURATION_MS 0 //(20 * TIMER_INTERVAL_MS) 104 | 105 | void setup() 106 | { 107 | pinMode(outputPin1, OUTPUT); 108 | pinMode(outputPin, OUTPUT); 109 | 110 | Serial.begin(115200); 111 | while (!Serial); 112 | 113 | Serial.print(F("\nStarting TimerDuration on ")); 114 | Serial.println(BOARD_TYPE); 115 | Serial.println(TIMER_INTERRUPT_VERSION); 116 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 117 | 118 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 119 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 120 | // Timer 2 is 8-bit timer, only for higher frequency 121 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 122 | 123 | #if USE_TIMER_1 124 | 125 | ITimer1.init(); 126 | 127 | // Using ATmega328 used in UNO => 16MHz CPU clock , 128 | 129 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler1, outputPin1, TIMER1_DURATION_MS)) 130 | { 131 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 132 | } 133 | else 134 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 135 | 136 | #endif 137 | 138 | #if USE_TIMER_2 139 | 140 | ITimer2.init(); 141 | 142 | if (ITimer2.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, outputPin, TIMER_DURATION_MS)) 143 | { 144 | Serial.print(F("Starting ITimer2 OK, millis() = ")); Serial.println(millis()); 145 | } 146 | else 147 | Serial.println(F("Can't set ITimer2. Select another freq. or timer")); 148 | 149 | #elif USE_TIMER_3 150 | 151 | ITimer3.init(); 152 | 153 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, outputPin, TIMER_DURATION_MS)) 154 | { 155 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 156 | } 157 | else 158 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 159 | 160 | #endif 161 | } 162 | 163 | void loop() 164 | { 165 | } 166 | -------------------------------------------------------------------------------- /src/TimerInterrupt_Generic_Debug.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerInterrupt_Generic_Debug.h 3 | For Arduino boards (UNO, Nano, Mega, etc. ) 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | TCNTx - Timer/Counter Register. The actual timer value is stored here. 10 | OCRx - Output Compare Register 11 | ICRx - Input Capture Register (only for 16bit timer) 12 | TIMSKx - Timer/Counter Interrupt Mask Register. To enable/disable timer interrupts. 13 | TIFRx - Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt. 14 | 15 | Now with we can use these new 16 ISR-based timers, while consuming only 1 hwarware Timer. 16 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 17 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 18 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 19 | This important feature is absolutely necessary for mission-critical tasks. 20 | 21 | Version: 1.8.0 22 | 23 | Version Modified By Date Comments 24 | ------- ----------- ---------- ----------- 25 | 1.0.0 K Hoang 23/11/2019 Initial coding 26 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 27 | 1.0.2 K.Hoang 28/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 28 | 1.0.3 K.Hoang 01/12/2020 Add complex examples ISR_16_Timers_Array_Complex and ISR_16_Timers_Array_Complex 29 | 1.1.1 K.Hoang 06/12/2020 Add example Change_Interval. Bump up version to sync with other TimerInterrupt Libraries 30 | 1.1.2 K.Hoang 05/01/2021 Fix warnings. Optimize examples to reduce memory usage 31 | 1.2.0 K.Hoang 07/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 32 | 1.3.0 K.Hoang 25/02/2021 Add support to AVR ATMEGA_32U4 such as Leonardo, YUN, ESPLORA, etc. 33 | 1.4.0 K.Hoang 01/04/2021 Add support to Adafruit 32U4 and 328(P) such as FEATHER32U4, FEATHER328P, etc. 34 | 1.4.1 K.Hoang 02/04/2021 Add support to Sparkfun 32U4, 328(P), 128RFA1 such as AVR_PROMICRO, REDBOT, etc. 35 | 1.5.0 K.Hoang 08/05/2021 Add Timer 3 and 4 to 32U4. Add Timer auto-selection to examples. 36 | 1.6.0 K.Hoang 15/11/2021 Fix bug resulting half frequency when using high frequencies. 37 | 1.7.0 K.Hoang 19/11/2021 Fix bug resulting wrong frequency for some frequencies. 38 | 1.8.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 39 | *****************************************************************************************************************************/ 40 | 41 | #pragma once 42 | 43 | #ifndef TIMERINTERRUPT_GENERIC_DEBUG_H 44 | #define TIMERINTERRUPT_GENERIC_DEBUG_H 45 | 46 | #ifdef TIMERINTERRUPT_DEBUG_PORT 47 | #define TISR_DBG_PORT TIMERINTERRUPT_DEBUG_PORT 48 | #else 49 | #define TISR_DBG_PORT Serial 50 | #endif 51 | 52 | // Change _TIMERINTERRUPT_LOGLEVEL_ to set tracing and logging verbosity 53 | // 0: DISABLED: no logging 54 | // 1: ERROR: errors 55 | // 2: WARN: errors and warnings 56 | // 3: INFO: errors, warnings and informational (default) 57 | // 4: DEBUG: errors, warnings, informational and debug 58 | 59 | #ifndef _TIMERINTERRUPT_LOGLEVEL_ 60 | #define _TIMERINTERRUPT_LOGLEVEL_ 1 61 | #endif 62 | 63 | ///////////////////////////////////////////////////////// 64 | 65 | const char TISR_MARK[] = "[TISR] "; 66 | 67 | #define TISR_PRINT_MARK TISR_PRINT(TISR_MARK) 68 | #define TISR_PRINT_SP TISR_DBG_PORT.print(" ") 69 | 70 | #define TISR_PRINT TISR_DBG_PORT.print 71 | #define TISR_PRINTLN TISR_DBG_PORT.println 72 | 73 | ///////////////////////////////////////////////////////// 74 | 75 | #define TISR_LOGERROR(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_PRINT_MARK; TISR_PRINTLN(x); } 76 | #define TISR_LOGERROR0(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_PRINT(x); } 77 | #define TISR_LOGERROR1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINTLN(y); } 78 | #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); } 79 | #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); } 80 | 81 | ///////////////////////////////////////////////////////// 82 | 83 | #define TISR_LOGWARN(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_PRINT_MARK; TISR_PRINTLN(x); } 84 | #define TISR_LOGWARN0(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_PRINT(x); } 85 | #define TISR_LOGWARN1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINTLN(y); } 86 | #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); } 87 | #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); } 88 | 89 | ///////////////////////////////////////////////////////// 90 | 91 | #define TISR_LOGINFO(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_PRINT_MARK; TISR_PRINTLN(x); } 92 | #define TISR_LOGINFO0(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_PRINT(x); } 93 | #define TISR_LOGINFO1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINTLN(y); } 94 | #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); } 95 | #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); } 96 | 97 | ///////////////////////////////////////////////////////// 98 | 99 | #define TISR_LOGDEBUG(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_PRINT_MARK; TISR_PRINTLN(x); } 100 | #define TISR_LOGDEBUG0(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_PRINT(x); } 101 | #define TISR_LOGDEBUG1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_PRINT_MARK; TISR_PRINT(x); TISR_PRINT_SP; TISR_PRINTLN(y); } 102 | #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); } 103 | #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); } 104 | 105 | ///////////////////////////////////////////////////////// 106 | 107 | #endif //TIMERINTERRUPT_GENERIC_DEBUG_H 108 | -------------------------------------------------------------------------------- /examples/Argument_Simple/Argument_Simple.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Argument_Simple.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 27 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 28 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 29 | #define TIMER_INTERRUPT_DEBUG 0 30 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 31 | 32 | #define USE_TIMER_1 true 33 | 34 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 35 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 36 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 37 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 38 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 39 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 40 | #define USE_TIMER_2 true 41 | #warning Using Timer1 42 | #else 43 | #define USE_TIMER_3 true 44 | #warning Using Timer3 45 | #endif 46 | 47 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 48 | #include "TimerInterrupt.h" 49 | 50 | #if !defined(LED_BUILTIN) 51 | #define LED_BUILTIN 13 52 | #endif 53 | 54 | unsigned int outputPin1 = LED_BUILTIN; 55 | unsigned int outputPin = A0; 56 | 57 | #define TIMER1_INTERVAL_MS 1000 58 | 59 | void TimerHandler1(unsigned int outputPin = LED_BUILTIN) 60 | { 61 | static bool toggle1 = false; 62 | static bool started = false; 63 | 64 | if (!started) 65 | { 66 | started = true; 67 | pinMode(outputPin, OUTPUT); 68 | } 69 | 70 | #if (TIMER_INTERRUPT_DEBUG > 1) 71 | //timer interrupt toggles pin outputPin, default LED_BUILTIN 72 | Serial.print("pin1 = "); Serial.print(outputPin); 73 | Serial.print(" address: "); Serial.println((uint32_t) &outputPin ); 74 | #endif 75 | 76 | digitalWrite(outputPin, toggle1); 77 | toggle1 = !toggle1; 78 | } 79 | 80 | #define TIMER_INTERVAL_MS 2000 81 | 82 | void TimerHandler(unsigned int outputPin = LED_BUILTIN) 83 | { 84 | static bool toggle = false; 85 | static bool started = false; 86 | 87 | if (!started) 88 | { 89 | started = true; 90 | pinMode(outputPin, OUTPUT); 91 | } 92 | 93 | //timer interrupt toggles pin outputPin, default LED_BUILTIN 94 | digitalWrite(outputPin, toggle); 95 | toggle = !toggle; 96 | } 97 | 98 | void setup() 99 | { 100 | Serial.begin(115200); 101 | while (!Serial); 102 | 103 | Serial.print(F("\nStarting Argument_Simple on ")); 104 | Serial.println(BOARD_TYPE); 105 | Serial.println(TIMER_INTERRUPT_VERSION); 106 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 107 | 108 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 109 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 110 | // Timer 2 is 8-bit timer, only for higher frequency 111 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 112 | 113 | ITimer1.init(); 114 | 115 | // Using ATmega328 used in UNO => 16MHz CPU clock , 116 | // For 16-bit timer 1, 3, 4 and 5, set frequency from 0.2385 to some KHz 117 | // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz 118 | 119 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler1, outputPin1)) 120 | { 121 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 122 | 123 | #if (TIMER_INTERRUPT_DEBUG > 1) 124 | Serial.print(F("OutputPin1 = ")); Serial.print(outputPin1); 125 | Serial.print(F(" address: ")); Serial.println((uint32_t) &outputPin1 ); 126 | #endif 127 | } 128 | else 129 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 130 | 131 | #if USE_TIMER_2 132 | 133 | ITimer2.init(); 134 | 135 | if (ITimer2.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, outputPin)) 136 | { 137 | Serial.print(F("Starting ITimer2 OK, millis() = ")); Serial.println(millis()); 138 | 139 | #if (TIMER_INTERRUPT_DEBUG > 1) 140 | Serial.print(F("OutputPin = ")); Serial.print(outputPin); 141 | Serial.print(F(" address: ")); Serial.println((uint32_t) &outputPin ); 142 | #endif 143 | } 144 | else 145 | Serial.println(F("Can't set ITimer2. Select another freq. or timer")); 146 | 147 | #elif USE_TIMER_3 148 | 149 | ITimer3.init(); 150 | 151 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, outputPin)) 152 | { 153 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 154 | 155 | #if (TIMER_INTERRUPT_DEBUG > 1) 156 | Serial.print(F("OutputPin = ")); Serial.print(outputPin); 157 | Serial.print(F(" address: ")); Serial.println((uint32_t) &outputPin ); 158 | #endif 159 | } 160 | else 161 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 162 | 163 | #endif 164 | } 165 | 166 | void loop() 167 | { 168 | } 169 | -------------------------------------------------------------------------------- /examples/ISR_RPM_Measure/ISR_RPM_Measure.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_RPM_Measure.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | /* RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms 26 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor 27 | Asssuming LOW is active. 28 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing 29 | If the time between active state is less than 8ms => consider noise. 30 | RPM = 60000 / (rotation time in ms) 31 | 32 | We use interrupt to detect whenever the SW is active, set a flag 33 | then use timer to count the time between active state 34 | */ 35 | 36 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 37 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 38 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 39 | #define TIMER_INTERRUPT_DEBUG 0 40 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 41 | 42 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 43 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 44 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 45 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 46 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 47 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 48 | #define USE_TIMER_1 true 49 | #warning Using Timer1 50 | #else 51 | #define USE_TIMER_3 true 52 | #warning Using Timer3 53 | #endif 54 | 55 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 56 | #include "TimerInterrupt.h" 57 | 58 | #if !defined(LED_BUILTIN) 59 | #define LED_BUILTIN 13 60 | #endif 61 | 62 | unsigned int interruptPin = 2; 63 | 64 | #define TIMER_INTERVAL_MS 1 65 | #define DEBOUNCING_INTERVAL_MS 80 66 | 67 | #define LOCAL_DEBUG 1 68 | 69 | volatile unsigned long rotationTime = 0; 70 | float RPM = 0.00; 71 | float avgRPM = 0.00; 72 | 73 | volatile int debounceCounter; 74 | 75 | #define KAVG 100 76 | 77 | volatile bool activeState = false; 78 | 79 | void detectRotation(void) 80 | { 81 | activeState = true; 82 | } 83 | 84 | void TimerHandler() 85 | { 86 | if ( activeState ) 87 | { 88 | // Reset to prepare for next round of interrupt 89 | activeState = false; 90 | 91 | if (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS ) 92 | { 93 | 94 | //min time between pulses has passed 95 | RPM = (float) ( 60000.0f / ( rotationTime * TIMER_INTERVAL_MS ) ); 96 | 97 | avgRPM = ( 2 * avgRPM + RPM) / 3, 98 | 99 | #if (TIMER_INTERRUPT_DEBUG > 1) 100 | Serial.print("RPM = "); Serial.print(avgRPM); 101 | Serial.print(", rotationTime ms = "); Serial.println(rotationTime * TIMER_INTERVAL_MS); 102 | #endif 103 | 104 | rotationTime = 0; 105 | debounceCounter = 0; 106 | } 107 | else 108 | debounceCounter++; 109 | } 110 | else 111 | { 112 | debounceCounter++; 113 | } 114 | 115 | if (rotationTime >= 5000) 116 | { 117 | // If idle, set RPM to 0, don't increase rotationTime 118 | RPM = 0; 119 | 120 | #if (TIMER_INTERRUPT_DEBUG > 1) 121 | Serial.print("RPM = "); Serial.print(RPM); Serial.print(", rotationTime = "); Serial.println(rotationTime); 122 | #endif 123 | 124 | rotationTime = 0; 125 | } 126 | else 127 | { 128 | rotationTime++; 129 | } 130 | } 131 | 132 | 133 | void setup() 134 | { 135 | Serial.begin(115200); 136 | while (!Serial); 137 | 138 | Serial.print(F("\nStarting ISR_RPM_Measure on ")); 139 | Serial.println(BOARD_TYPE); 140 | Serial.println(TIMER_INTERRUPT_VERSION); 141 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 142 | 143 | pinMode(LED_BUILTIN, OUTPUT); 144 | pinMode(interruptPin, INPUT_PULLUP); 145 | 146 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 147 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 148 | // Timer 2 is 8-bit timer, only for higher frequency 149 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 150 | 151 | #if USE_TIMER_1 152 | 153 | ITimer1.init(); 154 | 155 | // Using ATmega328 used in UNO => 16MHz CPU clock , 156 | 157 | if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 158 | { 159 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 160 | } 161 | else 162 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 163 | 164 | #elif USE_TIMER_3 165 | 166 | ITimer3.init(); 167 | 168 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 169 | { 170 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 171 | } 172 | else 173 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 174 | 175 | #endif 176 | 177 | // Assumming the interruptPin will go LOW 178 | attachInterrupt(digitalPinToInterrupt(interruptPin), detectRotation, FALLING); 179 | } 180 | 181 | void loop() 182 | { 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/ISR_Timer.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_Timer.hpp 3 | For Arduino boards (UNO, Nano, Mega, etc. ) 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | TCNTx - Timer/Counter Register. The actual timer value is stored here. 10 | OCRx - Output Compare Register 11 | ICRx - Input Capture Register (only for 16bit timer) 12 | TIMSKx - Timer/Counter Interrupt Mask Register. To enable/disable timer interrupts. 13 | TIFRx - Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt. 14 | 15 | Now with we can use these new 16 ISR-based timers, while consuming only 1 hwarware Timer. 16 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 17 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 18 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 19 | This important feature is absolutely necessary for mission-critical tasks. 20 | 21 | Version: 1.8.0 22 | 23 | Version Modified By Date Comments 24 | ------- ----------- ---------- ----------- 25 | 1.0.0 K Hoang 23/11/2019 Initial coding 26 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 27 | 1.0.2 K.Hoang 28/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 28 | 1.0.3 K.Hoang 01/12/2020 Add complex examples ISR_16_Timers_Array_Complex and ISR_16_Timers_Array_Complex 29 | 1.1.1 K.Hoang 06/12/2020 Add example Change_Interval. Bump up version to sync with other TimerInterrupt Libraries 30 | 1.1.2 K.Hoang 05/01/2021 Fix warnings. Optimize examples to reduce memory usage 31 | 1.2.0 K.Hoang 07/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 32 | 1.3.0 K.Hoang 25/02/2021 Add support to AVR ATMEGA_32U4 such as Leonardo, YUN, ESPLORA, etc. 33 | 1.4.0 K.Hoang 01/04/2021 Add support to Adafruit 32U4 and 328(P) such as FEATHER32U4, FEATHER328P, etc. 34 | 1.4.1 K.Hoang 02/04/2021 Add support to Sparkfun 32U4, 328(P), 128RFA1 such as AVR_PROMICRO, REDBOT, etc. 35 | 1.5.0 K.Hoang 08/05/2021 Add Timer 3 and 4 to 32U4. Add Timer auto-selection to examples. 36 | 1.6.0 K.Hoang 15/11/2021 Fix bug resulting half frequency when using high frequencies. 37 | 1.7.0 K.Hoang 19/11/2021 Fix bug resulting wrong frequency for some frequencies. 38 | 1.8.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 39 | *****************************************************************************************************************************/ 40 | 41 | #pragma once 42 | 43 | #ifndef ISR_TIMER_HPP 44 | #define ISR_TIMER_HPP 45 | 46 | #ifndef TIMER_INTERRUPT_VERSION 47 | #define TIMER_INTERRUPT_VERSION "TimerInterrupt v1.8.0" 48 | 49 | #define TIMER_INTERRUPT_VERSION_MAJOR 1 50 | #define TIMER_INTERRUPT_VERSION_MINOR 8 51 | #define TIMER_INTERRUPT_VERSION_PATCH 0 52 | 53 | #define TIMER_INTERRUPT_VERSION_INT 1008000 54 | #endif 55 | 56 | #include "TimerInterrupt_Generic_Debug.h" 57 | 58 | #include 59 | #include 60 | 61 | #if defined(ARDUINO) 62 | #if ARDUINO >= 100 63 | #include 64 | #else 65 | #include 66 | #endif 67 | #endif 68 | 69 | typedef void (*timer_callback)(void); 70 | typedef void (*timer_callback_p)(void *); 71 | 72 | class ISR_Timer 73 | { 74 | public: 75 | // maximum number of timers 76 | const static int MAX_TIMERS = 16; 77 | 78 | // setTimer() constants 79 | const static int RUN_FOREVER = 0; 80 | const static int RUN_ONCE = 1; 81 | 82 | // constructor 83 | ISR_Timer(); 84 | 85 | void init(); 86 | 87 | // this function must be called inside loop() 88 | void run(); 89 | 90 | // Timer will call function 'f' every 'd' milliseconds forever 91 | // returns the timer number (numTimer) on success or 92 | // -1 on failure (f == NULL) or no free timers 93 | int setInterval(unsigned long d, timer_callback f); 94 | 95 | // Timer will call function 'f' with parameter 'p' every 'd' milliseconds forever 96 | // returns the timer number (numTimer) on success or 97 | // -1 on failure (f == NULL) or no free timers 98 | int setInterval(unsigned long d, timer_callback_p f, void* p); 99 | 100 | // Timer will call function 'f' after 'd' milliseconds one time 101 | // returns the timer number (numTimer) on success or 102 | // -1 on failure (f == NULL) or no free timers 103 | int setTimeout(unsigned long d, timer_callback f); 104 | 105 | // Timer will call function 'f' with parameter 'p' after 'd' milliseconds one time 106 | // returns the timer number (numTimer) on success or 107 | // -1 on failure (f == NULL) or no free timers 108 | int setTimeout(unsigned long d, timer_callback_p f, void* p); 109 | 110 | // Timer will call function 'f' every 'd' milliseconds 'n' times 111 | // returns the timer number (numTimer) on success or 112 | // -1 on failure (f == NULL) or no free timers 113 | int setTimer(unsigned long d, timer_callback f, unsigned n); 114 | 115 | // Timer will call function 'f' with parameter 'p' every 'd' milliseconds 'n' times 116 | // returns the timer number (numTimer) on success or 117 | // -1 on failure (f == NULL) or no free timers 118 | int setTimer(unsigned long d, timer_callback_p f, void* p, unsigned n); 119 | 120 | // updates interval of the specified timer 121 | bool changeInterval(unsigned numTimer, unsigned long d); 122 | 123 | // destroy the specified timer 124 | void deleteTimer(unsigned numTimer); 125 | 126 | // restart the specified timer 127 | void restartTimer(unsigned numTimer); 128 | 129 | // returns true if the specified timer is enabled 130 | bool isEnabled(unsigned numTimer); 131 | 132 | // enables the specified timer 133 | void enable(unsigned numTimer); 134 | 135 | // disables the specified timer 136 | void disable(unsigned numTimer); 137 | 138 | // enables all timers 139 | void enableAll(); 140 | 141 | // disables all timers 142 | void disableAll(); 143 | 144 | // enables the specified timer if it's currently disabled, 145 | // and vice-versa 146 | void toggle(unsigned numTimer); 147 | 148 | // returns the number of used timers 149 | unsigned getNumTimers(); 150 | 151 | // returns the number of available timers 152 | unsigned getNumAvailableTimers() 153 | { 154 | return MAX_TIMERS - numTimers; 155 | }; 156 | 157 | private: 158 | 159 | // deferred call constants 160 | const static int DEFCALL_DONTRUN = 0; // don't call the callback function 161 | const static int DEFCALL_RUNONLY = 1; // call the callback function but don't delete the timer 162 | const static int DEFCALL_RUNANDDEL = 2; // call the callback function and delete the timer 163 | 164 | // low level function to initialize and enable a new timer 165 | // returns the timer number (numTimer) on success or 166 | // -1 on failure (f == NULL) or no free timers 167 | int setupTimer(unsigned long d, void* f, void* p, bool h, unsigned n); 168 | 169 | // find the first available slot 170 | int findFirstFreeSlot(); 171 | 172 | typedef struct 173 | { 174 | unsigned long prev_millis; // value returned by the millis() function in the previous run() call 175 | void* callback; // pointer to the callback function 176 | void* param; // function parameter 177 | bool hasParam; // true if callback takes a parameter 178 | unsigned long delay; // delay value 179 | unsigned maxNumRuns; // number of runs to be executed 180 | unsigned numRuns; // number of executed runs 181 | bool enabled; // true if enabled 182 | unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run() 183 | } timer_t; 184 | 185 | volatile timer_t timer[MAX_TIMERS]; 186 | 187 | // actual number of timers in use (-1 means uninitialized) 188 | volatile int numTimers; 189 | }; 190 | 191 | 192 | #endif // #ifndef ISR_TIMER_HPP 193 | -------------------------------------------------------------------------------- /examples/ISR_Timers_Array_Simple/ISR_Timers_Array_Simple.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_Timers_Array_Simple.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 27 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 28 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 29 | #define TIMER_INTERRUPT_DEBUG 0 30 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 31 | 32 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 33 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 34 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 35 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 36 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 37 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 38 | #define USE_TIMER_1 true 39 | #warning Using Timer1 40 | #else 41 | #define USE_TIMER_3 true 42 | #warning Using Timer3 43 | #endif 44 | 45 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 46 | #include "TimerInterrupt.h" 47 | 48 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 49 | #include "ISR_Timer.h" 50 | 51 | #include // https://github.com/schinken/SimpleTimer 52 | 53 | ISR_Timer ISR_timer; 54 | 55 | #ifndef LED_BUILTIN 56 | #define LED_BUILTIN 13 57 | #endif 58 | 59 | #define LED_TOGGLE_INTERVAL_MS 1000L 60 | 61 | // You have to use longer time here if having problem because Arduino AVR clock is low, 16MHz => lower accuracy. 62 | // Tested OK with 1ms when not much load => higher accuracy. 63 | #define TIMER_INTERVAL_MS 1L 64 | 65 | volatile uint32_t startMillis = 0; 66 | 67 | volatile uint32_t deltaMillis2s = 0; 68 | volatile uint32_t deltaMillis5s = 0; 69 | 70 | volatile uint32_t previousMillis2s = 0; 71 | volatile uint32_t previousMillis5s = 0; 72 | 73 | 74 | void TimerHandler() 75 | { 76 | static bool toggle = false; 77 | static int timeRun = 0; 78 | 79 | ISR_timer.run(); 80 | 81 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s 82 | if (++timeRun == ((LED_TOGGLE_INTERVAL_MS) / TIMER_INTERVAL_MS) ) 83 | { 84 | timeRun = 0; 85 | 86 | //timer interrupt toggles pin LED_BUILTIN 87 | digitalWrite(LED_BUILTIN, toggle); 88 | toggle = !toggle; 89 | } 90 | } 91 | 92 | void doingSomething2s() 93 | { 94 | unsigned long currentMillis = millis(); 95 | 96 | deltaMillis2s = currentMillis - previousMillis2s; 97 | previousMillis2s = currentMillis; 98 | } 99 | 100 | void doingSomething5s() 101 | { 102 | unsigned long currentMillis = millis(); 103 | 104 | deltaMillis5s = currentMillis - previousMillis5s; 105 | previousMillis5s = currentMillis; 106 | } 107 | 108 | ///////////////////////////////////////////////// 109 | 110 | #define SIMPLE_TIMER_MS 2000L 111 | 112 | // Init SimpleTimer 113 | SimpleTimer simpleTimer; 114 | 115 | // Here is software Timer, you can do somewhat fancy stuffs without many issues. 116 | // But always avoid 117 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead 118 | // 2. Very long "do", "while", "for" loops without predetermined exit time. 119 | void simpleTimerDoingSomething2s() 120 | { 121 | static unsigned long previousMillis = startMillis; 122 | 123 | unsigned long currMillis = millis(); 124 | 125 | Serial.print(F("SimpleTimer : programmed ")); Serial.print(SIMPLE_TIMER_MS); 126 | Serial.print(F("ms, current time ms : ")); Serial.print(currMillis); 127 | Serial.print(F(", Delta ms : ")); Serial.println(currMillis - previousMillis); 128 | 129 | Serial.print(F("Timer2s actual : ")); Serial.println(deltaMillis2s); 130 | Serial.print(F("Timer5s actual : ")); Serial.println(deltaMillis5s); 131 | 132 | previousMillis = currMillis; 133 | } 134 | 135 | //////////////////////////////////////////////// 136 | 137 | void setup() 138 | { 139 | pinMode(LED_BUILTIN, OUTPUT); 140 | 141 | Serial.begin(115200); 142 | while (!Serial); 143 | 144 | Serial.print(F("\nStarting ISR_Timers_Array_Simple on ")); 145 | Serial.println(BOARD_TYPE); 146 | Serial.println(TIMER_INTERRUPT_VERSION); 147 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 148 | 149 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 150 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 151 | // Timer 2 is 8-bit timer, only for higher frequency 152 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 153 | 154 | #if USE_TIMER_1 155 | 156 | ITimer1.init(); 157 | 158 | // Using ATmega328 used in UNO => 16MHz CPU clock , 159 | 160 | if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 161 | { 162 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 163 | } 164 | else 165 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 166 | 167 | #elif USE_TIMER_3 168 | 169 | ITimer3.init(); 170 | 171 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 172 | { 173 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 174 | } 175 | else 176 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 177 | 178 | #endif 179 | 180 | ISR_timer.setInterval(2000L, doingSomething2s); 181 | ISR_timer.setInterval(5000L, doingSomething5s); 182 | 183 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. 184 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); 185 | } 186 | 187 | #define BLOCKING_TIME_MS 10000L 188 | 189 | void loop() 190 | { 191 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer 192 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer 193 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) 194 | // While that of ISR_Timer is still prefect. 195 | delay(BLOCKING_TIME_MS); 196 | 197 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary 198 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. 199 | simpleTimer.run(); 200 | } 201 | -------------------------------------------------------------------------------- /examples/SwitchDebounce/SwitchDebounce.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | SwitchDebounce.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | /**************************************************************************************************************************** 26 | Switch Debouncing uses high frequency hardware timer 50Hz == 20ms) to measure the time from the SW is pressed, 27 | debouncing time is 100ms => SW is considered pressed if timer count is > 5, then call / flag SW is pressed 28 | When the SW is released, timer will count (debounce) until more than 50ms until consider SW is released. 29 | We can set to flag or call a function whenever SW is pressed more than certain predetermined time, even before 30 | SW is released. 31 | *****************************************************************************************************************************/ 32 | 33 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 34 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 35 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 36 | #define TIMER_INTERRUPT_DEBUG 0 37 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 38 | 39 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 40 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 41 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 42 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 43 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 44 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 45 | #define USE_TIMER_1 true 46 | #warning Using Timer1 47 | #else 48 | #define USE_TIMER_3 true 49 | #warning Using Timer3 50 | #endif 51 | 52 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 53 | #include "TimerInterrupt.h" 54 | 55 | unsigned int SWPin = A0; 56 | 57 | #define TIMER_INTERVAL_MS 20 58 | #define DEBOUNCING_INTERVAL_MS 100 59 | #define LONG_PRESS_INTERVAL_MS 5000 60 | 61 | volatile bool SWPressed = false; 62 | volatile bool SWLongPressed = false; 63 | 64 | void TimerHandler() 65 | { 66 | static unsigned int debounceCountSWPressed = 0; 67 | static unsigned int debounceCountSWReleased = 0; 68 | 69 | #if (TIMER_INTERRUPT_DEBUG > 1) 70 | static unsigned long SWPressedTime; 71 | static unsigned long SWReleasedTime; 72 | 73 | unsigned long currentMillis = millis(); 74 | #endif 75 | 76 | static bool started = false; 77 | 78 | if (!started) 79 | { 80 | started = true; 81 | pinMode(SWPin, INPUT_PULLUP); 82 | } 83 | 84 | if ( (!digitalRead(SWPin)) ) 85 | { 86 | // Start debouncing counting debounceCountSWPressed and clear debounceCountSWReleased 87 | debounceCountSWReleased = 0; 88 | 89 | if (++debounceCountSWPressed >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS) 90 | { 91 | // Call and flag SWPressed 92 | if (!SWPressed) 93 | { 94 | #if (TIMER_INTERRUPT_DEBUG > 1) 95 | SWPressedTime = currentMillis; 96 | 97 | Serial.print("SW Press, from millis() = "); Serial.println(SWPressedTime); 98 | #endif 99 | 100 | SWPressed = true; 101 | // Do something for SWPressed here in ISR 102 | // But it's better to use outside software timer to do your job instead of inside ISR 103 | //Your_Response_To_Press(); 104 | } 105 | 106 | if (debounceCountSWPressed >= LONG_PRESS_INTERVAL_MS / TIMER_INTERVAL_MS) 107 | { 108 | // Call and flag SWLongPressed 109 | if (!SWLongPressed) 110 | { 111 | #if (TIMER_INTERRUPT_DEBUG > 1) 112 | Serial.print("SW Long Pressed, total time ms = "); Serial.print(currentMillis); 113 | Serial.print(" - "); Serial.print(SWPressedTime); 114 | Serial.print(" = "); Serial.println(currentMillis - SWPressedTime); 115 | #endif 116 | 117 | SWLongPressed = true; 118 | // Do something for SWLongPressed here in ISR 119 | // But it's better to use outside software timer to do your job instead of inside ISR 120 | //Your_Response_To_Long_Press(); 121 | } 122 | } 123 | } 124 | } 125 | else 126 | { 127 | // Start debouncing counting debounceCountSWReleased and clear debounceCountSWPressed 128 | if ( SWPressed && (++debounceCountSWReleased >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS)) 129 | { 130 | #if (TIMER_INTERRUPT_DEBUG > 1) 131 | SWReleasedTime = currentMillis; 132 | 133 | // Call and flag SWPressed 134 | Serial.print("SW Released, from millis() = "); Serial.println(SWReleasedTime); 135 | #endif 136 | 137 | SWPressed = false; 138 | SWLongPressed = false; 139 | 140 | // Do something for !SWPressed here in ISR 141 | // But it's better to use outside software timer to do your job instead of inside ISR 142 | //Your_Response_To_Release(); 143 | 144 | // Call and flag SWPressed 145 | #if (TIMER_INTERRUPT_DEBUG > 1) 146 | Serial.print("SW Pressed total time ms = "); 147 | Serial.println(SWReleasedTime - SWPressedTime); 148 | #endif 149 | 150 | debounceCountSWPressed = 0; 151 | } 152 | } 153 | } 154 | 155 | void setup() 156 | { 157 | Serial.begin(115200); 158 | while (!Serial); 159 | 160 | Serial.print(F("\nStarting SwitchDebounce on ")); 161 | Serial.println(BOARD_TYPE); 162 | Serial.println(TIMER_INTERRUPT_VERSION); 163 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 164 | 165 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 166 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 167 | // Timer 2 is 8-bit timer, only for higher frequency 168 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 169 | 170 | #if USE_TIMER_1 171 | 172 | ITimer1.init(); 173 | 174 | // Using ATmega328 used in UNO => 16MHz CPU clock , 175 | 176 | if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 177 | { 178 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 179 | } 180 | else 181 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 182 | 183 | #elif USE_TIMER_3 184 | 185 | ITimer3.init(); 186 | 187 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 188 | { 189 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 190 | } 191 | else 192 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 193 | 194 | #endif 195 | } 196 | 197 | void loop() 198 | { 199 | 200 | } 201 | -------------------------------------------------------------------------------- /examples/Change_Interval_HF/Change_Interval_HF.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Change_Interval_HF.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | /* 27 | Notes: 28 | Special design is necessary to share data between interrupt code and the rest of your program. 29 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 30 | variable can not spontaneously change. Because your function may change variables while your program is using them, 31 | the compiler needs this hint. But volatile alone is often not enough. 32 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 33 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 34 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 35 | or the entire sequence of your code which accesses the data. 36 | */ 37 | 38 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 39 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 40 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 41 | #define TIMER_INTERRUPT_DEBUG 0 42 | #define _TIMERINTERRUPT_LOGLEVEL_ 3 43 | 44 | #define USE_TIMER_1 true 45 | 46 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 47 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 48 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 49 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 50 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 51 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 52 | #define USE_TIMER_2 true 53 | #warning Using Timer1, Timer2 54 | #else 55 | #define USE_TIMER_3 true 56 | #warning Using Timer1, Timer3 57 | #endif 58 | 59 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 60 | #include "TimerInterrupt.h" 61 | 62 | #if !defined(LED_BUILTIN) 63 | #define LED_BUILTIN 13 64 | #endif 65 | 66 | #ifndef LED_BLUE 67 | #define LED_BLUE 7 68 | #endif 69 | 70 | #define TIMER1_FREQUENCY 5000UL 71 | 72 | #define TIMER_FREQUENCY 1000UL 73 | 74 | volatile uint32_t Timer1Count = 0; 75 | volatile uint32_t TimerCount = 0; 76 | 77 | void printResult(uint32_t currTime) 78 | { 79 | Serial.print(F("Time = ")); Serial.print(currTime); 80 | Serial.print(F(", Timer1Count = ")); Serial.print(Timer1Count); 81 | 82 | Serial.print(F(", TimerCount = ")); Serial.println(TimerCount); 83 | } 84 | 85 | void TimerHandler1(void) 86 | { 87 | static bool toggle1 = false; 88 | 89 | // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR 90 | Timer1Count++; 91 | 92 | //timer interrupt toggles pin LED_BUILTIN 93 | digitalWrite(LED_BUILTIN, toggle1); 94 | toggle1 = !toggle1; 95 | } 96 | 97 | void TimerHandler(void) 98 | { 99 | static bool toggle = false; 100 | 101 | // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR 102 | TimerCount++; 103 | 104 | //timer interrupt toggles outputPin 105 | digitalWrite(LED_BLUE, toggle); 106 | toggle = !toggle; 107 | } 108 | 109 | void setup() 110 | { 111 | pinMode(LED_BUILTIN, OUTPUT); 112 | pinMode(LED_BLUE, OUTPUT); 113 | 114 | Serial.begin(115200); 115 | while (!Serial); 116 | 117 | Serial.print(F("\nStarting Change_Interval_HF on ")); 118 | Serial.println(BOARD_TYPE); 119 | Serial.println(TIMER_INTERRUPT_VERSION); 120 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 121 | 122 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 123 | // Timer 2 is 8-bit timer, only for higher frequency 124 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 125 | ITimer1.init(); 126 | 127 | // Using ATmega328 used in UNO => 16MHz CPU clock , 128 | // For 16-bit timer 1, 3, 4 and 5, set frequency from 0.2385 to some KHz 129 | // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz 130 | 131 | if (ITimer1.attachInterrupt(TIMER1_FREQUENCY, TimerHandler1)) 132 | { 133 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 134 | } 135 | else 136 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 137 | 138 | #if USE_TIMER_2 139 | 140 | ITimer2.init(); 141 | 142 | if (ITimer2.attachInterrupt(TIMER_FREQUENCY, TimerHandler)) 143 | { 144 | Serial.print(F("Starting ITimer2 OK, millis() = ")); Serial.println(millis()); 145 | } 146 | else 147 | Serial.println(F("Can't set ITimer2. Select another freq. or timer")); 148 | 149 | #elif USE_TIMER_3 150 | 151 | ITimer3.init(); 152 | 153 | if (ITimer3.attachInterrupt(TIMER_FREQUENCY, TimerHandler)) 154 | { 155 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 156 | } 157 | else 158 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 159 | 160 | #endif 161 | } 162 | 163 | #define CHECK_INTERVAL_MS 10000L 164 | #define CHANGE_INTERVAL_MS 20000L 165 | 166 | void loop() 167 | { 168 | static uint32_t lastTime = 0; 169 | static uint32_t lastChangeTime = 0; 170 | static uint32_t currTime; 171 | static uint32_t multFactor = 0; 172 | 173 | currTime = millis(); 174 | 175 | if (currTime - lastTime > CHECK_INTERVAL_MS) 176 | { 177 | printResult(currTime); 178 | lastTime = currTime; 179 | 180 | if (currTime - lastChangeTime > CHANGE_INTERVAL_MS) 181 | { 182 | //setInterval(unsigned long interval, timerCallback callback) 183 | multFactor = (multFactor + 1) % 2; 184 | 185 | // interval (in ms) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 186 | // bool setInterval(unsigned long interval, timer_callback callback, unsigned long duration) 187 | 188 | ITimer1.setFrequency(TIMER1_FREQUENCY / (multFactor + 1), TimerHandler1); 189 | 190 | Serial.print(F("Changing Frequency, Timer1 = ")); Serial.println(TIMER1_FREQUENCY / (multFactor + 1)); 191 | 192 | #if USE_TIMER_2 193 | ITimer2.setFrequency(TIMER_FREQUENCY / (multFactor + 1), TimerHandler); 194 | 195 | Serial.print(F("Changing Frequency, Timer2 = ")); Serial.println(TIMER_FREQUENCY / (multFactor + 1)); 196 | 197 | #elif USE_TIMER_3 198 | ITimer3.setFrequency(TIMER_FREQUENCY / (multFactor + 1), TimerHandler); 199 | 200 | Serial.print(F("Changing Frequency, Timer3 = ")); Serial.println(TIMER_FREQUENCY / (multFactor + 1)); 201 | #endif 202 | 203 | lastChangeTime = currTime; 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /examples/TimerInterruptTest/TimerInterruptTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerInterruptTest.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 27 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 28 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 29 | #define TIMER_INTERRUPT_DEBUG 2 30 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 31 | 32 | #define USE_TIMER_1 true 33 | 34 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 35 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 36 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 37 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 38 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 39 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 40 | #define USE_TIMER_2 true 41 | #warning Using Timer1, Timer2 42 | #else 43 | #define USE_TIMER_3 true 44 | #warning Using Timer1, Timer3 45 | #endif 46 | 47 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 48 | #include "TimerInterrupt.h" 49 | 50 | #if !defined(LED_BUILTIN) 51 | #define LED_BUILTIN 13 52 | #endif 53 | 54 | #if USE_TIMER_1 55 | 56 | void TimerHandler1(unsigned int outputPin = LED_BUILTIN) 57 | { 58 | static bool toggle1 = false; 59 | 60 | #if (TIMER_INTERRUPT_DEBUG > 1) 61 | Serial.print("ITimer1 called, millis() = "); Serial.println(millis()); 62 | #endif 63 | 64 | //timer interrupt toggles pin LED_BUILTIN 65 | digitalWrite(outputPin, toggle1); 66 | toggle1 = !toggle1; 67 | } 68 | 69 | #endif 70 | 71 | #if (USE_TIMER_2 || USE_TIMER_3) 72 | 73 | void TimerHandler(unsigned int outputPin = LED_BUILTIN) 74 | { 75 | static bool toggle = false; 76 | 77 | #if (TIMER_INTERRUPT_DEBUG > 1) 78 | #if USE_TIMER_2 79 | Serial.print("ITimer2 called, millis() = "); 80 | #elif USE_TIMER_3 81 | Serial.print("ITimer3 called, millis() = "); 82 | #endif 83 | 84 | Serial.println(millis()); 85 | #endif 86 | 87 | //timer interrupt toggles outputPin 88 | digitalWrite(outputPin, toggle); 89 | toggle = !toggle; 90 | } 91 | 92 | #endif 93 | 94 | unsigned int outputPin1 = LED_BUILTIN; 95 | unsigned int outputPin = A0; 96 | 97 | #define USING_LOOP_TEST false 98 | 99 | #define TIMER1_INTERVAL_MS 1000 100 | #define TIMER1_FREQUENCY (float) (1000.0f / TIMER1_INTERVAL_MS) 101 | 102 | #define TIMER_INTERVAL_MS 2000 103 | #define TIMER_FREQUENCY (float) (1000.0f / TIMER_INTERVAL_MS) 104 | 105 | 106 | #if USING_LOOP_TEST 107 | #define TIMER1_DURATION_MS (10UL * TIMER1_INTERVAL_MS) 108 | #define TIMER_DURATION_MS (20UL * TIMER_INTERVAL_MS) 109 | #else 110 | #define TIMER1_DURATION_MS 0 111 | #define TIMER_DURATION_MS 0 112 | #endif 113 | 114 | void setup() 115 | { 116 | pinMode(outputPin1, OUTPUT); 117 | pinMode(outputPin, OUTPUT); 118 | 119 | Serial.begin(115200); 120 | while (!Serial); 121 | 122 | Serial.print(F("\nStarting TimerInterruptTest on ")); 123 | Serial.println(BOARD_TYPE); 124 | Serial.println(TIMER_INTERRUPT_VERSION); 125 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 126 | 127 | #if USE_TIMER_1 128 | 129 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 130 | // Select Timer 1-2 for UNO, 0-5 for MEGA 131 | // Timer 2 is 8-bit timer, only for higher frequency 132 | 133 | ITimer1.init(); 134 | 135 | // Using ATmega328 used in UNO => 16MHz CPU clock , 136 | 137 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler1, outputPin1, TIMER1_DURATION_MS)) 138 | { 139 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 140 | } 141 | else 142 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 143 | 144 | #endif 145 | 146 | #if USE_TIMER_2 147 | 148 | ITimer2.init(); 149 | 150 | if (ITimer2.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, outputPin, TIMER_DURATION_MS)) 151 | { 152 | Serial.print(F("Starting ITimer2 OK, millis() = ")); Serial.println(millis()); 153 | } 154 | else 155 | Serial.println(F("Can't set ITimer2. Select another freq. or timer")); 156 | 157 | #elif USE_TIMER_3 158 | 159 | ITimer3.init(); 160 | 161 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, outputPin, TIMER_DURATION_MS)) 162 | { 163 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 164 | } 165 | else 166 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 167 | 168 | #endif 169 | } 170 | 171 | void loop() 172 | { 173 | #if USING_LOOP_TEST 174 | static unsigned long lastTimer1 = 0; 175 | static unsigned long lastTimer = 0; 176 | 177 | static bool timerPaused = false; 178 | static bool timerResumed = false; 179 | 180 | if (millis() - lastTimer1 > TIMER1_DURATION_MS * 3) 181 | { 182 | #if USE_TIMER_2 183 | 184 | if (millis() - lastTimer > TIMER_DURATION_MS * 3) 185 | { 186 | lastTimer = millis(); 187 | 188 | Serial.print(F("Re-enable ITimer2, millis() = ")); Serial.println(lastTimer); 189 | 190 | ITimer2.reattachInterrupt(TIMER_DURATION_MS); 191 | } 192 | 193 | #elif USE_TIMER_3 194 | 195 | if (millis() - lastTimer > TIMER_DURATION_MS * 3) 196 | { 197 | lastTimer = millis(); 198 | 199 | Serial.print(F("Re-enable ITimer3, millis() = ")); Serial.println(lastTimer); 200 | 201 | ITimer3.reattachInterrupt(TIMER_DURATION_MS); 202 | } 203 | 204 | #endif 205 | 206 | lastTimer1 = millis(); 207 | // try reinit timer 208 | Serial.print(F("Re-enable ITimer1, millis() = ")); Serial.print(lastTimer1); 209 | Serial.print(F(" count = ")); Serial.println(ITimer1.getCount() - 1); 210 | 211 | ITimer1.reattachInterrupt(TIMER1_DURATION_MS); 212 | timerPaused = false; 213 | timerResumed = false; 214 | } 215 | else if ( !timerPaused && (millis() - lastTimer1 > TIMER1_DURATION_MS / 2) ) 216 | { 217 | timerPaused = true; 218 | 219 | Serial.print(F("Pause ITimer1, millis() = ")); Serial.print(millis()); 220 | Serial.print(F(" count = ")); Serial.println(ITimer1.getCount() - 1); 221 | 222 | ITimer1.pauseTimer(); 223 | } 224 | else if ( !timerResumed && (millis() - lastTimer1 > ( TIMER1_DURATION_MS * 3 ) / 2) ) 225 | { 226 | timerResumed = true; 227 | 228 | Serial.print(F("Resume ITimer1, millis() = ")); Serial.print(millis()); 229 | Serial.print(F(" count = ")); Serial.println(ITimer1.getCount() - 1); 230 | 231 | ITimer1.resumeTimer(); 232 | } 233 | #endif 234 | 235 | } 236 | -------------------------------------------------------------------------------- /examples/Change_Interval/Change_Interval.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Change_Interval.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | /* 27 | Notes: 28 | Special design is necessary to share data between interrupt code and the rest of your program. 29 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 30 | variable can not spontaneously change. Because your function may change variables while your program is using them, 31 | the compiler needs this hint. But volatile alone is often not enough. 32 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 33 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 34 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 35 | or the entire sequence of your code which accesses the data. 36 | */ 37 | 38 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 39 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 40 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 41 | #define TIMER_INTERRUPT_DEBUG 0 42 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 43 | 44 | #define USE_TIMER_1 true 45 | 46 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 47 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 48 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 49 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 50 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 51 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 52 | #define USE_TIMER_2 true 53 | #warning Using Timer1, Timer2 54 | #else 55 | #define USE_TIMER_3 true 56 | #warning Using Timer1, Timer3 57 | #endif 58 | 59 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 60 | #include "TimerInterrupt.h" 61 | 62 | #if !defined(LED_BUILTIN) 63 | #define LED_BUILTIN 13 64 | #endif 65 | 66 | #ifndef LED_BLUE 67 | #define LED_BLUE 7 68 | #endif 69 | 70 | 71 | #define TIMER1_INTERVAL_MS 100UL 72 | #define TIMER_INTERVAL_MS 200UL 73 | 74 | volatile uint32_t Timer1Count = 0; 75 | volatile uint32_t TimerCount = 0; 76 | 77 | void printResult(uint32_t currTime) 78 | { 79 | Serial.print(F("Time = ")); Serial.print(currTime); 80 | Serial.print(F(", Timer1Count = ")); Serial.print(Timer1Count); 81 | 82 | Serial.print(F(", TimerCount = ")); Serial.println(TimerCount); 83 | } 84 | 85 | void TimerHandler1(void) 86 | { 87 | static bool toggle1 = false; 88 | 89 | // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR 90 | Timer1Count++; 91 | 92 | //timer interrupt toggles pin LED_BUILTIN 93 | digitalWrite(LED_BUILTIN, toggle1); 94 | toggle1 = !toggle1; 95 | } 96 | 97 | void TimerHandler(void) 98 | { 99 | static bool toggle = false; 100 | 101 | // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR 102 | TimerCount++; 103 | 104 | //timer interrupt toggles outputPin 105 | digitalWrite(LED_BLUE, toggle); 106 | toggle = !toggle; 107 | } 108 | 109 | void setup() 110 | { 111 | pinMode(LED_BUILTIN, OUTPUT); 112 | pinMode(LED_BLUE, OUTPUT); 113 | 114 | Serial.begin(115200); 115 | while (!Serial); 116 | 117 | Serial.print(F("\nStarting Change_Interval on ")); 118 | Serial.println(BOARD_TYPE); 119 | Serial.println(TIMER_INTERRUPT_VERSION); 120 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 121 | 122 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 123 | // Timer 2 is 8-bit timer, only for higher frequency 124 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 125 | ITimer1.init(); 126 | 127 | // Using ATmega328 used in UNO => 16MHz CPU clock , 128 | // For 16-bit timer 1, 3, 4 and 5, set frequency from 0.2385 to some KHz 129 | // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz 130 | 131 | if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler1)) 132 | { 133 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 134 | } 135 | else 136 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 137 | 138 | #if USE_TIMER_2 139 | 140 | ITimer2.init(); 141 | 142 | if (ITimer2.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 143 | { 144 | Serial.print(F("Starting ITimer2 OK, millis() = ")); Serial.println(millis()); 145 | } 146 | else 147 | Serial.println(F("Can't set ITimer2. Select another freq. or timer")); 148 | 149 | #elif USE_TIMER_3 150 | 151 | ITimer3.init(); 152 | 153 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 154 | { 155 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 156 | } 157 | else 158 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 159 | 160 | #endif 161 | } 162 | 163 | #define CHECK_INTERVAL_MS 10000L 164 | #define CHANGE_INTERVAL_MS 20000L 165 | 166 | void loop() 167 | { 168 | static uint32_t lastTime = 0; 169 | static uint32_t lastChangeTime = 0; 170 | static uint32_t currTime; 171 | static uint32_t multFactor = 0; 172 | 173 | currTime = millis(); 174 | 175 | if (currTime - lastTime > CHECK_INTERVAL_MS) 176 | { 177 | printResult(currTime); 178 | lastTime = currTime; 179 | 180 | if (currTime - lastChangeTime > CHANGE_INTERVAL_MS) 181 | { 182 | //setInterval(unsigned long interval, timerCallback callback) 183 | multFactor = (multFactor + 1) % 2; 184 | 185 | // interval (in ms) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 186 | // bool setInterval(unsigned long interval, timer_callback callback, unsigned long duration) 187 | 188 | ITimer1.setInterval(TIMER1_INTERVAL_MS * (multFactor + 1), TimerHandler1); 189 | 190 | Serial.print(F("Changing Interval, Timer1 = ")); Serial.println(TIMER1_INTERVAL_MS * (multFactor + 1)); 191 | 192 | #if USE_TIMER_2 193 | ITimer2.setInterval(TIMER_INTERVAL_MS * (multFactor + 1), TimerHandler); 194 | 195 | Serial.print(F("Changing Interval, Timer2 = ")); Serial.println(TIMER_INTERVAL_MS * (multFactor + 1)); 196 | #elif USE_TIMER_3 197 | ITimer3.setInterval(TIMER_INTERVAL_MS * (multFactor + 1), TimerHandler); 198 | 199 | Serial.print(F("Changing Interval, Timer3 = ")); Serial.println(TIMER_INTERVAL_MS * (multFactor + 1)); 200 | #endif 201 | 202 | lastChangeTime = currTime; 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/ISR_Timer-Impl.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_Timer.cpp 3 | For Arduino boards (UNO, Nano, Mega, etc. ) 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | TCNTx - Timer/Counter Register. The actual timer value is stored here. 10 | OCRx - Output Compare Register 11 | ICRx - Input Capture Register (only for 16bit timer) 12 | TIMSKx - Timer/Counter Interrupt Mask Register. To enable/disable timer interrupts. 13 | TIFRx - Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt. 14 | 15 | Now with we can use these new 16 ISR-based timers, while consuming only 1 hwarware Timer. 16 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 17 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 18 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 19 | This important feature is absolutely necessary for mission-critical tasks. 20 | 21 | Version: 1.8.0 22 | 23 | Version Modified By Date Comments 24 | ------- ----------- ---------- ----------- 25 | 1.0.0 K Hoang 23/11/2019 Initial coding 26 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 27 | 1.0.2 K.Hoang 28/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 28 | 1.0.3 K.Hoang 01/12/2020 Add complex examples ISR_16_Timers_Array_Complex and ISR_16_Timers_Array_Complex 29 | 1.1.1 K.Hoang 06/12/2020 Add example Change_Interval. Bump up version to sync with other TimerInterrupt Libraries 30 | 1.1.2 K.Hoang 05/01/2021 Fix warnings. Optimize examples to reduce memory usage 31 | 1.2.0 K.Hoang 07/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 32 | 1.3.0 K.Hoang 25/02/2021 Add support to AVR ATMEGA_32U4 such as Leonardo, YUN, ESPLORA, etc. 33 | 1.4.0 K.Hoang 01/04/2021 Add support to Adafruit 32U4 and 328(P) such as FEATHER32U4, FEATHER328P, etc. 34 | 1.4.1 K.Hoang 02/04/2021 Add support to Sparkfun 32U4, 328(P), 128RFA1 such as AVR_PROMICRO, REDBOT, etc. 35 | 1.5.0 K.Hoang 08/05/2021 Add Timer 3 and 4 to 32U4. Add Timer auto-selection to examples. 36 | 1.6.0 K.Hoang 15/11/2021 Fix bug resulting half frequency when using high frequencies. 37 | 1.7.0 K.Hoang 19/11/2021 Fix bug resulting wrong frequency for some frequencies. 38 | 1.8.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 39 | *****************************************************************************************************************************/ 40 | 41 | #pragma once 42 | 43 | #ifndef ISR_TIMER_IMPL_H 44 | #define ISR_TIMER_IMPL_H 45 | 46 | #include 47 | 48 | // Select time function: 49 | //static inline unsigned long elapsed() { return micros(); } 50 | static inline unsigned long elapsed() 51 | { 52 | return millis(); 53 | } 54 | 55 | 56 | ISR_Timer::ISR_Timer() 57 | : numTimers (-1) 58 | { 59 | } 60 | 61 | void ISR_Timer::init() 62 | { 63 | unsigned long current_millis = millis(); //elapsed(); 64 | 65 | for (uint8_t i = 0; i < MAX_TIMERS; i++) 66 | { 67 | memset((void*) &timer[i], 0, sizeof (timer_t)); 68 | timer[i].prev_millis = current_millis; 69 | } 70 | 71 | numTimers = 0; 72 | } 73 | 74 | 75 | void ISR_Timer::run() 76 | { 77 | uint8_t i; 78 | unsigned long current_millis; 79 | 80 | // get current time 81 | current_millis = millis(); //elapsed(); 82 | 83 | for (i = 0; i < MAX_TIMERS; i++) 84 | { 85 | timer[i].toBeCalled = DEFCALL_DONTRUN; 86 | 87 | // no callback == no timer, i.e. jump over empty slots 88 | if (timer[i].callback != NULL) 89 | { 90 | // is it time to process this timer ? 91 | // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592 92 | 93 | if ((current_millis - timer[i].prev_millis) >= timer[i].delay) 94 | { 95 | unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay; 96 | // update time 97 | timer[i].prev_millis += timer[i].delay * skipTimes; 98 | 99 | // check if the timer callback has to be executed 100 | if (timer[i].enabled) 101 | { 102 | // "run forever" timers must always be executed 103 | if (timer[i].maxNumRuns == RUN_FOREVER) 104 | { 105 | timer[i].toBeCalled = DEFCALL_RUNONLY; 106 | } 107 | // other timers get executed the specified number of times 108 | else if (timer[i].numRuns < timer[i].maxNumRuns) 109 | { 110 | timer[i].toBeCalled = DEFCALL_RUNONLY; 111 | timer[i].numRuns++; 112 | 113 | // after the last run, delete the timer 114 | if (timer[i].numRuns >= timer[i].maxNumRuns) 115 | { 116 | timer[i].toBeCalled = DEFCALL_RUNANDDEL; 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | 124 | for (i = 0; i < MAX_TIMERS; i++) 125 | { 126 | if (timer[i].toBeCalled == DEFCALL_DONTRUN) 127 | continue; 128 | 129 | if (timer[i].hasParam) 130 | (*(timer_callback_p)timer[i].callback)(timer[i].param); 131 | else 132 | (*(timer_callback)timer[i].callback)(); 133 | 134 | if (timer[i].toBeCalled == DEFCALL_RUNANDDEL) 135 | deleteTimer(i); 136 | } 137 | } 138 | 139 | 140 | // find the first available slot 141 | // return -1 if none found 142 | int ISR_Timer::findFirstFreeSlot() 143 | { 144 | // all slots are used 145 | if (numTimers >= MAX_TIMERS) 146 | { 147 | return -1; 148 | } 149 | 150 | // return the first slot with no callback (i.e. free) 151 | for (uint8_t i = 0; i < MAX_TIMERS; i++) 152 | { 153 | if (timer[i].callback == NULL) 154 | { 155 | return i; 156 | } 157 | } 158 | 159 | // no free slots found 160 | return -1; 161 | } 162 | 163 | 164 | int ISR_Timer::setupTimer(unsigned long d, void* f, void* p, bool h, unsigned n) 165 | { 166 | int freeTimer; 167 | 168 | if (numTimers < 0) 169 | { 170 | init(); 171 | } 172 | 173 | freeTimer = findFirstFreeSlot(); 174 | 175 | if (freeTimer < 0) 176 | { 177 | return -1; 178 | } 179 | 180 | if (f == NULL) 181 | { 182 | return -1; 183 | } 184 | 185 | timer[freeTimer].delay = d; 186 | timer[freeTimer].callback = f; 187 | timer[freeTimer].param = p; 188 | timer[freeTimer].hasParam = h; 189 | timer[freeTimer].maxNumRuns = n; 190 | timer[freeTimer].enabled = true; 191 | timer[freeTimer].prev_millis = elapsed(); 192 | 193 | numTimers++; 194 | 195 | return freeTimer; 196 | } 197 | 198 | 199 | int ISR_Timer::setTimer(unsigned long d, timer_callback f, unsigned n) 200 | { 201 | return setupTimer(d, (void *)f, NULL, false, n); 202 | } 203 | 204 | int ISR_Timer::setTimer(unsigned long d, timer_callback_p f, void* p, unsigned n) 205 | { 206 | return setupTimer(d, (void *)f, p, true, n); 207 | } 208 | 209 | int ISR_Timer::setInterval(unsigned long d, timer_callback f) 210 | { 211 | return setupTimer(d, (void *)f, NULL, false, RUN_FOREVER); 212 | } 213 | 214 | int ISR_Timer::setInterval(unsigned long d, timer_callback_p f, void* p) 215 | { 216 | return setupTimer(d, (void *)f, p, true, RUN_FOREVER); 217 | } 218 | 219 | int ISR_Timer::setTimeout(unsigned long d, timer_callback f) 220 | { 221 | return setupTimer(d, (void *)f, NULL, false, RUN_ONCE); 222 | } 223 | 224 | int ISR_Timer::setTimeout(unsigned long d, timer_callback_p f, void* p) 225 | { 226 | return setupTimer(d, (void *)f, p, true, RUN_ONCE); 227 | } 228 | 229 | bool ISR_Timer::changeInterval(unsigned numTimer, unsigned long d) 230 | { 231 | if (numTimer >= MAX_TIMERS) 232 | { 233 | return false; 234 | } 235 | 236 | // Updates interval of existing specified timer 237 | if (timer[numTimer].callback != NULL) 238 | { 239 | timer[numTimer].delay = d; 240 | timer[numTimer].prev_millis = elapsed(); 241 | return true; 242 | } 243 | 244 | // false return for non-used numTimer, no callback 245 | return false; 246 | } 247 | 248 | void ISR_Timer::deleteTimer(unsigned timerId) 249 | { 250 | if (timerId >= MAX_TIMERS) 251 | { 252 | return; 253 | } 254 | 255 | // nothing to delete if no timers are in use 256 | if (numTimers == 0) 257 | { 258 | return; 259 | } 260 | 261 | // don't decrease the number of timers if the 262 | // specified slot is already empty 263 | if (timer[timerId].callback != NULL) 264 | { 265 | memset((void*) &timer[timerId], 0, sizeof (timer_t)); 266 | timer[timerId].prev_millis = elapsed(); 267 | 268 | // update number of timers 269 | numTimers--; 270 | } 271 | } 272 | 273 | 274 | // function contributed by code@rowansimms.com 275 | void ISR_Timer::restartTimer(unsigned numTimer) 276 | { 277 | if (numTimer >= MAX_TIMERS) 278 | { 279 | return; 280 | } 281 | 282 | timer[numTimer].prev_millis = elapsed(); 283 | } 284 | 285 | 286 | bool ISR_Timer::isEnabled(unsigned numTimer) 287 | { 288 | if (numTimer >= MAX_TIMERS) 289 | { 290 | return false; 291 | } 292 | 293 | return timer[numTimer].enabled; 294 | } 295 | 296 | 297 | void ISR_Timer::enable(unsigned numTimer) 298 | { 299 | if (numTimer >= MAX_TIMERS) 300 | { 301 | return; 302 | } 303 | 304 | timer[numTimer].enabled = true; 305 | } 306 | 307 | 308 | void ISR_Timer::disable(unsigned numTimer) 309 | { 310 | if (numTimer >= MAX_TIMERS) 311 | { 312 | return; 313 | } 314 | 315 | timer[numTimer].enabled = false; 316 | } 317 | 318 | void ISR_Timer::enableAll() 319 | { 320 | // Enable all timers with a callback assigned (used) 321 | for (uint8_t i = 0; i < MAX_TIMERS; i++) 322 | { 323 | if (timer[i].callback != NULL && timer[i].numRuns == RUN_FOREVER) 324 | { 325 | timer[i].enabled = true; 326 | } 327 | } 328 | } 329 | 330 | void ISR_Timer::disableAll() 331 | { 332 | // Disable all timers with a callback assigned (used) 333 | for (uint8_t i = 0; i < MAX_TIMERS; i++) 334 | { 335 | if (timer[i].callback != NULL && timer[i].numRuns == RUN_FOREVER) 336 | { 337 | timer[i].enabled = false; 338 | } 339 | } 340 | } 341 | 342 | void ISR_Timer::toggle(unsigned numTimer) 343 | { 344 | if (numTimer >= MAX_TIMERS) 345 | { 346 | return; 347 | } 348 | 349 | timer[numTimer].enabled = !timer[numTimer].enabled; 350 | } 351 | 352 | 353 | unsigned ISR_Timer::getNumTimers() 354 | { 355 | return numTimers; 356 | } 357 | 358 | #endif // ISR_TIMER_IMPL_H 359 | -------------------------------------------------------------------------------- /examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_16_Timers_Array_Complex.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | 26 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 27 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 28 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 29 | #define TIMER_INTERRUPT_DEBUG 0 30 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 31 | 32 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 33 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 34 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 35 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 36 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 37 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 38 | #define USE_TIMER_1 true 39 | #warning Using Timer1 40 | #else 41 | #define USE_TIMER_3 true 42 | #warning Using Timer3 43 | #endif 44 | 45 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 46 | #include "TimerInterrupt.h" 47 | 48 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 49 | #include "ISR_Timer.h" 50 | 51 | #include // https://github.com/schinken/SimpleTimer 52 | 53 | #ifndef LED_BUILTIN 54 | #define LED_BUILTIN 13 55 | #endif 56 | 57 | ISR_Timer ISR_timer; 58 | 59 | #define LED_TOGGLE_INTERVAL_MS 1000L 60 | 61 | // You have to use longer time here if having problem because Arduino AVR clock is low, 16MHz => lower accuracy. 62 | // Tested OK with 1ms when not much load => higher accuracy. 63 | #define TIMER_INTERVAL_MS 5L 64 | 65 | volatile uint32_t startMillis = 0; 66 | 67 | void TimerHandler() 68 | { 69 | static bool toggle = false; 70 | static int timeRun = 0; 71 | 72 | ISR_timer.run(); 73 | 74 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s 75 | if (++timeRun == ((LED_TOGGLE_INTERVAL_MS) / TIMER_INTERVAL_MS) ) 76 | { 77 | timeRun = 0; 78 | 79 | //timer interrupt toggles pin LED_BUILTIN 80 | digitalWrite(LED_BUILTIN, toggle); 81 | toggle = !toggle; 82 | } 83 | } 84 | 85 | ///////////////////////////////////////////////// 86 | 87 | #define NUMBER_ISR_TIMERS 16 88 | 89 | typedef void (*irqCallback) (void); 90 | 91 | ///////////////////////////////////////////////// 92 | 93 | #define USE_COMPLEX_STRUCT true 94 | 95 | #if USE_COMPLEX_STRUCT 96 | 97 | typedef struct 98 | { 99 | irqCallback irqCallbackFunc; 100 | uint32_t TimerInterval; 101 | unsigned long deltaMillis; 102 | unsigned long previousMillis; 103 | } ISRTimerData; 104 | 105 | // In NRF52, avoid doing something fancy in ISR, for example Serial.print() 106 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment 107 | // Or you can get this run-time error / crash 108 | 109 | void doingSomething(int index); 110 | 111 | #else 112 | 113 | volatile unsigned long deltaMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 114 | volatile unsigned long previousMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 115 | 116 | // You can assign any interval for any timer here, in milliseconds 117 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] = 118 | { 119 | 5000L, 10000L, 15000L, 20000L, 25000L, 30000L, 35000L, 40000L, 120 | 45000L, 50000L, 55000L, 60000L, 65000L, 70000L, 75000L, 80000L 121 | }; 122 | 123 | void doingSomething(int index) 124 | { 125 | unsigned long currentMillis = millis(); 126 | 127 | deltaMillis[index] = currentMillis - previousMillis[index]; 128 | previousMillis[index] = currentMillis; 129 | } 130 | 131 | #endif 132 | 133 | //////////////////////////////////// 134 | // Shared 135 | //////////////////////////////////// 136 | 137 | void doingSomething0() 138 | { 139 | doingSomething(0); 140 | } 141 | 142 | void doingSomething1() 143 | { 144 | doingSomething(1); 145 | } 146 | 147 | void doingSomething2() 148 | { 149 | doingSomething(2); 150 | } 151 | 152 | void doingSomething3() 153 | { 154 | doingSomething(3); 155 | } 156 | 157 | void doingSomething4() 158 | { 159 | doingSomething(4); 160 | } 161 | 162 | void doingSomething5() 163 | { 164 | doingSomething(5); 165 | } 166 | 167 | void doingSomething6() 168 | { 169 | doingSomething(6); 170 | } 171 | 172 | void doingSomething7() 173 | { 174 | doingSomething(7); 175 | } 176 | 177 | void doingSomething8() 178 | { 179 | doingSomething(8); 180 | } 181 | 182 | void doingSomething9() 183 | { 184 | doingSomething(9); 185 | } 186 | 187 | void doingSomething10() 188 | { 189 | doingSomething(10); 190 | } 191 | 192 | void doingSomething11() 193 | { 194 | doingSomething(11); 195 | } 196 | 197 | void doingSomething12() 198 | { 199 | doingSomething(12); 200 | } 201 | 202 | void doingSomething13() 203 | { 204 | doingSomething(13); 205 | } 206 | 207 | void doingSomething14() 208 | { 209 | doingSomething(14); 210 | } 211 | 212 | void doingSomething15() 213 | { 214 | doingSomething(15); 215 | } 216 | 217 | #if USE_COMPLEX_STRUCT 218 | 219 | ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] = 220 | { 221 | //irqCallbackFunc, TimerInterval, deltaMillis, previousMillis 222 | { doingSomething0, 5000L, 0, 0 }, 223 | { doingSomething1, 10000L, 0, 0 }, 224 | { doingSomething2, 15000L, 0, 0 }, 225 | { doingSomething3, 20000L, 0, 0 }, 226 | { doingSomething4, 25000L, 0, 0 }, 227 | { doingSomething5, 30000L, 0, 0 }, 228 | { doingSomething6, 35000L, 0, 0 }, 229 | { doingSomething7, 40000L, 0, 0 }, 230 | { doingSomething8, 45000L, 0, 0 }, 231 | { doingSomething9, 50000L, 0, 0 }, 232 | { doingSomething10, 55000L, 0, 0 }, 233 | { doingSomething11, 60000L, 0, 0 }, 234 | { doingSomething12, 65000L, 0, 0 }, 235 | { doingSomething13, 70000L, 0, 0 }, 236 | { doingSomething14, 75000L, 0, 0 }, 237 | { doingSomething15, 80000L, 0, 0 } 238 | }; 239 | 240 | void doingSomething(int index) 241 | { 242 | unsigned long currentMillis = millis(); 243 | 244 | curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis; 245 | curISRTimerData[index].previousMillis = currentMillis; 246 | } 247 | 248 | #else 249 | 250 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = 251 | { 252 | doingSomething0, doingSomething1, doingSomething2, doingSomething3, 253 | doingSomething4, doingSomething5, doingSomething6, doingSomething7, 254 | doingSomething8, doingSomething9, doingSomething10, doingSomething11, 255 | doingSomething12, doingSomething13, doingSomething14, doingSomething15 256 | }; 257 | 258 | #endif 259 | 260 | //////////////////////////////////////////////// 261 | 262 | 263 | #define SIMPLE_TIMER_MS 2000L 264 | 265 | // Init SimpleTimer 266 | SimpleTimer simpleTimer; 267 | 268 | // Here is software Timer, you can do somewhat fancy stuffs without many issues. 269 | // But always avoid 270 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead 271 | // 2. Very long "do", "while", "for" loops without predetermined exit time. 272 | void simpleTimerDoingSomething2s() 273 | { 274 | static unsigned long previousMillis = startMillis; 275 | 276 | unsigned long currMillis = millis(); 277 | 278 | Serial.print(F("SimpleTimer : "));Serial.print(SIMPLE_TIMER_MS / 1000); 279 | Serial.print(F(", ms : ")); Serial.print(currMillis); 280 | Serial.print(F(", Dms : ")); Serial.println(currMillis - previousMillis); 281 | 282 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 283 | { 284 | #if USE_COMPLEX_STRUCT 285 | Serial.print(F("Timer : ")); Serial.print(i); 286 | Serial.print(F(", programmed : ")); Serial.print(curISRTimerData[i].TimerInterval); 287 | Serial.print(F(", actual : ")); Serial.println(curISRTimerData[i].deltaMillis); 288 | #else 289 | Serial.print(F("Timer : ")); Serial.print(i); 290 | Serial.print(F(", programmed : ")); Serial.print(TimerInterval[i]); 291 | Serial.print(F(", actual : ")); Serial.println(deltaMillis[i]); 292 | #endif 293 | } 294 | 295 | previousMillis = currMillis; 296 | } 297 | 298 | void setup() 299 | { 300 | pinMode(LED_BUILTIN, OUTPUT); 301 | 302 | Serial.begin(115200); 303 | while (!Serial); 304 | 305 | Serial.print(F("\nStarting ISR_16_Timers_Array_Complex on ")); 306 | Serial.println(BOARD_TYPE); 307 | Serial.println(TIMER_INTERRUPT_VERSION); 308 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 309 | 310 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 311 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 312 | // Timer 2 is 8-bit timer, only for higher frequency 313 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 314 | 315 | #if USE_TIMER_1 316 | 317 | ITimer1.init(); 318 | 319 | // Using ATmega328 used in UNO => 16MHz CPU clock , 320 | 321 | if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 322 | { 323 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 324 | } 325 | else 326 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 327 | 328 | #elif USE_TIMER_3 329 | 330 | ITimer3.init(); 331 | 332 | if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 333 | { 334 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 335 | } 336 | else 337 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 338 | 339 | #endif 340 | 341 | //ISR_timer.setInterval(2000L, doingSomething2s); 342 | //ISR_timer.setInterval(5000L, doingSomething5s); 343 | 344 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 345 | // You can use up to 16 timer for each ISR_Timer 346 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 347 | { 348 | #if USE_COMPLEX_STRUCT 349 | curISRTimerData[i].previousMillis = startMillis; 350 | ISR_timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc); 351 | #else 352 | previousMillis[i] = startMillis; 353 | ISR_timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); 354 | #endif 355 | } 356 | 357 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. 358 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); 359 | } 360 | 361 | #define BLOCKING_TIME_MS 10000L 362 | 363 | void loop() 364 | { 365 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer 366 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer 367 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) 368 | // While that of ISR_Timer is still prefect. 369 | delay(BLOCKING_TIME_MS); 370 | 371 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary 372 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. 373 | simpleTimer.run(); 374 | } 375 | -------------------------------------------------------------------------------- /examples/FakeAnalogWrite/FakeAnalogWrite.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | FakeAnalogWrite.ino 3 | For Arduino and Adadruit AVR 328(P) and 32u4 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer. 10 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Notes: 16 | Special design is necessary to share data between interrupt code and the rest of your program. 17 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 18 | variable can not spontaneously change. Because your function may change variables while your program is using them, 19 | the compiler needs this hint. But volatile alone is often not enough. 20 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 21 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 22 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 23 | or the entire sequence of your code which accesses the data. 24 | *****************************************************************************************************************************/ 25 | /* 26 | Notes: 27 | Special design is necessary to share data between interrupt code and the rest of your program. 28 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 29 | variable can not spontaneously change. Because your function may change variables while your program is using them, 30 | the compiler needs this hint. But volatile alone is often not enough. 31 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 32 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 33 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 34 | or the entire sequence of your code which accesses the data. 35 | 36 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 37 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 38 | and Blynk services. You can also have many (up to 16) timers to use. 39 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 40 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 41 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 42 | written 43 | */ 44 | 45 | // These define's must be placed at the beginning before #include "TimerInterrupt.h" 46 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 47 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 48 | #define TIMER_INTERRUPT_DEBUG 0 49 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 50 | 51 | #if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 52 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 53 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 54 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \ 55 | defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \ 56 | defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 57 | #define USE_TIMER_1 true 58 | #warning Using Timer1 59 | #else 60 | #define USE_TIMER_3 true 61 | #warning Using Timer3 62 | #endif 63 | 64 | #define LOCAL_DEBUG 1 65 | 66 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 67 | #include "TimerInterrupt.h" 68 | 69 | #ifndef LED_BUILTIN 70 | #define LED_BUILTIN 13 71 | #endif 72 | 73 | // For PWM_Value from 0-255.You can change to 1024 or 2048 74 | #define MAX_PWM_VALUE 256 75 | 76 | #define MAPPING_TABLE_SIZE (MAX_PWM_VALUE / 10) + 1 77 | 78 | // You have to calibrate and update this mapping table 79 | float mappingTable[MAPPING_TABLE_SIZE] = 80 | { 0.0, 3.281, 6.860, 10.886, 15.285, 20.355, 26.096, 32.732, 40.785, 50.180, 81 | 62.557, 79.557, 104.461, 136.075, 163.066, 181.930, 195.724, 207.132, 216.228, 223.684, 82 | 230.395, 236.136, 241.206, 245.680, 249.781, 253.509 83 | }; 84 | 85 | #define TIMER_FREQUENCY_HZ 10000UL 86 | #define TIMER_INTERVAL_US (1000000UL / TIMER_FREQUENCY_HZ) 87 | 88 | volatile uint32_t startMillis = 0; 89 | 90 | // Depending on the board, you can select NRF52 Hardware Timer from NRF_TIMER_1,NRF_TIMER_3,NRF_TIMER_4 (1,3 and 4) 91 | // If you select the already-used NRF_TIMER_0 or NRF_TIMER_2, it'll be auto modified to use NRF_TIMER_1 92 | 93 | //ISR_Timer ISR_timer; 94 | 95 | #define LED_TOGGLE_INTERVAL_MS 500L 96 | 97 | #define NUMBER_ISR_TIMERS 16 98 | 99 | void TimerHandler(void) 100 | { 101 | static bool toggle = false; 102 | static uint32_t timeRun = 0; 103 | 104 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 105 | { 106 | doingSomething(i); 107 | } 108 | 109 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 500ms = 0.5s 110 | if (++timeRun == ((LED_TOGGLE_INTERVAL_MS * TIMER_FREQUENCY_HZ) / 1000) ) 111 | { 112 | timeRun = 0; 113 | 114 | //timer interrupt toggles pin LED_BUILTIN 115 | digitalWrite(LED_BUILTIN, toggle); 116 | toggle = !toggle; 117 | } 118 | } 119 | 120 | ///////////////////////////////////////////////// 121 | 122 | 123 | 124 | typedef void (*irqCallback) (void); 125 | 126 | ///////////////////////////////////////////////// 127 | 128 | 129 | typedef struct 130 | { 131 | int16_t PWM_Value; // Writing negative value to stop and free this PWM 132 | uint16_t PWM_PremapValue; // To detect if use the same PWM_Value setting => don't do anything 133 | uint16_t pin; 134 | int16_t countPWM; 135 | bool beingUsed; 136 | } ISRTimerData; 137 | 138 | // Avoid doing something fancy in ISR, for example Serial.print() 139 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment 140 | // Or you can get this run-time error / crash 141 | 142 | /////////////////////////////////// 143 | 144 | volatile ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] = 145 | { 146 | { 0, 0, 0, 0, false }, 147 | { 0, 0, 0, 0, false }, 148 | { 0, 0, 0, 0, false }, 149 | { 0, 0, 0, 0, false }, 150 | { 0, 0, 0, 0, false }, 151 | { 0, 0, 0, 0, false }, 152 | { 0, 0, 0, 0, false }, 153 | { 0, 0, 0, 0, false }, 154 | { 0, 0, 0, 0, false }, 155 | { 0, 0, 0, 0, false }, 156 | { 0, 0, 0, 0, false }, 157 | { 0, 0, 0, 0, false }, 158 | { 0, 0, 0, 0, false }, 159 | { 0, 0, 0, 0, false }, 160 | { 0, 0, 0, 0, false }, 161 | { 0, 0, 0, 0, false }, 162 | }; 163 | 164 | void doingSomething(int index) 165 | { 166 | // First check if enabled and pin != 0 167 | if ( (curISRTimerData[index].beingUsed) && (curISRTimerData[index].PWM_Value > 0) && (curISRTimerData[index].pin != 0) ) 168 | { 169 | // Divide the time into MAX_PWM_VALUE slots. 170 | // PWM_Value = 0 => no digitalWrite(pin, 1) 171 | // PWM_Value > 0 => digitalWrite(pin, 1) from countPWM = 0 to countPWM = PWM_Value 172 | 173 | if (curISRTimerData[index].countPWM == 0) 174 | { 175 | if (curISRTimerData[index].PWM_Value > 0) 176 | digitalWrite(curISRTimerData[index].pin, 1); 177 | else 178 | digitalWrite(curISRTimerData[index].pin, 0); 179 | } 180 | else if (curISRTimerData[index].countPWM == curISRTimerData[index].PWM_Value) 181 | { 182 | digitalWrite(curISRTimerData[index].pin, 0); 183 | } 184 | } 185 | 186 | curISRTimerData[index].countPWM = (curISRTimerData[index].countPWM + 1) % MAX_PWM_VALUE; 187 | } 188 | 189 | /////////////////////////////////////////// 190 | 191 | 192 | void setup() 193 | { 194 | pinMode(LED_BUILTIN, OUTPUT); 195 | 196 | Serial.begin(115200); 197 | while (!Serial); 198 | 199 | Serial.print(F("\nStarting FakeAnalogWrite on ")); 200 | Serial.println(BOARD_TYPE); 201 | Serial.println(TIMER_INTERRUPT_VERSION); 202 | Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); 203 | 204 | // Timer0 is used for micros(), millis(), delay(), etc and can't be used 205 | // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4 206 | // Timer 2 is 8-bit timer, only for higher frequency 207 | // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency 208 | 209 | #if USE_TIMER_1 210 | 211 | ITimer1.init(); 212 | 213 | // Using ATmega328 used in UNO => 16MHz CPU clock , 214 | 215 | //if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 216 | if (ITimer1.attachInterrupt(TIMER_FREQUENCY_HZ, TimerHandler)) 217 | { 218 | Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis()); 219 | } 220 | else 221 | Serial.println(F("Can't set ITimer1. Select another freq. or timer")); 222 | 223 | #elif USE_TIMER_3 224 | 225 | ITimer3.init(); 226 | 227 | //if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler)) 228 | if (ITimer3.attachInterrupt(TIMER_FREQUENCY_HZ, TimerHandler)) 229 | { 230 | Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis()); 231 | } 232 | else 233 | Serial.println(F("Can't set ITimer3. Select another freq. or timer")); 234 | 235 | #endif 236 | 237 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 238 | // You can use up to 16 timer for each ISR_Timer 239 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 240 | { 241 | curISRTimerData[i].beingUsed = false; 242 | curISRTimerData[i].pin = 0; 243 | curISRTimerData[i].PWM_Value = 0; 244 | 245 | //ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc); 246 | } 247 | } 248 | 249 | #define USING_MAPPING_TABLE false 250 | 251 | void fakeAnalogWrite(uint16_t pin, uint16_t value) 252 | { 253 | uint16_t localValue; 254 | 255 | #if USING_MAPPING_TABLE 256 | uint16_t localIndex = 0; 257 | #endif 258 | 259 | // First check if already got that pin, then just update the PWM_Value 260 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 261 | { 262 | if ( (curISRTimerData[i].beingUsed) && (curISRTimerData[i].pin == pin) ) 263 | { 264 | localValue = (value < MAX_PWM_VALUE) ? value : MAX_PWM_VALUE; 265 | 266 | if (curISRTimerData[i].PWM_PremapValue == localValue) 267 | { 268 | #if (LOCAL_DEBUG > 0) 269 | Serial.print(F("Ignore : Same Value for index = ")); Serial.println(i); 270 | #endif 271 | 272 | return; 273 | } 274 | else if (curISRTimerData[i].PWM_Value >= 0) 275 | { 276 | curISRTimerData[i].PWM_PremapValue = localValue; 277 | 278 | // Mapping to corect value 279 | if ( ( localValue == 0) || ( localValue == MAX_PWM_VALUE - 1) ) 280 | { 281 | // Keep MAX_PWM_VALUE 282 | curISRTimerData[i].PWM_Value = localValue; 283 | } 284 | else 285 | { 286 | 287 | #if USING_MAPPING_TABLE 288 | 289 | // Get the mapping index 290 | for (uint16_t j = 0; j < MAPPING_TABLE_SIZE; j++) 291 | { 292 | if ( (float) localValue < mappingTable[j]) 293 | { 294 | localIndex = j - 1; 295 | break; 296 | } 297 | } 298 | 299 | #if (LOCAL_DEBUG > 1) 300 | Serial.print(F("localIndex = ")); Serial.println(localIndex); 301 | #endif 302 | 303 | // Can use map() function 304 | // Can use map() function 305 | curISRTimerData[i].PWM_Value = (uint16_t) ( (localIndex * 10 ) + 306 | ( (localValue - mappingTable[localIndex]) * 10 ) / (mappingTable[localIndex + 1] - mappingTable[localIndex]) ); 307 | 308 | #else 309 | curISRTimerData[i].PWM_Value = localValue; 310 | #endif 311 | 312 | #if (LOCAL_DEBUG > 0) 313 | Serial.print(F("Update index = ")); Serial.print(i); 314 | Serial.print(F(", pin = ")); Serial.print(pin); 315 | Serial.print(F(", input PWM_Value = ")); Serial.print(value); 316 | Serial.print(F(", mapped PWM_Value = ")); Serial.println(curISRTimerData[i].PWM_Value); 317 | #endif 318 | } 319 | } 320 | else 321 | { 322 | curISRTimerData[i].beingUsed = false; 323 | curISRTimerData[i].pin = 0; 324 | curISRTimerData[i].PWM_Value = 0; 325 | } 326 | 327 | // Reset countPWM 328 | curISRTimerData[i].countPWM = 0; 329 | 330 | return; 331 | } 332 | } 333 | 334 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 335 | { 336 | if (!curISRTimerData[i].beingUsed) 337 | { 338 | curISRTimerData[i].beingUsed = true; 339 | curISRTimerData[i].pin = pin; 340 | 341 | // Mapping to corect value 342 | localValue = (value < MAX_PWM_VALUE) ? value : MAX_PWM_VALUE; 343 | 344 | if ( ( localValue == 0) || ( localValue == MAX_PWM_VALUE - 1) ) 345 | { 346 | // Keep MAX_PWM_VALUE 347 | curISRTimerData[i].PWM_Value = localValue; 348 | } 349 | else 350 | { 351 | curISRTimerData[i].PWM_PremapValue = localValue; 352 | 353 | #if USING_MAPPING_TABLE 354 | 355 | // Get the mapping index 356 | for (uint16_t j = 0; j < MAPPING_TABLE_SIZE; j++) 357 | { 358 | if ( (float) localValue < mappingTable[j]) 359 | { 360 | localIndex = j - 1; 361 | break; 362 | } 363 | } 364 | 365 | #if (LOCAL_DEBUG > 1) 366 | Serial.print(F("localIndex = ")); Serial.println(localIndex); 367 | #endif 368 | 369 | // Can use map() function 370 | // Can use map() function 371 | curISRTimerData[i].PWM_Value = (uint16_t) ( (localIndex * 10 ) + 372 | ( (localValue - mappingTable[localIndex]) * 10 ) / (mappingTable[localIndex + 1] - mappingTable[localIndex]) ); 373 | #else 374 | curISRTimerData[i].PWM_Value = localValue; 375 | #endif 376 | } 377 | 378 | curISRTimerData[i].countPWM = 0; 379 | 380 | pinMode(pin, OUTPUT); 381 | 382 | #if (LOCAL_DEBUG > 0) 383 | Serial.print(F("Add index = ")); Serial.print(i); 384 | Serial.print(F(", pin = ")); Serial.print(pin); 385 | Serial.print(F(", input PWM_Value = ")); Serial.print(value); 386 | Serial.print(F(", mapped PWM_Value = ")); Serial.println(curISRTimerData[i].PWM_Value); 387 | #endif 388 | 389 | return; 390 | } 391 | } 392 | } 393 | 394 | #define DELAY_BETWEEN_CHANGE_MS 5000L 395 | #define REPEAT_INTERVAL_MS 10000L 396 | 397 | #define DIVIDER 5 398 | 399 | void loop() 400 | { 401 | for (uint16_t i = 0; i <= MAX_PWM_VALUE / DIVIDER; i++) 402 | { 403 | // Change the pin according to board 404 | // UNI, Nano, etc can use pins from 2-12. Pin 13 is used for LED_BUILTIN 405 | // Mega can use many more pins, such as 22-53 406 | 407 | #if (defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)) 408 | fakeAnalogWrite(22, i * DIVIDER); 409 | fakeAnalogWrite(23, i * DIVIDER); 410 | fakeAnalogWrite(24, i * DIVIDER); 411 | fakeAnalogWrite(25, i * DIVIDER); 412 | fakeAnalogWrite(26, i * DIVIDER); 413 | fakeAnalogWrite(27, i * DIVIDER); 414 | fakeAnalogWrite(28, i * DIVIDER); 415 | fakeAnalogWrite(29, i * DIVIDER); 416 | fakeAnalogWrite(30, i * DIVIDER); 417 | fakeAnalogWrite(31, i * DIVIDER); 418 | fakeAnalogWrite(32, i * DIVIDER); 419 | fakeAnalogWrite(33, i * DIVIDER); 420 | fakeAnalogWrite(34, i * DIVIDER); 421 | fakeAnalogWrite(35, i * DIVIDER); 422 | fakeAnalogWrite(36, i * DIVIDER); 423 | fakeAnalogWrite(37, i * DIVIDER); 424 | #else 425 | fakeAnalogWrite( 2, i * DIVIDER); 426 | fakeAnalogWrite( 3, i * DIVIDER); 427 | fakeAnalogWrite( 4, i * DIVIDER); 428 | fakeAnalogWrite( 5, i * DIVIDER); 429 | fakeAnalogWrite( 6, i * DIVIDER); 430 | fakeAnalogWrite( 7, i * DIVIDER); 431 | fakeAnalogWrite( 8, i * DIVIDER); 432 | fakeAnalogWrite( 9, i * DIVIDER); 433 | fakeAnalogWrite(10, i * DIVIDER); 434 | fakeAnalogWrite(11, i * DIVIDER); 435 | fakeAnalogWrite(12, i * DIVIDER); 436 | fakeAnalogWrite(A0, i * DIVIDER); 437 | fakeAnalogWrite(A1, i * DIVIDER); 438 | fakeAnalogWrite(A2, i * DIVIDER); 439 | fakeAnalogWrite(A3, i * DIVIDER); 440 | fakeAnalogWrite(A4, i * DIVIDER); 441 | #endif 442 | 443 | #if (LOCAL_DEBUG > 0) 444 | Serial.print(F("Test PWM_Value = ")); Serial.print(i * DIVIDER); 445 | Serial.print(F(", max = ")); Serial.println(MAX_PWM_VALUE - 1); 446 | #endif 447 | 448 | delay(DELAY_BETWEEN_CHANGE_MS); 449 | } 450 | 451 | Serial.println(F("===================")); 452 | delay(REPEAT_INTERVAL_MS); 453 | } 454 | -------------------------------------------------------------------------------- /src/TimerInterrupt.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerInterrupt.h 3 | For Arduino boards (UNO, Nano, Mega, etc. ) 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt 7 | Licensed under MIT license 8 | 9 | TCNTx - Timer/Counter Register. The actual timer value is stored here. 10 | OCRx - Output Compare Register 11 | ICRx - Input Capture Register (only for 16bit timer) 12 | TIMSKx - Timer/Counter Interrupt Mask Register. To enable/disable timer interrupts. 13 | TIFRx - Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt. 14 | 15 | Now with we can use these new 16 ISR-based timers, while consuming only 1 hwarware Timer. 16 | Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds) 17 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 18 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 19 | This important feature is absolutely necessary for mission-critical tasks. 20 | 21 | Version: 1.8.0 22 | 23 | Version Modified By Date Comments 24 | ------- ----------- ---------- ----------- 25 | 1.0.0 K Hoang 23/11/2019 Initial coding 26 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 27 | 1.0.2 K.Hoang 28/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 28 | 1.0.3 K.Hoang 01/12/2020 Add complex examples ISR_16_Timers_Array_Complex and ISR_16_Timers_Array_Complex 29 | 1.1.1 K.Hoang 06/12/2020 Add example Change_Interval. Bump up version to sync with other TimerInterrupt Libraries 30 | 1.1.2 K.Hoang 05/01/2021 Fix warnings. Optimize examples to reduce memory usage 31 | 1.2.0 K.Hoang 07/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 32 | 1.3.0 K.Hoang 25/02/2021 Add support to AVR ATMEGA_32U4 such as Leonardo, YUN, ESPLORA, etc. 33 | 1.4.0 K.Hoang 01/04/2021 Add support to Adafruit 32U4 and 328(P) such as FEATHER32U4, FEATHER328P, etc. 34 | 1.4.1 K.Hoang 02/04/2021 Add support to Sparkfun 32U4, 328(P), 128RFA1 such as AVR_PROMICRO, REDBOT, etc. 35 | 1.5.0 K.Hoang 08/05/2021 Add Timer 3 and 4 to 32U4. Add Timer auto-selection to examples. 36 | 1.6.0 K.Hoang 15/11/2021 Fix bug resulting half frequency when using high frequencies. 37 | 1.7.0 K.Hoang 19/11/2021 Fix bug resulting wrong frequency for some frequencies. 38 | 1.8.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 39 | ****************************************************************************************************************************/ 40 | 41 | #pragma once 42 | 43 | #ifndef TimerInterrupt_hpp 44 | #define TimerInterrupt_hpp 45 | 46 | #if defined(BOARD_TYPE) 47 | #undef BOARD_TYPE 48 | #endif 49 | 50 | #if ( defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || \ 51 | defined(__AVR_ATmega640__) || defined(__AVR_ATmega641__)) 52 | #if defined(TIMER_INTERRUPT_USING_ATMEGA2560) 53 | #undef TIMER_INTERRUPT_USING_ATMEGA2560 54 | #endif 55 | #define TIMER_INTERRUPT_USING_ATMEGA2560 true 56 | #define BOARD_TYPE "Arduino AVR Mega2560/ADK" 57 | #warning Using Arduino AVR Mega, Mega640(P), Mega2560/ADK. Timer1-5 available 58 | 59 | #elif ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ 60 | defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ 61 | defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ 62 | defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) ) 63 | #define BOARD_TYPE "Arduino AVR UNO, Nano, etc." 64 | #warning Using Aduino AVR ATMega644(P), ATMega328(P) such as UNO, Nano. Only Timer1,2 available 65 | 66 | #elif ( defined(ARDUINO_AVR_FEATHER328P) || defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || \ 67 | defined(ARDUINO_AVR_PROTRINKET5FTDI) || defined(ARDUINO_AVR_PROTRINKET3FTDI) ) 68 | #define BOARD_TYPE "Adafruit AVR ATMega328(P)" 69 | #warning Using Adafruit ATMega328(P), such as AVR_FEATHER328P or AVR_METRO. Only Timer1,2 available 70 | 71 | #elif ( defined(ARDUINO_AVR_LEONARDO) || defined(ARDUINO_AVR_LEONARDO_ETH) || defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_MICRO) || \ 72 | defined(ARDUINO_AVR_ESPLORA) || defined(ARDUINO_AVR_LILYPAD_USB) || defined(ARDUINO_AVR_ROBOT_CONTROL) || defined(ARDUINO_AVR_ROBOT_MOTOR) || \ 73 | defined(ARDUINO_AVR_CIRCUITPLAY) || defined(ARDUINO_AVR_YUNMINI) || defined(ARDUINO_AVR_INDUSTRIAL101) || defined(ARDUINO_AVR_LININO_ONE) ) 74 | #if defined(TIMER_INTERRUPT_USING_ATMEGA_32U4) 75 | #undef TIMER_INTERRUPT_USING_ATMEGA_32U4 76 | #endif 77 | #define TIMER_INTERRUPT_USING_ATMEGA_32U4 true 78 | #define BOARD_TYPE "Arduino AVR ATMega32U4" 79 | #warning Using Arduino ATMega32U4, such as Leonardo or Leonardo ETH. Only Timer1,3,4 available 80 | 81 | #elif ( defined(ARDUINO_AVR_FLORA8 ) || defined(ARDUINO_AVR_FEATHER32U4) || defined(ARDUINO_AVR_CIRCUITPLAY) || defined(ARDUINO_AVR_ITSYBITSY32U4_5V) || \ 82 | defined(ARDUINO_AVR_ITSYBITSY32U4_3V) || defined(ARDUINO_AVR_BLUEFRUITMICRO) || defined(ARDUINO_AVR_ADAFRUIT32U4) ) 83 | #if defined(TIMER_INTERRUPT_USING_ATMEGA_32U4) 84 | #undef TIMER_INTERRUPT_USING_ATMEGA_32U4 85 | #endif 86 | #define TIMER_INTERRUPT_USING_ATMEGA_32U4 true 87 | #define BOARD_TYPE "Adafruit AVR ATMega32U4" 88 | #warning Using Adafruit ATMega32U4, such as Feather_32u4, AVR_CIRCUITPLAY, etc.. Only Timer1,3,4 available 89 | 90 | #elif ( defined(__AVR_ATmega32U4__) || defined(ARDUINO_AVR_MAKEYMAKEY ) || defined(ARDUINO_AVR_PROMICRO) || defined(ARDUINO_AVR_FIOV3) || \ 91 | defined(ARDUINO_AVR_QDUINOMINI) || defined(ARDUINO_AVR_LILYPAD_ARDUINO_USB_PLUS_BOARD ) ) 92 | #if defined(TIMER_INTERRUPT_USING_ATMEGA_32U4) 93 | #undef TIMER_INTERRUPT_USING_ATMEGA_32U4 94 | #endif 95 | #define TIMER_INTERRUPT_USING_ATMEGA_32U4 true 96 | #define BOARD_TYPE "Generic or Sparkfun AVR ATMega32U4" 97 | #warning Using Generic ATMega32U4, such as Sparkfun AVR_MAKEYMAKEY, AVR_PROMICRO, etc. Only Timer1,3,4 available 98 | 99 | #elif ( defined(__AVR_ATmega328P__) || defined(ARDUINO_AVR_DIGITAL_SANDBOX ) || defined(ARDUINO_REDBOT) || defined(ARDUINO_AVR_SERIAL_7_SEGMENT) ) 100 | #define BOARD_TYPE "Generic or Sparkfun AVR ATMega328P" 101 | #warning Using Generic ATMega328P, such as Sparkfun AVR_DIGITAL_SANDBOX, REDBOT, etc. 102 | 103 | #elif ( defined(__AVR_ATmega128RFA1__) || defined(ARDUINO_ATMEGA128RFA1_DEV_BOARD) ) 104 | #define BOARD_TYPE "Generic or Sparkfun AVR ATMega128RFA1" 105 | #warning Using Generic ATMega128RFA1, such as Sparkfun ATMEGA128RFA1_DEV_BOARD, etc. 106 | 107 | #elif ( defined(ARDUINO_AVR_GEMMA) || defined(ARDUINO_AVR_TRINKET3) || defined(ARDUINO_AVR_TRINKET5) ) 108 | #error These AVR boards are not supported! Please check your Tools->Board setting. 109 | 110 | #else 111 | #error This is designed only for Arduino or Adafruit AVR board! Please check your Tools->Board setting. 112 | #endif 113 | 114 | #ifndef TIMER_INTERRUPT_DEBUG 115 | #define TIMER_INTERRUPT_DEBUG 0 116 | #endif 117 | 118 | #include "TimerInterrupt_Generic_Debug.h" 119 | 120 | #ifndef TIMER_INTERRUPT_VERSION 121 | #define TIMER_INTERRUPT_VERSION "TimerInterrupt v1.8.0" 122 | 123 | #define TIMER_INTERRUPT_VERSION_MAJOR 1 124 | #define TIMER_INTERRUPT_VERSION_MINOR 8 125 | #define TIMER_INTERRUPT_VERSION_PATCH 0 126 | 127 | #define TIMER_INTERRUPT_VERSION_INT 1008000 128 | #endif 129 | 130 | #include 131 | #include 132 | #include "Arduino.h" 133 | #include "pins_arduino.h" 134 | 135 | #define MAX_COUNT_8BIT 255 136 | #define MAX_COUNT_10BIT 1023 137 | #define MAX_COUNT_16BIT 65535 138 | 139 | #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) 140 | #define TCCR2A TCCR2 141 | #define TCCR2B TCCR2 142 | #define COM2A1 COM21 143 | #define COM2A0 COM20 144 | #define OCR2A OCR2 145 | #define TIMSK2 TIMSK 146 | #define OCIE2A OCIE2 147 | #define TIMER2_COMPA_vect TIMER2_COMP_vect 148 | #define TIMSK1 TIMSK 149 | #endif 150 | 151 | typedef void (*timer_callback)(); 152 | typedef void (*timer_callback_p)(void *); 153 | 154 | enum 155 | { 156 | HW_TIMER_0 = 0, 157 | HW_TIMER_1, 158 | HW_TIMER_2, 159 | HW_TIMER_3, 160 | HW_TIMER_4, 161 | HW_TIMER_5, 162 | NUM_HW_TIMERS 163 | }; 164 | 165 | enum 166 | { 167 | NO_CLOCK_SOURCE = 0, 168 | NO_PRESCALER, 169 | PRESCALER_8, 170 | PRESCALER_64, 171 | PRESCALER_256, 172 | PRESCALER_1024, 173 | NUM_ITEMS 174 | }; 175 | 176 | enum 177 | { 178 | T2_NO_CLOCK_SOURCE = 0, 179 | T2_NO_PRESCALER, 180 | T2_PRESCALER_8, 181 | T2_PRESCALER_32, 182 | T2_PRESCALER_64, 183 | T2_PRESCALER_128, 184 | T2_PRESCALER_256, 185 | T2_PRESCALER_1024, 186 | T2_NUM_ITEMS 187 | }; 188 | 189 | const unsigned int prescalerDiv [NUM_ITEMS] = { 1, 1, 8, 64, 256, 1024 }; 190 | const unsigned int prescalerDivT2 [T2_NUM_ITEMS] = { 1, 1, 8, 32, 64, 128, 256, 1024 }; 191 | 192 | class TimerInterrupt 193 | { 194 | private: 195 | 196 | bool _timerDone; 197 | int8_t _timer; 198 | unsigned int _prescalerIndex; 199 | uint32_t _OCRValue; 200 | uint32_t _OCRValueRemaining; 201 | volatile long _toggle_count; 202 | double _frequency; 203 | 204 | void* _callback; // pointer to the callback function 205 | void* _params; // function parameter 206 | 207 | void set_OCR(); 208 | 209 | public: 210 | 211 | TimerInterrupt() 212 | { 213 | _timer = -1; 214 | _frequency = 0; 215 | _callback = NULL; 216 | _params = NULL; 217 | _timerDone = false; 218 | _prescalerIndex = NO_PRESCALER; 219 | _OCRValue = 0; 220 | _OCRValueRemaining = 0; 221 | _toggle_count = -1; 222 | }; 223 | 224 | explicit TimerInterrupt(uint8_t timerNo) 225 | { 226 | _timer = timerNo; 227 | _frequency = 0; 228 | _callback = NULL; 229 | _params = NULL; 230 | _timerDone = false; 231 | _prescalerIndex = NO_PRESCALER; 232 | _OCRValue = 0; 233 | _OCRValueRemaining = 0; 234 | _toggle_count = -1; 235 | }; 236 | 237 | void callback() __attribute__((always_inline)) 238 | { 239 | if (_callback != NULL) 240 | { 241 | if (_params != NULL) 242 | (*(timer_callback_p)_callback)(_params); 243 | else 244 | (*(timer_callback)_callback)(); 245 | } 246 | } 247 | 248 | void init(int8_t timer); 249 | 250 | void init() 251 | { 252 | init(_timer); 253 | }; 254 | 255 | // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 256 | bool setFrequency(float frequency, timer_callback_p callback, /* void* */ uint32_t params, unsigned long duration = 0); 257 | 258 | // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 259 | bool setFrequency(float frequency, timer_callback callback, unsigned long duration = 0) 260 | { 261 | return setFrequency(frequency, reinterpret_cast(callback), /*NULL*/ 0, duration); 262 | } 263 | 264 | // interval (in ms) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 265 | template 266 | bool setInterval(unsigned long interval, void (*callback)(TArg), TArg params, unsigned long duration = 0) 267 | { 268 | static_assert(sizeof(TArg) <= sizeof(uint32_t), "setInterval() callback argument size must be <= 4 bytes"); 269 | return setFrequency((float) (1000.0f / interval), reinterpret_cast(callback), (uint32_t) params, duration); 270 | } 271 | 272 | // interval (in ms) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 273 | bool setInterval(unsigned long interval, timer_callback callback, unsigned long duration = 0) 274 | { 275 | return setFrequency((float) (1000.0f / interval), reinterpret_cast(callback), /*NULL*/ 0, duration); 276 | } 277 | 278 | template 279 | bool attachInterrupt(float frequency, void (*callback)(TArg), TArg params, unsigned long duration = 0) 280 | { 281 | static_assert(sizeof(TArg) <= sizeof(uint32_t), "attachInterrupt() callback argument size must be <= 4 bytes"); 282 | return setFrequency(frequency, reinterpret_cast(callback), (uint32_t) params, duration); 283 | } 284 | 285 | bool attachInterrupt(float frequency, timer_callback callback, unsigned long duration = 0) 286 | { 287 | return setFrequency(frequency, reinterpret_cast(callback), /*NULL*/ 0, duration); 288 | } 289 | 290 | // Interval (in ms) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 291 | template 292 | bool attachInterruptInterval(unsigned long interval, void (*callback)(TArg), TArg params, unsigned long duration = 0) 293 | { 294 | static_assert(sizeof(TArg) <= sizeof(uint32_t), "attachInterruptInterval() callback argument size must be <= 4 bytes"); 295 | return setFrequency( (float) ( 1000.0f / interval), reinterpret_cast(callback), (uint32_t) params, duration); 296 | } 297 | 298 | // Interval (in ms) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 299 | bool attachInterruptInterval(unsigned long interval, timer_callback callback, unsigned long duration = 0) 300 | { 301 | return setFrequency( (float) ( 1000.0f / interval), reinterpret_cast (callback), /*NULL*/ 0, duration); 302 | } 303 | 304 | void detachInterrupt(); 305 | 306 | void disableTimer() 307 | { 308 | detachInterrupt(); 309 | } 310 | 311 | // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely 312 | void reattachInterrupt(unsigned long duration = 0); 313 | 314 | // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely 315 | void enableTimer(unsigned long duration = 0) __attribute__((always_inline)) 316 | { 317 | reattachInterrupt(duration); 318 | } 319 | 320 | // Just stop clock source, still keep the count 321 | void pauseTimer(); 322 | 323 | // Just reconnect clock source, continue from the current count 324 | void resumeTimer(); 325 | 326 | // Just stop clock source, clear the count 327 | void stopTimer() 328 | { 329 | detachInterrupt(); 330 | } 331 | 332 | // Just reconnect clock source, start current count from 0 333 | void restartTimer(unsigned long duration = 0) 334 | { 335 | reattachInterrupt(duration); 336 | } 337 | 338 | int8_t getTimer() __attribute__((always_inline)) 339 | { 340 | return _timer; 341 | }; 342 | 343 | long getCount() __attribute__((always_inline)) 344 | { 345 | return _toggle_count; 346 | }; 347 | 348 | void setCount(long countInput) __attribute__((always_inline)) 349 | { 350 | //cli();//stop interrupts 351 | //noInterrupts(); 352 | 353 | _toggle_count = countInput; 354 | 355 | //sei();//enable interrupts 356 | //interrupts(); 357 | }; 358 | 359 | long get_OCRValue() __attribute__((always_inline)) 360 | { 361 | return _OCRValue; 362 | }; 363 | 364 | long get_OCRValueRemaining() __attribute__((always_inline)) 365 | { 366 | return _OCRValueRemaining; 367 | }; 368 | 369 | void adjust_OCRValue() //__attribute__((always_inline)) 370 | { 371 | //cli();//stop interrupts 372 | noInterrupts(); 373 | 374 | if (_timer != 2) 375 | { 376 | #if TIMER_INTERRUPT_USING_ATMEGA_32U4 377 | if (_timer == 4) 378 | { 379 | if (_OCRValueRemaining < MAX_COUNT_8BIT) 380 | { 381 | set_OCR(); 382 | } 383 | 384 | _OCRValueRemaining -= min(MAX_COUNT_8BIT, _OCRValueRemaining); 385 | } 386 | else 387 | { 388 | if (_OCRValueRemaining < MAX_COUNT_16BIT) 389 | { 390 | set_OCR(); 391 | } 392 | 393 | _OCRValueRemaining -= min(MAX_COUNT_16BIT, _OCRValueRemaining); 394 | } 395 | #else 396 | if (_OCRValueRemaining < MAX_COUNT_16BIT) 397 | { 398 | set_OCR(); 399 | } 400 | 401 | _OCRValueRemaining -= min(MAX_COUNT_16BIT, _OCRValueRemaining); 402 | #endif 403 | } 404 | else 405 | { 406 | if (_OCRValueRemaining < MAX_COUNT_8BIT) 407 | { 408 | set_OCR(); 409 | } 410 | 411 | _OCRValueRemaining -= min(MAX_COUNT_8BIT, _OCRValueRemaining); 412 | } 413 | 414 | if (_OCRValueRemaining <= 0) 415 | { 416 | // Reset value for next cycle 417 | _OCRValueRemaining = _OCRValue; 418 | _timerDone = true; 419 | } 420 | else 421 | _timerDone = false; 422 | 423 | //sei();//enable interrupts 424 | interrupts(); 425 | }; 426 | 427 | void reload_OCRValue() //__attribute__((always_inline)) 428 | { 429 | //cli();//stop interrupts 430 | noInterrupts(); 431 | 432 | // Reset value for next cycle, have to deduct the value already loaded to OCR register 433 | 434 | _OCRValueRemaining = _OCRValue; 435 | set_OCR(); 436 | 437 | _timerDone = false; 438 | 439 | //sei();//enable interrupts 440 | interrupts(); 441 | }; 442 | 443 | bool checkTimerDone() //__attribute__((always_inline)) 444 | { 445 | return _timerDone; 446 | }; 447 | 448 | }; // class TimerInterrupt 449 | 450 | ////////////////////////////////////////////// 451 | 452 | // To be sure not used Timers are disabled 453 | #if !defined(USE_TIMER_1) 454 | #define USE_TIMER_1 false 455 | #endif 456 | 457 | #if !defined(USE_TIMER_2) 458 | #define USE_TIMER_2 false 459 | #elif ( USE_TIMER_2 && TIMER_INTERRUPT_USING_ATMEGA_32U4 ) 460 | #error Timer2 is disabled for ATMEGA_32U4, only available for ATMEGA_328(P) and Mega 461 | #endif 462 | 463 | #if !defined(USE_TIMER_3) 464 | #define USE_TIMER_3 false 465 | #elif ( USE_TIMER_3 && ( TIMER_INTERRUPT_USING_ATMEGA_32U4 || TIMER_INTERRUPT_USING_ATMEGA2560 ) ) 466 | #warning Timer3 (16-bit) is OK to use for ATMEGA_32U4 and Mega 467 | #elif USE_TIMER_3 468 | #error Timer3 is only available for ATMEGA_32U4 and Mega 469 | #endif 470 | 471 | #if !defined(USE_TIMER_4) 472 | #define USE_TIMER_4 false 473 | #elif ( USE_TIMER_4 && ( TIMER_INTERRUPT_USING_ATMEGA_32U4 || TIMER_INTERRUPT_USING_ATMEGA2560 ) ) 474 | #warning Timer4 is OK to use for ATMEGA_32U4 (10-bit but using as 8-bit) and Mega (16-bit) 475 | #elif USE_TIMER_4 476 | #error Timer4 is only available for ATMEGA_32U4 and Mega 477 | #endif 478 | 479 | #if !defined(USE_TIMER_5) 480 | #define USE_TIMER_5 false 481 | #elif ( USE_TIMER_5 && TIMER_INTERRUPT_USING_ATMEGA2560 ) 482 | #warning Timer5 is OK to use for Mega 483 | #elif USE_TIMER_5 484 | #error Timer5 is only available for Mega 485 | #endif 486 | 487 | ////////////////////////////////////////////// 488 | 489 | #endif //#ifndef TimerInterrupt_hpp 490 | --------------------------------------------------------------------------------