Skip to content
This repository has been archived by the owner on Feb 4, 2023. It is now read-only.
/ ATtiny_PWM Public archive

This library enables you to use Hardware-based PWM channels on Arduino AVR ATtiny-based boards (ATtiny3217, etc.), using megaTinyCore, to create and output PWM to pins. Using the same functions as other FastPWM libraries to enable you to port PWM code easily between platforms

License

Notifications You must be signed in to change notification settings

khoih-prog/ATtiny_PWM

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ATtiny_PWM Library

arduino-library-badge GitHub release GitHub contributions welcome GitHub issues

Donate to my libraries using BuyMeACoffee



Table of Contents



Important Note for Arduino IDE

With some Arduino IDE versions, such as v1.8.19, upload directly via USB to some boards, such as AVR_CuriosityNano3217 can't be done without unknown-to-me fix. We'll get the following error when uploading

avrdude: Version 6.3-20201216
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "/home/kh/.arduino15/packages/megaTinyCore/hardware/megaavr/2.5.11/avrdude.conf"
         User configuration file is "/home/kh/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping

         Using Port                    : usb
         Using Programmer              : curiosity_updi
avrdude: usbdev_open(): Found nEDBG CMSIS-DAP, serno: MCHP3333021800000998
avrdude: usbdev_open(): WARNING: failed to set configuration 1: Device or resource busy
avrdude: Found CMSIS-DAP compliant device, using EDBG protocol
avrdude: usbdev_send(): wrote -5 out of 912 bytes, err = Input/output error
avrdude: jtag3_edbg_prepare(): failed to send command to serial port

avrdude done.  Thank you.

the selected serial port 
 does not exist or your board is not connected

We can use drag-and-drop method to drag-and-drop the compiled hex file to CURIOSITY virtual drive.

If success, The LED blinks slowly for 2 sec, or rapidly for 2 sec if failure

For example, to run Change_Interval example, use Arduino IDE to compile, and get the Change_Interval.ino.hex file. For Ubuntu Linux, the file is stored in directory /tmp/arduino_build_xxxxxx

After drag-and-drop the Change_Interval.ino.hex into CURIOSITY virtual drive, the code will run immediately if successfully loaded (LED blinks slowly)



Why do we need this ATtiny_PWM library

Features

This hardware-based PWM library, a wrapper and enhancement around megaTinyCore analogWrite() code, enables you to use Hardware-PWM on Arduino AVR ATtiny-based boards (ATtiny3217, etc.) using megaTinyCore, to create and output PWM. These purely hardware-based PWM channels can generate very high PWM frequencies, depending on CPU clock and acceptable accuracy. The maximum and default resolution is 8-bit resolution.

This library is using the same or similar functions as other FastPWM libraries, as follows, to enable you to port your PWM code easily between platforms

  1. RP2040_PWM
  2. AVR_PWM
  3. megaAVR_PWM
  4. ESP32_FastPWM
  5. SAMD_PWM
  6. SAMDUE_PWM
  7. nRF52_PWM
  8. Teensy_PWM
  9. ATtiny_PWM
  10. Dx_PWM
  11. Portenta_H7_PWM
  12. MBED_RP2040_PWM
  13. nRF52_MBED_PWM
  14. STM32_PWM

The most important feature is they're purely hardware-based PWM channels. Therefore, their operations are not blocked by bad-behaving software functions / tasks.

This important feature is absolutely necessary for mission-critical tasks. These hardware PWM-channels, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to control external systems (Servo, etc.) requiring better accuracy.

New efficient setPWM_manual() function enables waveform creation using PWM.

The PWM_Multi example will demonstrate the usage of multichannel PWM using multiple Hardware-PWM blocks (Timer & Channel). The 2 independent Hardware-PWM channels are used to control 2 different PWM outputs, with totally independent frequencies and dutycycles on ATtiny-based boards.

Being hardware-based PWM, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or Blynk services.

This non-being-blocked important feature is absolutely necessary for mission-critical tasks.


Why using hardware-based PWM is better

Imagine you have a system with a mission-critical function, controlling a robot or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is blocking the loop() or setup().

So your function might not be executed, and the result would be disastrous.

You'd prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.).

The correct choice is to use hardware-based PWM.

These hardware-based PWM channels 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 PWMs, using millis() or micros().

Functions using normal software-based PWMs, relying on loop() and calling millis(), won't work if the loop() or setup() is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services.


Currently supported Boards

  • tinyAVR boards using megaTinyCore
