From ff713bf99b8950ff456d07ea7671ae63d4a9c12d Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Fri, 28 Oct 2022 22:53:56 -0400 Subject: [PATCH] v1.0.0 for hardware-PWM on AVR boards ### Initial Releases v1.0.0 1. Initial coding to support **AVR boards, such as Mega-2560, UNO, Nano, Leonardo, etc.**, etc. using AVR core 2. The hardware-based PWM channels can generate very high frequencies. --- CONTRIBUTING.md | 76 + changelog.md | 34 + examples/PWM_Basic/PWM_Basic.ino | 123 ++ .../PWM_DynamicDutyCycle.ino | 173 ++ .../PWM_DynamicDutyCycle_Int.ino | 163 ++ examples/PWM_DynamicFreq/PWM_DynamicFreq.ino | 156 ++ examples/PWM_Multi/PWM_Multi.ino | 176 ++ .../PWM_MultiChannel/PWM_MultiChannel.ino | 185 ++ examples/PWM_Waveform/PWM_Waveform.ino | 221 +++ keywords.txt | 47 + library.json | 30 + library.properties | 12 + platformio/platformio.ini | 116 ++ src/AVR_PWM.h | 1483 +++++++++++++++++ src/PWM_Generic_Debug.h | 100 ++ utils/astyle_library.conf | 70 + utils/restyle.sh | 6 + 17 files changed, 3171 insertions(+) create mode 100644 CONTRIBUTING.md create mode 100644 changelog.md create mode 100644 examples/PWM_Basic/PWM_Basic.ino create mode 100644 examples/PWM_DynamicDutyCycle/PWM_DynamicDutyCycle.ino create mode 100644 examples/PWM_DynamicDutyCycle_Int/PWM_DynamicDutyCycle_Int.ino create mode 100644 examples/PWM_DynamicFreq/PWM_DynamicFreq.ino create mode 100644 examples/PWM_Multi/PWM_Multi.ino create mode 100644 examples/PWM_MultiChannel/PWM_MultiChannel.ino create mode 100644 examples/PWM_Waveform/PWM_Waveform.ino create mode 100644 keywords.txt create mode 100644 library.json create mode 100644 library.properties create mode 100644 platformio/platformio.ini create mode 100644 src/AVR_PWM.h create mode 100644 src/PWM_Generic_Debug.h create mode 100644 utils/astyle_library.conf create mode 100644 utils/restyle.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..45ed0ca --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,76 @@ +## Contributing to AVR_PWM + +### Reporting Bugs + +Please report bugs in AVR_PWM if you find them. + +However, before reporting a bug please check through the following: + +* [Existing Open Issues](https://github.com/khoih-prog/AVR_PWM/issues) - someone might have already encountered this. + +If you don't find anything, please [open a new issue](https://github.com/khoih-prog/AVR_PWM/issues/new). + +### How to submit a bug report + +Please ensure to specify the following: + +* Arduino IDE version (e.g. 1.8.19) or Platform.io version +* Arduino / Adafruit / Sparkfun `AVR` Core Version (e.g. Arduino AVR core v1.8.5) +* Contextual information (e.g. what you were trying to achieve) +* Simplest possible steps to reproduce +* Anything that might be relevant in your opinion, such as: + * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` + * Network configuration + + +### Example + +``` +Arduino IDE version: 1.8.19 +Arduino AVR core v1.8.5 +OS: Ubuntu 20.04 LTS +Linux xy-Inspiron-3593 5.15.0-52-generic #58~20.04.1-Ubuntu SMP Thu Oct 13 13:09:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux + +Context: +I encountered a crash while using this library +Steps to reproduce: +1. ... +2. ... +3. ... +4. ... +``` + +### Additional context + +Add any other context about the problem here. + +--- + +### Sending Feature Requests + +Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. + +There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/AVR_PWM/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. + +--- + +### Sending Pull Requests + +Pull Requests with changes and fixes are also welcome! + +Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) + +1. Change directory to the library GitHub + +``` +xy@xy-Inspiron-3593:~$ cd Arduino/xy/AVR_PWM_GitHub/ +xy@xy-Inspiron-3593:~/Arduino/xy/AVR_PWM_GitHub$ +``` + +2. Issue astyle command + +``` +xy@xy-Inspiron-3593:~/Arduino/xy/AVR_PWM_GitHub$ bash utils/restyle.sh +``` + + diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..ab4c5ec --- /dev/null +++ b/changelog.md @@ -0,0 +1,34 @@ +# AVR_PWM Library + +[![arduino-library-badge](https://www.ardu-badge.com/badge/AVR_PWM.svg?)](https://www.ardu-badge.com/AVR_PWM) +[![GitHub release](https://img.shields.io/github/release/khoih-prog/AVR_PWM.svg)](https://github.com/khoih-prog/AVR_PWM/releases) +[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/AVR_PWM/blob/master/LICENSE) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) +[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/AVR_PWM.svg)](http://github.com/khoih-prog/AVR_PWM/issues) + +--- +--- + +## Table of Contents + +* [Changelog](#changelog) + * [Initial Releases v1.0.0](#Initial-Releases-v100) + +--- +--- + +## Changelog + +### Initial Releases v1.0.0 + +1. Initial coding to support **AVR boards, such as Mega-2560, UNO, Nano, Leonardo, etc.**, etc. using AVR core +2. The hardware-based PWM channels can generate very high frequencies. + +--- +--- + +## Copyright + +Copyright 2022- Khoi Hoang + + diff --git a/examples/PWM_Basic/PWM_Basic.ino b/examples/PWM_Basic/PWM_Basic.ino new file mode 100644 index 0000000..c9f3256 --- /dev/null +++ b/examples/PWM_Basic/PWM_Basic.ino @@ -0,0 +1,123 @@ +/**************************************************************************************************************************** + PWM_Basic.ino + + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM +*****************************************************************************************************************************/ +/****************************************************************************************************************************** + // For UNO / Nano + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 5 (6 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 9 and 10 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 3 and 11 + // For Mega + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 4 (13 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 11, 12 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 9 and 10 + Timer3 (16-bit) used by PWM generation on pins 2, 3 and 5 + Timer4 (16-bit) used by PWM generation on pins 6, 7 and 8 + Timer5 (16-bit) used by PWM generation on pins 44, 45 and 46 + + //////////////////////////////////////////// + // For Mega (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 44, 45, 46) + Pin 2 => TIMER3B // PE 4 ** 2 ** PWM2 + Pin 3 => TIMER3C // PE 5 ** 3 ** PWM3 + Pin 4 => TIMER0B // PG 5 ** 4 ** PWM4 + Pin 5 => TIMER3A // PE 3 ** 5 ** PWM5 + Pin 6 => TIMER4A // PH 3 ** 6 ** PWM6 + Pin 7 => TIMER4B // PH 4 ** 7 ** PWM7 + Pin 8 => TIMER4C // PH 5 ** 8 ** PWM8 + Pin 9 => TIMER2B // PH 6 ** 9 ** PWM9 + Pin 10 => TIMER2A // PB 4 ** 10 ** PWM10 + Pin 11 => TIMER1A // PB 5 ** 11 ** PWM11 + Pin 12 => TIMER1B // PB 6 ** 12 ** PWM12 + Pin 13 => TIMER0A // PB 7 ** 13 ** PWM13 + Pin 44 => TIMER5C // PL 5 ** 44 ** D44 + Pin 45 => TIMER5B // PL 4 ** 45 ** D45 + Pin 46 => TIMER5A // PL 3 ** 46 ** D46 + //////////////////////////////////////////// + // For 32u4 (3, 5, 6, 9, 10, 11, 13) + Pin 3 => TIMER0B + Pin 5 => TIMER3A + Pin 6 => TIMER4D + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER0A + Pin 13 => TIMER4A + //////////////////////////////////////////// + // For UNO, Nano (3, 5, 6, 9, 10, 11) + Pin 3 => TIMER2B, + Pin 5 => TIMER0B + Pin 6 => TIMER0A + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER2(A) +******************************************************************************************************************************/ + +#define _PWM_LOGLEVEL_ 4 + +#include "AVR_PWM.h" + +#if ( PWM_USING_ATMEGA2560 ) + // Pins tested OK in Mega + //#define pinToUse 12 // Timer1B on Mega + //#define pinToUse 11 // Timer1A on Mega + //#define pinToUse 9 // Timer2B on Mega + //#define pinToUse 2 // Timer3B on Mega + //#define pinToUse 3 // Timer3C on Mega + //#define pinToUse 5 // Timer3A on Mega + //#define pinToUse 6 // Timer4A on Mega + //#define pinToUse 7 // Timer4B on Mega + #define pinToUse 8 // Timer4C on Mega + //#define pinToUse 46 // Timer5A on Mega + //#define pinToUse 45 // Timer5B on Mega + //#define pinToUse 44 // Timer5C on Mega + +#elif ( PWM_USING_ATMEGA_32U4 ) + // Pins tested OK on 32u4 + //#define pinToUse 5 // Timer3A on 32u4 + #define pinToUse 9 // Timer1A on 32u4 + //#define pinToUse 10 // Timer1B on 32u4 + +#else + + // Pins tested OK on Nano / UNO + //#define pinToUse 9 // Timer1A on UNO, Nano, etc + #define pinToUse 10 // Timer1B on UNO, Nano, etc + //#define pinToUse 5 // Timer0B on UNO, Nano, e + //#define pinToUse 3 // Timer2B on UNO, Nano, etc +#endif + +//creates pwm instance +AVR_PWM* PWM_Instance; + +float frequency; +float dutyCycle; + +void setup() +{ + Serial.print(F("\nStarting PWM_Basic on ")); + Serial.println(BOARD_NAME); + Serial.println(AVR_PWM_VERSION); + + //assigns PWM frequency of 20 KHz and a duty cycle of 0% + PWM_Instance = new AVR_PWM(pinToUse, 20000, 0); +} + +void loop() +{ + delay(10000); + frequency = 20000; + dutyCycle = 90; + + PWM_Instance->setPWM(pinToUse, frequency, dutyCycle); + + delay(10000); + dutyCycle = 10; + + PWM_Instance->setPWM(pinToUse, frequency, dutyCycle); +} diff --git a/examples/PWM_DynamicDutyCycle/PWM_DynamicDutyCycle.ino b/examples/PWM_DynamicDutyCycle/PWM_DynamicDutyCycle.ino new file mode 100644 index 0000000..27422a7 --- /dev/null +++ b/examples/PWM_DynamicDutyCycle/PWM_DynamicDutyCycle.ino @@ -0,0 +1,173 @@ +/**************************************************************************************************************************** + PWM_DynamicDutyCycle.ino + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM +*****************************************************************************************************************************/ +/****************************************************************************************************************************** + // For UNO / Nano + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 5 (6 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 9 and 10 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 3 and 11 + // For Mega + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 4 (13 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 11, 12 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 9 and 10 + Timer3 (16-bit) used by PWM generation on pins 2, 3 and 5 + Timer4 (16-bit) used by PWM generation on pins 6, 7 and 8 + Timer5 (16-bit) used by PWM generation on pins 44, 45 and 46 + + //////////////////////////////////////////// + // For Mega (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 44, 45, 46) + Pin 2 => TIMER3B // PE 4 ** 2 ** PWM2 + Pin 3 => TIMER3C // PE 5 ** 3 ** PWM3 + Pin 4 => TIMER0B // PG 5 ** 4 ** PWM4 + Pin 5 => TIMER3A // PE 3 ** 5 ** PWM5 + Pin 6 => TIMER4A // PH 3 ** 6 ** PWM6 + Pin 7 => TIMER4B // PH 4 ** 7 ** PWM7 + Pin 8 => TIMER4C // PH 5 ** 8 ** PWM8 + Pin 9 => TIMER2B // PH 6 ** 9 ** PWM9 + Pin 10 => TIMER2A // PB 4 ** 10 ** PWM10 + Pin 11 => TIMER1A // PB 5 ** 11 ** PWM11 + Pin 12 => TIMER1B // PB 6 ** 12 ** PWM12 + Pin 13 => TIMER0A // PB 7 ** 13 ** PWM13 + Pin 44 => TIMER5C // PL 5 ** 44 ** D44 + Pin 45 => TIMER5B // PL 4 ** 45 ** D45 + Pin 46 => TIMER5A // PL 3 ** 46 ** D46 + //////////////////////////////////////////// + // For 32u4 (3, 5, 6, 9, 10, 11, 13) + Pin 3 => TIMER0B + Pin 5 => TIMER3A + Pin 6 => TIMER4D + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER0A + Pin 13 => TIMER4A + //////////////////////////////////////////// + // For UNO, Nano (3, 5, 6, 9, 10, 11) + Pin 3 => TIMER2B, + Pin 5 => TIMER0B + Pin 6 => TIMER0A + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER2(A) +******************************************************************************************************************************/ + +#define _PWM_LOGLEVEL_ 4 + +#include "AVR_PWM.h" + +#define LED_ON LOW +#define LED_OFF HIGH + +#if ( PWM_USING_ATMEGA2560 ) + // Pins tested OK in Mega + //#define pinToUse 12 // Timer1B on Mega + //#define pinToUse 11 // Timer1A on Mega + //#define pinToUse 9 // Timer2B on Mega + //#define pinToUse 2 // Timer3B on Mega + //#define pinToUse 3 // Timer3C on Mega + //#define pinToUse 5 // Timer3A on Mega + //#define pinToUse 6 // Timer4A on Mega + //#define pinToUse 7 // Timer4B on Mega + #define pinToUse 8 // Timer4C on Mega + //#define pinToUse 46 // Timer5A on Mega + //#define pinToUse 45 // Timer5B on Mega + //#define pinToUse 44 // Timer5C on Mega + +#elif ( PWM_USING_ATMEGA_32U4 ) + // Pins tested OK on 32u4 + ////#define pinToUse 3 // Timer0B on 32u4 + //#define pinToUse 5 // Timer3A on 32u4 + #define pinToUse 9 // Timer1A on 32u4 + //#define pinToUse 10 // Timer1B on 32u4 + +#else + + // Pins tested OK on Nano / UNO + //#define pinToUse 9 // Timer1A on UNO, Nano, etc + #define pinToUse 10 // Timer1B on UNO, Nano, etc + //#define pinToUse 5 // Timer0B on UNO, Nano, e + //#define pinToUse 3 // Timer2B on UNO, Nano, etc +#endif + +AVR_PWM* PWM_Instance; + +float frequency; +float dutyCycle; + +char dashLine[] = "====================================================================================="; + +void printPWMInfo(AVR_PWM* PWM_Instance) +{ + Serial.println(dashLine); + Serial.print("Actual data: pin = "); + Serial.print(PWM_Instance->getPin()); + Serial.print(", PWM DC = "); + Serial.print(PWM_Instance->getActualDutyCycle()); + Serial.print(", PWMPeriod = "); + Serial.print(PWM_Instance->getPWMPeriod()); + Serial.print(", PWM Freq (Hz) = "); + Serial.println(PWM_Instance->getActualFreq(), 4); + Serial.println(dashLine); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial); + + delay(100); + + Serial.print(F("\nStarting PWM_DynamicDutyCycle on ")); + Serial.println(BOARD_NAME); + Serial.println(AVR_PWM_VERSION); + + frequency = 5000; + //frequency = 70.0f; + + PWM_Instance = new AVR_PWM(pinToUse, frequency, 50); + + if (PWM_Instance) + { + if (!PWM_Instance->setPWM()) + { + Serial.println(F("Stop here")); + + // stop here + while (true) + delay(1000); + } + } + + Serial.println(dashLine); +} + +void loop() +{ + //frequency = 10000.0f; + frequency = 1000.0f; + + dutyCycle = 90.0f; + + Serial.print(F("Change PWM DutyCycle to ")); + Serial.println(dutyCycle); + PWM_Instance->setPWM(pinToUse, frequency, dutyCycle); + + printPWMInfo(PWM_Instance); + + delay(5000); + dutyCycle = 10.0f; + + Serial.print(F("Change PWM DutyCycle to ")); + Serial.println(dutyCycle); + PWM_Instance->setPWM(pinToUse, frequency, dutyCycle); + printPWMInfo(PWM_Instance); + + delay(5000); +} diff --git a/examples/PWM_DynamicDutyCycle_Int/PWM_DynamicDutyCycle_Int.ino b/examples/PWM_DynamicDutyCycle_Int/PWM_DynamicDutyCycle_Int.ino new file mode 100644 index 0000000..a0118eb --- /dev/null +++ b/examples/PWM_DynamicDutyCycle_Int/PWM_DynamicDutyCycle_Int.ino @@ -0,0 +1,163 @@ +/**************************************************************************************************************************** + PWM_DynamicDutyCycle_Int.ino + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM +*****************************************************************************************************************************/ +/****************************************************************************************************************************** + // For UNO / Nano + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 5 (6 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 9 and 10 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 3 and 11 + // For Mega + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 4 (13 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 11, 12 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 9 and 10 + Timer3 (16-bit) used by PWM generation on pins 2, 3 and 5 + Timer4 (16-bit) used by PWM generation on pins 6, 7 and 8 + Timer5 (16-bit) used by PWM generation on pins 44, 45 and 46 + + //////////////////////////////////////////// + // For Mega (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 44, 45, 46) + Pin 2 => TIMER3B // PE 4 ** 2 ** PWM2 + Pin 3 => TIMER3C // PE 5 ** 3 ** PWM3 + Pin 4 => TIMER0B // PG 5 ** 4 ** PWM4 + Pin 5 => TIMER3A // PE 3 ** 5 ** PWM5 + Pin 6 => TIMER4A // PH 3 ** 6 ** PWM6 + Pin 7 => TIMER4B // PH 4 ** 7 ** PWM7 + Pin 8 => TIMER4C // PH 5 ** 8 ** PWM8 + Pin 9 => TIMER2B // PH 6 ** 9 ** PWM9 + Pin 10 => TIMER2A // PB 4 ** 10 ** PWM10 + Pin 11 => TIMER1A // PB 5 ** 11 ** PWM11 + Pin 12 => TIMER1B // PB 6 ** 12 ** PWM12 + Pin 13 => TIMER0A // PB 7 ** 13 ** PWM13 + Pin 44 => TIMER5C // PL 5 ** 44 ** D44 + Pin 45 => TIMER5B // PL 4 ** 45 ** D45 + Pin 46 => TIMER5A // PL 3 ** 46 ** D46 + //////////////////////////////////////////// + // For 32u4 (3, 5, 6, 9, 10, 11, 13) + Pin 3 => TIMER0B + Pin 5 => TIMER3A + Pin 6 => TIMER4D + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER0A + Pin 13 => TIMER4A + //////////////////////////////////////////// + // For UNO, Nano (3, 5, 6, 9, 10, 11) + Pin 3 => TIMER2B, + Pin 5 => TIMER0B + Pin 6 => TIMER0A + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER2(A) +******************************************************************************************************************************/ + +#define _PWM_LOGLEVEL_ 4 + +#include "AVR_PWM.h" + +#if ( PWM_USING_ATMEGA2560 ) + // Pins tested OK in Mega + //#define pinToUse 12 // Timer1B on Mega + //#define pinToUse 11 // Timer1A on Mega + //#define pinToUse 9 // Timer2B on Mega + //#define pinToUse 2 // Timer3B on Mega + //#define pinToUse 3 // Timer3C on Mega + //#define pinToUse 5 // Timer3A on Mega + //#define pinToUse 6 // Timer4A on Mega + //#define pinToUse 7 // Timer4B on Mega + #define pinToUse 8 // Timer4C on Mega + //#define pinToUse 46 // Timer5A on Mega + //#define pinToUse 45 // Timer5B on Mega + //#define pinToUse 44 // Timer5C on Mega + +#elif ( PWM_USING_ATMEGA_32U4 ) + // Pins tested OK on 32u4 + //#define pinToUse 5 // Timer3A on 32u4 + #define pinToUse 9 // Timer1A on 32u4 + //#define pinToUse 10 // Timer1B on 32u4 + +#else + + // Pins tested OK on Nano / UNO + //#define pinToUse 9 // Timer1A on UNO, Nano, etc + #define pinToUse 10 // Timer1B on UNO, Nano, etc + //#define pinToUse 5 // Timer0B on UNO, Nano, e + //#define pinToUse 3 // Timer2B on UNO, Nano, etc +#endif + +AVR_PWM* PWM_Instance; + +float frequency; +uint32_t dutyCycle; + +char dashLine[] = "====================================================================================="; + +void printPWMInfo(AVR_PWM* PWM_Instance) +{ + Serial.println(dashLine); + Serial.print("Actual data: pin = "); + Serial.print(PWM_Instance->getPin()); + Serial.print(", PWM DC = "); + Serial.print(PWM_Instance->getActualDutyCycle()); + Serial.print(", PWMPeriod = "); + Serial.print(PWM_Instance->getPWMPeriod()); + Serial.print(", PWM Freq (Hz) = "); + Serial.println(PWM_Instance->getActualFreq(), 4); + Serial.println(dashLine); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial); + + delay(100); + + Serial.print(F("\nStarting PWM_DynamicDutyCycle_Int on ")); + Serial.println(BOARD_NAME); + Serial.println(AVR_PWM_VERSION); + + frequency = 1000; + + PWM_Instance = new AVR_PWM(pinToUse, frequency, 50); + + if (PWM_Instance) + { + PWM_Instance->setPWM(); + } + + Serial.println(dashLine); +} + +void loop() +{ + delay(5000); + + frequency = 5000; + + // 50% dutyCycle = (real_dutyCycle * 65536) / 100 + dutyCycle = 32768; + + Serial.print(F("Change PWM DutyCycle to (%) ")); + Serial.println((float) dutyCycle * 100 / 65536); + PWM_Instance->setPWM_Int(pinToUse, frequency, dutyCycle); + + printPWMInfo(PWM_Instance); + + delay(5000); + + // 20% dutyCycle = (real_dutyCycle * 65536) / 100 + dutyCycle = 13107; + + Serial.print(F("Change PWM DutyCycle to (%) ")); + Serial.println((float) dutyCycle * 100 / 65536); + PWM_Instance->setPWM_Int(pinToUse, frequency, dutyCycle); + printPWMInfo(PWM_Instance); +} diff --git a/examples/PWM_DynamicFreq/PWM_DynamicFreq.ino b/examples/PWM_DynamicFreq/PWM_DynamicFreq.ino new file mode 100644 index 0000000..267fe3d --- /dev/null +++ b/examples/PWM_DynamicFreq/PWM_DynamicFreq.ino @@ -0,0 +1,156 @@ +/**************************************************************************************************************************** + PWM_DynamicFreq.ino + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM +*****************************************************************************************************************************/ +/****************************************************************************************************************************** + // For UNO / Nano + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 5 (6 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 9 and 10 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 3 and 11 + // For Mega + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 4 (13 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 11, 12 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 9 and 10 + Timer3 (16-bit) used by PWM generation on pins 2, 3 and 5 + Timer4 (16-bit) used by PWM generation on pins 6, 7 and 8 + Timer5 (16-bit) used by PWM generation on pins 44, 45 and 46 + + //////////////////////////////////////////// + // For Mega (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 44, 45, 46) + Pin 2 => TIMER3B // PE 4 ** 2 ** PWM2 + Pin 3 => TIMER3C // PE 5 ** 3 ** PWM3 + Pin 4 => TIMER0B // PG 5 ** 4 ** PWM4 + Pin 5 => TIMER3A // PE 3 ** 5 ** PWM5 + Pin 6 => TIMER4A // PH 3 ** 6 ** PWM6 + Pin 7 => TIMER4B // PH 4 ** 7 ** PWM7 + Pin 8 => TIMER4C // PH 5 ** 8 ** PWM8 + Pin 9 => TIMER2B // PH 6 ** 9 ** PWM9 + Pin 10 => TIMER2A // PB 4 ** 10 ** PWM10 + Pin 11 => TIMER1A // PB 5 ** 11 ** PWM11 + Pin 12 => TIMER1B // PB 6 ** 12 ** PWM12 + Pin 13 => TIMER0A // PB 7 ** 13 ** PWM13 + Pin 44 => TIMER5C // PL 5 ** 44 ** D44 + Pin 45 => TIMER5B // PL 4 ** 45 ** D45 + Pin 46 => TIMER5A // PL 3 ** 46 ** D46 + //////////////////////////////////////////// + // For 32u4 (3, 5, 6, 9, 10, 11, 13) + Pin 3 => TIMER0B + Pin 5 => TIMER3A + Pin 6 => TIMER4D + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER0A + Pin 13 => TIMER4A + //////////////////////////////////////////// + // For UNO, Nano (3, 5, 6, 9, 10, 11) + Pin 3 => TIMER2B, + Pin 5 => TIMER0B + Pin 6 => TIMER0A + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER2(A) +******************************************************************************************************************************/ + +#define _PWM_LOGLEVEL_ 4 + +#include "AVR_PWM.h" + +#if ( PWM_USING_ATMEGA2560 ) + // Pins tested OK in Mega + //#define pinToUse 12 // Timer1B on Mega + //#define pinToUse 11 // Timer1A on Mega + //#define pinToUse 9 // Timer2B on Mega + //#define pinToUse 2 // Timer3B on Mega + //#define pinToUse 3 // Timer3C on Mega + //#define pinToUse 5 // Timer3A on Mega + //#define pinToUse 6 // Timer4A on Mega + //#define pinToUse 7 // Timer4B on Mega + #define pinToUse 8 // Timer4C on Mega + //#define pinToUse 46 // Timer5A on Mega + //#define pinToUse 45 // Timer5B on Mega + //#define pinToUse 44 // Timer5C on Mega + +#elif ( PWM_USING_ATMEGA_32U4 ) + // Pins tested OK on 32u4 + //#define pinToUse 5 // Timer3A on 32u4 + #define pinToUse 9 // Timer1A on 32u4 + //#define pinToUse 10 // Timer1B on 32u4 + +#else + + // Pins tested OK on Nano / UNO + //#define pinToUse 9 // Timer1A on UNO, Nano, etc + #define pinToUse 10 // Timer1B on UNO, Nano, etc + //#define pinToUse 5 // Timer0B on UNO, Nano, e + //#define pinToUse 3 // Timer2B on UNO, Nano, etc +#endif + +AVR_PWM* PWM_Instance; + +float frequency; + +char dashLine[] = "====================================================================================="; + +void printPWMInfo(AVR_PWM* PWM_Instance) +{ + Serial.println(dashLine); + Serial.print("Actual data: pin = "); + Serial.print(PWM_Instance->getPin()); + Serial.print(", PWM DC = "); + Serial.print(PWM_Instance->getActualDutyCycle()); + Serial.print(", PWMPeriod = "); + Serial.print(PWM_Instance->getPWMPeriod()); + Serial.print(", PWM Freq (Hz) = "); + Serial.println(PWM_Instance->getActualFreq(), 4); + Serial.println(dashLine); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial); + + delay(100); + + Serial.print(F("\nStarting PWM_DynamicFreq on ")); + Serial.println(BOARD_NAME); + Serial.println(AVR_PWM_VERSION); + + frequency = 10000.0f; + PWM_Instance = new AVR_PWM(pinToUse, frequency, 50.0f); + + if (PWM_Instance) + { + PWM_Instance->setPWM(); + } + + Serial.println(dashLine); +} + +void loop() +{ + delay(5000); + + frequency = 20000.0f; + + Serial.print(F("Change PWM Freq to ")); + Serial.println(frequency); + PWM_Instance->setPWM(pinToUse, frequency, 50.0f); + printPWMInfo(PWM_Instance); + + delay(5000); + + frequency = 10000.0f; + + Serial.print(F("Change PWM Freq to ")); + Serial.println(frequency); + PWM_Instance->setPWM(pinToUse, frequency, 50.0f); + printPWMInfo(PWM_Instance); +} diff --git a/examples/PWM_Multi/PWM_Multi.ino b/examples/PWM_Multi/PWM_Multi.ino new file mode 100644 index 0000000..db40472 --- /dev/null +++ b/examples/PWM_Multi/PWM_Multi.ino @@ -0,0 +1,176 @@ +/**************************************************************************************************************************** + PWM_Multi.ino + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM +*****************************************************************************************************************************/ +/****************************************************************************************************************************** + // For UNO / Nano + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 5 (6 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 9 and 10 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 3 and 11 + // For Mega + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 4 (13 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 11, 12 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 9 and 10 + Timer3 (16-bit) used by PWM generation on pins 2, 3 and 5 + Timer4 (16-bit) used by PWM generation on pins 6, 7 and 8 + Timer5 (16-bit) used by PWM generation on pins 44, 45 and 46 + + //////////////////////////////////////////// + // For Mega (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 44, 45, 46) + Pin 2 => TIMER3B // PE 4 ** 2 ** PWM2 + Pin 3 => TIMER3C // PE 5 ** 3 ** PWM3 + Pin 4 => TIMER0B // PG 5 ** 4 ** PWM4 + Pin 5 => TIMER3A // PE 3 ** 5 ** PWM5 + Pin 6 => TIMER4A // PH 3 ** 6 ** PWM6 + Pin 7 => TIMER4B // PH 4 ** 7 ** PWM7 + Pin 8 => TIMER4C // PH 5 ** 8 ** PWM8 + Pin 9 => TIMER2B // PH 6 ** 9 ** PWM9 + Pin 10 => TIMER2A // PB 4 ** 10 ** PWM10 + Pin 11 => TIMER1A // PB 5 ** 11 ** PWM11 + Pin 12 => TIMER1B // PB 6 ** 12 ** PWM12 + Pin 13 => TIMER0A // PB 7 ** 13 ** PWM13 + Pin 44 => TIMER5C // PL 5 ** 44 ** D44 + Pin 45 => TIMER5B // PL 4 ** 45 ** D45 + Pin 46 => TIMER5A // PL 3 ** 46 ** D46 + //////////////////////////////////////////// + // For 32u4 (3, 5, 6, 9, 10, 11, 13) + Pin 3 => TIMER0B + Pin 5 => TIMER3A + Pin 6 => TIMER4D + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER0A + Pin 13 => TIMER4A + //////////////////////////////////////////// + // For UNO, Nano (3, 5, 6, 9, 10, 11) + Pin 3 => TIMER2B, + Pin 5 => TIMER0B + Pin 6 => TIMER0A + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER2(A) +******************************************************************************************************************************/ + +#define _PWM_LOGLEVEL_ 1 + +#include "AVR_PWM.h" + +#if ( PWM_USING_ATMEGA2560 ) + // Pins tested OK in Mega + //#define pinToUse 12 // Timer1B on Mega + //#define pinToUse 11 // Timer1A on Mega + //#define pinToUse 9 // Timer2B on Mega + //#define pinToUse 2 // Timer3B on Mega + //#define pinToUse 3 // Timer3C on Mega + //#define pinToUse 5 // Timer3A on Mega + //#define pinToUse 6 // Timer4A on Mega + //#define pinToUse 7 // Timer4B on Mega + //#define pinToUse 8 // Timer4C on Mega + //#define pinToUse 46 // Timer5A on Mega + //#define pinToUse 45 // Timer5B on Mega + //#define pinToUse 44 // Timer5C on Mega + + uint32_t PWM_Pins[] = { 5, 8, 9, 12 }; + +#elif ( PWM_USING_ATMEGA_32U4 ) + // Pins tested OK on 32u4 + //#define pinToUse 5 // Timer3A on 32u4 + //#define pinToUse 9 // Timer1A on 32u4 + //#define pinToUse 10 // Timer1B on 32u4 + + uint32_t PWM_Pins[] = { 5, 9 }; + +#else + + // Pins tested OK on Nano / UNO + //#define pinToUse 9 // Timer1A on UNO, Nano, etc + //#define pinToUse 10 // Timer1B on UNO, Nano, etc + //#define pinToUse 5 // Timer0B on UNO, Nano, e + //#define pinToUse 3 // Timer2B on UNO, Nano, etc + + uint32_t PWM_Pins[] = { 3, 5, 9 }; + +#endif + +#define NUM_OF_PINS ( sizeof(PWM_Pins) / sizeof(uint32_t) ) + +float dutyCycle[] = { 10.0f, 30.0f, 50.0f, 90.0f }; + +float freq[] = { 2000.0f, 3000.0f, 4000.0f, 8000.0f }; + + +AVR_PWM* PWM_Instance[NUM_OF_PINS]; + +char dashLine[] = "====================================================================================="; + +void printPWMInfo(AVR_PWM* PWM_Instance) +{ + Serial.println(dashLine); + Serial.print("Actual data: pin = "); + Serial.print(PWM_Instance->getPin()); + Serial.print(", PWM DC = "); + Serial.print(PWM_Instance->getActualDutyCycle()); + Serial.print(", PWMPeriod = "); + Serial.print(PWM_Instance->getPWMPeriod()); + Serial.print(", PWM Freq (Hz) = "); + Serial.println(PWM_Instance->getActualFreq(), 4); + Serial.println(dashLine); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial); + + delay(100); + + Serial.print(F("\nStarting PWM_Multi on ")); + Serial.println(BOARD_NAME); + Serial.println(AVR_PWM_VERSION); + + Serial.println(dashLine); + Serial.println("Index\tPin\tPWM_freq\tDutyCycle\tActual Freq"); + Serial.println(dashLine); + + for (uint8_t index = 0; index < NUM_OF_PINS; index++) + { + PWM_Instance[index] = new AVR_PWM(PWM_Pins[index], freq[index], dutyCycle[index]); + + if (PWM_Instance[index]) + { + PWM_Instance[index]->setPWM(); + + Serial.print(index); + Serial.print("\t"); + Serial.print(PWM_Pins[index]); + Serial.print("\t"); + Serial.print(freq[index]); + Serial.print("\t\t"); + Serial.print(dutyCycle[index]); + Serial.print("\t\t"); + Serial.println(PWM_Instance[index]->getActualFreq(), 4); + } + else + { + Serial.println(); + } + } + + for (uint8_t index = 0; index < NUM_OF_PINS; index++) + { + printPWMInfo(PWM_Instance[index]); + } +} + +void loop() +{ + //Long delay has no effect on the operation of hardware-based PWM channels + delay(1000000); +} diff --git a/examples/PWM_MultiChannel/PWM_MultiChannel.ino b/examples/PWM_MultiChannel/PWM_MultiChannel.ino new file mode 100644 index 0000000..50e116d --- /dev/null +++ b/examples/PWM_MultiChannel/PWM_MultiChannel.ino @@ -0,0 +1,185 @@ +/**************************************************************************************************************************** + PWM_MultiChannel.ino + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM +*****************************************************************************************************************************/ +/****************************************************************************************************************************** + // For UNO / Nano + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 5 (6 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 9 and 10 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 3 and 11 + // For Mega + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 4 (13 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 11, 12 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 9 and 10 + Timer3 (16-bit) used by PWM generation on pins 2, 3 and 5 + Timer4 (16-bit) used by PWM generation on pins 6, 7 and 8 + Timer5 (16-bit) used by PWM generation on pins 44, 45 and 46 + + //////////////////////////////////////////// + // For Mega (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 44, 45, 46) + Pin 2 => TIMER3B // PE 4 ** 2 ** PWM2 + Pin 3 => TIMER3C // PE 5 ** 3 ** PWM3 + Pin 4 => TIMER0B // PG 5 ** 4 ** PWM4 + Pin 5 => TIMER3A // PE 3 ** 5 ** PWM5 + Pin 6 => TIMER4A // PH 3 ** 6 ** PWM6 + Pin 7 => TIMER4B // PH 4 ** 7 ** PWM7 + Pin 8 => TIMER4C // PH 5 ** 8 ** PWM8 + Pin 9 => TIMER2B // PH 6 ** 9 ** PWM9 + Pin 10 => TIMER2A // PB 4 ** 10 ** PWM10 + Pin 11 => TIMER1A // PB 5 ** 11 ** PWM11 + Pin 12 => TIMER1B // PB 6 ** 12 ** PWM12 + Pin 13 => TIMER0A // PB 7 ** 13 ** PWM13 + Pin 44 => TIMER5C // PL 5 ** 44 ** D44 + Pin 45 => TIMER5B // PL 4 ** 45 ** D45 + Pin 46 => TIMER5A // PL 3 ** 46 ** D46 + //////////////////////////////////////////// + // For 32u4 (3, 5, 6, 9, 10, 11, 13) + Pin 3 => TIMER0B + Pin 5 => TIMER3A + Pin 6 => TIMER4D + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER0A + Pin 13 => TIMER4A + //////////////////////////////////////////// + // For UNO, Nano (3, 5, 6, 9, 10, 11) + Pin 3 => TIMER2B, + Pin 5 => TIMER0B + Pin 6 => TIMER0A + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER2(A) +******************************************************************************************************************************/ + +#define _PWM_LOGLEVEL_ 4 + +#include "AVR_PWM.h" + +#define LED_ON LOW +#define LED_OFF HIGH + +#if ( PWM_USING_ATMEGA2560 ) +// Pins tested OK in Mega +//#define pinToUse 12 // Timer1B on Mega +//#define pinToUse 11 // Timer1A on Mega +//#define pinToUse 9 // Timer2B on Mega +//#define pinToUse 2 // Timer3B on Mega +//#define pinToUse 3 // Timer3C on Mega +//#define pinToUse 5 // Timer3A on Mega +//#define pinToUse 6 // Timer4A on Mega +//#define pinToUse 7 // Timer4B on Mega +//#define pinToUse 8 // Timer4C on Mega +//#define pinToUse 46 // Timer5A on Mega +//#define pinToUse 45 // Timer5B on Mega +//#define pinToUse 44 // Timer5C on Mega + +uint32_t PWM_Pins[] = { 8, 11 }; + +#elif ( PWM_USING_ATMEGA_32U4 ) +// Pins tested OK on 32u4 +//#define pinToUse 5 // Timer3A on 32u4 +//#define pinToUse 9 // Timer1A on 32u4 +//#define pinToUse 10 // Timer1B on 32u4 + +uint32_t PWM_Pins[] = { 5, 10 }; + +#else + +// Pins tested OK on Nano / UNO +//#define pinToUse 9 // Timer1A on UNO, Nano, etc +//#define pinToUse 10 // Timer1B on UNO, Nano, etc +//#define pinToUse 5 // Timer0B on UNO, Nano, e +//#define pinToUse 3 // Timer2B on UNO, Nano, etc + +uint32_t PWM_Pins[] = { 3, 9 }; + +#endif + +#define NUM_OF_PINS ( sizeof(PWM_Pins) / sizeof(uint32_t) ) + +float dutyCycle[] = { 10.0f, 50.0f }; + +// Must be same frequency for same timer +float freq = 1000.0f; + +AVR_PWM* PWM_Instance[NUM_OF_PINS]; + +char dashLine[] = "====================================================================================="; + +void printPWMInfo(AVR_PWM* PWM_Instance) +{ + Serial.println(dashLine); + Serial.print("Actual data: pin = "); + Serial.print(PWM_Instance->getPin()); + Serial.print(", PWM DC = "); + Serial.print(PWM_Instance->getActualDutyCycle()); + Serial.print(", PWMPeriod = "); + Serial.print(PWM_Instance->getPWMPeriod()); + Serial.print(", PWM Freq (Hz) = "); + Serial.println(PWM_Instance->getActualFreq(), 4); + Serial.println(dashLine); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial); + + delay(100); + + Serial.print(F("\nStarting PWM_MultiChannel on ")); + Serial.println(BOARD_NAME); + Serial.println(AVR_PWM_VERSION); + + for (uint8_t index = 0; index < NUM_OF_PINS; index++) + { + PWM_Instance[index] = new AVR_PWM(PWM_Pins[index], freq, dutyCycle[index]); + + if (PWM_Instance[index]) + { + PWM_Instance[index]->setPWM(); + } + } + + Serial.println(dashLine); + Serial.println("Index\tPin\tPWM_freq\tDutyCycle\tActual Freq"); + Serial.println(dashLine); + + for (uint8_t index = 0; index < NUM_OF_PINS; index++) + { + if (PWM_Instance[index]) + { + Serial.print(index); + Serial.print("\t"); + Serial.print(PWM_Pins[index]); + Serial.print("\t"); + Serial.print(freq); + Serial.print("\t\t"); + Serial.print(dutyCycle[index]); + Serial.print("\t\t"); + Serial.println(PWM_Instance[index]->getActualFreq(), 4); + } + else + { + Serial.println(); + } + } + + for (uint8_t index = 0; index < NUM_OF_PINS; index++) + { + printPWMInfo(PWM_Instance[index]); + } +} + +void loop() +{ + //Long delay has no effect on the operation of hardware-based PWM channels + delay(1000000); +} diff --git a/examples/PWM_Waveform/PWM_Waveform.ino b/examples/PWM_Waveform/PWM_Waveform.ino new file mode 100644 index 0000000..3a86da3 --- /dev/null +++ b/examples/PWM_Waveform/PWM_Waveform.ino @@ -0,0 +1,221 @@ +/**************************************************************************************************************************** + PWM_Waveform.ino + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM +*****************************************************************************************************************************/ +/****************************************************************************************************************************** + // For UNO / Nano + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 5 (6 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 9 and 10 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 3 and 11 + // For Mega + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 4 (13 not usable) + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 11, 12 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 9 and 10 + Timer3 (16-bit) used by PWM generation on pins 2, 3 and 5 + Timer4 (16-bit) used by PWM generation on pins 6, 7 and 8 + Timer5 (16-bit) used by PWM generation on pins 44, 45 and 46 + + //////////////////////////////////////////// + // For Mega (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 44, 45, 46) + Pin 2 => TIMER3B // PE 4 ** 2 ** PWM2 + Pin 3 => TIMER3C // PE 5 ** 3 ** PWM3 + Pin 4 => TIMER0B // PG 5 ** 4 ** PWM4 + Pin 5 => TIMER3A // PE 3 ** 5 ** PWM5 + Pin 6 => TIMER4A // PH 3 ** 6 ** PWM6 + Pin 7 => TIMER4B // PH 4 ** 7 ** PWM7 + Pin 8 => TIMER4C // PH 5 ** 8 ** PWM8 + Pin 9 => TIMER2B // PH 6 ** 9 ** PWM9 + Pin 10 => TIMER2A // PB 4 ** 10 ** PWM10 + Pin 11 => TIMER1A // PB 5 ** 11 ** PWM11 + Pin 12 => TIMER1B // PB 6 ** 12 ** PWM12 + Pin 13 => TIMER0A // PB 7 ** 13 ** PWM13 + Pin 44 => TIMER5C // PL 5 ** 44 ** D44 + Pin 45 => TIMER5B // PL 4 ** 45 ** D45 + Pin 46 => TIMER5A // PL 3 ** 46 ** D46 + //////////////////////////////////////////// + // For 32u4 (3, 5, 6, 9, 10, 11, 13) + Pin 3 => TIMER0B + Pin 5 => TIMER3A + Pin 6 => TIMER4D + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER0A + Pin 13 => TIMER4A + //////////////////////////////////////////// + // For UNO, Nano (3, 5, 6, 9, 10, 11) + Pin 3 => TIMER2B, + Pin 5 => TIMER0B + Pin 6 => TIMER0A + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER2(A) +******************************************************************************************************************************/ + +#define _PWM_LOGLEVEL_ 1 + +#include "AVR_PWM.h" + +#define LED_ON LOW +#define LED_OFF HIGH + +#if ( PWM_USING_ATMEGA2560 ) +// Pins tested OK in Mega +//#define pinToUse 12 // Timer1B on Mega +//#define pinToUse 11 // Timer1A on Mega +//#define pinToUse 9 // Timer2B on Mega +//#define pinToUse 2 // Timer3B on Mega +//#define pinToUse 3 // Timer3C on Mega +//#define pinToUse 5 // Timer3A on Mega +//#define pinToUse 6 // Timer4A on Mega +//#define pinToUse 7 // Timer4B on Mega +#define pinToUse 8 // Timer4C on Mega +//#define pinToUse 46 // Timer5A on Mega +//#define pinToUse 45 // Timer5B on Mega +//#define pinToUse 44 // Timer5C on Mega + +#elif ( PWM_USING_ATMEGA_32U4 ) +// Pins tested OK on 32u4 +////#define pinToUse 3 // Timer0B on 32u4 +//#define pinToUse 5 // Timer3A on 32u4 +#define pinToUse 9 // Timer1A on 32u4 +//#define pinToUse 10 // Timer1B on 32u4 + +#else + +// Pins tested OK on Nano / UNO +//#define pinToUse 9 // Timer1A on UNO, Nano, etc +#define pinToUse 10 // Timer1B on UNO, Nano, etc +//#define pinToUse 5 // Timer0B on UNO, Nano, e +//#define pinToUse 3 // Timer2B on UNO, Nano, etc +#endif + + +AVR_PWM* PWM_Instance; + +typedef struct +{ + uint16_t level; +} PWD_Data; + +PWD_Data PWM_data[] = +{ + { 0 }, + { 500 }, + { 1000 }, + { 1500 }, + { 2000 }, + { 2500 }, + { 3000 }, + { 3500 }, + { 4000 }, + { 4500 }, + { 5000 }, + { 5500 }, + { 6000 }, + { 6500 }, + { 7000 }, + { 7500 }, + { 8000 }, + { 7500 }, + { 7000 }, + { 6500 }, + { 6000 }, + { 5500 }, + { 5000 }, + { 4500 }, + { 4000 }, + { 3500 }, + { 3000 }, + { 2500 }, + { 2000 }, + { 1500 }, + { 1000 }, + { 500 }, + { 0 }, +}; + +#define NUM_PWM_POINTS ( sizeof(PWM_data) / sizeof(PWD_Data) ) + +float frequency; + +// You can select any value +PWD_Data PWM_data_idle = PWM_data[0]; + +char dashLine[] = "============================================================================================"; + +void printPWMInfo(AVR_PWM* PWM_Instance) +{ + Serial.println(dashLine); + Serial.print("Actual data: pin = "); + Serial.print(PWM_Instance->getPin()); + Serial.print(", PWM DutyCycle = "); + Serial.print(PWM_Instance->getActualDutyCycle()); + Serial.print(", PWMPeriod = "); + Serial.print(PWM_Instance->getPWMPeriod()); + Serial.print(", PWM Freq (Hz) = "); + Serial.println(PWM_Instance->getActualFreq(), 4); + Serial.println(dashLine); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(100); + + Serial.print(F("\nStarting PWM_Waveform on ")); + Serial.println(BOARD_NAME); + Serial.println(AVR_PWM_VERSION); + + frequency = 1000.0f; + + // Create a dummy instance + PWM_Instance = new AVR_PWM(pinToUse, frequency, 0); + + if (PWM_Instance) + { + // setPWM_manual(uint8_t pin, uint16_t level) + PWM_Instance->setPWM(pinToUse, frequency, 0); + + printPWMInfo(PWM_Instance); + } +} + +void updateDC() +{ + static uint16_t index = 0; + + PWM_Instance->setPWM_manual(pinToUse, PWM_data[index].level); + + // Use at low freq to check + //printPWMInfo(PWM_Instance); + + index = (index + 1) % NUM_PWM_POINTS; +} + +void check_status() +{ +#define UPDATE_INTERVAL 100L + + static unsigned long update_timeout = UPDATE_INTERVAL; + + // Update DC every UPDATE_INTERVAL (100) milliseconds + if (millis() > update_timeout) + { + updateDC(); + update_timeout = millis() + UPDATE_INTERVAL; + } +} + +void loop() +{ + check_status(); +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..66abde1 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,47 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +AVR_PWM KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +################################### +# Class AVR_PWM +################################### + +setPWM_Int KEYWORD2 +setPWM KEYWORD2 +setPWM_Period KEYWORD2 +setPWM_manual KEYWORD2 +getActualDutyCycle KEYWORD2 +getActualFreq KEYWORD2 +getPWMPeriod KEYWORD2 +get_freq_CPU KEYWORD2 +getPin KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### + +AVR_PWM_VERSION LITERAL1 +AVR_PWM_VERSION_MAJOR LITERAL1 +AVR_PWM_VERSION_MINOR LITERAL1 +AVR_PWM_VERSION_PATCH LITERAL1 +AVR_PWM_VERSION_INT LITERAL1 + +TIMER0_RESOLUTION LITERAL1 +TIMER1_RESOLUTION LITERAL1 +TIMER2_RESOLUTION LITERAL1 +TIMER3_RESOLUTION LITERAL1 +TIMER4_RESOLUTION LITERAL1 +TIMER5_RESOLUTION LITERAL1 + +MAX_COUNT_8BIT LITERAL1 +MAX_COUNT_10BIT LITERAL1 +MAX_COUNT_16BIT LITERAL1 + +_PWM_LOGLEVEL_ LITERAL1 diff --git a/library.json b/library.json new file mode 100644 index 0000000..0a5a574 --- /dev/null +++ b/library.json @@ -0,0 +1,30 @@ +{ + "name": "AVR_PWM", + "version": "1.0.0", + "keywords": "timing, device, control, timer, pwm, fast-pwm, hardware-based-pwm, high-frequency-pwm, hardware-pwm, mission-critical, accuracy, precise, non-blocking, avr, mega-2560, nano, uno, leonardo, 32u4, 16u4, at-mega", + "description": "This library enables you to use Hardware-based PWM channels on AVR-based boards, such as Nano, UNO, Mega, Leonardo, 32u4, etc., to create and output PWM. The most important feature is they're purely hardware-based PWM channels, supporting very high PWM frequencies. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using ISR, millis() or micros(). That's necessary if you need to control devices requiring high precision. New efficient setPWM_manual function to facilitate waveform creation using PWM", + "authors": + { + "name": "Khoi Hoang", + "url": "https://github.com/khoih-prog", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "https://github.com/khoih-prog/AVR_PWM.git" + }, + "homepage": "https://github.com/khoih-prog/AVR_PWM", + "export": { + "exclude": [ + "linux", + "extras", + "tests" + ] + }, + "license": "MIT", + "frameworks": "*", + "platforms": "avr", + "examples": "examples/*/*/*.ino", + "headers": ["AVR_PWM.h"] +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..e64c6e6 --- /dev/null +++ b/library.properties @@ -0,0 +1,12 @@ +name=AVR_PWM +version=1.0.0 +author=Khoi Hoang +maintainer=Khoi Hoang +sentence=This library enables you to use Hardware-based PWM channels on AVR-based boards, such as Nano, UNO, Mega, Leonardo, 32u4, etc., to create and output PWM. +paragraph=The most important feature is they're purely hardware-based PWM channels, supporting very high PWM frequencies. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using ISR, millis() or micros(). That's necessary if you need to control devices requiring high precision. New efficient setPWM_manual function to facilitate waveform creation using PWM +category=Device Control +url=https://github.com/khoih-prog/AVR_PWM +architectures=avr +repository=https://github.com/khoih-prog/AVR_PWM +license=MIT +includes=AVR_PWM.h diff --git a/platformio/platformio.ini b/platformio/platformio.ini new file mode 100644 index 0000000..91f72a6 --- /dev/null +++ b/platformio/platformio.ini @@ -0,0 +1,116 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +; ============================================================ +; chose environment: +; MEGAAVR +; AVR +; TEENSY + +; ============================================================ +;default_envs = MEGAAVR +default_envs = AVR +;default_envs = TEENSY + +[env] +; ============================================================ +; Serial configuration +; choose upload speed, serial-monitor speed +; ============================================================ +upload_speed = 921600 +;upload_port = COM11 +;monitor_speed = 9600 +;monitor_port = COM11 + +; Checks for the compatibility with frameworks and dev/platforms +lib_compat_mode = strict +lib_ldf_mode = chain+ +;lib_ldf_mode = deep+ + +lib_deps = ${common.lib_deps} + +build_flags = + +[env:MEGAAVR] +platform = atmelmegaavr +framework = arduino + +; ============================================================ +; Choose your board by uncommenting one of the following lines +; ============================================================ + +; ============================================================ +; Board configuration MEGAAVR +; ============================================================ + +;board = uno2018 +;board = nona4809 +;board = uno_wifi_rev2 +;board = nano_every + +[env:AVR] +platform = atmelavr +framework = arduino + +; ============================================================ +; Board configuration AVR +; ============================================================ + +;board = yun +;board = uno +;board = diecimila +;board = nano +;board = nanoatmega328 +;board = mega +board = megaatmega2560 +;board = megaADK +;board = leonardo +;board = leonardoeth +;board = micro +;board = esplora +;board = mini +;board = ethernet +;board = fio +;board = bt +;board = LilyPadUSB +;board = lilypad +;board = pro +;board = atmegang +;board = robotControl +;board = robotMotor +;board = gemma +;board = circuitplay32u4cat +;board = yunmini +;board = chiwawa +;board = one +;board = unowifi + +[env:TEENSY] +platform = teensy +framework = arduino + +; ============================================================ +; Choose your board by uncommenting one of the following lines +; ============================================================ + +; ============================================================ +; Board configuration MEGAAVR +; ============================================================ + +;board = teensy2 +;board = teensy2pp +;board = teensy30 +;board = teensy31 +;board = teensylc +;board = teensy35 +;board = teensy36 +;board = teensy40 +;board = teensy41 diff --git a/src/AVR_PWM.h b/src/AVR_PWM.h new file mode 100644 index 0000000..6b39d35 --- /dev/null +++ b/src/AVR_PWM.h @@ -0,0 +1,1483 @@ +/**************************************************************************************************************************** + AVR_PWM.h + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM + + Version: 1.0.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K.Hoang 27/10/2022 Initial coding for AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) +*****************************************************************************************************************************/ + +#pragma once + +#ifndef AVR_PWM_H +#define AVR_PWM_H + +#if defined(BOARD_NAME) + #undef BOARD_NAME +#endif + +#ifndef _PWM_LOGLEVEL_ + #define _PWM_LOGLEVEL_ 1 +#endif + +#if ( defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || \ + defined(__AVR_ATmega640__) || defined(__AVR_ATmega641__)) +#if defined(PWM_USING_ATMEGA2560) + #undef PWM_USING_ATMEGA2560 +#endif +#define PWM_USING_ATMEGA2560 true +#define BOARD_NAME F("Arduino AVR Mega2560/ADK") + +#if (_PWM_LOGLEVEL_ > 3) + #warning Using Arduino AVR Mega, Mega640(P), Mega2560/ADK. Timer1-5 available +#endif + +#elif ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \ + defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \ + defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \ + defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) ) +#define BOARD_NAME "Arduino AVR UNO, Nano, etc." + +#if (_PWM_LOGLEVEL_ > 3) + #warning Using Aduino AVR ATMega644(P), ATMega328(P) such as UNO, Nano. Only Timer1,2 available +#endif + +#elif ( defined(ARDUINO_AVR_FEATHER328P) || defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || \ + defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || defined(ARDUINO_AVR_PROTRINKET3FTDI) ) +#define BOARD_NAME F("Adafruit AVR ATMega328(P)") + +#if (_PWM_LOGLEVEL_ > 3) + #warning Using Adafruit ATMega328(P), such as AVR_FEATHER328P or AVR_METRO. Only Timer1,2 available +#endif + +#elif ( defined(ARDUINO_AVR_LEONARDO) || defined(ARDUINO_AVR_LEONARDO_ETH) || defined(ARDUINO_AVR_YUN) || \ + defined(ARDUINO_AVR_MICRO) || defined(ARDUINO_AVR_ESPLORA) || defined(ARDUINO_AVR_LILYPAD_USB) || \ + defined(ARDUINO_AVR_ROBOT_CONTROL) || defined(ARDUINO_AVR_ROBOT_MOTOR) || defined(ARDUINO_AVR_CIRCUITPLAY) || \ + defined(ARDUINO_AVR_YUNMINI) || defined(ARDUINO_AVR_INDUSTRIAL101) || defined(ARDUINO_AVR_LININO_ONE) ) +#if defined(PWM_USING_ATMEGA_32U4) + #undef PWM_USING_ATMEGA_32U4 +#endif +#define PWM_USING_ATMEGA_32U4 true +#define BOARD_NAME F("Arduino AVR ATMega32U4") + +#if (_PWM_LOGLEVEL_ > 3) + #warning Using Arduino ATMega32U4, such as Leonardo or Leonardo ETH. Only Timer1,3,4 available +#endif + +#elif ( defined(ARDUINO_AVR_FLORA8 ) || defined(ARDUINO_AVR_FEATHER32U4) || defined(ARDUINO_AVR_CIRCUITPLAY) || \ + defined(ARDUINO_AVR_ITSYBITSY32U4_5V) || defined(ARDUINO_AVR_ITSYBITSY32U4_3V) || defined(ARDUINO_AVR_BLUEFRUITMICRO) || \ + defined(ARDUINO_AVR_ADAFRUIT32U4) ) +#if defined(PWM_USING_ATMEGA_32U4) + #undef PWM_USING_ATMEGA_32U4 +#endif +#define PWM_USING_ATMEGA_32U4 true +#define BOARD_NAME F("Adafruit AVR ATMega32U4") + +#if (_PWM_LOGLEVEL_ > 3) + #warning Using Adafruit ATMega32U4, such as Feather_32u4, AVR_CIRCUITPLAY, etc.. Only Timer1,3,4 available +#endif + +#elif ( defined(__AVR_ATmega32U4__) || defined(ARDUINO_AVR_MAKEYMAKEY ) || defined(ARDUINO_AVR_PROMICRO) || \ + defined(ARDUINO_AVR_FIOV3) || defined(ARDUINO_AVR_QDUINOMINI) || defined(ARDUINO_AVR_LILYPAD_ARDUINO_USB_PLUS_BOARD ) ) +#if defined(PWM_USING_ATMEGA_32U4) + #undef PWM_USING_ATMEGA_32U4 +#endif +#define PWM_USING_ATMEGA_32U4 true +#define BOARD_NAME F("Generic or Sparkfun AVR ATMega32U4") + +#if (_PWM_LOGLEVEL_ > 3) + #warning Using Generic ATMega32U4, such as Sparkfun AVR_MAKEYMAKEY, AVR_PROMICRO, etc. Only Timer1,3,4 available +#endif + +#elif ( defined(__AVR_ATmega328P__) || defined(ARDUINO_AVR_DIGITAL_SANDBOX ) || defined(ARDUINO_REDBOT) || \ + defined(ARDUINO_AVR_SERIAL_7_SEGMENT) ) +#define BOARD_NAME F("Generic or Sparkfun AVR ATMega328P") + +#if (_PWM_LOGLEVEL_ > 3) + #warning Using Generic ATMega328P, such as Sparkfun AVR_DIGITAL_SANDBOX, REDBOT, etc. +#endif + +#elif ( defined(__AVR_ATmega128RFA1__) || defined(ARDUINO_ATMEGA128RFA1_DEV_BOARD) ) +#define BOARD_NAME F("Generic or Sparkfun AVR ATMega128RFA1") + +#if (_PWM_LOGLEVEL_ > 3) + #warning Using Generic ATMega128RFA1, such as Sparkfun ATMEGA128RFA1_DEV_BOARD, etc. +#endif + +#elif ( defined(ARDUINO_AVR_GEMMA) || defined(ARDUINO_AVR_TRINKET3) || defined(ARDUINO_AVR_TRINKET5) ) +#error These AVR boards are not supported! Please check your Tools->Board setting. + +#else +#error This is designed only for Arduino or Adafruit AVR board! Please check your Tools->Board setting. +#endif + +//////////////////////////////////////// + +#ifndef AVR_PWM_VERSION + #define AVR_PWM_VERSION F("AVR_PWM v1.0.0") + + #define AVR_PWM_VERSION_MAJOR 1 + #define AVR_PWM_VERSION_MINOR 0 + #define AVR_PWM_VERSION_PATCH 0 + + #define AVR_PWM_VERSION_INT 1000000 +#endif + +//////////////////////////////////////// + +#include +#include +#include "Arduino.h" +#include "pins_arduino.h" + +#include "PWM_Generic_Debug.h" + +//////////////////////////////////////// + +#define MAX_COUNT_8BIT 255 +#define MAX_COUNT_10BIT 1023 +#define MAX_COUNT_16BIT 65535 + +//////////////////////////////////////// + +/* + Pins can be used for hardware-PWM + // For UNO / Nano + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 5 and 6 + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 9 and 10 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 3 and 11 + // For Mega + Timer0 ( 8-bit) used by delay(), millis() and micros(), and PWM generation on pins 4 and 13 + Timer1 (16-bit) used by the Servo.h library and PWM generation on pins 11, 12 + Timer2 ( 8-bit) used by Tone() and PWM generation on pins 9 and 10 + Timer3 (16-bit) used by PWM generation on pins 2, 3 and 5 + Timer4 (??-bit) used by PWM generation on pins 6, 7 and 8 + Timer5 (??-bit) used by PWM generation on pins 44, 45 and 46 + //////////////////////////////////////////// + // For Mega (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 44, 45, 46) + Pin 2 => TIMER3B // PE 4 ** 2 ** PWM2 + Pin 3 => TIMER3C // PE 5 ** 3 ** PWM3 + Pin 4 => TIMER0B // PG 5 ** 4 ** PWM4 + Pin 5 => TIMER3A // PE 3 ** 5 ** PWM5 + Pin 6 => TIMER4A // PH 3 ** 6 ** PWM6 + Pin 7 => TIMER4B // PH 4 ** 7 ** PWM7 + Pin 8 => TIMER4C // PH 5 ** 8 ** PWM8 + Pin 9 => TIMER2B // PH 6 ** 9 ** PWM9 + Pin 10 => TIMER2A // PB 4 ** 10 ** PWM10 + Pin 11 => TIMER1A // PB 5 ** 11 ** PWM11 + Pin 12 => TIMER1B // PB 6 ** 12 ** PWM12 + Pin 13 => TIMER0A // PB 7 ** 13 ** PWM13 + Pin 44 => TIMER5C // PL 5 ** 44 ** D44 + Pin 45 => TIMER5B // PL 4 ** 45 ** D45 + Pin 46 => TIMER5A // PL 3 ** 46 ** D46 + //////////////////////////////////////////// + // For 32u4 (3, 5, 6, 9, 10, 11, 13) + Pin 3 => TIMER0B + Pin 5 => TIMER3A + Pin 6 => TIMER4D + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER0A + Pin 13 => TIMER4A + //////////////////////////////////////////// + // For UNO, Nano (3, 5, 6, 9, 10, 11) + Pin 3 => TIMER2B, + Pin 5 => TIMER0B + Pin 6 => TIMER0A + Pin 9 => TIMER1A + Pin 10 => TIMER1B + Pin 11 => TIMER2(A) +*/ + +// #define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) ) + +#if defined (__AVR_ATtiny85__) + + #define TIMER0_RESOLUTION 256UL // Timer0 is 8 bit + #define TIMER1_RESOLUTION 256UL // Timer1 is 8 bit + #define TIMER2_RESOLUTION 256UL // Timer2 is 8 bit + +#elif defined(__AVR__) + + #define TIMER0_RESOLUTION 256UL // Timer0 is 8 bit + #define TIMER1_RESOLUTION 65536UL // Timer1 is 16 bit + #define TIMER2_RESOLUTION 256UL // Timer2 is 8 bit + + #if ( PWM_USING_ATMEGA2560 || PWM_USING_ATMEGA_32U4 ) + #define TIMER3_RESOLUTION 65536UL // Timer3 is 16 bit + #define TIMER4_RESOLUTION 65536UL // Timer3 is 16 bit + #define TIMER5_RESOLUTION 65536UL // Timer3 is 16 bit + #endif + +#else + #define TIMER1_RESOLUTION 65536UL // assume 16 bits for non-AVR chips +#endif + +//////////////////////////////////////// + +#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) + #define TCCR2A TCCR2 + #define TCCR2B TCCR2 + #define COM2A1 COM21 + #define COM2A0 COM20 + #define OCR2A OCR2 + #define TIMSK2 TIMSK + #define OCIE2A OCIE2 + #define TIMER2_COMPA_vect TIMER2_COMP_vect + #define TIMSK1 TIMSK +#endif + +//////////////////////////////////////// + +class AVR_PWM +{ + public: + + AVR_PWM(const uint8_t& pin, const float& frequency, const float& dutycycle) + { + _pin = pin; + _frequency = frequency; + _dutycycle = map(dutycycle, 0, 100.0f, 0, MAX_COUNT_16BIT); + + _timer = digitalPinToTimer(pin); + + PWM_LOGDEBUG1("AVR_PWM: _dutycycle =", _dutycycle); + + pinMode(pin, OUTPUT); + } + + /////////////////////////////////////////// + + ~AVR_PWM(); + + /////////////////////////////////////////// + /////////////////////////////////////////// + + private: + + void setPeriod_Timer0(unsigned long microseconds) __attribute__((always_inline)) + { + const unsigned long cycles = ( ( (F_CPU / 100000) * microseconds ) / 20 ); + + PWM_LOGDEBUG3("setPeriod_Timer0: F_CPU =", F_CPU, ", cycles =", cycles); + + if (cycles < TIMER0_RESOLUTION) + { + clockSelectBits = _BV(CS00); + pwmPeriod = cycles; + } + else if (cycles < TIMER0_RESOLUTION * 8) + { + clockSelectBits = _BV(CS01); + pwmPeriod = cycles / 8; + } + else if (cycles < TIMER0_RESOLUTION * 32) + { + clockSelectBits = _BV(CS01) | _BV(CS00); + pwmPeriod = cycles / 32; + } + else if (cycles < TIMER0_RESOLUTION * 64) + { + clockSelectBits = _BV(CS02); + pwmPeriod = cycles / 64; + } + else if (cycles < TIMER0_RESOLUTION * 128) + { + clockSelectBits = _BV(CS02) | _BV(CS00); + pwmPeriod = cycles / 128; + } + else if (cycles < TIMER0_RESOLUTION * 256) + { + clockSelectBits = _BV(CS02) | _BV(CS01); + pwmPeriod = cycles / 256; + } + else if (cycles < TIMER0_RESOLUTION * 1024) + { + clockSelectBits = _BV(CS02) | _BV(CS01) | _BV(CS00); + pwmPeriod = cycles / 1024; + } + else + { + PWM_LOGERROR1("setPeriod_Timer0: Error, min freq (Hz) =", (float) F_CPU / (1024 * TIMER0_RESOLUTION) ); + + clockSelectBits = _BV(CS02) | _BV(CS01) | _BV(CS00); + pwmPeriod = TIMER0_RESOLUTION - 1; + } + + TCCR0B |= clockSelectBits; + + OCR0A = pwmPeriod; // set pwm period + + _actualFrequency = (float) F_CPU / ( pwmPeriod * 32); + + PWM_LOGDEBUG3("setPeriod_Timer0: clockSelectBits =", clockSelectBits, ", pwmPeriod =", pwmPeriod); + } + + /////////////////////////////////////////// + + void setPeriod_Timer1(unsigned long microseconds) __attribute__((always_inline)) + { + const unsigned long cycles = ( ( (F_CPU / 100000) * microseconds ) / 20 ); + + PWM_LOGDEBUG3("setPeriod_Timer1: F_CPU =", F_CPU, ", cycles =", cycles); + + if (cycles < TIMER1_RESOLUTION) + { + clockSelectBits = _BV(CS10); + pwmPeriod = cycles; + } + else if (cycles < TIMER1_RESOLUTION * 8) + { + clockSelectBits = _BV(CS11); + pwmPeriod = cycles / 8; + } + else if (cycles < TIMER1_RESOLUTION * 64) + { + clockSelectBits = _BV(CS11) | _BV(CS10); + pwmPeriod = cycles / 64; + } + else if (cycles < TIMER1_RESOLUTION * 256) + { + clockSelectBits = _BV(CS12); + pwmPeriod = cycles / 256; + } + else if (cycles < TIMER1_RESOLUTION * 1024) + { + clockSelectBits = _BV(CS12) | _BV(CS10); + pwmPeriod = cycles / 1024; + } + else + { + PWM_LOGERROR1("setPeriod_Timer1: Error, min freq (Hz) =", (float) F_CPU / (1024 * TIMER1_RESOLUTION) ); + + clockSelectBits = _BV(CS12) | _BV(CS10); + pwmPeriod = TIMER1_RESOLUTION - 1; + } + + ICR1 = pwmPeriod; // set pwm period + + TCCR1B |= clockSelectBits; + + _actualFrequency = (float) F_CPU / ( pwmPeriod * 2); + + PWM_LOGDEBUG3("setPeriod_Timer1: clockSelectBits =", clockSelectBits, ", pwmPeriod =", pwmPeriod); + } + + /////////////////////////////////////////// + +#if !( PWM_USING_ATMEGA_32U4 ) + + void setPeriod_Timer2(unsigned long microseconds) __attribute__((always_inline)) + { + const unsigned long cycles = ( ( (F_CPU / 100000) * microseconds ) / 10 ); + + PWM_LOGDEBUG3("setPeriod_Timer2: F_CPU =", F_CPU, ", cycles =", cycles); + + if (cycles < TIMER2_RESOLUTION) + { + clockSelectBits = _BV(CS20); + pwmPeriod = cycles; + } + else if (cycles < TIMER2_RESOLUTION * 8) + { + clockSelectBits = _BV(CS21); + pwmPeriod = cycles / 8; + } + else if (cycles < TIMER2_RESOLUTION * 32) + { + clockSelectBits = _BV(CS21) | _BV(CS20); + pwmPeriod = cycles / 32; + } + else if (cycles < TIMER2_RESOLUTION * 64) + { + clockSelectBits = _BV(CS22); + pwmPeriod = cycles / 64; + } + else if (cycles < TIMER2_RESOLUTION * 128) + { + clockSelectBits = _BV(CS22) | _BV(CS20); + pwmPeriod = cycles / 128; + } + else if (cycles < TIMER2_RESOLUTION * 256) + { + clockSelectBits = _BV(CS22) | _BV(CS21); + pwmPeriod = cycles / 256; + } + else if (cycles < TIMER2_RESOLUTION * 1024) + { + clockSelectBits = _BV(CS22) | _BV(CS21) | _BV(CS20); + pwmPeriod = cycles / 1024; + } + else + { + PWM_LOGERROR1("setPeriod_Timer2: Error, min freq (Hz) =", (float) F_CPU / (1024 * TIMER2_RESOLUTION) ); + + clockSelectBits = _BV(CS22) | _BV(CS21) | _BV(CS20); + pwmPeriod = TIMER2_RESOLUTION - 1; + } + + TCCR2B |= clockSelectBits; + + OCR2A = pwmPeriod; // set pwm period + + _actualFrequency = (float) F_CPU / ( pwmPeriod * 32); + + PWM_LOGDEBUG3("setPeriod_Timer2: clockSelectBits =", clockSelectBits, ", pwmPeriod =", pwmPeriod); + } +#endif + + /////////////////////////////////////////// + +#if ( PWM_USING_ATMEGA2560 || PWM_USING_ATMEGA_32U4 ) + void setPeriod_Timer3(unsigned long microseconds) __attribute__((always_inline)) + { + const unsigned long cycles = ( ( (F_CPU / 100000) * microseconds ) / 20 ); + + PWM_LOGDEBUG3("setPeriod_Timer3: F_CPU =", F_CPU, ", cycles =", cycles); + + if (cycles < TIMER3_RESOLUTION) + { + clockSelectBits = _BV(CS30); + pwmPeriod = cycles; + } + else if (cycles < TIMER3_RESOLUTION * 8) + { + clockSelectBits = _BV(CS31); + pwmPeriod = cycles / 8; + } + else if (cycles < TIMER3_RESOLUTION * 64) + { + clockSelectBits = _BV(CS31) | _BV(CS30); + pwmPeriod = cycles / 64; + } + else if (cycles < TIMER3_RESOLUTION * 256) + { + clockSelectBits = _BV(CS32); + pwmPeriod = cycles / 256; + } + else if (cycles < TIMER3_RESOLUTION * 1024) + { + clockSelectBits = _BV(CS32) | _BV(CS30); + pwmPeriod = cycles / 1024; + } + else + { + PWM_LOGERROR1("setPeriod_Timer3: Error, min freq (Hz) =", (float) F_CPU / (1024 * TIMER3_RESOLUTION) ); + + clockSelectBits = _BV(CS32) | _BV(CS30); + pwmPeriod = TIMER3_RESOLUTION - 1; + } + + ICR3 = pwmPeriod; // set pwm period + + TCCR3B |= clockSelectBits; + + _actualFrequency = (float) F_CPU / ( pwmPeriod * 2); + + PWM_LOGDEBUG3("setPeriod_Timer3: clockSelectBits =", clockSelectBits, ", pwmPeriod =", pwmPeriod); + } +#endif + + /////////////////////////////////////////// + +#if ( PWM_USING_ATMEGA2560 ) + + void setPeriod_Timer4(unsigned long microseconds) __attribute__((always_inline)) + { + const unsigned long cycles = ( ( (F_CPU / 100000) * microseconds ) / 20 ); + + PWM_LOGDEBUG3("setPeriod_Timer4: F_CPU =", F_CPU, ", cycles =", cycles); + + if (cycles < TIMER4_RESOLUTION) + { + clockSelectBits = _BV(CS40); + pwmPeriod = cycles; + } + else if (cycles < TIMER4_RESOLUTION * 8) + { + clockSelectBits = _BV(CS41); + pwmPeriod = cycles / 8; + } + else if (cycles < TIMER4_RESOLUTION * 64) + { + clockSelectBits = _BV(CS41) | _BV(CS40); + pwmPeriod = cycles / 64; + } + else if (cycles < TIMER4_RESOLUTION * 256) + { + clockSelectBits = _BV(CS42); + pwmPeriod = cycles / 256; + } + else if (cycles < TIMER4_RESOLUTION * 1024) + { + clockSelectBits = _BV(CS42) | _BV(CS40); + pwmPeriod = cycles / 1024; + } + else + { + PWM_LOGERROR1("setPeriod_Timer4: Error, min freq (Hz) =", (float) F_CPU / (1024 * TIMER4_RESOLUTION) ); + + clockSelectBits = _BV(CS42) | _BV(CS40); + pwmPeriod = TIMER4_RESOLUTION - 1; + } + + ICR4 = pwmPeriod; // set pwm period + + TCCR4B |= clockSelectBits; + + _actualFrequency = (float) F_CPU / ( pwmPeriod * 2); + + PWM_LOGDEBUG3("setPeriod_Timer4: clockSelectBits =", clockSelectBits, ", pwmPeriod =", pwmPeriod); + } + + /////////////////////////////////////////// + + void setPeriod_Timer5(unsigned long microseconds) __attribute__((always_inline)) + { + const unsigned long cycles = ( ( (F_CPU / 100000) * microseconds ) / 20 ); + + PWM_LOGDEBUG3("setPeriod_Timer5: F_CPU =", F_CPU, ", cycles =", cycles); + + if (cycles < TIMER5_RESOLUTION) + { + clockSelectBits = _BV(CS50); + pwmPeriod = cycles; + } + else if (cycles < TIMER5_RESOLUTION * 8) + { + clockSelectBits = _BV(CS51); + pwmPeriod = cycles / 8; + } + else if (cycles < TIMER5_RESOLUTION * 64) + { + clockSelectBits = _BV(CS51) | _BV(CS50); + pwmPeriod = cycles / 64; + } + else if (cycles < TIMER5_RESOLUTION * 256) + { + clockSelectBits = _BV(CS52); + pwmPeriod = cycles / 256; + } + else if (cycles < TIMER5_RESOLUTION * 1024) + { + clockSelectBits = _BV(CS52) | _BV(CS50); + pwmPeriod = cycles / 1024; + } + else + { + PWM_LOGERROR1("setPeriod_Timer5: Error, min freq (Hz) =", (float) F_CPU / (1024 * TIMER5_RESOLUTION) ); + + clockSelectBits = _BV(CS52) | _BV(CS50); + pwmPeriod = TIMER5_RESOLUTION - 1; + } + + ICR5 = pwmPeriod; // set pwm period + + TCCR5B |= clockSelectBits; + + _actualFrequency = (float) F_CPU / ( pwmPeriod * 2); + + PWM_LOGDEBUG3("setPeriod_Timer5: clockSelectBits =", clockSelectBits, ", pwmPeriod =", pwmPeriod); + } +#endif + + /////////////////////////////////////////// + /////////////////////////////////////////// + + public: + + // dutycycle from 0-65536 for 0%-100% to make use of 16-bit top register + bool setPWM_Int(const uint8_t& pin, const float& frequency, const uint16_t& dutycycle) + { + uint16_t MAX_COUNT = MAX_COUNT_16BIT; + + pinMode(pin, OUTPUT); + + if ( (_timer == TIMER0A) || (_timer == TIMER0B) || \ + (_timer == TIMER2) || (_timer == TIMER2A) || (_timer == TIMER2B) ) + { + // Convert to 8-bit + _dutycycle = map(dutycycle, 0, MAX_COUNT_16BIT, 0, MAX_COUNT_8BIT); + MAX_COUNT = MAX_COUNT_8BIT; + } + else + { + // Keep 16 bit dutycycle + MAX_COUNT = MAX_COUNT_16BIT; + } + + PWM_LOGDEBUG1("setPWM_Int: _dutycycle =", _dutycycle); + + if (_dutycycle == 0) + { + digitalWrite(pin, LOW); + } + else if (_dutycycle >= MAX_COUNT) + { + PWM_LOGERROR1("Error, exceeded _dutycycle, reset to ", MAX_COUNT); + + digitalWrite(pin, HIGH); + } + + switch (_timer) + { + ////////////////////////////////////// + ////////////////////////////////////// + + // TODO To check for atmega8 +#if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__) + case TIMER0A: + PWM_LOGDEBUG("setPWM_Int:using TIMER0A"); + + // connect pwm to pin on timer 0, channel A + // Set frequency + TCCR0B = _BV(WGM02); // set mode as phase correct pwm, stop the timer + TCCR0A = 0; // clear control register A + + setPeriod_Timer0(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 8; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER2B, _dutycycle =", _dutycycle); + + // connect Fast PWM to pin on timer 0, channel A + TCCR0A |= _BV(COM0A1) | _BV(WGM01) | _BV(WGM00); + + OCR0B = _dutycycle; // set pwm duty + + PWM_LOGDEBUG3("setPWM_Int:using TIMER0A, TCCR0A =", TCCR0A, ", TCCR0B =", TCCR0B); + + break; + +#endif + + ////////////////////////////////////// + +#if 0 + // Not usable +#if defined(TCCR0A) && defined(COM0A1) + + case TIMER0A: + PWM_LOGDEBUG("setPWM_Int:using TIMER0A"); + + // connect pwm to pin on timer 0, channel A + // Set frequency + TCCR0B = _BV(WGM02); // set mode as phase correct pwm, stop the timer + TCCR0A = 0; // clear control register A + + setPeriod_Timer0(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 8; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER0A, _dutycycle =", _dutycycle); + + // connect Fast PWM to pin on timer 0, channel A + TCCR0A |= _BV(COM0A1) | _BV(WGM01) | _BV(WGM00); + + OCR0B = _dutycycle; // set pwm duty + + PWM_LOGDEBUG3("setPWM_Int:using TIMER0A, TCCR0A =", TCCR0A, ", TCCR0B =", TCCR0B); + + break; +#endif +#endif + + ////////////////////////////////////// + +#if defined(TCCR0A) && defined(COM0B1) + + case TIMER0B: + PWM_LOGDEBUG("setPWM_Int:using TIMER0B"); + + // connect pwm to pin on timer 0, channel B + // Set frequency + TCCR0B = _BV(WGM02); // set mode as phase correct pwm, stop the timer + TCCR0A = 0; // clear control register A + + setPeriod_Timer0(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 8; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER2B, _dutycycle =", _dutycycle); + + // connect Fast PWM to pin on timer 0, channel B + TCCR0A |= _BV(COM0B1) | _BV(WGM01) | _BV(WGM00); + + OCR0B = _dutycycle; // set pwm duty + + PWM_LOGDEBUG3("setPWM_Int:using TIMER0B, TCCR0A =", TCCR0A, ", TCCR0B =", TCCR0B); + + break; +#endif + + ////////////////////////////////////// + ////////////////////////////////////// + +#if defined(TCCR1A) && defined(COM1A1) + + case TIMER1A: + PWM_LOGDEBUG("setPWM_Int:using TIMER1A"); + + // connect pwm to pin on timer 1, channel A + // Set frequency + TCCR1B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timer + TCCR1A = 0; // clear control register A + + setPeriod_Timer1(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + TCCR1A |= _BV(COM1A1); + + OCR1A = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR1A) && defined(COM1B1) + + case TIMER1B: + PWM_LOGDEBUG("setPWM_Int:using TIMER1B"); + + // connect pwm to pin on timer 1, channel B + // Set frequency + TCCR1B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timer + TCCR1A = 0; // clear control register A + + setPeriod_Timer1(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + TCCR1A |= _BV(COM1B1); + + OCR1B = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR1A) && defined(COM1C1) + + case TIMER1C: + PWM_LOGDEBUG("setPWM_Int:using TIMER1C"); + + // connect pwm to pin on timer 1, channel C + // Set frequency + TCCR1B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timer + TCCR1A = 0; // clear control register A + + setPeriod_Timer1(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + TCCR1A |= _BV(COM1C1); + + OCR1C = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + ////////////////////////////////////// + +#if defined(TCCR2) && defined(COM21) + + case TIMER2: + PWM_LOGDEBUG("setPWM_Int:using TIMER2"); + + // connect pwm to pin on timer 0, channel A + // Set frequency + TCCR2B = _BV(WGM20); // set mode as phase correct pwm, stop the timer + TCCR2A = 0; // clear control register A + + setPeriod_Timer2(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 8; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER2, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 2 + TCCR2 |= _BV(COM21); + OCR2 = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if 0 + // TODO, Somehow not usable +#if defined(TCCR2A) && defined(COM2A1) + + case TIMER2A: + PWM_LOGDEBUG("setPWM_Int:using TIMER2A"); + + // connect pwm to pin on timer 2, channel A + // Set frequency + TCCR2B = _BV(WGM22); // set mode as phase correct pwm, stop the timer + TCCR2A = 0; // clear control register A + + setPeriod_Timer2(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 8; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER2A, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 2, channel A + TCCR2A |= _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); + + OCR2B = _dutycycle; // set pwm duty + + PWM_LOGDEBUG3("setPWM_Int:using TIMER2A, TCCR2A =", TCCR2A, ", TCCR2B =", TCCR2B); + + break; +#endif +#endif + ////////////////////////////////////// + +#if defined(TCCR2A) && defined(COM2B1) + + case TIMER2B: + PWM_LOGDEBUG("setPWM_Int:using TIMER2B"); + + // connect pwm to pin on timer 2, channel B + // Set frequency + TCCR2B = _BV(WGM22); // set mode as phase correct pwm, stop the timer + TCCR2A = 0; // clear control register A + + setPeriod_Timer2(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 8; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER2B, _dutycycle =", _dutycycle); + + // connect Fast PWM to pin on timer 2, channel B + TCCR2A |= _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); + + OCR2B = _dutycycle; // set pwm duty + + PWM_LOGDEBUG3("setPWM_Int:using TIMER2B, TCCR2A =", TCCR2A, ", TCCR2B =", TCCR2B); + + break; +#endif + + ////////////////////////////////////// + ////////////////////////////////////// + +#if ( PWM_USING_ATMEGA2560 || PWM_USING_ATMEGA_32U4 ) + +#if defined(TCCR3A) && defined(COM3A1) + + case TIMER3A: + + TCCR3B = _BV(WGM33); // set mode as phase and frequency correct pwm, stop the timer + TCCR3A = 0; // clear control register A + + setPeriod_Timer3(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER3A, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 3, channel A + TCCR3A |= _BV(COM3A1); + + OCR3A = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR3A) && defined(COM3B1) + + case TIMER3B: + + TCCR3B = _BV(WGM33); // set mode as phase and frequency correct pwm, stop the timer + TCCR3A = 0; // clear control register A + + setPeriod_Timer3(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER3B, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 3, channel B + TCCR3A |= _BV(COM3B1); + + OCR3B = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR3A) && defined(COM3C1) + + case TIMER3C: + + + TCCR3B = _BV(WGM33); // set mode as phase and frequency correct pwm, stop the timer + TCCR3A = 0; // clear control register A + + setPeriod_Timer3(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER3C, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 3, channel C + TCCR3A |= _BV(COM3C1); + + OCR3C = _dutycycle; // set pwm duty + + break; +#endif +#endif // #if ( PWM_USING_ATMEGA2560 || PWM_USING_ATMEGA_32U4 ) + + ////////////////////////////////////// + ////////////////////////////////////// + +#if ( PWM_USING_ATMEGA2560 ) + +#if defined(TCCR4A) + + case TIMER4A: + PWM_LOGDEBUG("setPWM_Int:using TIMER4A"); + + TCCR4B = _BV(WGM43); // set mode as phase and frequency correct pwm, stop the timer + TCCR4A = 0; // clear control register A + + setPeriod_Timer4(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER4A, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 4, channel A + TCCR4A |= _BV(COM4A1); + +#if defined(COM4A0) // only used on 32U4 + TCCR4A &= ~_BV(COM4A0); +#endif + + OCR4A = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR4A) && defined(COM4B1) + + case TIMER4B: + PWM_LOGDEBUG("setPWM_Int:using TIMER4B"); + + TCCR4B = _BV(WGM43); // set mode as phase and frequency correct pwm, stop the timer + TCCR4A = 0; // clear control register A + + setPeriod_Timer4(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER4B, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 4, channel B + TCCR4A |= _BV(COM4B1); + + OCR4B = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR4A) && defined(COM4C1) + + case TIMER4C: + PWM_LOGDEBUG("setPWM_Int:using TIMER4C"); + + TCCR4B = _BV(WGM43); // set mode as phase and frequency correct pwm, stop the timer + TCCR4A = 0; // clear control register A + + setPeriod_Timer4(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER4C, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 4, channel C + TCCR4A |= _BV(COM4C1); + + OCR4C = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR4C) && defined(COM4D1) + + case TIMER4D: + PWM_LOGDEBUG("setPWM_Int:using TIMER4D"); + TCCR4B = _BV(WGM43); // set mode as phase and frequency correct pwm, stop the timer + TCCR4A = 0; // clear control register A + + setPeriod_Timer4(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER4D, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 4, channel D + TCCR4A |= _BV(COM4D1); + + OCR4D = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + ////////////////////////////////////// + +#if defined(TCCR5A) && defined(COM5A1) + + case TIMER5A: + PWM_LOGDEBUG("setPWM_Int:using TIMER5A"); + + TCCR5B = _BV(WGM53); // set mode as phase and frequency correct pwm, stop the timer + TCCR5A = 0; // clear control register A + + setPeriod_Timer5(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER5A, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 5, channel A + TCCR5A |= _BV(COM5A1); + + OCR5A = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR5A) && defined(COM5B1) + + case TIMER5B: + PWM_LOGDEBUG("setPWM_Int:using TIMER5B"); + + TCCR5B = _BV(WGM53); // set mode as phase and frequency correct pwm, stop the timer + TCCR5A = 0; // clear control register A + + setPeriod_Timer5(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER5B, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 5, channel B + TCCR5A |= _BV(COM5B1); + + OCR5B = _dutycycle; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR5A) && defined(COM5C1) + + case TIMER5C: + PWM_LOGDEBUG("setPWM_Int:using TIMER5C"); + + TCCR5B = _BV(WGM53); // set mode as phase and frequency correct pwm, stop the timer + TCCR5A = 0; // clear control register A + + setPeriod_Timer5(1000000UL / frequency); + + _dutycycle = ((uint32_t) pwmPeriod * dutycycle) >> 16; + + PWM_LOGDEBUG1("setPWM_Int:using TIMER5C, _dutycycle =", _dutycycle); + + // connect pwm to pin on timer 5, channel C + TCCR5A |= _BV(COM5C1); + + OCR5C = _dutycycle; // set pwm duty + + break; +#endif + +#endif // #if ( PWM_USING_ATMEGA2560 ) + + ////////////////////////////////////// + ////////////////////////////////////// + + case NOT_ON_TIMER: + default: + + PWM_LOGERROR1("Error, not usable for PWM, pin =", pin); + + if (_dutycycle < MAX_COUNT / 2) + { + digitalWrite(pin, LOW); + } + else + { + digitalWrite(pin, HIGH); + } + + return false; + } + + return true; + } + + /////////////////////////////////////////// + + bool setPWM() + { + return setPWM_Int(_pin, _frequency, _dutycycle); + } + + /////////////////////////////////////////// + + bool setPWM(const uint8_t& pin, const float& frequency, const float& dutycycle) + { + _dutycycle = map(dutycycle, 0, 100.0f, 0, MAX_COUNT_16BIT); + PWM_LOGDEBUG1("setPWM: _dutycycle =", _dutycycle); + + return setPWM_Int(pin, frequency, _dutycycle); + } + + /////////////////////////////////////////// + + bool setPWM_Period(const uint8_t& pin, const float& period_us, const float& dutycycle) + { + _dutycycle = map(dutycycle, 0, 100.0f, 0, MAX_COUNT_16BIT); + + PWM_LOGDEBUG1("setPWM_Period: _dutycycle =", _dutycycle); + + return setPWM_Int(pin, 1000000.0f / period_us, _dutycycle); + } + + /////////////////////////////////////////// + + // Must have same frequency + bool setPWM_manual(const uint8_t& pin, const uint16_t& DCValue) + { + // Not the same pin or overvalue + if ( (_pin != pin) || (DCValue > pwmPeriod) ) + return false; + + switch (_timer) + { + ////////////////////////////////////// + ////////////////////////////////////// + + // TODO To check for atmega8 +#if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__) + case TIMER0A: + OCR0B = DCValue; // set pwm duty + + break; + +#endif + + ////////////////////////////////////// + +#if 0 + // Not usable +#if defined(TCCR0A) && defined(COM0A1) + + case TIMER0A: + OCR0B = DCValue; // set pwm duty + + PWM_LOGDEBUG3("setPWM_Int:using TIMER0A, TCCR0A =", TCCR0A, ", TCCR0B =", TCCR0B); + + break; +#endif +#endif + + ////////////////////////////////////// + +#if defined(TCCR0A) && defined(COM0B1) + + case TIMER0B: + OCR0B = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + ////////////////////////////////////// + +#if defined(TCCR1A) && defined(COM1A1) + + case TIMER1A: + OCR1A = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR1A) && defined(COM1B1) + + case TIMER1B: + OCR1B = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR1A) && defined(COM1C1) + + case TIMER1C: + OCR1C = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + ////////////////////////////////////// + +#if defined(TCCR2) && defined(COM21) + + case TIMER2: + OCR2 = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if 0 + // TODO, Somehow not usable +#if defined(TCCR2A) && defined(COM2A1) + + case TIMER2A: + OCR2B = DCValue; // set pwm duty + + break; +#endif +#endif + ////////////////////////////////////// + +#if defined(TCCR2A) && defined(COM2B1) + + case TIMER2B: + OCR2B = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + ////////////////////////////////////// + +#if ( PWM_USING_ATMEGA2560 || PWM_USING_ATMEGA_32U4 ) + +#if defined(TCCR3A) && defined(COM3A1) + + case TIMER3A: + OCR3A = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR3A) && defined(COM3B1) + + case TIMER3B: + OCR3B = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR3A) && defined(COM3C1) + + case TIMER3C: + OCR3C = DCValue; // set pwm duty + + break; +#endif +#endif // #if ( PWM_USING_ATMEGA2560 || PWM_USING_ATMEGA_32U4 ) + + ////////////////////////////////////// + ////////////////////////////////////// + +#if ( PWM_USING_ATMEGA2560 ) + +#if defined(TCCR4A) + + case TIMER4A: + OCR4A = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR4A) && defined(COM4B1) + + case TIMER4B: + OCR4B = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR4A) && defined(COM4C1) + + case TIMER4C: + OCR4C = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR4C) && defined(COM4D1) + + case TIMER4D: + OCR4D = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + ////////////////////////////////////// + +#if defined(TCCR5A) && defined(COM5A1) + + case TIMER5A: + OCR5A = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR5A) && defined(COM5B1) + + case TIMER5B: + OCR5B = DCValue; // set pwm duty + + break; +#endif + + ////////////////////////////////////// + +#if defined(TCCR5A) && defined(COM5C1) + + case TIMER5C: + OCR5C = DCValue; // set pwm duty + + break; +#endif + +#endif // #if ( PWM_USING_ATMEGA2560 ) + + ////////////////////////////////////// + ////////////////////////////////////// + + case NOT_ON_TIMER: + default: + + PWM_LOGERROR1("Error, not usable for PWM, pin =", pin); + + return false; + } + + + + PWM_LOGINFO7("PWM enabled, DCValue =", DCValue, ", pwmPeriod =", pwmPeriod, + ", _frequency =", _frequency, ", _actualFrequency =", _actualFrequency); + + return true; + } + + /////////////////////////////////////////// + + inline float getActualDutyCycle() + { + return ( (float) _dutycycle * 100 / pwmPeriod); + } + + /////////////////////////////////////////// + + inline float getActualFreq() + { + return _actualFrequency; + } + + /////////////////////////////////////////// + + inline float getPWMPeriod() + { + return pwmPeriod; + } + + /////////////////////////////////////////// + + inline uint32_t get_freq_CPU() + { + return freq_CPU; + } + + /////////////////////////////////////////// + + inline uint32_t getPin() + { + return _pin; + } + + /////////////////////////////////////////////////////////////////// + + private: + + uint32_t freq_CPU; + + float _actualFrequency; + float _frequency; + + // dutycycle from 0-65535 for 0%-100% to make use of 16-bit top register + // _dutycycle = map(dutycycle, 0, 100.0f, 0, 65535) for better accuracy + uint16_t _dutycycle; + ////////// + + uint8_t _pin; + +#define INVALID_TIMER 255 + + uint8_t _timer = INVALID_TIMER; + + uint8_t clockSelectBits; + uint16_t pwmPeriod; + + /////////////////////////////////////////// +}; + +/////////////////////////////////////////// + + +#endif // AVR_PWM_H + diff --git a/src/PWM_Generic_Debug.h b/src/PWM_Generic_Debug.h new file mode 100644 index 0000000..965ca43 --- /dev/null +++ b/src/PWM_Generic_Debug.h @@ -0,0 +1,100 @@ +/**************************************************************************************************************************** + PWM_Generic_Debug.h + For AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/AVR_PWM + Licensed under MIT license + + This is pure hardware-based PWM + + Version: 1.0.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K.Hoang 27/10/2022 Initial coding for AVR-based boards (UNO, Nano, Mega, 32U4, 16U4, etc. ) + *****************************************************************************************************************************/ + +#pragma once + +#ifndef PWM_GENERIC_DEBUG_H +#define PWM_GENERIC_DEBUG_H + +#ifdef PWM_GENERIC_DEBUG_PORT + #define PWM_DBG_PORT PWM_GENERIC_DEBUG_PORT +#else + #define PWM_DBG_PORT Serial +#endif + +// Change _PWM_LOGLEVEL_ to set tracing and logging verbosity +// 0: DISABLED: no logging +// 1: ERROR: errors +// 2: WARN: errors and warnings +// 3: INFO: errors, warnings and informational (default) +// 4: DEBUG: errors, warnings, informational and debug + +#ifndef _PWM_LOGLEVEL_ + #define _PWM_LOGLEVEL_ 1 +#endif + +/////////////////////////////////////// + +const char PWM_MARK[] = "[PWM] "; +const char PWM_SPACE[] = " "; + +#define PWM_PRINT PWM_DBG_PORT.print +#define PWM_PRINTLN PWM_DBG_PORT.println + +#define PWM_PRINT_MARK PWM_PRINT(PWM_MARK) +#define PWM_PRINT_SP PWM_PRINT(PWM_SPACE) +#define PWM_PRINT_LINE PWM_PRINT(PWM_LINE) + +/////////////////////////////////////// + +#define PWM_LOGERROR0(x) if(_PWM_LOGLEVEL_>0) { PWM_PRINT(x); } +#define PWM_LOGERROR(x) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINTLN(x); } +#define PWM_LOGERROR1(x,y) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINTLN(y); } +#define PWM_HEXLOGERROR1(x,y) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT(" 0x"); PWM_PRINTLN(y, HEX); } +#define PWM_LOGERROR2(x,y,z) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } +#define PWM_LOGERROR3(x,y,z,w) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } +#define PWM_LOGERROR5(x,y,z,w,xx,yy) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); } +#define PWM_LOGERROR7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINT_SP; PWM_PRINTLN(ww); } + +////////////////////////////////////////// + +#define PWM_LOGWARN0(x) if(_PWM_LOGLEVEL_>1) { PWM_PRINT(x); } +#define PWM_LOGWARN(x) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINTLN(x); } +#define PWM_LOGWARN1(x,y) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); } +#define PWM_HEXLOGWARN1(x,y) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT(" 0x"); PWM_PRINTLN(y, HEX); } +#define PWM_LOGWARN2(x,y,z) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } +#define PWM_LOGWARN3(x,y,z,w) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } +#define PWM_LOGWARN5(x,y,z,w,xx,yy) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); } +#define PWM_LOGWARN7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINT_SP; PWM_PRINTLN(ww); } + +////////////////////////////////////////// + +#define PWM_LOGINFO0(x) if(_PWM_LOGLEVEL_>2) { PWM_PRINT(x); } +#define PWM_LOGINFO(x) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINTLN(x); } +#define PWM_LOGINFO1(x,y) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); } +#define PWM_HEXLOGINFO1(x,y) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT(" 0x"); PWM_PRINTLN(y, HEX); } +#define PWM_LOGINFO2(x,y,z) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } +#define PWM_LOGINFO3(x,y,z,w) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } +#define PWM_LOGINFO5(x,y,z,w,xx,yy) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); } +#define PWM_LOGINFO7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINT_SP; PWM_PRINTLN(ww); } + +////////////////////////////////////////// + +#define PWM_LOGDEBUG0(x) if(_PWM_LOGLEVEL_>3) { PWM_PRINT(x); } +#define PWM_LOGDEBUG(x) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINTLN(x); } +#define PWM_LOGDEBUG1(x,y) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); } +#define PWM_HEXLOGDEBUG1(x,y) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT(" 0x"); PWM_PRINTLN(y, HEX); } +#define PWM_LOGDEBUG2(x,y,z) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } +#define PWM_LOGDEBUG3(x,y,z,w) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } +#define PWM_LOGDEBUG5(x,y,z,w,xx,yy) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); } +#define PWM_LOGDEBUG7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINT_SP; PWM_PRINTLN(ww); } + +////////////////////////////////////////// + +/////////////////////////////////////// + +#endif //PWM_GENERIC_DEBUG_H diff --git a/utils/astyle_library.conf b/utils/astyle_library.conf new file mode 100644 index 0000000..8a73bc2 --- /dev/null +++ b/utils/astyle_library.conf @@ -0,0 +1,70 @@ +# Code formatting rules for Arduino libraries, modified from for KH libraries: +# +# https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf +# + +# astyle --style=allman -s2 -t2 -C -S -xW -Y -M120 -f -p -xg -H -xb -c --xC120 -xL *.h *.cpp *.ino + +--mode=c +--lineend=linux +--style=allman + +# -r or -R +#--recursive + +# -c => Converts tabs into spaces +convert-tabs + +# -s2 => 2 spaces indentation +--indent=spaces=2 + +# -t2 => tab =2 spaces +#--indent=tab=2 + +# -C +--indent-classes + +# -S +--indent-switches + +# -xW +--indent-preproc-block + +# -Y => indent classes, switches (and cases), comments starting at column 1 +--indent-col1-comments + +# -M120 => maximum of 120 spaces to indent a continuation line +--max-continuation-indent=120 + +# -xC120 => max‑code‑length will break a line if the code exceeds # characters +--max-code-length=120 + +# -f => +--break-blocks + +# -p => put a space around operators +--pad-oper + +# -xg => Insert space padding after commas +--pad-comma + +# -H => put a space after if/for/while +pad-header + +# -xb => Break one line headers (e.g. if/for/while) +--break-one-line-headers + +# -c => Converts tabs into spaces +#--convert-tabs + +# if you like one-liners, keep them +#keep-one-line-statements + +# -xV +--attach-closing-while + +#unpad-paren + +# -xp +remove-comment-prefix + diff --git a/utils/restyle.sh b/utils/restyle.sh new file mode 100644 index 0000000..bcd846f --- /dev/null +++ b/utils/restyle.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +for dir in . ; do + find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.ino" \) -exec astyle --suffix=none --options=./utils/astyle_library.conf \{\} \; +done +