├── keywords.txt ├── library.properties ├── examples ├── SlowPwmBlink │ └── SlowPwmBlink.ino └── FastPwmPin │ └── FastPwmPin.ino ├── FastPwmPin.h ├── README.md └── FastPwmPin.cpp /keywords.txt: -------------------------------------------------------------------------------- 1 | FastPwmPin KEYWORD1 2 | enablePwmPin KEYWORD2 3 | disablePwmPin KEYWORD2 4 | FASTPWMPIN_TOGGLE KEYWORD2 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Fast PWM Pin 2 | version=0.3.2504 3 | author=Maxint 4 | maintainer=Maxint R&D 5 | sentence=Enable fast PWM on a specific output pin. 6 | paragraph=Generate a very fast (or very slow) PWM signal on a specific output pin at a specified frequency. Examples included. 7 | category=Signal Input/Output 8 | url=https://github.com/maxint-rd/FastPwmPin 9 | architectures=* -------------------------------------------------------------------------------- /examples/SlowPwmBlink/SlowPwmBlink.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Blink a led using a PWM pin 3 | Turns on an LED on and off, repeatedly, using slow PWM toggle of a specific pin. 4 | 5 | Note: Not every supported MCU/pin can generate 1 Hz. See these test results: 6 | - ATmega328 @ 16 MHz, Arduino Pro Mini, output 40 Hz - 4 MHz on pin 11 (toggle only) 7 | output 80 Hz - 4 MHz on pin 3 8 | output 1 Hz - 4 MHz on pin 9 (toggle only) 9 | output 1 Hz - 4 MHz on pin 10 10 | - ATmega8A @ 8 MHz, MiniCore, output 1Hz - 4MHz on pin 9 using 16-bit Timer1 (prescaler for <128Hz) 11 | - ATtiny85 @ 1 MHz, ATTinyCore, output 1Hz - 4MHz on pin 1 or 4 12 | - ATtiny13A @ 9,6 Mhz, MicroCore, output 38Hz - 4MHz on pin 1 or 4 13 | - ATtiny44A @ 8 Mhz, ATTinyCore, output 1Hz - 4MHz on pins 5,6 using 16-bit Timer1 (prescaler for <64Hz) 14 | output 32,16Hz - 4MHz on pins 7,8 using 8-bit Timer0 (prescaler for <40kHz) 15 | 16 | Made by Maxint-RD MMOLE 2020 17 | For more information see https://github.com/maxint-rd/FastPwmPin 18 | */ 19 | 20 | #include 21 | #define PIN_PWM 4 22 | 23 | 24 | void setup() { 25 | // enable PWM-blinking on the specified pin using low frequency PWM 26 | FastPwmPin::enablePwmPin(PIN_PWM, 4L, 50); 27 | delay(3000); 28 | FastPwmPin::enablePwmPin(PIN_PWM, 1L, 10); 29 | } 30 | 31 | void loop() { 32 | // put your main code here, in this example we do nothing 33 | } -------------------------------------------------------------------------------- /examples/FastPwmPin/FastPwmPin.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Blink with fast PWM pin 3 | Turns on an LED on and off, repeatedly, along with fast toggle of a specific pin. 4 | For more information see https://github.com/maxint-rd/FastPwmPin 5 | 6 | Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO 7 | it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to 8 | the correct LED pin independent of which board is used. 9 | If you want to know what pin the on-board LED is connected to on your Arduino model, check 10 | the Technical Specs of your board at https://www.arduino.cc/en/Main/Products 11 | For ATtiny85 and ATtiny13A the library set LED_BUILTIN to pin 2. 12 | 13 | This example code is based on the Arduino Blink example, released in the public domain. 14 | Original version modified by Scott Fitzgerald, Arturo Guadalupi & Colby Newman. 15 | This version modified 17 January 2018 by Maxint R&D (github.com/maxint-rd) 16 | */ 17 | 18 | #include 19 | 20 | // the setup function runs once when you press reset or power the board 21 | void setup() { 22 | // initialize digital pin LED_BUILTIN as an output. 23 | pinMode(LED_BUILTIN, OUTPUT); 24 | 25 | // Enable fast PWM on a pin 26 | // ATmega328/168 (Uno/Nano/Pro Mini): pin D3 or pin D11 (D11 toggle only) 27 | // ATtiny85: pin D1, D4 (D0/D3 only as inverted for D1/D4, 51%-99%) 28 | // ATtiny13A: pin D1, D0 (D0 toggle only) 29 | FastPwmPin::enablePwmPin(11, 4000000L, 50); 30 | } 31 | 32 | // the loop function runs over and over again forever 33 | void loop() { 34 | digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) 35 | delay(100); // wait for a second 36 | digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW 37 | delay(100); // wait for a second 38 | } 39 | -------------------------------------------------------------------------------- /FastPwmPin.h: -------------------------------------------------------------------------------- 1 | #ifndef __FASTPWMPIN_H__ 2 | #define __FASTPWMPIN_H__ 3 | #include 4 | 5 | // For the blink example its nice to have LED_BUILTIN defined, also for ATtiny13 and ATtiny85. 6 | // Depending on the core ATtiny44A may already have LED_BUILTIN as 8. 7 | #ifndef LED_BUILTIN 8 | #if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__) || defined(__AVR_ATtiny44__) 9 | #define LED_BUILTIN 2 10 | #endif 11 | #endif 12 | 13 | #define FASTPWMPIN_TOGGLE 50 14 | 15 | // macro used for disabling PWM on a pin. Resets the specified bit of the specific register 16 | #if !defined(cbi) 17 | #define cbi(reg,bit) (reg &= ~(_BV(bit))) 18 | #endif 19 | 20 | class FastPwmPin 21 | { 22 | public: 23 | FastPwmPin() {}; // constructor 24 | /** Enable fast PWM on the preferred pin. Try to set frequency and period as close as possible 25 | * @arg const int nPreferredPin - the preferred pin to be used (returns -1 if not available for PWM) 26 | * @arg unsigned long ulFrequency - e.g. 250000L for 250 kHz 27 | * @arg uint8_t nPeriodPercentage - 1-99 percentage of dutycycle. Default is 50% 28 | */ 29 | static int enablePwmPin(const int nPreferredPin=0, unsigned long ulFrequency=0L, uint8_t nPeriodPercentage=FASTPWMPIN_TOGGLE); 30 | /** Disable PWM on the indicated pin. Should be a previously enabled pin 31 | * @arg const int nPin - the pin for PWM to be disabled 32 | * @arg uint8_t nPinState - state of pin after disabling. Default is LOW. 33 | */ 34 | static void disablePwmPin(const int nPin, uint8_t nPinState=LOW); 35 | 36 | private: 37 | static uint8_t findPrescaler(unsigned long ulFrequency, uint8_t nTimer=0); 38 | static const uint16_t aPrescale1[]; // Set of prescalar values 39 | #if !defined(__AVR_ATtiny13__) && !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny25__) 40 | static const uint16_t aPrescale2[]; // Extended set of prescalar values 41 | #endif 42 | }; 43 | 44 | #if (defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)) 45 | // ATTiny24A/44A/84A uses Timer0 for Fast PWM on pins 7 and 8. This impacts delay() and millis(). 46 | // To mitigate that, the watchdog timer can be used to replace the original delay() and millis(). 47 | #define TINYX4_ENABLE_WDTMILLIS 1 48 | #endif 49 | #if defined(TINYX4_ENABLE_WDTMILLIS) 50 | uint32_t wdt_millis(); 51 | void wdt_delay(uint16_t ms); 52 | //#define delay(x) wdt_delay(x) 53 | //#define millis(x) wdt_millis(x) 54 | #endif // TINYX4_ENABLE_WDTMILLIS 55 | 56 | #endif //__FASTPWMPIN_H__ 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastPwmPin 2 | Arduino library to generate a fast PWM signal on an output pin at maximum frequency. Examples included. 3 | 4 | ## Introduction 5 | FastPwmPin provides a means to generate a high frequency PWM signal on one specific output pin. Where the regular Arduino analogWrite() function allows for generating a fixed frequency signal, this library achieves frequencies as high as 4 MHz using fast timer manipulation. The library produces a PWM signal on a single output pin. The frequency and duty cycle can be selected. The library supports multiple MCU's. The capabilities depend on the specific MCU. While originally aimed at high frequencies, the library can now also generate very low frequencies; depending on the MCU used as low as 1 Hz. 6 | 7 | ## Support for different MCUs 8 | This library supports generating a high frequency signal on different MCUs such as ATmega 328, 168 and ATtiny85. Depending on the MCU, it uses different timers and registers to produce the high frequency signal. The table below gives an overview: 9 | 10 | MCU (Board) | Available pins | Timer used | Remarks 11 | ------------ | ------------- | ------------- | ------------- 12 | ATmega328 ATmega168
(Arduino Uno, Nano, Pro Mini) | 9*,10 - 3,11* | 16-bit Timer1 - 8-bit Timer2 | Pins 9, 10 have 16-bit resolution, pins 3, 11 have 8-bit resolution. Pin 9, 11 only support toggle mode (50% PWM) 13 | LGT8F328P | 9*,10 - 3,11* - 1*,2| 16-bit Timer1 - 8-bit Timer2 - 16-bit Timer3| Tested on QFP32 model. Pin 1 not supported on SSOP20. For Timer 3 fixed core github.com/LaZsolt/lgt8fx is required 14 | ATmega8A | 9*,10 - 11* | Timer1 - Timer2 | Pins 9, 10 have 16-bit resolution, pin 11 has 8-bit resolution. Pins 9, 11 toggle mode only (50% PWM) 15 | ATtiny85 | 1,3 | Timer1 | Only 8-bit resolution. Pins 0, 4 can also be used, but only inverted (51%-99%) 16 | ATtiny44A ATtiny84A| 5,6* - 7,8* | Timer1 - Timer0 | Pins 5, 6 have 16-bit resolution, pins 7, 8 have 8-bit resolution. Pins 6 and 8 only support toggle mode (50% PWM) 17 | ATtiny13A | 0*, 1 | Timer0 | Pin 0 only supports toggle mode (50% PWM) 18 | ESP8266 | | | Minimal implementation using analogWriteFreq() and analogWrite() 19 | ESP32 | | | NOT SUPPORTED (YET) 20 | STM32 | | | NOT SUPPORTED (YET) 21 | * *support for toggle mode (50%) only 22 | 23 | ## Tested frequencies 24 | This library has been tested on multiple MCU's under various condititions\*. The generated signal frequency has been measured using different methods\*\*. The table below lists frequencies measured: 25 | 26 | MCU (Board) | Clock (voltage) | Highest frequency | Lowest frequency | Remarks 27 | ------------ | ------------- | ------------- | ------------- | ------------- 28 | ATmega328 (Pro Mini) | 16 Mhz (3v3/5V) | 4 MHz | 1 Hz | Toggle only on pin 9, 11 and at highest frequencies. Lowest frequency is 40 and 80 Hz on pins 11 and 3 29 | LGT8F328P |32 Mhz (3v3) | 16 MHz | 1 Hz | Toggle only on pin 1, 9, 11 and at highest frequencies. Lowest frequency is 40 and 80 Hz on pins 11 and 3 (8-bit Timer2). Tested using QFP32 board. 30 | ATmega168 (Pro Mini) | 8 Mhz (3v3) | 4 MHz | | Toggle only on pin 11 and at highest frequencies 31 | ATmega8A | 8 MHz (5V) | 4 MHz | 1 Hz | best resolution on pins 9, 10 32 | ATtiny85 | 1 MHz, 8 MHz (3v3), 16 MHz | 16 MHz | 1 Hz | when > 250 kHz fast PLL clock is activated, 1 Hz only on 1MHz clock 33 | ATtiny84A | 8 MHz (5V) | 4 MHz | 1 Hz | lowest frequency measured on pin 7 is 32 Hz, on pin 5 it is 1 Hz 34 | ATtiny44A | 8 MHz (3v3/5V) | 4 MHz | 1 Hz | lowest frequency measured on pin 7 is 32 Hz, on pin 5 it is 1 Hz 35 | ATtiny13A | 9.6 MHz (3v3) | 1.6 MHz | 38 Hz | frequencies > 1.6 MHz are instable 36 | 37 | \* *If you tested this library on a different board-setup, please send me your findings, so I can update the table.*
38 | \*\* *Frequency was measured using UT89C multimeter, DSO112 mini oscilloscope, Arduino [FreqCount](https://github.com/PaulStoffregen/FreqCount/tree/master/examples/Serial_Output) serial example on 16MHz Nano and a logic analyzer (@16MS/s).* 39 | 40 | ## Installation/Usage 41 | The library can be downloaded from https://github.com/maxint-rd/FastPwmPin. It can be installed as an Arduino library using the Sketch|Library menu. 42 | Just add the zipfile library and the enclosed example should appear in the menu automatically. 43 | 44 | Initialisation before `Setup()`: 45 | ``` 46 | // include header 47 | #include 48 | ``` 49 | ### Enable PWM on a pin 50 | To initialize the high frequency signal, call the `enablePwmPin()` method in `Setup()`: 51 | ``` 52 | FastPwmPin::enablePwmPin(11, 4000000L, 50); 53 | ``` 54 | 55 | The `enablePwmPin()` method has the following syntax: 56 | ``` 57 | int FastPwmPin::enablePwmPin( 58 | const int nPreferredPin=0, 59 | unsigned long ulFrequency=0L, 60 |   uint8_t nDutyPercentage=FASTPWMPIN_TOGGLE 61 | ); 62 | 63 | Parameters: 64 | nPreferredPin - prefered pin to generate the high frequency signal 65 | ulFrequency - frequency in Hertz 66 |   nDutyPercentage - PWM percentage (duty cycle) 67 | 68 | Return value: 69 | When succesful the method returns the preferred pin as set. If unsuccesful -1 is returned. 70 | ``` 71 | Note: To switch a pin fully on or fully off, you can use the `disablePwmPin()` method (see below). Duty percentages of 100 and 0 result in -1 error return value. 72 | 73 | See the enclosed [example](examples/FastPwmPin) for more details. 74 | 75 | ### Disable PWM on a pin 76 | In case you want to disable PWM you can call the `disablePwmPin()` method: 77 | ``` 78 | FastPwmPin::disablePwmPin(11); 79 | ``` 80 | 81 | The `disablePwmPin()` method has the following syntax: 82 | ``` 83 | void FastPwmPin::disablePwmPin(const int nPin, uint8_t nPinState=LOW); 84 | 85 | Parameters: 86 | nPin - the pin for PWM to be disabled 87 | nPinState - state of pin after disabling. Default is LOW. 88 | ``` 89 | 90 | Note: Be sure to use the same pin number that was returned by the `enablePwmPin()` method. 91 | 92 | ## Features & limitations 93 | - When Timer 0 is used (ATtiny84A/44A/13A) the `delay()` and `millis()` functions can be impacted (depending on the core used). For the ATtiny84A/44A an alternative implementation of delay() and millis() using the watchdog timer, is included in the library. 94 | - When Timer1 is used (ATtiny85/84A/44A and ATmega 328/168/8A), regular PWM output is impacted. 95 | - When Timer2 is used (ATmega 328/168), the tone() function is impacted. 96 | - For Timer 3 (LGT8F328P) a fixed core with definition of OCR3A is required. See github.com/LaZsolt/lgt8fx 97 | - When Timer3 is used (LGT8F328P) for pin 1 (TX) of the QFP32 board, the serial port cannot be used. WARNING: using this pin for PWM may impact recognition of the Holtec USB chip and may impact flashing firmware! (During my testing a small cap between pin and 3v3 saved unbricked my board). 98 | - The resolution and frequency of the actually generated signal depend on the timer of the MCU used. The supplied parameters are truncated during calculations. The precision of the generated signal depends on this integer truncation as well as on the stability of the MCU clock. A crystal clock is more stable than an RC oscillator or PLL. 99 | - The theoretical maximum frequency at full duty cycle resulution (256 levels) is clock speed divided by 256. At higher frequencies the resolution of the duty cycle gets smaller (converging to 50%). When the MCU is running at lower voltages than specified, the higher frequencies may become unstable. 100 | - On ATtiny84A/44A and ATmega328/168/8A the 16-bit Timer1 is also supported, allowing for lower frequencies and for higher PWM precision (at those lower frequencies). 101 | - At lower frequencies the prescaler is enabled, allowing for lower frequencies and for higher PWM precision (at those lower frequencies). The frequencies that use the different prescaler settings were determined through experimentation and are depending on the MCU used. 102 | - The stability (jitter) of the generated signal depends on the MCU and the selected frequency and duty cycle. In testing the ATtiny13A showed more jitter than the ATtiny85 at higher frequencies. The jitter can easily be measured using the Arduino [FreqCount](https://github.com/PaulStoffregen/FreqCount/tree/master/examples/Serial_Output) serial example an the serial plotter of the Arduino IDE. 103 | - Disabling PWM may not work for all pins. Toggle-only pins were found to have issues. For the ATmega328P (Uno/Nano/Pro-mini), ATmega168 (some Nano clones) and the LGT8F328P this was resolved. Other MCU's such as ATtiny still need to be tested. 104 | 105 | ### Credits 106 | - This library is based on information found in various sources. See the links below for references. 107 | 108 | ### Links 109 | - About regular PWM:
110 | https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/ 111 | - Extensive post about timers and using them for PWM:
112 | http://www.gammon.com.au/timers 113 | - About register manipulation to enable fast PWM:
114 | https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM \[[original with more graphics](http://www.righto.com/2009/07/secrets-of-arduino-pwm.html)\]
115 | http://www.technoblogy.com/show?LE0 - *"four PWMs on ATtiny85"*
116 | https://www.re-innovation.co.uk/docs/fast-pwm-on-attiny85/ 117 | - About limited modes on certain pins:
118 | https://electronics.stackexchange.com/questions/49401/cant-set-to-fast-pwm-ocra-mode 119 | 120 | ### Disclaimer 121 | - All code on this GitHub account, including this library is provided to you on an as-is basis without guarantees and with all liability dismissed. It may be used at your own risk. Unfortunately I have no means to provide support. 122 | -------------------------------------------------------------------------------- /FastPwmPin.cpp: -------------------------------------------------------------------------------- 1 | // FastPwmPin - Arduino library to enable fast PWM on an output pin 2 | // For documentation see https://github.com/maxint-rd/FastPwmPin 3 | // 4 | // Support for fast PWM is depending on the used MCU. 5 | // On ATtiny85 Timer1 is used for fast PWM. Timer0 is reserved for the delay() and millis() functions. 6 | // Using the internal oscillator the ATtiny85 can be set to a 1 MHz, 8 Mhz or 16 Mhz system clock. 7 | // Using the 64 MHz prescaler, the generated frequency can be higher than the system clock. 8 | // 9 | // On ATtiny85 this library only supports pin 4 and pin 1 (0 and 3 partly supported as inverted PWM) 10 | // 11 | // 12 | // 13 | // Pinout ATtiny13A 14 | // +---v---+ 15 | // (PCINT5/!RESET/ADC0/dW) PB5 --|1 8|-- VCC 16 | // (PCINT3/CLKI/ADC3) PB3 --|2 7|-- PB2 (SCK/ADC1/T0/PCINT2) 17 | // (PCINT4/ADC2) PB4 --|3 6|-- PB1 (MISO/AIN1/OC0B/INT0/PCINT1) 18 | // GND --|4 5|-- PB0 (MOSI/AIN0/OC0A/PCINT0) 19 | // +-------+ 20 | // ATtiny13A Fast PWM pins: PB0=OC0A, PB1=OC0B 21 | // 22 | 23 | // Pinout ATtiny24A/44A/84A 24 | // +---v---+ 25 | // VCC --|1 14|-- GND 26 | // (PCINT8/CLKI/XTAL1) D10/PB0 --|2 13|-- PA0/D0 (ADC0/AREF/PCINT0) 27 | // (PCINT9/XTAL2) D8/PB1 --|3 12|-- PA1/D1 (ADC1/AIN0/PCINT1) 28 | // (PCINT11/!RESET/dW) PB3 --|4 11|-- PA2/D2 (ADC2/AIN1/PCINT2) 29 | // (PCINT10/INT0/OC0A/CKOUT) D8/PB2 --|5 10|-- PA3/D3 (ADC3/T0/PCINT3) 30 | // (PCINT7/ICP/OC0B/ADC7) D7/PA7 --|6 9|-- PA4/D4 (ADC4/USCK/SCL/T1/PCINT4) 31 | // (PCINT6/OC1A/SDA/MOSI/DI/ADC6) D6/PA6 --|7 8|-- PA5/D5 (ADC5/DO/MISO/OC1B/PCINT5) 32 | // +-------+ 33 | // ATtiny24A/44A/84A PWM pins: PB2(D8)=OC0A, PA7(D7)=OC0B, PA6(D6)=OC1A, PA5(D5)=OC1B 34 | // 35 | 36 | 37 | // 38 | // Pinout ATtiny85/45/25 39 | // +---v---+ 40 | // (PCINT5/!RESET/ADC0/dW) PB5 --|1 8|-- VCC 41 | // (PCINT3/XTAL1/CLK1/!OC1B/ADC3) PB3 --|2 7|-- PB2 (SCK/USCK/SCL/ADC1/T0/INT0/PCINT2) 42 | // (PCINT4/XTAL2/CLK0/OC1B/ADC2) PB4 --|3 6|-- PB1 (MISO/DO/AIN1/OC0B/OC1A/PCINT1) 43 | // GND --|4 5|-- PB0 (MOSI/DI/SDA/AIN0/OC0A/!OC1A/AREF/PCINT0) 44 | // +-------+ 45 | // ATtiny85 Fast PWM pins: PB0 (D0)=!OC1A, PB1 (D1)=OC1A, PB3 (D4)=!OC1B, PB4 (D3)=OC1B 46 | // Note currently this library only supports Timer1 on ATtiny85, with pins 0 and 4 as inverted mode pins. 47 | // Note: Arduino pin 3 is PB4 and pin 4 is PB3 48 | // 49 | 50 | // ATmega168/328 Fast PWM pins: PD3(D3)=OC2B, PD5(D5)=OC0B, PD6(D6)=OC0A, PB1(D9)=OC1A, PB2(D10)=OC1B, PB3(D11)=OC2A, 51 | // Timers: TC0:8-bit, TC1:16-bit, TC2:8-bit 52 | 53 | // 54 | // ATmega8A Fast PWM pins: PB1(D9)=OC1A, PB2(D10)=OC1B, PB3(D11)=OC2, 55 | // Timers: TC0:8-bit without PWM, TC1:16-bit with PWM, TC2:8-bit with PWM 56 | 57 | // LGT8F328P (SSOP20) Fast PWM up to 16 MHz 58 | // pins: PC6(RST)=OC3A, PD2(D2)=OC3B, PD3(D3)=OC2B, PD5(D5)=OC0B, PD6(D6)=OC0A/OC3A, PB1(D9)=OC1A, PB2(D10)=OC1B, PB3(D11)=OC2A, PE4()=OC0A, PF1()=OC3A, PF2()=OC3B, PF3()=OC3C/OC0B, PF4()=OC1B, PF5()=OC1A, PF6()=OC2A 59 | // Timers: TC0:8-bit, TC1:16-bit, TC2:8-bit, TC3:16-bit 60 | 61 | // Support for 16-bit Timer3 on LGT8F328P: 62 | // QFP48: OC3A on #15 (PF1) is ??, OC3B on #48 (PF2) is ??, OC3C on PF3 (#4) is ??? 63 | // QFP32L: OC3A on #31 (PF1, shared PD1, alternate function on PD6 via PMX1/PMX2) is D1 (TX), OC3B on #32 (PF2, shared PD2) is D2, OC3C on PF3 is not mapped to pin 64 | // SSOP20: OC3A on #01 (PF1, shared PC6/RST), OC3B on #02 (PF2, shared PD2) is D2, OC3C on PF3 is not mapped to pin 65 | 66 | 67 | 68 | #include "FastPwmPin.h" 69 | 70 | #if defined (ARDUINO_ARCH_ESP8266) 71 | #error FastPwmPin does not support ESP8266 (yet) 72 | #endif 73 | 74 | #if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny25__) 75 | const uint16_t FastPwmPin::aPrescale1[] = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384}; // prescale settings for ATtiny85/45/25 8-bit Timer1 76 | #else 77 | const uint16_t FastPwmPin::aPrescale1[] = {0, 1, 8, 64, 256, 1024}; // prescale settings for ATmega328/168/8A/etc, 8-bit Timer0 and 16-bit Timer1 78 | #if !defined(__AVR_ATtiny13__) 79 | const uint16_t FastPwmPin::aPrescale2[] = {0, 1, 8, 32, 64, 128, 256, 1024}; // prescale settings for ATmega328/168/8A/etc, 8-bit Timer2 80 | #endif 81 | #endif 82 | 83 | uint8_t FastPwmPin::findPrescaler(unsigned long ulFrequency, uint8_t nTimer) 84 | { // Find the proper prescale setting for the desired frequency 85 | // Top value (OCR1A, OCR2A) is only valid when F_CPU/prescaler/ulFrequency <= 255 for 8-bit or 65535 for 16-bit 86 | // Method used is inspired by the PWM library of Mark Cooke: https://github.com/micooke/PWM 87 | // Note: Timer2 has more prescale values than Timer0/Timer1 88 | //unsigned long ulCnt=F_CPU/ulFrequency; 89 | uint8_t nPrescaler=1; 90 | // TODO: fix needed for ATtiny85 91 | #if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny25__) 92 | // smaller code for tiny85 (actually no real difference when using an optimizer) 93 | while(nPrescaler<(sizeof(aPrescale1)/sizeof(uint16_t)-1) && (F_CPU/ulFrequency/aPrescale1[nPrescaler] > 255L)) // Timer1 is 8-bit on ATtiny85 94 | nPrescaler++; 95 | #elif defined(__AVR_ATtiny13__) 96 | // smaller code for tiny13 (actually no real difference when using an optimizer) 97 | while(nPrescaler<(sizeof(aPrescale1)/sizeof(uint16_t)-1) && (F_CPU/ulFrequency/aPrescale1[nPrescaler] > 255L)) // timer is 8-bit on ATtiny13A 98 | nPrescaler++; 99 | #else 100 | switch(nTimer) 101 | { 102 | case 0: 103 | case 1: 104 | case 3: // LGT8F328P has an additional Timer3 which is similar to Timer1 105 | while(nPrescaler<(sizeof(aPrescale1)/sizeof(uint16_t)-1) && (F_CPU/ulFrequency/aPrescale1[nPrescaler] > (nTimer==0? 255L : 65535L))) // timer 1 is 16-bit with 64K levels 106 | nPrescaler++; 107 | break; 108 | case 2: 109 | while(nPrescaler<(sizeof(aPrescale2)/sizeof(uint16_t)-1) && (F_CPU/ulFrequency/aPrescale2[nPrescaler] > 255L)) // timer 2 is 8-bit, but has more prescaler options 110 | nPrescaler++; 111 | break; 112 | } 113 | #endif 114 | return(nPrescaler); 115 | } 116 | 117 | int FastPwmPin::enablePwmPin(const int nPreferredPin, unsigned long ulFrequency, uint8_t nPeriodPercentage) // nPreferredPin=0 118 | { // Enable FastPwm on the desired pin 119 | // Note: since not all MCU's support fast PWM on every pin, the nPreferredPin indicates preference 120 | // Depending on the MCU a different pin may actually be enabled, pin number is returned 121 | // Supported frequencies: 122 | // LGT8F328P@32MHz, : 1Hz - 8.00MHz (8000000L) 123 | // ATmega328@16MHz, ATmega168@8MHz: 1Hz - 4.00MHz (4000000L) 124 | // The period (duty-cycle) percentage 1-99%. When larger than 50% the output mode is inverting. The resolution is lower at higher frequencies. 125 | 126 | #if defined (ARDUINO_ARCH_ESP8266) 127 | if(nFrequency!=0) 128 | analogWriteFreq(ulFrequency); // Note: analogWriteFreq(0); gives a spontaneous WDT reset 129 | analogWrite(nPreferredPin, 1024/(100/nPeriodPercentage); // default range is 1024, use 0 to start quiet using pulse-width zero 130 | return(nPreferredPin); 131 | #elif defined(__AVR_ATmega168P__) || defined (__AVR_ATmega168__) || defined (__AVR_ATmega328P__) || defined (__AVR_ATmega328__) 132 | // 133 | // ATmega328/168 134 | // 135 | 136 | // ATtiny168@8MHz/3v3 can generate 31.25kHz - 4.0MHz fast PWM 137 | // ATmega328/168 (Uno/Nano/Pro Mini): pin D3 or pin D11 (D11 toggle only) 138 | 139 | // https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/ 140 | // PWM pins ATmega168P: 3, 5, 6, 9, 10, and 11. 141 | // Timer2: D3, D11 142 | // Timer1: D9, D10 143 | // Timer0: D5, D6 144 | 145 | // Timer3: LGT8F328P D1, D2 146 | 147 | // LGT8F328P PWM3 on pins 1,2 (QFP32) 148 | // NOTE: For proper support of the LGT8F328P SSOP20 use this PR: https://github.com/LaZsolt/lgt8fx 149 | // The 328D-SSOP20 defined in dbuezas/lgt8fx has no proper definition of OCR3A 150 | 151 | if((nPreferredPin!=11 && nPreferredPin!=3 && nPreferredPin!=9 && nPreferredPin!=10) || nPeriodPercentage>99 || nPeriodPercentage==0) 152 | #if defined(OCR3A) // LGT8F328P PWM3 on pins 1,2 153 | if(nPreferredPin!=1 && nPreferredPin!=2) 154 | #endif 155 | return(-1); 156 | 157 | // Pin D3: Timer2, OC2B 158 | // Pin D11: Timer2, OC2A 159 | // ATmega168 @ 8MHz: 160 | // pwm-pulse OCR2B=0x00, OCR2A=0xFF => f=31.25kHz 161 | // pwm OCR2B=0x7F, OCR2A=0xFF => f=31.25kHz 162 | // pwm OCR2B=0x07, OCR2A=0x0F => f=499kHz 163 | // pwm OCR2B=0x03, OCR2A=0x07 => f=999kHz 164 | // pwm OCR2B=0x02, OCR2A=0x03 => f=1.99MHz (25%) 165 | // pwm OCR2B=0x01, OCR2A=0x03 => f=2.00MHz (50%) 166 | // pwm OCR2B=0x01, OCR2A=0x02 => f=2.66MHz (22%-33%) 167 | // pwm OCR2B=0x00, OCR2A=0x01 => f=4.00MHz (50%) 168 | 169 | byte nPrescale=1; // 1=CK/1 (no prescaling) 170 | if(nPreferredPin==3 || nPreferredPin==11) 171 | { // TIMER0, TIMER2 have 8-bit resolution 172 | nPrescale=findPrescaler(ulFrequency, 2); 173 | if(nPreferredPin==3) 174 | TCCR2A = 1<50)<50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom, determines duty cycle, for 50%: (top+1)/2-1; thanks @cesarab for more accuracy! 190 | unsigned long ulPercentage=(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage); // using a local variables gives smaller code after optimization 191 | unsigned long ulResult = ((OCR2A + 1) * ulPercentage * 10L ) / 1000L -1; // using integer calculation instead of floating point saves 840 bytes on ATmega328, thanks @shaddyx for the suggestion! 192 | OCR2B=ulResult; 193 | } 194 | 195 | else 196 | #if defined(OCR3A) // LGT8F328P PWM3 on pins 1,2 requires definitions in updated core 197 | //#define OCR3A _SFR_MEM16(0x98) 198 | // support for 16-bit Timer3 on LGT8F328P (#1=PF1/OC3A=D1, #2=PF2/OC3B=D2, 199 | if(nPreferredPin==2 || nPreferredPin==1) 200 | { // LGT8F328P PWM3 on pins 1,2 using 16-bit Timer3, same as Timer1 201 | // Pin 2 is supported on models QFP32 and SSOP20, pin 1 only on QFP32. QFP48 was not tested (yet). 202 | nPrescale=findPrescaler(ulFrequency, 3); 203 | if(nPreferredPin==1) 204 | { 205 | // Like AVR, LGT8F328P only supports toggle-mode on OC3A 206 | ulFrequency*=2; // compensate for half frequency in toggle mode 207 | TCCR3A = 3<50)<50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR3A) 229 | unsigned long ulPercentage=(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage); // using a local variables gives smaller code after optimization 230 | unsigned long ulResult = ((OCR3A + 1) * ulPercentage * 10L ) / 1000L -1; // using integer calculation instead of floating point saves 840 bytes on ATmega328, thanks @shaddyx for the suggestion! 231 | OCR3B=ulResult; 232 | 233 | return(nPreferredPin); // return now, to avoid resetting pin to output. 234 | } 235 | else 236 | #endif 237 | { // pin 9 or 10, 16-bit Timer1, almost identical to Timer1 on ATtiny44A and ATmega8A 238 | nPrescale=findPrescaler(ulFrequency, 1); 239 | if(nPreferredPin==9) 240 | { 241 | ulFrequency*=2; // compensate for half frequency in toggle mode 242 | TCCR1A = 3<50)< TOP=Fsys/(Fpwm*Prescale) - 1 248 | OCR1A = (F_CPU/(ulFrequency*aPrescale1[nPrescale]))-1; // pwm top, F_CPU/freq -1 // pwm top, used as BOTTOM for OC0A (D0) in WGM mode 3 249 | //OCR1B = (OCR1A+1)/(100.0/(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom for pin D1, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR0A) 250 | unsigned long ulPercentage=(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage); // using a local variables gives smaller code after optimization 251 | unsigned long ulResult = ((OCR1A + 1) * ulPercentage * 10L ) / 1000L -1; // using integer calculation instead of floating point saves 840 bytes on ATmega328, thanks @shaddyx for the suggestion! 252 | OCR1B=ulResult; 253 | } 254 | pinMode(nPreferredPin,OUTPUT); // Set pin to output 255 | return(nPreferredPin); 256 | #elif defined (__AVR_ATmega8__) 257 | // 258 | // ATmega8/ATmega8A 259 | // 260 | // ATmega8 / ATmega8A has two timers with output pins: PB1(D9)=OC1A, PB2(D10)=OC1B, PB3(D11)=OC2, 261 | // Timers: TC0:8-bit without PWM, TC1:16-bit with PWM, TC2:8-bit with PWM 262 | // Prescaler for Timer2 is same as ATmega328/168 but the control register is different. Timer2 only supports 50% toggle on pin 11. 263 | // 264 | // Timer2 is quite limited. Fast PWM mode for Timer2 only supports 7 fixed frequencies. Therefor we use (toggle only) CTC mode to allow 256 frequencies without prescaler. 265 | // By activating the prescaler for frequencies below 40kHz we can reach lower frequencies. 266 | // Measured frequencies on ATmega8A @ 8Mzh using pin 11 (Timer2): 16Hz-4Mhz (toggle only) 267 | // 268 | // Timer1: 16 bit resolution. Note: seems also used by Serial in miniCore 269 | // Pin 9 appears to only support toggle mode 270 | // Measured frequencies on ATmega8A @ 8Mzh using pin 9 (Timer1): 1Hz-4Mhz (toggle only) 271 | // Measured frequencies on ATmega8A @ 8Mzh using pin 10 (Timer1): 1Hz-4Mhz (PWM resolution depends on frequency) 272 | // 273 | if((nPreferredPin!=11 && nPreferredPin!=10 && nPreferredPin!=9) || nPeriodPercentage>99 || nPeriodPercentage==0) 274 | return(-1); 275 | 276 | byte nPrescale=1; // 1=CK/1 (no prescaling) 277 | if(nPreferredPin==11) 278 | { 279 | nPrescale=findPrescaler(ulFrequency, 2); 280 | TCCR2 = 0<50)<50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR1A) 297 | unsigned long ulPercentage=(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage); // using a local variables gives smaller code after optimization 298 | unsigned long ulResult = ((OCR1A + 1) * ulPercentage * 10L ) / 1000L -1; // using integer calculation instead of floating point saves 826 bytes on ATmega8A, thanks @shaddyx for the suggestion! 299 | OCR1B=ulResult; 300 | } 301 | pinMode(nPreferredPin,OUTPUT); // Set pin to output 302 | return(nPreferredPin); 303 | #elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny25__) 304 | // 305 | // ATtiny85 306 | // 307 | // ATtiny85@1MHz can generate 4.35kHz - 16.16MHz fast PWM (Fast PLL for >250 kHz) 308 | // For frequencies below 4kHz the prescaler is set using CS00:CS02 => 1Hz - 16MHz can be generated 309 | // ATtiny85: pin D1, D4 (D0/D3 only as inverted for D1/D4, 51%-99%) 310 | if((nPreferredPin!=4 && nPreferredPin!=3 && nPreferredPin!=1 && nPreferredPin!=0) || nPeriodPercentage>99 || nPeriodPercentage==0) 311 | return(-1); 312 | // http://www.technoblogy.com/show?QVN - waveform generation 313 | // http://www.technoblogy.com/show?LE0 - four PWMs on ATtiny85 314 | // https://www.re-innovation.co.uk/docs/fast-pwm-on-attiny85/ 315 | 316 | // Tested with 317 | // ATtiny85 @ 16MHz (PLL) - ATTinyCore 1.33 - Option Timer 1 Clock: 64MHz/32MHz/CPU. Pin 3: 4Hz - 4MHz. 318 | // ATtiny85 @ 8MHz (Internal) - ATTinyCore 1.33 - Option Timer 1 Clock: 64MHz/32MHz/CPU. Pin 3: 2Hz - 4MHz 319 | // ATtiny85 @ 1MHz (Internal) - ATTinyCore 1.33 - Option Timer 1 Clock: 64MHz/32MHz/CPU. Pin 3: 1Hz - 4MHz. Pin 4: 1Hz - 4MHz, 50% at F<250KHz 25%PWM<50% 320 | byte nPrescale=findPrescaler(ulFrequency, 1); // ATtiny85 has 16 prescalers, 1=CK/1 (no prescaling), 15=CK/16384 321 | 322 | if(nPreferredPin==4 || nPreferredPin==3) 323 | { 324 | TCCR1 = 0<50)<50) 333 | TCCR1 |= 3<50)< f=252kHz / s=3.94KHz 351 | // pwm OCR0B=0x3F, OCR0C=0x7F => f=504kHz 352 | // pwm OCR0B=0x1F, OCR0C=0x3F => f=1.01MHz 353 | // pwm OCR0B=0x0F, OCR0C=0x1F => f=2.02MHz 354 | // pwm OCR0B=0x07, OCR0C=0x0F => f=4.04MHz (@33%-50%) 355 | // pwm OCR0B=0x03, OCR0C=0x07 => f=8.08MHz (@33%) 356 | // pwm OCR0B=0x01, OCR0C=0x03 => f=16.17MHz (@??) / s=252kHz (@33%) 357 | // pwm OCR0B=0x02, OCR0C=0x03 => f=16.17MHz (@??) / s=252kHz (@50%) 358 | // pwm OCR0B=0x01, OCR0C=0x02 => f=21.55MHz (multimeter reading) 359 | // pwm OCR0B=0x00, OCR0C=0x01 => NO PWM! 360 | OCR1B = 0x07; 361 | OCR1C = 0x0F; // pwm top, determines duty cycle (should be below top in OCR1B) 0x02 362 | */ 363 | if(ulFrequency>=250000L && nPrescale==1) 364 | { 365 | #define FASTPWMPIN_TINY85PLL 64000000L 366 | OCR1C = (FASTPWMPIN_TINY85PLL/ulFrequency)-1; // pwm top, F_CPU/freq -1 // pwm top 367 | 368 | // PLLCSR= 1<50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom for pin D4, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR1C) 379 | //if(nPreferredPin==1 || nPreferredPin==0) OCR1A = (OCR1C+1)/(100.0/(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom for pin D1, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR1C) 380 | unsigned long ulPercentage=(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage); // using a local variables gives smaller code after optimization 381 | unsigned long ulResult = ((OCR1C + 1) * ulPercentage * 10L ) / 1000L -1; // using integer calculation instead of floating point saves 866 bytes on ATtiny85, thanks @shaddyx for the suggestion! 382 | if(nPreferredPin==4 || nPreferredPin==3) OCR1B = ulResult; // pwm bottom for pin D4, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR1C) 383 | if(nPreferredPin==1 || nPreferredPin==0) OCR1A = ulResult; // pwm bottom for pin D1, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR1C) 384 | pinMode(nPreferredPin,OUTPUT); // Set pin to output 385 | return(nPreferredPin); 386 | #elif defined(__AVR_ATtiny13__) 387 | // 388 | // ATtiny13 389 | // 390 | // ATtiny13@9.6MHz/3v3 can generate 39.5kHz - 1.6MHz fast PWM (higher becomes unstable, 5V not tested ok) 391 | // The prescaler is set using CS00:CS02 for lower frequencies 392 | if((nPreferredPin!=0 && nPreferredPin!=1) || nPeriodPercentage>99 || nPeriodPercentage==0) 393 | return(-1); 394 | 395 | byte nPrescale=1; // 1=CK/1 (no prescaling) 396 | nPrescale=findPrescaler(ulFrequency, 0); 397 | if(nPreferredPin==0) // TODO: only pin==1 seems to work ok 398 | { 399 | //TCCR0A = ((1<50) << COM0A0)) ; // Fast PWM mode 7, Clear OC0A on Compare Match, set OC0A at TOP (non inverting) 400 | // inverting/non-inverting modes seem not working on pin 0 in fast mode! 401 | // See also https://electronics.stackexchange.com/questions/49401/cant-set-to-fast-pwm-ocra-mode 402 | ulFrequency*=2; // compensate for half frequency in toggle mode 403 | TCCR0A = 3<50) << COM0B0)) ; // Fast PWM mode 7, Clear OC0B on Compare Match, set OC0B at TOP (non inverting) 407 | TCCR0B = ((1< f=73.5KHz 410 | // pwm OCR0A=0x0F, OCR0B=0x05 => f=598khz (+/-1kHz) 411 | // pwm OCR0A=0x0D, OCR0B=0x05 => f=686kHz 412 | // pwm OCR0A=0x0A, OCR0B=0x05 => f=877kHz 413 | // pwm OCR0A=0x0A, OCR0B=0x08 => f=877.0kHz (+0.6kHz) 414 | // pwm OCR0A=0x09, OCR0B=0x04 => f=967kHz (+/-2kHz) 415 | // pwm OCR0A=0x08, OCR0B=0x01 => f=1.074MHz (+/-3kHz) 416 | // pwm OCR0A=0x06, OCR0B=0x00 => f=1.395MHz (+/-2kHz) 417 | // pwm OCR0A=0x05, OCR0B=0x04 => f=1.635MHz (+/-3kHz) 418 | // pwm OCR0A=0x05, OCR0B=0x00 => f=1.635MHz 419 | // pwm OCR0A=0x04, OCR0B=0x00 => f=1.975MHz (+/-2kHz) 420 | // pwm OCR0A=0x04, OCR0B=0x03 => f=1.979MHz (+/-2kHz) 421 | // pwm OCR0A=0x03, OCR0B=0x02 => f=2.50MHz (+/-7kHz) 422 | // pwm OCR0A=0x02, OCR0B=0x01 => f=3.39MHz 423 | // pwm OCR0A=0x02, OCR0B=0x00 => f=3.37MHz (+/-10kHz) 424 | // pwm OCR0A=0x01, OCR0B=0x00 => f=5.10MHz (+/-7kHz) 425 | OCR0A = (F_CPU/(ulFrequency*aPrescale1[nPrescale]))-1; // pwm top, used as BOTTOM for OC0A (D0) in WGM mode 3, F_CPU/freq -1 426 | //OCR0B = (OCR0A+1)/(100.0/(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom for pin D1, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR1A) 427 | unsigned long ulPercentage=(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage); // using a local variables gives smaller code after optimization 428 | unsigned long ulResult = ((OCR0A + 1) * ulPercentage * 10L ) / 1000L -1; // using integer calculation instead of floating point saves 870 bytes on ATtiny13A 429 | OCR0B=ulResult; 430 | 431 | 432 | DDRB |= (1 << nPreferredPin); // pinMode(nPreferredPin,OUTPUT); // Set pin to output 433 | return(nPreferredPin); 434 | #elif (defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)) 435 | // 436 | // ATtiny24/24A/44/44A/84/84A 437 | // 438 | // ATtiny24A/44A/84A PWM pins: PB2(D8)=OC0A, PA7(D7)=OC0B, PA6(D6)=OC1A, PA5(D5)=OC1B 439 | // TC0: 8-bit. Note that Timer0 is often used for delay(). 440 | // TC1: supports 16-bit precision on PA6/D6 (OC1A) and PA5/D5 (OC1B) 441 | // Tested frequencies @8MHz on pin 5: 1 Hz - 4 MHz (+10%) 442 | // Tested frequencies @8MHz on pin 6: 1 Hz - 4 MHz (+10%) 443 | // Tested frequencies @8MHz on pin 7: 32 Hz - 4 MHz (+10%) 444 | // Tested frequencies @8MHz on pin 8: 16 Hz - 4 MHz (+10%) 445 | // Pins 6 and 8 support toggle only 446 | if(nPreferredPin<5 || nPreferredPin>8 || nPeriodPercentage>99 || nPeriodPercentage==0) 447 | return(-1); 448 | byte nPrescale=1; // 1=CK/1 (no prescaling) 449 | if(nPreferredPin==8 || nPreferredPin==7) 450 | { // 8-bit Timer0 451 | nPrescale=findPrescaler(ulFrequency, 0); 452 | if(nPreferredPin==8) 453 | { 454 | // Like the ATtiny13A, only toggle mode seems to work on OC0A (PB2/D8) 455 | // See also https://electronics.stackexchange.com/questions/49401/cant-set-to-fast-pwm-ocra-mode 456 | ulFrequency*=2; // compensate for half frequency in toggle mode 457 | TCCR0A = 3<50)<50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom for pin D1, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR0A) 464 | unsigned long ulPercentage=(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage); // using a local variables gives smaller code after optimization 465 | unsigned long ulResult = ((OCR0A + 1) * ulPercentage * 10L ) / 1000L -1; // using integer calculation instead of floating point saves 750 bytes on ATtiny44A, thanks @shaddyx for the suggestion! 466 | OCR0B=ulResult; 467 | } 468 | else 469 | { // 16-bit Timer1 470 | nPrescale=findPrescaler(ulFrequency, 1); 471 | if(nPreferredPin==6) 472 | { 473 | ulFrequency*=2; // compensate for half frequency in toggle mode 474 | TCCR1A = 3<50)<50?(100-nPeriodPercentage):nPeriodPercentage))-1; // pwm bottom, determines duty cycle, for 50%: (top+1)/2-1 (should be below top in OCR1A) 487 | //Serial.print(", Floated: "); 488 | //Serial.print(OCR1B); 489 | //if(nPeriodPercentage>50) nPeriodPercentage=100-nPeriodPercentage; 490 | //OCR1B=((OCR1A + 1) * (nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage) * 10L ) / 1000L -1; 491 | unsigned long ulPercentage=(nPeriodPercentage>50?(100-nPeriodPercentage):nPeriodPercentage); // using a local variables gives smaller code after optimization 492 | unsigned long ulResult = ((OCR1A + 1) * ulPercentage * 10L ) / 1000L -1; 493 | OCR1B=ulResult; 494 | //Serial.print(", OCR1B: "); 495 | //Serial.println(OCR1B); 496 | } 497 | pinMode(nPreferredPin, OUTPUT); 498 | return(nPreferredPin); 499 | #else 500 | #error Unsupported MCU for FastPwmPin 501 | // 502 | // Unknown! 503 | // 504 | /* 505 | // https://github.com/allenhuffman/MusicSequencerTest/blob/master/MusicSequencerTest.ino 506 | 507 | // Notes about using an Arduino pin to generate the 4Mhz pulse: 508 | // For the Teensy 2.0, this is how to make a pin act as a 4MHz pulse. 509 | // I am using this on my Teensy 2.0 hardware for testing. This can be 510 | // done on other Arduino models, too, but I have only been using my 511 | // Teensy for this so far. My original NANO prototype is using an 512 | // external crystal. 513 | 514 | //pinMode(14, OUTPUT); 515 | // Turn on toggle pin mode. 516 | //TCCR1A |= ((1< 587 | 588 | // The millis counter is based on the watchdog timer, and takes very little processing time and power. 589 | // If 16 ms accuracy is enough, I strongly recommend you to use millis() instead of micros(). 590 | volatile uint32_t wdt_interrupt_counter = 0; 591 | 592 | // This ISR will execute every 16 ms, and increase 593 | ISR(WDT_vect) 594 | { 595 | wdt_interrupt_counter++; 596 | } 597 | 598 | bool _fWdtInit=false; 599 | void wdt_init() 600 | { 601 | /* 602 | if(_fWdtInit) 603 | return; 604 | */ 605 | _fWdtInit=true; 606 | 607 | // Enable WDT interrupt and enable global interrupts 608 | cli(); // Disable global interrupts 609 | wdt_reset(); // Reset watchdog 610 | WDTCSR = _BV(WDIE); // Set up WDT interrupt with 16 ms prescaler (Note: different names than ATtiny13A) 611 | sei(); // Enable global interrupts 612 | } 613 | 614 | // Since the WDT counter counts every 16th ms, we'll need to multiply to get the correct millis value. 615 | // The WDT uses it's own clock, so this function is valid for all F_CPUs. 616 | // Note: According the ATtiny44A datasheet the Watchdog oscillator frequency is about 118 kHz (at 5V/25`C) 617 | // According the ATtiny13A datasheet that Watchdog oscillator frequency is about 111 kHz (at 5V/25`C) 618 | // Using prescaler WDP0[0:3] of zero the WDT times out at 2K cycles or 16ms (for both ATtiny13A and ATtiny44). 619 | // Therefor a multiplier of 16 was used in Micro Core for the ATtiny13A. For ATtiny44A 19 was measured to be more accurate. 620 | uint32_t wdt_millis() 621 | { 622 | if(!_fWdtInit) 623 | wdt_init(); 624 | return wdt_interrupt_counter * 19; 625 | } 626 | 627 | void wdt_delay(uint16_t ms) 628 | { 629 | unsigned long msstart=wdt_millis(); 630 | while(wdt_millis()