Curiosity Nano ATtiny3217



Prerequisites

  1. Arduino IDE 1.8.19 for Arduino. GitHub release
  2. SpenceKonde megaTinyCore core 2.6.4 for Arduino ATtiny boards. GitHub release. Follow megaTinyCore Installation.


Installation

Use Arduino Library Manager

The best and easiest way is to use Arduino Library Manager. Search for ATtiny_PWM, then select / install the latest version. You can also use this link arduino-library-badge for more detailed instructions.

Manual Install

Another way to install is to:

  1. Navigate to ATtiny_PWM page.
  2. Download the latest release ATtiny_PWM-main.zip.
  3. Extract the zip file to ATtiny_PWM-main directory
  4. Copy whole ATtiny_PWM-main folder to Arduino libraries' directory such as ~/Arduino/libraries/.

VS Code & PlatformIO

  1. Install VS Code
  2. Install PlatformIO
  3. Install ATtiny_PWM library by using Library Manager. Search for ATtiny_PWM in Platform.io Author's Libraries
  4. Use included platformio.ini file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at Project Configuration File


More useful Information

1. Documents

  1. Arduino 101: Timers and Interrupts
  2. Getting Started with Timer/Counter Type B (TCB)
  3. megaTinyCore README.md
  4. ATtiny3217 Curiosity Nano Hardware User Guide
  5. AVR128DA48-Curiosity-Nano-Hardware-User Guide
  6. AVR128DB48-Curiosity-Nano-Hardware-User Guide

2. Timer TCB0-TCB1

TCB0-TCB1 are 16-bit timers

The ATtiny boards, such as ATtiny3217, ATtiny1617, will have only maximum 2 TCB timers, (TCB0-TCB1).

The ATtiny boards, such as ATtiny817, will have only maximum 1 TCB timer, (TCB0).

The number of TCB timers will be automatically configured by the library.

The following is the partial list of number of TCBs for each ATtiny board/chip

TCB0-TCB1, TCA, TCD

ATtiny3217, ATtiny1617, ATtiny3216, ATtiny1616, ATtiny1614

TCB0, TCA, TCD

ATtinyx12, ATtinyx14, ATtinyx16, ATtinyx17, such as ATtiny817, ATtiny417, ATtiny816, etc.

TCB0, TCA, no TCD

ATtinyx02, ATtinyx04, ATtinyx06, ATtinyx07, such as ATtiny1607, ATtiny807, ATtiny1606, etc.



Usage

Before using any PWM Timer and channel, you have to make sure the Timer and channel has not been used by any other purpose.

1. Create PWM Instance with Pin, Frequency, dutycycle

ATtiny_PWM* PWM_Instance;

PWM_Instance = new ATtiny_PWM(pinToUse, frequency, dutyCycle, channel, PWM_resolution);

2. Initialize PWM Instance

if (PWM_Instance)
{
  PWM_Instance->setPWM();
}

3. Set or change PWM frequency or dutyCycle

To use float new_dutyCycle

PWM_Instance->setPWM(PWM_Pins, new_frequency, new_dutyCycle);

such as

dutyCycle = 10.0f;
  
Serial.print(F("Change PWM DutyCycle to ")); Serial.println(dutyCycle);
PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);

To use uint32_t new_dutyCycle = (real_dutyCycle * 65536) / 100

PWM_Instance->setPWM_Int(PWM_Pins, new_frequency, new_dutyCycle);

such as for real_dutyCycle = 50%

// 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);

for real_dutyCycle = 50%

// 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);

4. Set or change PWM frequency and dutyCycle manually and efficiently in waveform creation

Function prototype

bool setPWM_manual(const uint8_t& pin, const uint16_t& DCValue);
bool setPWM_DCPercentage_manual(const uint8_t& pin, const float& DCPercentage);
bool setPWM_DCPercentageInt_manual(const uint8_t& pin, const uint16_t& DCPercentage);

Need to call only once for each pin

PWM_Instance->setPWM(PWM_Pins, frequency, dutyCycle);

after that, if just changing dutyCycle / level, use the faster

// For 50.0f dutycycle
// 97465ns
new_level = 50.0f * PWM_Instance->getPWMPeriod() / 100.0f ;
PWM_Instance->setPWM_manual(PWM_Pins, new_level);

or better and much easier to use, but slowest

// v1.2.0: 129500ns. Previous v1.1.0 : 169577ns
new_DCPercentage = 50.0f;
PWM_Instance->setPWM_DCPercentage_manual(PWM_Pins, new_DCPercentage);

