Skip to content
This repository has been archived by the owner on Jan 29, 2023. It is now read-only.

Commit

Permalink
v1.0.1 for up to 64 PWM channels
Browse files Browse the repository at this point in the history
### Release v1.0.1

1. Make `MAX_NUMBER_CHANNELS` configurable to max **64 PWM channels**
2. Remove debug codes possibly causing hang
3. Improve debug to use `Serialx` port automatically according to boards
  • Loading branch information
khoih-prog authored Aug 25, 2022
1 parent 0d82fe8 commit c67b7f2
Show file tree
Hide file tree
Showing 19 changed files with 209 additions and 171 deletions.
51 changes: 30 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,21 @@ After drag-and-drop the `Change_Interval.ino.hex` into `CURIOSITY` virtual drive

### Features

This library enables you to use ISR-based PWM channels on Arduino AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.), using [DxCore](https://github.com/SpenceKonde/DxCore), to create and output PWM any GPIO pin. Because this library doesn't use the powerful purely hardware-controlled PWM with many limitations, the maximum PWM frequency is currently limited at **500Hz**, which is still suitable for many real-life applications. Now you can change the PWM settings on-the-fly
This library enables you to use ISR-based PWM channels on Arduino AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.), using [DxCore](https://github.com/SpenceKonde/DxCore), to create and output PWM any GPIO pin. Because this library doesn't use the powerful purely hardware-controlled PWM with many limitations, the maximum PWM frequency is currently limited at **1000Hz**, which is still suitable for many real-life applications. Now you can change the PWM settings on-the-fly

---

This library enables you to use Interrupt from Hardware Timers on AVRDx-based boards to create and output PWM to pins. It now supports 16 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer. PWM interval can be very long (uint64_t microsecs / millisecs). The most important feature is they're ISR-based PWM channels. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware PWM channels, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software PWM using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy.
This library enables you to use Interrupt from Hardware Timers on AVRDx-based boards to create and output PWM to pins. It now supports 64 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer. PWM interval can be very long (uint64_t microsecs / millisecs). The most important feature is they're ISR-based PWM channels. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware PWM channels, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software PWM using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy.

As **Hardware Timers are rare, and very precious assets** of any board, this library now enables you to use up to **16 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer**. Timers' interval is very long (**ulong millisecs**).
As **Hardware Timers are rare, and very precious assets** of any board, this library now enables you to use up to **64 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer**. Timers' interval is very long (**ulong millisecs**).

Now with these new **16 ISR-based PWM-channels**, the maximum interval is **practically unlimited** (limited only by unsigned long miliseconds) while **the accuracy is nearly perfect** compared to software PWM channels.
Now with these new **64 ISR-based PWM-channels**, the maximum interval is **practically unlimited** (limited only by unsigned long miliseconds) while **the accuracy is nearly perfect** compared to software PWM channels.

The most important feature is they're ISR-based PWM channels. Therefore, their executions are **not blocked by bad-behaving functions / tasks**. This important feature is absolutely necessary for mission-critical tasks.

The [**ISR_8_PWMs_Array_Complex**](examples/ISR_8_PWMs_Array_Complex) example will demonstrate the nearly perfect accuracy, compared to software PWM, by printing the actual period / duty-cycle in `microsecs` of each of PWM-channels.

Being ISR-based PWM, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or Blynk services. You can also have many `(up to 16)` PWM channels to use.
Being ISR-based PWM, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or Blynk services. You can also have many `(up to 64)` PWM channels to use.

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

Expand Down Expand Up @@ -206,7 +206,7 @@ The catch is **your function is now part of an ISR (Interrupt Service Routine),
1. [`Arduino IDE 1.8.19+` for Arduino](https://github.com/arduino/Arduino). [![GitHub release](https://img.shields.io/github/release/arduino/Arduino.svg)](https://github.com/arduino/Arduino/releases/latest)
2. [`SpenceKonde DxCore core 1.4.10+`](https://github.com/SpenceKonde/DxCore) for Arduino AVRDx boards. [![GitHub release](https://img.shields.io/github/release/SpenceKonde/DxCore.svg)](https://github.com/SpenceKonde/DxCore/releases/latest). Follow [**DxCore Installation**](https://github.com/SpenceKonde/DxCore/blob/main/Installation.md).
3. To use with certain example
- [`SimpleTimer library`](https://github.com/jfturcot/SimpleTimer) for [ISR_Timers_Array_Simple](examples/ISR_Timers_Array_Simple) and [ISR_16_Timers_Array_Complex](examples/ISR_16_Timers_Array_Complex) examples.
- [`SimpleTimer library`](https://github.com/jfturcot/SimpleTimer) for [ISR_8_PWMs_Array_Simple](examples/ISR_8_PWMs_Array_Simple) and [ISR_8_PWMs_Array_Complex](examples/ISR_8_PWMs_Array_Complex) examples.


---
Expand Down Expand Up @@ -328,7 +328,7 @@ Before using any Timer, you have to make sure the Timer has not been used by any
#error You must select one Timer
#endif
// Init Dx_Slow_PWM, each can service 16 different ISR-based PWM channels
// Init Dx_Slow_PWM, each can service 64 different ISR-based PWM channels
Dx_Slow_PWM ISR_PWM;
```

Expand Down Expand Up @@ -389,7 +389,7 @@ The following is the sample terminal output when running example [ISR_8_PWMs_Arr

```
Starting ISR_8_PWMs_Array_Complex on AVR128DB
Dx_Slow_PWM v1.0.0
Dx_Slow_PWM v1.0.1
CPU Frequency = 24 MHz
TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy
Starting ITimer1 OK, micros() = 13691
Expand Down Expand Up @@ -431,7 +431,7 @@ The following is the sample terminal output when running example [**ISR_8_PWMs_A

```
Starting ISR_8_PWMs_Array on AVR128DB
Dx_Slow_PWM v1.0.0
Dx_Slow_PWM v1.0.1
CPU Frequency = 24 MHz
TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy
Starting ITimer1 OK, micros() = 12894
Expand All @@ -445,7 +445,7 @@ The following is the sample terminal output when running example [**ISR_8_PWMs_A

```
Starting ISR_8_PWMs_Array_Simple on AVR128DB
Dx_Slow_PWM v1.0.0
Dx_Slow_PWM v1.0.1
CPU Frequency = 24 MHz
TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy
Starting ITimer1 OK, micros() = 14169
Expand All @@ -459,7 +459,7 @@ The following is the sample terminal output when running example [ISR_Modify_PWM

```
Starting ISR_Modify_PWM on AVR128DB
Dx_Slow_PWM v1.0.0
Dx_Slow_PWM v1.0.1
CPU Frequency = 24 MHz
TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy
Starting ITimer1 OK, micros() = 12823
Expand All @@ -474,7 +474,7 @@ The following is the sample terminal output when running example [ISR_Changing_P

```
Starting ISR_Changing_PWM on AVR128DB
Dx_Slow_PWM v1.0.0
Dx_Slow_PWM v1.0.1
CPU Frequency = 24 MHz
TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy
Starting ITimer1 OK, micros() = 12998
Expand Down Expand Up @@ -526,14 +526,18 @@ Submit issues to: [Dx_Slow_PWM issues](https://github.com/khoih-prog/Dx_Slow_PWM

## DONE

1. Basic hardware multi-channel PWM for **AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore**
2. Add Table of Contents
3. Add functions to modify PWM settings on-the-fly
4. Fix `multiple-definitions` linker error
5. Optimize library code by using `reference-passing` instead of `value-passing`
6. Improve accuracy by using `float`, instead of `uint32_t` for `dutycycle`
7. DutyCycle to be optionally updated at the end current PWM period instead of immediately.
8. Display informational warning only when `_PWM_LOGLEVEL_` > 3
1. Basic hardware multi-channel PWM for **AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore**
2. Add Table of Contents
3. Add functions to modify PWM settings on-the-fly
4. Fix `multiple-definitions` linker error
5. Optimize library code by using `reference-passing` instead of `value-passing`
6. Improve accuracy by using `float`, instead of `uint32_t` for `dutycycle`
7. DutyCycle to be optionally updated at the end current PWM period instead of immediately.
8. Display informational warning only when `_PWM_LOGLEVEL_` > 3
9 Make `MAX_NUMBER_CHANNELS` configurable to max **64 PWM channels**
10. Remove debug codes possibly causing hang
11. Improve debug to use `Serialx` port automatically according to boards.


---
---
Expand All @@ -543,7 +547,12 @@ Submit issues to: [Dx_Slow_PWM issues](https://github.com/khoih-prog/Dx_Slow_PWM
Many thanks for everyone for bug reporting, new feature suggesting, testing and contributing to the development of this library. Especially to these people who have directly or indirectly contributed to this [Dx_TimerInterrupt library](https://github.com/khoih-prog/Dx_TimerInterrupt)

1. Thanks to good work of [Spence Konde (aka Dr. Azzy)](https://github.com/SpenceKonde) for the [DxCore](https://github.com/SpenceKonde/DxCore) and [megaTinyCore](https://github.com/SpenceKonde/megaTinyCore)
2. Thanks to [LaurentR59](https://github.com/LaurentR59) to request the enhancement [Support for DX CORE CPU and MightyCORE CPU possible? #8](https://github.com/khoih-prog/TimerInterrupt_Generic/issues/8) leading to this new library
2. Thanks to [LaurentR59](https://github.com/LaurentR59) to request

- the enhancement [Support for DX CORE CPU and MightyCORE CPU possible? #8](https://github.com/khoih-prog/TimerInterrupt_Generic/issues/8) leading to this new library
- the enhancement [PWM to drive over 16 channels #1](https://github.com/khoih-prog/Dx_Slow_PWM/issues/1) leading to new v1.0.1



<table>
<tr>
Expand Down
7 changes: 7 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

* [Changelog](#changelog)

* [Release v1.0.1](#release-v101)
* [Initial Release v1.0.0](#initial-release-v100)

---
Expand All @@ -18,6 +19,12 @@
## Changelog


### Release v1.0.1

1. Make `MAX_NUMBER_CHANNELS` configurable to max **64 PWM channels**
2. Remove debug codes possibly causing hang
3. Improve debug to use `Serialx` port automatically according to boards

### Initial Release v1.0.0

1. Initial release to support Arduino **AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore**
Expand Down
28 changes: 17 additions & 11 deletions examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,21 @@
// These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h"
// _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_ 3
#define _PWM_LOGLEVEL_ 1

#if defined(__AVR_AVR128DA48__)
#define SerialDebug Serial1
#elif defined(__AVR_AVR128DB48__)
#define SerialDebug Serial3
#else
// standard Serial
#define SerialDebug Serial
#endif

#define PWM_GENERIC_DEBUG_PORT SerialDebug

// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64
#define MAX_NUMBER_CHANNELS 16

// Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy
// Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy
Expand Down Expand Up @@ -79,15 +93,6 @@
#endif
#endif

#if defined(__AVR_AVR128DA48__)
#define SerialDebug Serial1
#elif defined(__AVR_AVR128DB48__)
#define SerialDebug Serial3
#else
// standard Serial
#define SerialDebug Serial
#endif

#define USING_HW_TIMER_INTERVAL_MS false //true

// Don't change these numbers to make higher Timer freq. System can hang
Expand All @@ -96,7 +101,7 @@

volatile uint32_t startMicros = 0;

// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels
// Init DX_SLOW_PWM, each can service max 64 different ISR-based PWM channels
DX_SLOW_PWM_ISR ISR_PWM;

//////////////////////////////////////////////////////
Expand Down Expand Up @@ -193,6 +198,7 @@ void setup()
SerialDebug.print(F("\nStarting ISR_8_PWMs_Array on ")); SerialDebug.println(BOARD_NAME);
SerialDebug.println(DX_SLOW_PWM_VERSION);
SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz"));
SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS);

SerialDebug.print(F("TCB Clock Frequency = "));

Expand Down
19 changes: 17 additions & 2 deletions examples/ISR_8_PWMs_Array_Complex/ISR_8_PWMs_Array_Complex.ino
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,21 @@
// These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h"
// _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_ 3
#define _PWM_LOGLEVEL_ 1

#if defined(__AVR_AVR128DA48__)
#define SerialDebug Serial1
#elif defined(__AVR_AVR128DB48__)
#define SerialDebug Serial3
#else
// standard Serial
#define SerialDebug Serial
#endif

#define PWM_GENERIC_DEBUG_PORT SerialDebug

// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64
#define MAX_NUMBER_CHANNELS 16

// Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy
// Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy
Expand Down Expand Up @@ -96,7 +110,7 @@

volatile uint32_t startMicros = 0;

// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels
// Init DX_SLOW_PWM, each can service max 48 different ISR-based PWM channels
DX_SLOW_PWM_ISR ISR_PWM;

//////////////////////////////////////////////////////
Expand Down Expand Up @@ -415,6 +429,7 @@ void setup()
SerialDebug.print(F("\nStarting ISR_8_PWMs_Array_Complex on ")); SerialDebug.println(BOARD_NAME);
SerialDebug.println(DX_SLOW_PWM_VERSION);
SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz"));
SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS);

SerialDebug.print(F("TCB Clock Frequency = "));

Expand Down
28 changes: 17 additions & 11 deletions examples/ISR_8_PWMs_Array_Simple/ISR_8_PWMs_Array_Simple.ino
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,21 @@
// These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h"
// _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_ 4
#define _PWM_LOGLEVEL_ 1

#if defined(__AVR_AVR128DA48__)
#define SerialDebug Serial1
#elif defined(__AVR_AVR128DB48__)
#define SerialDebug Serial3
#else
// standard Serial
#define SerialDebug Serial
#endif

#define PWM_GENERIC_DEBUG_PORT SerialDebug

// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64
#define MAX_NUMBER_CHANNELS 16

// Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy
// Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy
Expand Down Expand Up @@ -77,15 +91,6 @@
#endif
#endif

#if defined(__AVR_AVR128DA48__)
#define SerialDebug Serial1
#elif defined(__AVR_AVR128DB48__)
#define SerialDebug Serial3
#else
// standard Serial
#define SerialDebug Serial
#endif

#define USING_HW_TIMER_INTERVAL_MS false //true

// Don't change these numbers to make higher Timer freq. System can hang
Expand All @@ -94,7 +99,7 @@

volatile uint32_t startMicros = 0;

// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels
// Init DX_SLOW_PWM, each can service max 64 different ISR-based PWM channels
DX_SLOW_PWM_ISR ISR_PWM;

//////////////////////////////////////////////////////
Expand Down Expand Up @@ -146,6 +151,7 @@ void setup()
SerialDebug.print(F("\nStarting ISR_8_PWMs_Array_Simple on ")); SerialDebug.println(BOARD_NAME);
SerialDebug.println(DX_SLOW_PWM_VERSION);
SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz"));
SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS);

SerialDebug.print(F("TCB Clock Frequency = "));

Expand Down
28 changes: 17 additions & 11 deletions examples/ISR_Changing_PWM/ISR_Changing_PWM.ino
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,21 @@
// These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h"
// _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_ 3
#define _PWM_LOGLEVEL_ 1

#if defined(__AVR_AVR128DA48__)
#define SerialDebug Serial1
#elif defined(__AVR_AVR128DB48__)
#define SerialDebug Serial3
#else
// standard Serial
#define SerialDebug Serial
#endif

#define PWM_GENERIC_DEBUG_PORT SerialDebug

// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64
#define MAX_NUMBER_CHANNELS 16

// Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy
// Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy
Expand Down Expand Up @@ -77,15 +91,6 @@
#endif
#endif

#if defined(__AVR_AVR128DA48__)
#define SerialDebug Serial1
#elif defined(__AVR_AVR128DB48__)
#define SerialDebug Serial3
#else
// standard Serial
#define SerialDebug Serial
#endif

#define USING_HW_TIMER_INTERVAL_MS false //true

// Don't change these numbers to make higher Timer freq. System can hang
Expand All @@ -94,7 +99,7 @@

volatile uint32_t startMicros = 0;

// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels
// Init DX_SLOW_PWM, each can service max 64 different ISR-based PWM channels
DX_SLOW_PWM_ISR ISR_PWM;

//////////////////////////////////////////////////////
Expand Down Expand Up @@ -141,6 +146,7 @@ void setup()
SerialDebug.print(F("\nStarting ISR_Changing_PWM on ")); SerialDebug.println(BOARD_NAME);
SerialDebug.println(DX_SLOW_PWM_VERSION);
SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz"));
SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS);

SerialDebug.print(F("TCB Clock Frequency = "));

Expand Down
Loading

0 comments on commit c67b7f2

Please sign in to comment.