or the fastest

// dutycyclePercent = 0-65535 == 0-100%
// Faster, 94197ns
dutycyclePercentInt = MAX_16BIT / 2;   // 50%
PWM_Instance->setPWM_DCPercentageInt_manual(pinToUse, dutycyclePercentInt);


Examples:

  1. PWM_Basic
  2. PWM_DynamicDutyCycle
  3. PWM_DynamicDutyCycle_Int
  4. PWM_DynamicFreq
  5. PWM_Multi
  6. PWM_MultiChannel
  7. PWM_Waveform
  8. PWM_StepperControl New
  9. PWM_manual New
  10. PWM_SpeedTest New


Example PWM_Multi

#define _PWM_LOGLEVEL_ 4
// Select false to use PWM
#define USING_TIMER false //true
#include "ATtiny_PWM.h"
/*
const uint8_t digital_pin_to_timer[] = {
// Left side, top to bottom
TIMERA0, // 0 PA4 WO4 WOA
TIMERA0, // 1 PA5 WO5 WOB
#if defined(DAC0)
DACOUT, // 2 PA6
#else
NOT_ON_TIMER, // 2 PA6
#endif
NOT_ON_TIMER, // 3 PA7
NOT_ON_TIMER, // 4 PB7
NOT_ON_TIMER, // 5 PB6
NOT_ON_TIMER, // 6 PB5 WO2 Alt
NOT_ON_TIMER, // 7 PB4 WO1 Alt
NOT_ON_TIMER, // 8 PB3 WO0 Alt
TIMERA0, // 9 PB2 WO2
TIMERA0, // 10 PB1 WO1
// Right side, bottom to top
TIMERA0, // 11 PB0 WO0
#if (defined(TCD0) && defined(USE_TIMERD0_PWM))
TIMERD0, // 12 PC0 WOC
TIMERD0, // 13 PC1 WOD
#else
NOT_ON_TIMER, // 12 PC0
NOT_ON_TIMER, // 13 PC1
#endif
NOT_ON_TIMER, // 14 PC2
NOT_ON_TIMER, // 15 PC3 WO3 Alt
NOT_ON_TIMER, // 16 PC4 WO4 Alt
NOT_ON_TIMER, // 17 PC5 WO5 Alt
NOT_ON_TIMER, // 18 PA1
NOT_ON_TIMER, // 19 PA2
TIMERA0, // 20 PA3 WO3
NOT_ON_TIMER // 21 PA0
};
*/
// OK, only PIN_PA4-5:TCA0
// PIN_PC0-1: TCD0 => not OK yet for frequency. Fixed @ preprogrammed 1.2KHz
// Not OK, PIN_PA6, 7, PIN_PB0-2:TCA0
// To select correct pins for different frequencies
uint32_t PWM_Pins[] = { PIN_PA4, PIN_PC0 };
float frequency[] = { 2000.0f, 8000.0f };
float dutyCycle[] = { 30.0f, 90.0f };
#define NUM_OF_PINS ( sizeof(PWM_Pins) / sizeof(uint32_t) )
ATtiny_PWM* PWM_Instance[NUM_OF_PINS];
char dashLine[] = "=====================================================================================";
void printPWMInfo(ATtiny_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 && millis() < 5000);
delay(500);
Serial.print(F("\nStarting PWM_Multi on "));
Serial.println(BOARD_NAME);
Serial.println(AT_TINY_PWM_VERSION);
for (uint8_t index = 0; index < NUM_OF_PINS; index )
{
PWM_Instance[index] = new ATtiny_PWM(PWM_Pins[index], frequency[index], 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(frequency[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);
}



Debug Terminal Output Samples

1. PWM_DynamicDutyCycle on AVR_ATtiny3217

The following is the sample terminal output when running example PWM_DynamicDutyCycle on AVR_ATtiny3217, to demonstrate the ability to provide high PWM frequencies and ability to change DutyCycle on-the-fly

Starting PWM_DynamicDutyCycle on AVR_ATtiny3217
ATtiny_PWM v1.2.0
[PWM] ATtiny_PWM: freq = 5000.00
[PWM] ATtiny_PWM: _dutycycle = 0
=====================================================================================
Change PWM DutyCycle to 90.00
[PWM] setPWM: _dutycycle = 58982 , frequency = 5000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 200 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 62 , _actualFrequency = 5040
[PWM] setPWM_Int: TIMERA0, _dutycycle = 58982 , dutycycle = 56
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 56 , actual DC% = 90.48
=====================================================================================
Actual data: pin = 1, PWM DC = 90.00, PWMPeriod = 200.00, PWM Freq (Hz) = 5000.0000
=====================================================================================
Change PWM DutyCycle to 20.00
[PWM] setPWM: _dutycycle = 13107 , frequency = 5000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 200 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 62 , _actualFrequency = 5040
[PWM] setPWM_Int: TIMERA0, _dutycycle = 13107 , dutycycle = 12
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 12 , actual DC% = 20.63
=====================================================================================
Actual data: pin = 1, PWM DC = 20.00, PWMPeriod = 200.00, PWM Freq (Hz) = 5000.0000
=====================================================================================
Change PWM DutyCycle to 90.00
[PWM] setPWM: _dutycycle = 58982 , frequency = 5000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 200 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 62 , _actualFrequency = 5040
[PWM] setPWM_Int: TIMERA0, _dutycycle = 58982 , dutycycle = 56
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 56 , actual DC% = 90.48
=====================================================================================
Actual data: pin = 1, PWM DC = 90.00, PWMPeriod = 200.00, PWM Freq (Hz) = 5000.0000
=====================================================================================

2. PWM_Multi on AVR_ATtiny3217

The following is the sample terminal output when running example PWM_Multi on TAVR_ATtiny3217, to demonstrate the ability to provide high PWM frequencies on multiple PWM-capable pins

Starting PWM_Multi on AVR_ATtiny3217
ATtiny_PWM v1.2.0
[PWM] ATtiny_PWM: freq = 2000.00 , _dutycycle = 19660
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 0 dutycycle = 47 , actual DC% = 30.57
[PWM] ATtiny_PWM: freq = 8000.00 , _dutycycle = 58981
[PWM] setPWM_Int: TIMERD0, _dutycycle = 58981 , dutycycle = 229
[PWM] setPWM_Int 3: TCD0 pin = 12 dutycycle = 229
=====================================================================================
Index	Pin	PWM_freq	DutyCycle	Actual Freq
=====================================================================================
0	0	2000.00		30.00		2000.0000
1	12	8000.00		90.00		8000.0000
=====================================================================================
Actual data: pin = 0, PWM DC = 30.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=====================================================================================
=====================================================================================
Actual data: pin = 12, PWM DC = 90.00, PWMPeriod = 125.00, PWM Freq (Hz) = 8000.0000
=====================================================================================

3. PWM_DynamicFreq on AVR_ATtiny3217

The following is the sample terminal output when running example PWM_DynamicFreq on AVR_ATtiny3217, to demonstrate the ability to change dynamically PWM frequencies

Starting PWM_DynamicFreq on AVR_ATtiny3217
ATtiny_PWM v1.2.0
[PWM] ATtiny_PWM: freq = 10000.00
[PWM] ATtiny_PWM: _dutycycle = 32767
=====================================================================================
Change PWM Freq to 20000.00
[PWM] setPWM: _dutycycle = 32768 , frequency = 20000.00
[PWM] setPWM_Int: new freq = 20000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 50 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 15 , _actualFrequency = 20833
[PWM] setPWM_Int: TIMERA0, _dutycycle = 32768 , dutycycle = 8
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 8 , actual DC% = 56.25
=====================================================================================
Actual data: pin = 1, PWM DC = 50.00, PWMPeriod = 50.00, PWM Freq (Hz) = 20000.0000
=====================================================================================
Change PWM Freq to 10000.00
[PWM] setPWM: _dutycycle = 32768 , frequency = 10000.00
[PWM] setPWM_Int: new freq = 10000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 100 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 31 , _actualFrequency = 10080
[PWM] setPWM_Int: TIMERA0, _dutycycle = 32768 , dutycycle = 16
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 16 , actual DC% = 53.13
=====================================================================================
Actual data: pin = 1, PWM DC = 50.00, PWMPeriod = 100.00, PWM Freq (Hz) = 10000.0000
=====================================================================================
Change PWM Freq to 20000.00
[PWM] setPWM: _dutycycle = 32768 , frequency = 20000.00
[PWM] setPWM_Int: new freq = 20000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 50 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 15 , _actualFrequency = 20833
[PWM] setPWM_Int: TIMERA0, _dutycycle = 32768 , dutycycle = 8
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 8 , actual DC% = 56.25
=====================================================================================
Actual data: pin = 1, PWM DC = 50.00, PWMPeriod = 50.00, PWM Freq (Hz) = 20000.0000
=====================================================================================

4. PWM_Waveform on AVR_ATtiny3217

The following is the sample terminal output when running example PWM_Waveform on AVR_ATtiny3217, to demonstrate how to use the setPWM_manual() function in wafeform creation

Starting PWM_Waveform on AVR_ATtiny3217
ATtiny_PWM v1.2.0
============================================================================================
Actual data: pin = 1, PWM DutyCycle = 0.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
============================================================================================
[PWM] setPWM_manual: _dutycycle = 0 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 3276 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 6553 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 9830 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 13107 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 16383 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 19660 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 22937 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 26214 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 29490 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 32767 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 36044 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 39321 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 42597 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 45874 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 49151 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 52428 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 55704 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 58981 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 62258 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 65535 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 62258 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 58981 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 55704 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 52428 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 49151 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 45874 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 42597 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 39321 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 36044 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 32767 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 29490 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 26214 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 22937 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 19660 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 16383 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 13107 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 9830 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 6553 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 3276 , frequency = 2000.00
[PWM] setPWM_manual: _dutycycle = 0 , frequency = 2000.00

5. PWM_manual on AVR_ATtiny3217

The following is the sample terminal output when running example PWM_manual on AVR_ATtiny3217, to demonstrate how to use the setPWM_manual() and setPWM_DCPercentage_manual() functions in wafeform creation

Starting PWM_manual on AVR_ATtiny3217
ATtiny_PWM v1.2.0
[PWM] ATtiny_PWM: freq = 2000.00 , _dutycycle = 0
[PWM] setPWM: _dutycycle = 0 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 0 , actual DC% = 0.64
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 0.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 0.00 , dc = 0.00
[PWM] setPWM_manual: _dutycycle = 0 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 0 , actual DC% = 0.64
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 0.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 5.00 , dc = 3276.75
[PWM] setPWM_manual: _dutycycle = 3276 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 7 , actual DC% = 5.10
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 5.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 10.00 , dc = 6553.50
[PWM] setPWM_manual: _dutycycle = 6553 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 15 , actual DC% = 10.19
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 10.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 15.00 , dc = 9830.25
[PWM] setPWM_manual: _dutycycle = 9830 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 23 , actual DC% = 15.29
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 15.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 20.00 , dc = 13107.00
[PWM] setPWM_manual: _dutycycle = 13107 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 31 , actual DC% = 20.38
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 20.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 25.00 , dc = 16383.75
[PWM] setPWM_manual: _dutycycle = 16383 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 39 , actual DC% = 25.48
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 25.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 30.00 , dc = 19660.50
[PWM] setPWM_manual: _dutycycle = 19660 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 47 , actual DC% = 30.57
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 30.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 35.00 , dc = 22937.25
[PWM] setPWM_manual: _dutycycle = 22937 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 55 , actual DC% = 35.67
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 35.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 40.00 , dc = 26214.00
[PWM] setPWM_manual: _dutycycle = 26214 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 63 , actual DC% = 40.76
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 40.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 45.00 , dc = 29490.75
[PWM] setPWM_manual: _dutycycle = 29490 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 70 , actual DC% = 45.22
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 45.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 50.00 , dc = 32767.50
[PWM] setPWM_manual: _dutycycle = 32767 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 78 , actual DC% = 50.32
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 50.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 55.00 , dc = 36044.25
[PWM] setPWM_manual: _dutycycle = 36044 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 86 , actual DC% = 55.41
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 55.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 60.00 , dc = 39321.00
[PWM] setPWM_manual: _dutycycle = 39321 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 94 , actual DC% = 60.51
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 60.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 65.00 , dc = 42597.75
[PWM] setPWM_manual: _dutycycle = 42597 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 101 , actual DC% = 64.97
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 65.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 70.00 , dc = 45874.50
[PWM] setPWM_manual: _dutycycle = 45874 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 109 , actual DC% = 70.06
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 70.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 75.00 , dc = 49151.25
[PWM] setPWM_manual: _dutycycle = 49151 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 117 , actual DC% = 75.16
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 75.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 80.00 , dc = 52428.00
[PWM] setPWM_manual: _dutycycle = 52428 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 125 , actual DC% = 80.25
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 80.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 85.00 , dc = 55704.75
[PWM] setPWM_manual: _dutycycle = 55704 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 133 , actual DC% = 85.35
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 85.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 90.00 , dc = 58981.50
[PWM] setPWM_manual: _dutycycle = 58981 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 141 , actual DC% = 90.45
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 90.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 95.00 , dc = 62258.25
[PWM] setPWM_manual: _dutycycle = 62258 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 149 , actual DC% = 95.54
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 95.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
[PWM] setPWM_DCPercentage_manual: DCPercentage = 100.00 , dc = 65535.00
[PWM] setPWM_manual: _dutycycle = 65535 , frequency = 2000.00
[PWM] setPeriod_TimerA0: F_CPU = 20000000 , microseconds = 500 , TCA_Freq_mult = 1.00
[PWM] setPeriod_TimerA0: pwmPeriod = 156 , _actualFrequency = 2003
[PWM] setPWM_Int: TCA0 pin = 1 dutycycle = 157 , actual DC% = 100.64
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 100.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================

6. PWM_SpeedTest on AVR_ATtiny3217

The following is the sample terminal output when running example PWM_SpeedTest on AVR_ATtiny3217, to demonstrate how to use new faster setPWM_DCPercentageInt_manual() function in wafeform creation, The time is 94197ns compared to 169577ns when using setPWM_DCPercentage_manual() function of v1.1.0. The setPWM_manual with 97465ns is to be used with pre-calculated values in array

USING_DC_PERCENT
Starting PWM_SpeedTest on AVR_ATtiny3217
ATtiny_PWM v1.2.0
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 0.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
Average time of setPWM function USING_DC_PERCENT
count=10607, ns=94277
count=10616, ns=94197
count=10616, ns=94197
count=10607, ns=94277
count=10616, ns=94197
count=10615, ns=94206
count=10607, ns=94277
count=10616, ns=94197
count=10616, ns=94197
count=10616, ns=94197
count=10607, ns=94277
not USING_DC_PERCENT
Starting PWM_SpeedTest on AVR_ATtiny3217
ATtiny_PWM v1.2.0
=================================================================================================
Actual data: pin = 1, PWM DutyCycle % = 0.00, PWMPeriod = 500.00, PWM Freq (Hz) = 2000.0000
=================================================================================================
Average time of setPWM function not USING_DC_PERCENT
count=10248, ns=97580
count=10261, ns=97456
count=10260, ns=97465
count=10252, ns=97541
count=10260, ns=97465
count=10260, ns=97465
count=10252, ns=97541
count=10260, ns=97465
count=10261, ns=97456
count=10260, ns=97465
count=10252, ns=97541
count=10260, ns=97465



Debug

Debug is enabled by default on Serial.

You can also change the debugging level _PWM_LOGLEVEL_ from 0 to 4

// Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
#define _PWM_LOGLEVEL_     0

Troubleshooting

If you get compilation errors, more often than not, you may need to install a newer version of the core for Arduino boards.

Sometimes, the library will only work if you update the board core to the latest version because I am using newly added functions.



Issues

Submit issues to: ATtiny_PWM issues



TO DO

  1. Search for bug and improvement.
  2. Support to TCD0 to change frequency

DONE

  1. Basic TCA0 hardware PWM-channels for Arduino AVR ATtiny-based boards (ATtiny3217, etc.) using megaTinyCore
  2. Add example PWM_StepperControl to demo how to control Stepper Motor using PWM
  3. Add example PWM_manual to demo how to correctly use PWM to generate waveform
  4. Add function setPWM_DCPercentage_manual() to facilitate the setting PWM DC manually by using DCPercentage, instead of absolute DCValue depending on varying PWMPeriod
  5. Catch low frequency error and use lowest permissible frequency
  6. Optimize speed with new setPWM_DCPercentageInt_manual function to improve speed almost 50% compared to setPWM_DCPercentage_manual of previous v1.1.0
  7. Add example PWM_SpeedTest to demo the better speed of new setPWM_DCPercentageInt_manual function


Contributions and Thanks

Many thanks for everyone for bug reporting, new feature suggesting, testing and contributing to the development of this library.

  1. Thanks to Paul van Dinther for proposing new way to use PWM to drive Stepper-Motor in Using PWM to step a stepper driver #16, leading to v1.0.1
dinther
Paul van Dinther


Contributing

If you want to contribute to this project:

  • Report bugs and errors
  • Ask for enhancements
  • Create issues and pull requests
  • Tell other people about this library

License

  • The library is licensed under MIT

Copyright

Copyright (c) 2022- Khoi Hoang