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

Commit c5c1aa5

Browse files
authoredFeb 8, 2022
v1.2.0 to fix multiple-definitions linker error
### Releases v1.2.0 1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories 2. Improve accuracy by using `float`, instead of `uint32_t` for `dutycycle`. Check [Change Duty Cycle #1](khoih-prog/ESP8266_PWM#1 (comment)) 3. DutyCycle to be optionally updated at the end current PWM period instead of immediately. Check [DutyCycle to be updated at the end current PWM period #2](khoih-prog/ESP8266_PWM#2) 4. Optimize library code by using `reference-passing` instead of `value-passing` 5. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project 6. Update examples accordingly
1 parent 32c65d7 commit c5c1aa5

21 files changed

+2151
-1773
lines changed
 

‎CONTRIBUTING.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ If you don't find anything, please [open a new issue](https://github.com/khoih-p
1414

1515
Please ensure to specify the following:
1616

17-
* Arduino IDE version (e.g. 1.8.16) or Platform.io version
18-
* `Teensyduino` Core Version (e.g. Teensy core v1.55)
17+
* Arduino IDE version (e.g. 1.8.19) or Platform.io version
18+
* `Teensyduino` Core Version (e.g. Teensy core v1.56)
1919
* Contextual information (e.g. what you were trying to achieve)
2020
* Simplest possible steps to reproduce
2121
* Anything that might be relevant in your opinion, such as:
@@ -26,10 +26,10 @@ Please ensure to specify the following:
2626
### Example
2727

2828
```
29-
Arduino IDE version: 1.8.16
30-
Teensyduino Core Version 1.55
29+
Arduino IDE version: 1.8.19
30+
Teensyduino Core Version 1.56
3131
OS: Ubuntu 20.04 LTS
32-
Linux xy-Inspiron-3593 5.4.0-90-generic #101-Ubuntu SMP Fri Oct 15 20:00:55 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
32+
Linux xy-Inspiron-3593 5.4.0-99-generic #112-Ubuntu SMP Thu Feb 3 13:50:55 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
3333
3434
Context:
3535
I encountered a crash while trying to use the Timer Interrupt.

‎README.md

+178-120
Large diffs are not rendered by default.

‎changelog.md

+11-8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
## Table of Contents
1313

1414
* [Changelog](#changelog)
15+
* [Releases v1.2.0](#releases-v120)
1516
* [Releases v1.1.0](#releases-v110)
1617
* [Initial Releases v1.0.0](#Initial-Releases-v100)
1718

@@ -20,6 +21,16 @@
2021

2122
## Changelog
2223

24+
### Releases v1.2.0
25+
26+
1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories
27+
2. Improve accuracy by using `float`, instead of `uint32_t` for `dutycycle`. Check [Change Duty Cycle #1](https://github.com/khoih-prog/ESP8266_PWM/issues/1#issuecomment-1024969658)
28+
3. DutyCycle to be optionally updated at the end current PWM period instead of immediately. Check [DutyCycle to be updated at the end current PWM period #2](https://github.com/khoih-prog/ESP8266_PWM/issues/2)
29+
4. Optimize library code by using `reference-passing` instead of `value-passing`
30+
5. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project
31+
6. Update examples accordingly
32+
33+
2334
### Releases v1.1.0
2435

2536
1. Add functions to modify PWM settings on-the-fly
@@ -31,11 +42,3 @@
3142

3243
2. The hybrid ISR-based PWM channels can generate from very low (much less than 1Hz) to highest PWM frequencies up to 500-1000Hz with acceptable accuracy.
3344

34-
---
35-
---
36-
37-
## Copyright
38-
39-
Copyright 2021- Khoi Hoang
40-
41-

‎examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino

+8-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#define USING_MICROS_RESOLUTION true //false
2626

27+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
2728
#include "Teensy_Slow_PWM.h"
2829

2930
#include <SimpleTimer.h> // https://github.com/jfturcot/SimpleTimer
@@ -48,8 +49,8 @@
4849
#if defined(__IMXRT1062__)
4950
// For Teensy 4.0 and 4.1
5051
// Don't change these numbers to make higher Timer freq. System can hang
51-
#define HW_TIMER_INTERVAL_MS 0.0333f
52-
#define HW_TIMER_INTERVAL_FREQ 30000L
52+
#define HW_TIMER_INTERVAL_MS 0.01f
53+
#define HW_TIMER_INTERVAL_FREQ 100000L
5354
#elif defined(__MK66FX1M0__)
5455
// For Teensy 3.6
5556
// Don't change these numbers to make higher Timer freq. System can hang
@@ -99,15 +100,15 @@ uint32_t PWM_Pin[] =
99100
#define NUMBER_ISR_PWMS ( sizeof(PWM_Pin) / sizeof(uint32_t) )
100101

101102
// You can assign any interval for any timer here, in Hz
102-
double PWM_Freq[NUMBER_ISR_PWMS] =
103+
float PWM_Freq[] =
103104
{
104105
1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f,
105106
};
106107

107108
// You can assign any interval for any timer here, in Microseconds
108-
uint32_t PWM_DutyCycle[NUMBER_ISR_PWMS] =
109+
float PWM_DutyCycle[] =
109110
{
110-
5, 10, 20, 25, 30, 35, 40, 45
111+
5.0, 10.0, 20.0, 30.0, 40.0, 45.0, 50.0, 55.0
111112
};
112113

113114
typedef void (*irqCallback) ();
@@ -149,7 +150,7 @@ void doingSomething7()
149150
}
150151

151152

152-
irqCallback irqCallbackStartFunc[NUMBER_ISR_PWMS] =
153+
irqCallback irqCallbackStartFunc[] =
153154
{
154155
doingSomething0, doingSomething1, doingSomething2, doingSomething3,
155156
doingSomething4, doingSomething5, doingSomething6, doingSomething7
@@ -200,7 +201,7 @@ void setup()
200201
// You can use up to 16 timer for each ISR_PWM
201202
for (uint16_t i = 0; i < NUMBER_ISR_PWMS; i++)
202203
{
203-
//void setPWM(uint32_t pin, uint32_t frequency, uint32_t dutycycle
204+
//void setPWM(uint32_t pin, float frequency, float dutycycle
204205
// , timer_callback_p StartCallback = nullptr, timer_callback_p StopCallback = nullptr)
205206

206207
// You can use this with PWM_Freq in Hz

‎examples/ISR_8_PWMs_Array_Complex/ISR_8_PWMs_Array_Complex.ino

+23-21
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#define USING_MICROS_RESOLUTION true //false
2626

27+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
2728
#include "Teensy_Slow_PWM.h"
2829

2930
#include <SimpleTimer.h> // https://github.com/jfturcot/SimpleTimer
@@ -48,8 +49,8 @@
4849
#if defined(__IMXRT1062__)
4950
// For Teensy 4.0 and 4.1
5051
// Don't change these numbers to make higher Timer freq. System can hang
51-
#define HW_TIMER_INTERVAL_MS 0.0333f
52-
#define HW_TIMER_INTERVAL_FREQ 30000L
52+
#define HW_TIMER_INTERVAL_MS 0.01f
53+
#define HW_TIMER_INTERVAL_FREQ 100000L
5354
#elif defined(__MK66FX1M0__)
5455
// For Teensy 3.6
5556
// Don't change these numbers to make higher Timer freq. System can hang
@@ -112,9 +113,9 @@ typedef struct
112113
irqCallback irqCallbackStartFunc;
113114
irqCallback irqCallbackStopFunc;
114115

115-
uint32_t PWM_Freq;
116+
float PWM_Freq;
116117

117-
uint32_t PWM_DutyCycle;
118+
float PWM_DutyCycle;
118119

119120
uint32_t deltaMicrosStart;
120121
uint32_t previousMicrosStart;
@@ -134,29 +135,30 @@ void doingSomethingStop(int index);
134135

135136
#else // #if USE_COMPLEX_STRUCT
136137

137-
volatile unsigned long deltaMicrosStart [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
138-
volatile unsigned long previousMicrosStart [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
138+
volatile unsigned long deltaMicrosStart [] = { 0, 0, 0, 0, 0, 0, 0, 0 };
139+
volatile unsigned long previousMicrosStart [] = { 0, 0, 0, 0, 0, 0, 0, 0 };
139140

140-
volatile unsigned long deltaMicrosStop [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
141-
volatile unsigned long previousMicrosStop [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
141+
volatile unsigned long deltaMicrosStop [] = { 0, 0, 0, 0, 0, 0, 0, 0 };
142+
volatile unsigned long previousMicrosStop [] = { 0, 0, 0, 0, 0, 0, 0, 0 };
142143

143144

144145
// You can assign any interval for any timer here, in Microseconds
145-
uint32_t PWM_Period[NUMBER_ISR_PWMS] =
146+
uint32_t PWM_Period[] =
146147
{
147148
1000L, 500L, 333L, 250L, 200L, 166L, 142L, 125L
148149
};
149150

150151
// You can assign any interval for any timer here, in Hz
151-
double PWM_Freq[NUMBER_ISR_PWMS] =
152+
// You can assign any interval for any timer here, in Hz
153+
float PWM_Freq[] =
152154
{
153155
1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f,
154156
};
155157

156158
// You can assign any interval for any timer here, in Microseconds
157-
uint32_t PWM_DutyCycle[NUMBER_ISR_PWMS] =
159+
float PWM_DutyCycle[] =
158160
{
159-
5, 10, 20, 25, 30, 35, 40, 45
161+
5.0, 10.0, 20.0, 30.0, 40.0, 45.0, 50.0, 55.0
160162
};
161163

162164
void doingSomethingStart(int index)
@@ -270,17 +272,17 @@ void doingSomethingStop7()
270272

271273
#if USE_COMPLEX_STRUCT
272274

273-
ISR_PWM_Data curISR_PWM_Data[NUMBER_ISR_PWMS] =
275+
ISR_PWM_Data curISR_PWM_Data[] =
274276
{
275277
// pin, irqCallbackStartFunc, irqCallbackStopFunc, PWM_Freq, PWM_DutyCycle, deltaMicrosStart, previousMicrosStart, deltaMicrosStop, previousMicrosStop
276278
{ LED_BUILTIN, doingSomethingStart0, doingSomethingStop0, 1, 5, 0, 0, 0, 0 },
277279
{ PIN_D0, doingSomethingStart1, doingSomethingStop1, 2, 10, 0, 0, 0, 0 },
278280
{ PIN_D1, doingSomethingStart2, doingSomethingStop2, 3, 20, 0, 0, 0, 0 },
279-
{ PIN_D2, doingSomethingStart3, doingSomethingStop3, 4, 25, 0, 0, 0, 0 },
280-
{ PIN_D3, doingSomethingStart4, doingSomethingStop4, 5, 30, 0, 0, 0, 0 },
281-
{ PIN_D4, doingSomethingStart5, doingSomethingStop5, 6, 35, 0, 0, 0, 0 },
282-
{ PIN_D5, doingSomethingStart6, doingSomethingStop6, 7, 40, 0, 0, 0, 0 },
283-
{ PIN_D6, doingSomethingStart7, doingSomethingStop7, 8, 45, 0, 0, 0, 0 },
281+
{ PIN_D2, doingSomethingStart3, doingSomethingStop3, 4, 30, 0, 0, 0, 0 },
282+
{ PIN_D3, doingSomethingStart4, doingSomethingStop4, 5, 40, 0, 0, 0, 0 },
283+
{ PIN_D4, doingSomethingStart5, doingSomethingStop5, 6, 45, 0, 0, 0, 0 },
284+
{ PIN_D5, doingSomethingStart6, doingSomethingStop6, 7, 50, 0, 0, 0, 0 },
285+
{ PIN_D6, doingSomethingStart7, doingSomethingStop7, 8, 55, 0, 0, 0, 0 },
284286
};
285287

286288

@@ -304,13 +306,13 @@ void doingSomethingStop(int index)
304306

305307
#else // #if USE_COMPLEX_STRUCT
306308

307-
irqCallback irqCallbackStartFunc[NUMBER_ISR_PWMS] =
309+
irqCallback irqCallbackStartFunc[] =
308310
{
309311
doingSomethingStart0, doingSomethingStart1, doingSomethingStart2, doingSomethingStart3,
310312
doingSomethingStart4, doingSomethingStart5, doingSomethingStart6, doingSomethingStart7
311313
};
312314

313-
irqCallback irqCallbackStopFunc[NUMBER_ISR_PWMS] =
315+
irqCallback irqCallbackStopFunc[] =
314316
{
315317
doingSomethingStop0, doingSomethingStop1, doingSomethingStop2, doingSomethingStop3,
316318
doingSomethingStop4, doingSomethingStop5, doingSomethingStop6, doingSomethingStop7
@@ -428,7 +430,7 @@ void setup()
428430
curISR_PWM_Data[i].previousMicrosStart = startMicros;
429431
//ISR_PWM.setInterval(curISR_PWM_Data[i].PWM_Period, curISR_PWM_Data[i].irqCallbackStartFunc);
430432

431-
//void setPWM(uint32_t pin, uint32_t frequency, uint32_t dutycycle
433+
//void setPWM(uint32_t pin, float frequency, float dutycycle
432434
// , timer_callback_p StartCallback = nullptr, timer_callback_p StopCallback = nullptr)
433435

434436
// You can use this with PWM_Freq in Hz

‎examples/ISR_8_PWMs_Array_Simple/ISR_8_PWMs_Array_Simple.ino

+7-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#define USING_MICROS_RESOLUTION true //false
2626

27+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
2728
#include "Teensy_Slow_PWM.h"
2829

2930
#include <SimpleTimer.h> // https://github.com/jfturcot/SimpleTimer
@@ -48,8 +49,8 @@
4849
#if defined(__IMXRT1062__)
4950
// For Teensy 4.0 and 4.1
5051
// Don't change these numbers to make higher Timer freq. System can hang
51-
#define HW_TIMER_INTERVAL_MS 0.0333f
52-
#define HW_TIMER_INTERVAL_FREQ 30000L
52+
#define HW_TIMER_INTERVAL_MS 0.01f
53+
#define HW_TIMER_INTERVAL_FREQ 100000L
5354
#elif defined(__MK66FX1M0__)
5455
// For Teensy 3.6
5556
// Don't change these numbers to make higher Timer freq. System can hang
@@ -99,15 +100,15 @@ uint32_t PWM_Pin[] =
99100
#define NUMBER_ISR_PWMS ( sizeof(PWM_Pin) / sizeof(uint32_t) )
100101

101102
// You can assign any interval for any timer here, in Hz
102-
double PWM_Freq[NUMBER_ISR_PWMS] =
103+
float PWM_Freq[] =
103104
{
104105
1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f,
105106
};
106107

107108
// You can assign any interval for any timer here, in Microseconds
108-
uint32_t PWM_DutyCycle[NUMBER_ISR_PWMS] =
109+
float PWM_DutyCycle[] =
109110
{
110-
5, 10, 20, 25, 30, 35, 40, 45
111+
5.0, 10.0, 20.0, 30.0, 40.0, 45.0, 50.0, 55.0
111112
};
112113

113114
////////////////////////////////////////////////
@@ -154,7 +155,7 @@ void setup()
154155
// You can use up to 16 timer for each ISR_PWM
155156
for (uint16_t i = 0; i < NUMBER_ISR_PWMS; i++)
156157
{
157-
//void setPWM(uint32_t pin, uint32_t frequency, uint32_t dutycycle
158+
//void setPWM(uint32_t pin, float frequency, float dutycycle
158159
// , timer_callback_p StartCallback = nullptr, timer_callback_p StopCallback = nullptr)
159160

160161
// You can use this with PWM_Freq in Hz

‎examples/ISR_Changing_PWM/ISR_Changing_PWM.ino

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#define USING_MICROS_RESOLUTION true //false
2626

27+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
2728
#include "Teensy_Slow_PWM.h"
2829

2930
#define LED_OFF HIGH
@@ -86,19 +87,19 @@ void TimerHandler()
8687
uint32_t PWM_Pin = LED_BUILTIN;
8788

8889
// You can assign any interval for any timer here, in Hz
89-
double PWM_Freq1 = 1.0f;
90+
float PWM_Freq1 = 1.0f;
9091
// You can assign any interval for any timer here, in Hz
91-
double PWM_Freq2 = 2.0f;
92+
float PWM_Freq2 = 2.0f;
9293

9394
// You can assign any interval for any timer here, in microseconds
9495
uint32_t PWM_Period1 = 1000000 / PWM_Freq1;
9596
// You can assign any interval for any timer here, in microseconds
9697
uint32_t PWM_Period2 = 1000000 / PWM_Freq2;
9798

9899
// You can assign any duty_cycle for any PWM here, from 0-100
99-
uint32_t PWM_DutyCycle1 = 50;
100+
float PWM_DutyCycle1 = 50.0;
100101
// You can assign any duty_cycle for any PWM here, from 0-100
101-
uint32_t PWM_DutyCycle2 = 90;
102+
float PWM_DutyCycle2 = 90.0;
102103

103104
// Channel number used to identify associated channel
104105
int channelNum;

‎examples/ISR_Modify_PWM/ISR_Modify_PWM.ino

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#define USING_MICROS_RESOLUTION true //false
2626

27+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
2728
#include "Teensy_Slow_PWM.h"
2829

2930
#define LED_OFF HIGH
@@ -86,19 +87,19 @@ void TimerHandler()
8687
uint32_t PWM_Pin = LED_BUILTIN;
8788

8889
// You can assign any interval for any timer here, in Hz
89-
double PWM_Freq1 = 1.0f;
90+
float PWM_Freq1 = 1.0f;
9091
// You can assign any interval for any timer here, in Hz
91-
double PWM_Freq2 = 2.0f;
92+
float PWM_Freq2 = 2.0f;
9293

9394
// You can assign any interval for any timer here, in microseconds
9495
uint32_t PWM_Period1 = 1000000 / PWM_Freq1;
9596
// You can assign any interval for any timer here, in microseconds
9697
uint32_t PWM_Period2 = 1000000 / PWM_Freq2;
9798

9899
// You can assign any duty_cycle for any PWM here, from 0-100
99-
uint32_t PWM_DutyCycle1 = 10;
100+
float PWM_DutyCycle1 = 50.0;
100101
// You can assign any duty_cycle for any PWM here, from 0-100
101-
uint32_t PWM_DutyCycle2 = 90;
102+
float PWM_DutyCycle2 = 90.0;
102103

103104
// Channel number used to identify associated channel
104105
int channelNum;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/****************************************************************************************************************************
2+
multiFileProject.cpp
3+
4+
For nRF52-based boards using Adafruit_nRF52_Arduino core
5+
Written by Khoi Hoang
6+
7+
Built by Khoi Hoang https://github.com/khoih-prog/nRF52_Slow_PWM
8+
Licensed under MIT license
9+
*****************************************************************************************************************************/
10+
11+
// To demo how to include files in multi-file Projects
12+
13+
#include "multiFileProject.h"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/****************************************************************************************************************************
2+
multiFileProject.h
3+
4+
For nRF52-based boards using Adafruit_nRF52_Arduino core
5+
Written by Khoi Hoang
6+
7+
Built by Khoi Hoang https://github.com/khoih-prog/nRF52_Slow_PWM
8+
Licensed under MIT license
9+
*****************************************************************************************************************************/
10+
11+
// To demo how to include files in multi-file Projects
12+
13+
#pragma once
14+
15+
#define USING_MICROS_RESOLUTION true //false
16+
17+
// Default is true, uncomment to false
18+
//#define CHANGING_PWM_END_OF_CYCLE false
19+
20+
// Can be included as many times as necessary, without `Multiple Definitions` Linker Error
21+
#include "Teensy_Slow_PWM.hpp"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/****************************************************************************************************************************
2+
multiFileProject.ino
3+
4+
For nRF52-based boards using Adafruit_nRF52_Arduino core
5+
Written by Khoi Hoang
6+
7+
Built by Khoi Hoang https://github.com/khoih-prog/nRF52_Slow_PWM
8+
Licensed under MIT license
9+
*****************************************************************************************************************************/
10+
11+
// To demo how to include files in multi-file Projects
12+
13+
#if !( defined(CORE_TEENSY) || defined(TEENSYDUINO) )
14+
#error This code is designed to run on Teensy platform! Please check your Tools->Board setting.
15+
#endif
16+
17+
#define TEENSY_SLOW_PWM_VERSION_MIN_TARGET F("Teensy_Slow_PWM v1.2.0")
18+
#define TEENSY_SLOW_PWM_VERSION_MIN 1002000
19+
20+
#include "multiFileProject.h"
21+
22+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
23+
#include "Teensy_Slow_PWM.h"
24+
25+
void setup()
26+
{
27+
Serial.begin(115200);
28+
while (!Serial);
29+
30+
Serial.println("\nStart multiFileProject");
31+
Serial.println(TEENSY_SLOW_PWM_VERSION);
32+
33+
#if defined(TEENSY_SLOW_PWM_VERSION_MIN)
34+
if (TEENSY_SLOW_PWM_VERSION_INT < TEENSY_SLOW_PWM_VERSION_MIN)
35+
{
36+
Serial.print("Warning. Must use this example on Version equal or later than : ");
37+
Serial.println(TEENSY_SLOW_PWM_VERSION_MIN_TARGET);
38+
}
39+
#endif
40+
}
41+
42+
void loop()
43+
{
44+
// put your main code here, to run repeatedly:
45+
}

‎keywords.txt

+8
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,12 @@ getNumAvailablePWMChannels KEYWORD2
5454
#######################################
5555

5656
TEENSY_SLOW_PWM_VERSION LITERAL1
57+
TEENSY_SLOW_PWM_VERSION_MAJOR LITERAL1
58+
TEENSY_SLOW_PWM_VERSION_MINOR LITERAL1
59+
TEENSY_SLOW_PWM_VERSION_PATCH LITERAL1
60+
TEENSY_SLOW_PWM_VERSION_INT LITERAL1
61+
62+
USING_MICROS_RESOLUTION LITERAL1
63+
CHANGING_PWM_END_OF_CYCLE LITERAL1
64+
5765
INVALID_TEENSY_PIN LITERAL1

‎library.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Teensy_Slow_PWM",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"keywords": "timing, device, control, timer, interrupt, hardware, isr, isr-based, hardware-timer, mission-critical, accuracy, precise, non-blocking, teensy-2x, teensy-3x, teensy-lc, teensy-4x, teensy-41, teensy-40, teensy-micromod",
55
"description": "This library enables you to use ISR-based PWM channels on Teensy boards, such as Teensy 2.x, Teensy LC, Teensy 3.x, Teensy 4.x, Teensy MicroMod, etc., to create and output PWM any GPIO pin. It now supports 16 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they're ISR-based PWM channels, supporting lower PWM frequencies with suitable accuracy. Their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These ISR-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using millis() or micros(). That's necessary if you need to control devices requiring high precision. Now you can change the PWM settings on-the-fly",
66
"authors":
@@ -12,7 +12,7 @@
1212
"repository":
1313
{
1414
"type": "git",
15-
"url": "//https://github.com/khoih-prog/Teensy_Slow_PWM"
15+
"url": "https://github.com/khoih-prog/Teensy_Slow_PWM"
1616
},
1717
"homepage": "https://github.com/khoih-prog/Teensy_Slow_PWM",
1818
"export": {
@@ -22,8 +22,9 @@
2222
"tests"
2323
]
2424
},
25+
"license": "MIT",
2526
"frameworks": "*",
2627
"platforms": "avr",
2728
"examples": "examples/*/*/*.ino",
28-
"license": "MIT"
29+
"headers": ["Teensy_Slow_PWM.h", "Teensy_Slow_PWM.hpp"]
2930
}

‎library.properties

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=Teensy_Slow_PWM
2-
version=1.1.0
2+
version=1.2.0
33
author=Khoi Hoang <khoih.prog@gmail.com>
44
maintainer=Khoi Hoang <khoih.prog@gmail.com>
55
sentence=This library enables you to use ISR-based PWM channels on Teensy boards, such as Teensy 2.x, Teensy LC, Teensy 3.x, Teensy 4.x, Teensy MicroMod, etc., to create and output PWM any GPIO pin.
@@ -9,4 +9,4 @@ url=https://github.com/khoih-prog/Teensy_Slow_PWM
99
architectures=teensy
1010
repository=https://github.com/khoih-prog/Teensy_Slow_PWM
1111
license=MIT
12-
includes=Teensy_Slow_PWM.h
12+
includes=Teensy_Slow_PWM.h,Teensy_Slow_PWM.hpp

‎src/PWM_Generic_Debug.h

+55-28
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@
1212
Therefore, their executions are not blocked by bad-behaving functions / tasks.
1313
This important feature is absolutely necessary for mission-critical tasks.
1414
15-
Version: 1.1.0
15+
Version: 1.2.0
1616
1717
Version Modified By Date Comments
1818
------- ----------- ---------- -----------
1919
1.0.0 K.Hoang 28/09/2021 Initial coding for Teensy 2.x, Teensy LC, Teensy 3.x, Teensy 4.x, etc.
2020
1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly
21+
1.2.0 K.Hoang 07/02/2022 Fix `multiple-definitions` linker error. Improve accuracy. Optimize code. Fix bug
2122
*****************************************************************************************************************************/
2223

2324
#pragma once
@@ -42,32 +43,58 @@
4243
#define _PWM_LOGLEVEL_ 1
4344
#endif
4445

45-
#define PWM_LOGERROR(x) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.println(x); }
46-
#define PWM_LOGERROR0(x) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print(x); }
47-
#define PWM_LOGERRORLN0(x) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.println(x); }
48-
#define PWM_LOGERROR1(x,y) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(y); }
49-
#define PWM_LOGERROR2(x,y,z) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(z); }
50-
#define PWM_LOGERROR3(x,y,z,w) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(z); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(w); }
51-
52-
#define PWM_LOGWARN(x) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.println(x); }
53-
#define PWM_LOGWARN0(x) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print(x); }
54-
#define PWM_LOGWARNLN0(x) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.println(x); }
55-
#define PWM_LOGWARN1(x,y) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(y); }
56-
#define PWM_LOGWARN2(x,y,z) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(z); }
57-
#define PWM_LOGWARN3(x,y,z,w) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(z); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(w); }
58-
59-
#define PWM_LOGINFO(x) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.println(x); }
60-
#define PWM_LOGINFO0(x) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print(x); }
61-
#define PWM_LOGINFOLN0(x) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.println(x); }
62-
#define PWM_LOGINFO1(x,y) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(y); }
63-
#define PWM_LOGINFO2(x,y,z) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(z); }
64-
#define PWM_LOGINFO3(x,y,z,w) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(z); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(w); }
65-
66-
#define PWM_LOGDEBUG(x) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.println(x); }
67-
#define PWM_LOGDEBUG0(x) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print(x); }
68-
#define PWM_LOGDEBUGLN0(x) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.println(x); }
69-
#define PWM_LOGDEBUG1(x,y) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(y); }
70-
#define PWM_LOGDEBUG2(x,y,z) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(z); }
71-
#define PWM_LOGDEBUG3(x,y,z,w) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(z); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(w); }
46+
///////////////////////////////////////
47+
48+
const char PWM_MARK[] = "[PWM] ";
49+
const char PWM_SPACE[] = " ";
50+
51+
#define PWM_PRINT PWM_DBG_PORT.print
52+
#define PWM_PRINTLN PWM_DBG_PORT.println
53+
54+
#define PWM_PRINT_MARK PWM_PRINT(PWM_MARK)
55+
#define PWM_PRINT_SP PWM_PRINT(PWM_SPACE)
56+
#define PWM_PRINT_LINE PWM_PRINT(PWM_LINE)
57+
58+
///////////////////////////////////////
59+
60+
#define PWM_LOGERROR(x) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINTLN(x); }
61+
#define PWM_LOGERROR0(x) if(_PWM_LOGLEVEL_>0) { PWM_PRINT(x); }
62+
#define PWM_LOGERRORLN0(x) if(_PWM_LOGLEVEL_>0) { PWM_PRINTLN(x); }
63+
#define PWM_LOGERROR1(x,y) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); }
64+
#define PWM_LOGERROR2(x,y,z) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); }
65+
#define PWM_LOGERROR3(x,y,z,w) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); }
66+
#define PWM_LOGERROR5(x,y,z,w, xx, yy) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); }
67+
68+
///////////////////////////////////////
69+
70+
#define PWM_LOGWARN(x) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINTLN(x); }
71+
#define PWM_LOGWARN0(x) if(_PWM_LOGLEVEL_>1) { PWM_PRINT(x); }
72+
#define PWM_LOGWARNLN0(x) if(_PWM_LOGLEVEL_>1) { PWM_PRINTLN(x); }
73+
#define PWM_LOGWARN1(x,y) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); }
74+
#define PWM_LOGWARN2(x,y,z) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); }
75+
#define PWM_LOGWARN3(x,y,z,w) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); }
76+
#define PWM_LOGWARN5(x,y,z,w, xx, yy) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); }
77+
78+
///////////////////////////////////////
79+
80+
#define PWM_LOGINFO(x) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINTLN(x); }
81+
#define PWM_LOGINFO0(x) if(_PWM_LOGLEVEL_>2) { PWM_PRINT(x); }
82+
#define PWM_LOGINFOLN0(x) if(_PWM_LOGLEVEL_>2) { PWM_PRINTLN(x); }
83+
#define PWM_LOGINFO1(x,y) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); }
84+
#define PWM_LOGINFO2(x,y,z) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); }
85+
#define PWM_LOGINFO3(x,y,z,w) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); }
86+
#define PWM_LOGINFO5(x,y,z,w, xx, yy) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); }
87+
88+
///////////////////////////////////////
89+
90+
#define PWM_LOGDEBUG(x) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINTLN(x); }
91+
#define PWM_LOGDEBUG0(x) if(_PWM_LOGLEVEL_>3) { PWM_PRINT(x); }
92+
#define PWM_LOGDEBUGLN0(x) if(_PWM_LOGLEVEL_>3) { PWM_PRINTLN(x); }
93+
#define PWM_LOGDEBUG1(x,y) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); }
94+
#define PWM_LOGDEBUG2(x,y,z) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); }
95+
#define PWM_LOGDEBUG3(x,y,z,w) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); }
96+
#define PWM_LOGDEBUG5(x,y,z,w, xx, yy) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); }
97+
98+
///////////////////////////////////////
7299

73100
#endif //PWM_GENERIC_DEBUG_H

‎src/Teensy_Slow_PWM.h

+4-1,078
Large diffs are not rendered by default.

‎src/Teensy_Slow_PWM.hpp

+1,086
Large diffs are not rendered by default.

‎src/Teensy_Slow_PWM_ISR.h

+3-197
Original file line numberDiff line numberDiff line change
@@ -12,217 +12,23 @@
1212
Therefore, their executions are not blocked by bad-behaving functions / tasks.
1313
This important feature is absolutely necessary for mission-critical tasks.
1414
15-
Version: 1.1.0
15+
Version: 1.2.0
1616
1717
Version Modified By Date Comments
1818
------- ----------- ---------- -----------
1919
1.0.0 K.Hoang 28/09/2021 Initial coding for Teensy 2.x, Teensy LC, Teensy 3.x, Teensy 4.x, etc.
2020
1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly
21+
1.2.0 K.Hoang 07/02/2022 Fix `multiple-definitions` linker error. Improve accuracy. Optimize code. Fix bug
2122
*****************************************************************************************************************************/
2223

2324
#pragma once
2425

2526
#ifndef TEENSY_SLOW_PWM_ISR_H
2627
#define TEENSY_SLOW_PWM_ISR_H
2728

28-
#if !( defined(CORE_TEENSY) || defined(TEENSYDUINO) )
29-
#error This code is designed to run on Teensy platform! Please check your Tools->Board setting.
30-
#endif
31-
32-
#ifndef TEENSY_SLOW_PWM_VERSION
33-
#define TEENSY_SLOW_PWM_VERSION F("Teensy_Slow_PWM v1.1.0")
34-
#endif
35-
36-
#ifndef _PWM_LOGLEVEL_
37-
#define _PWM_LOGLEVEL_ 1
38-
#endif
39-
40-
#include "PWM_Generic_Debug.h"
41-
42-
#include <stddef.h>
43-
44-
#include <inttypes.h>
45-
46-
#if defined(ARDUINO)
47-
#if ARDUINO >= 100
48-
#include <Arduino.h>
49-
#else
50-
#include <WProgram.h>
51-
#endif
52-
#endif
53-
54-
#define TEENSY_SLOW_PWM_ISR Teensy_SLOW_PWM
55-
56-
typedef void (*timer_callback)();
57-
typedef void (*timer_callback_p)(void *);
58-
59-
#if !defined(USING_MICROS_RESOLUTION)
60-
#warning Not USING_MICROS_RESOLUTION, using millis resolution
61-
#define USING_MICROS_RESOLUTION false
62-
#endif
63-
64-
#define INVALID_TEENSY_PIN 255
65-
66-
//////////////////////////////////////////////////////////////////
67-
68-
class TEENSY_SLOW_PWM_ISR
69-
{
70-
71-
public:
72-
// maximum number of PWM channels
73-
#define MAX_NUMBER_CHANNELS 16
74-
75-
// constructor
76-
TEENSY_SLOW_PWM_ISR();
77-
78-
void init();
79-
80-
// this function must be called inside loop()
81-
void run();
82-
83-
//////////////////////////////////////////////////////////////////
84-
// PWM
85-
// Return the channelNum if OK, -1 if error
86-
int setPWM(uint32_t pin, double frequency, uint32_t dutycycle, timer_callback StartCallback = nullptr,
87-
timer_callback StopCallback = nullptr)
88-
{
89-
uint32_t period = 0;
90-
91-
if ( ( frequency != 0 ) && ( frequency <= 1000 ) )
92-
{
93-
#if USING_MICROS_RESOLUTION
94-
// period in us
95-
period = 1000000.0f / frequency;
96-
#else
97-
// period in ms
98-
period = 1000.0f / frequency;
99-
#endif
100-
}
101-
else
102-
{
103-
PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz");
104-
105-
return -1;
106-
}
107-
108-
return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback);
109-
}
110-
111-
// period in us
112-
// Return the channelNum if OK, -1 if error
113-
int setPWM_Period(uint32_t pin, uint32_t period, uint32_t dutycycle, timer_callback StartCallback = nullptr,
114-
timer_callback StopCallback = nullptr)
115-
{
116-
return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback);
117-
}
118-
119-
//////////////////////////////////////////////////////////////////
120-
121-
// low level function to modify a PWM channel
122-
// returns the true on success or false on failure
123-
bool modifyPWMChannel(unsigned channelNum, uint32_t pin, double frequency, uint32_t dutycycle)
124-
{
125-
uint32_t period = 0;
126-
127-
if ( ( frequency > 0 ) && ( frequency <= 1000 ) )
128-
{
129-
#if USING_MICROS_RESOLUTION
130-
// period in us
131-
period = 1000000.0f / frequency;
132-
#else
133-
// period in ms
134-
period = 1000.0f / frequency;
135-
#endif
136-
}
137-
else
138-
{
139-
PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz");
140-
return false;
141-
}
142-
143-
return modifyPWMChannel_Period(channelNum, pin, period, dutycycle);
144-
}
145-
146-
//////////////////////////////////////////////////////////////////
147-
148-
//period in us
149-
bool modifyPWMChannel_Period(unsigned channelNum, uint32_t pin, uint32_t period, uint32_t dutycycle);
150-
151-
//////////////////////////////////////////////////////////////////
152-
153-
// destroy the specified PWM channel
154-
void deleteChannel(unsigned channelNum);
155-
156-
// restart the specified PWM channel
157-
void restartChannel(unsigned channelNum);
158-
159-
// returns true if the specified PWM channel is enabled
160-
bool isEnabled(unsigned channelNum);
161-
162-
// enables the specified PWM channel
163-
void enable(unsigned channelNum);
164-
165-
// disables the specified PWM channel
166-
void disable(unsigned channelNum);
167-
168-
// enables all PWM channels
169-
void enableAll();
170-
171-
// disables all PWM channels
172-
void disableAll();
173-
174-
// enables the specified PWM channel if it's currently disabled, and vice-versa
175-
void toggle(unsigned channelNum);
176-
177-
// returns the number of used PWM channels
178-
unsigned getnumChannels();
179-
180-
// returns the number of available PWM channels
181-
unsigned getNumAvailablePWMChannels()
182-
{
183-
return MAX_NUMBER_CHANNELS - numChannels;
184-
};
185-
186-
private:
187-
188-
// low level function to initialize and enable a new PWM channel
189-
// returns the PWM channel number (channelNum) on success or
190-
// -1 on failure (f == NULL) or no free PWM channels
191-
int setupPWMChannel(uint32_t pin, uint32_t period, uint32_t dutycycle, void* cbStartFunc = nullptr, void* cbStopFunc = nullptr);
192-
193-
// find the first available slot
194-
int findFirstFreeSlot();
195-
196-
typedef struct
197-
{
198-
///////////////////////////////////
199-
200-
201-
///////////////////////////////////
202-
203-
uint32_t prevTime; // value returned by the micros() or millis() function in the previous run() call
204-
uint32_t period; // period value, in us / ms
205-
uint32_t onTime; // onTime value, ( period * dutyCycle / 100 ) us / ms
206-
207-
void* callbackStart; // pointer to the callback function when PWM pulse starts (HIGH)
208-
void* callbackStop; // pointer to the callback function when PWM pulse stops (LOW)
209-
210-
////////////////////////////////////////////////////////////
211-
212-
uint32_t pin; // PWM pin
213-
bool pinHigh; // true if PWM pin is HIGH
214-
////////////////////////////////////////////////////////////
215-
216-
bool enabled; // true if enabled
217-
} PWM_t;
218-
219-
volatile PWM_t PWM[MAX_NUMBER_CHANNELS];
220-
221-
// actual number of PWM channels in use (-1 means uninitialized)
222-
volatile int numChannels;
223-
};
22429

22530
#include "Teensy_Slow_PWM_ISR.hpp"
31+
#include "Teensy_Slow_PWM_ISR_Impl.h"
22632

22733
#endif // TEENSY_SLOW_PWM_ISR_H
22834

‎src/Teensy_Slow_PWM_ISR.hpp

+187-290
Large diffs are not rendered by default.

‎src/Teensy_Slow_PWM_ISR_Impl.h

+382
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
/****************************************************************************************************************************
2+
Teensy_Slow_PWM_ISR.hpp
3+
For Teensy boards (Teensy 2.x, Teensy LC, Teensy 3.x, Teensy 4.x, etc.)
4+
Written by Khoi Hoang
5+
6+
Built by Khoi Hoang https://github.com/khoih-prog/Teensy_Slow_PWM
7+
Licensed under MIT license
8+
9+
Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
10+
unsigned long miliseconds), you just consume only one RP2040-based timer and avoid conflicting with other cores' tasks.
11+
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
12+
Therefore, their executions are not blocked by bad-behaving functions / tasks.
13+
This important feature is absolutely necessary for mission-critical tasks.
14+
15+
Version: 1.2.0
16+
17+
Version Modified By Date Comments
18+
------- ----------- ---------- -----------
19+
1.0.0 K.Hoang 28/09/2021 Initial coding for Teensy 2.x, Teensy LC, Teensy 3.x, Teensy 4.x, etc.
20+
1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly
21+
1.2.0 K.Hoang 07/02/2022 Fix `multiple-definitions` linker error. Improve accuracy. Optimize code. Fix bug
22+
*****************************************************************************************************************************/
23+
24+
#pragma once
25+
26+
#ifndef TEENSY_SLOW_PWM_ISR_IMPL_H
27+
#define TEENSY_SLOW_PWM_ISR_IMPL_H
28+
29+
#include <string.h>
30+
#include "Teensy_Slow_PWM_ISR.hpp"
31+
32+
///////////////////////////////////////////////////
33+
34+
timeUnit timeNow()
35+
{
36+
#if USING_MICROS_RESOLUTION
37+
return ( (timeUnit) micros() );
38+
#else
39+
return ( (timeUnit) millis() );
40+
#endif
41+
}
42+
43+
///////////////////////////////////////////////////
44+
45+
TEENSY_SLOW_PWM_ISR::TEENSY_SLOW_PWM_ISR()
46+
: numChannels (-1)
47+
{
48+
}
49+
50+
///////////////////////////////////////////////////
51+
52+
void TEENSY_SLOW_PWM_ISR::init()
53+
{
54+
timeUnit currentTime = timeNow();
55+
56+
for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++)
57+
{
58+
memset((void*) &PWM[channelNum], 0, sizeof (PWM_t));
59+
PWM[channelNum].prevTime = currentTime;
60+
PWM[channelNum].pin = INVALID_TEENSY_PIN;
61+
}
62+
63+
numChannels = 0;
64+
}
65+
66+
///////////////////////////////////////////////////
67+
68+
void TEENSY_SLOW_PWM_ISR::run()
69+
{
70+
timeUnit currentTime = timeNow();
71+
72+
for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++)
73+
{
74+
// If enabled => check
75+
// start period / dutyCycle => digitalWrite HIGH
76+
// end dutyCycle => digitalWrite LOW
77+
if (PWM[channelNum].enabled)
78+
{
79+
if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) <= PWM[channelNum].onTime )
80+
{
81+
if (!PWM[channelNum].pinHigh)
82+
{
83+
digitalWrite(PWM[channelNum].pin, HIGH);
84+
PWM[channelNum].pinHigh = true;
85+
86+
// callbackStart
87+
if (PWM[channelNum].callbackStart != nullptr)
88+
{
89+
(*(timer_callback) PWM[channelNum].callbackStart)();
90+
}
91+
}
92+
}
93+
else if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) < PWM[channelNum].period )
94+
{
95+
if (PWM[channelNum].pinHigh)
96+
{
97+
digitalWrite(PWM[channelNum].pin, LOW);
98+
PWM[channelNum].pinHigh = false;
99+
100+
// callback when PWM pulse stops (LOW)
101+
if (PWM[channelNum].callbackStop != nullptr)
102+
{
103+
(*(timer_callback) PWM[channelNum].callbackStop)();
104+
}
105+
}
106+
}
107+
//else
108+
else if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) >= PWM[channelNum].period )
109+
{
110+
PWM[channelNum].prevTime = currentTime;
111+
112+
#if CHANGING_PWM_END_OF_CYCLE
113+
// Only update whenever having newPeriod
114+
if (PWM[channelNum].newPeriod != 0)
115+
{
116+
PWM[channelNum].period = PWM[channelNum].newPeriod;
117+
PWM[channelNum].newPeriod = 0;
118+
119+
PWM[channelNum].onTime = PWM[channelNum].newOnTime;
120+
}
121+
#endif
122+
}
123+
}
124+
}
125+
}
126+
127+
128+
///////////////////////////////////////////////////
129+
130+
// find the first available slot
131+
// return -1 if none found
132+
int TEENSY_SLOW_PWM_ISR::findFirstFreeSlot()
133+
{
134+
// all slots are used
135+
if (numChannels >= MAX_NUMBER_CHANNELS)
136+
{
137+
return -1;
138+
}
139+
140+
// return the first slot with no callbackStart (i.e. free)
141+
for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++)
142+
{
143+
if ( (PWM[channelNum].period == 0) && !PWM[channelNum].enabled )
144+
{
145+
return channelNum;
146+
}
147+
}
148+
149+
// no free slots found
150+
return -1;
151+
}
152+
153+
///////////////////////////////////////////////////
154+
155+
int TEENSY_SLOW_PWM_ISR::setupPWMChannel(const uint32_t& pin, const uint32_t& period, const float& dutycycle, void* cbStartFunc, void* cbStopFunc)
156+
{
157+
int channelNum;
158+
159+
// Invalid input, such as period = 0, etc
160+
if ( (period == 0) || (dutycycle < 0.0) || (dutycycle > 100.0) )
161+
{
162+
PWM_LOGERROR("Error: Invalid period or dutycycle");
163+
return -1;
164+
}
165+
166+
if (numChannels < 0)
167+
{
168+
init();
169+
}
170+
171+
channelNum = findFirstFreeSlot();
172+
173+
if (channelNum < 0)
174+
{
175+
return -1;
176+
}
177+
178+
PWM[channelNum].pin = pin;
179+
PWM[channelNum].period = period;
180+
181+
// Must be 0 for new PWM channel
182+
PWM[channelNum].newPeriod = 0;
183+
184+
PWM[channelNum].onTime = ( period * dutycycle ) / 100;
185+
186+
pinMode(pin, OUTPUT);
187+
digitalWrite(pin, HIGH);
188+
PWM[channelNum].pinHigh = true;
189+
190+
PWM[channelNum].prevTime = timeNow();
191+
192+
PWM[channelNum].callbackStart = cbStartFunc;
193+
PWM[channelNum].callbackStop = cbStopFunc;
194+
195+
PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum);
196+
PWM_LOGINFO0("\t Period : "); PWM_LOGINFO0(PWM[channelNum].period);
197+
PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].onTime);
198+
PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime);
199+
200+
numChannels++;
201+
202+
PWM[channelNum].enabled = true;
203+
204+
return channelNum;
205+
}
206+
207+
///////////////////////////////////////////////////
208+
209+
bool TEENSY_SLOW_PWM_ISR::modifyPWMChannel_Period(const uint8_t& channelNum, const uint32_t& pin, const uint32_t& period, const float& dutycycle)
210+
{
211+
// Invalid input, such as period = 0, etc
212+
if ( (period == 0) || (dutycycle < 0.0) || (dutycycle > 100.0) )
213+
{
214+
PWM_LOGERROR("Error: Invalid period or dutycycle");
215+
return false;
216+
}
217+
218+
if (channelNum > MAX_NUMBER_CHANNELS)
219+
{
220+
PWM_LOGERROR("Error: channelNum > MAX_NUMBER_CHANNELS");
221+
return false;
222+
}
223+
224+
if (PWM[channelNum].pin != pin)
225+
{
226+
PWM_LOGERROR("Error: channelNum and pin mismatched");
227+
return false;
228+
}
229+
230+
#if CHANGING_PWM_END_OF_CYCLE
231+
232+
PWM[channelNum].newPeriod = period;
233+
PWM[channelNum].newDutyCycle = dutycycle;
234+
PWM[channelNum].newOnTime = ( period * dutycycle ) / 100;
235+
236+
PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum);
237+
PWM_LOGINFO0("\tNew Period : "); PWM_LOGINFO0(PWM[channelNum].newPeriod);
238+
PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].newOnTime);
239+
PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime);
240+
241+
#else
242+
243+
PWM[channelNum].period = period;
244+
245+
PWM[channelNum].onTime = ( period * dutycycle ) / 100;
246+
247+
digitalWrite(pin, HIGH);
248+
PWM[channelNum].pinHigh = true;
249+
250+
PWM[channelNum].prevTime = timeNow();
251+
252+
PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum);
253+
PWM_LOGINFO0("\t Period : "); PWM_LOGINFO0(PWM[channelNum].period);
254+
PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].onTime);
255+
PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime);
256+
257+
#endif
258+
259+
return true;
260+
}
261+
262+
263+
///////////////////////////////////////////////////
264+
265+
void TEENSY_SLOW_PWM_ISR::deleteChannel(const uint8_t& channelNum)
266+
{
267+
// nothing to delete if no timers are in use
268+
if ( (channelNum >= MAX_NUMBER_CHANNELS) || (numChannels == 0) )
269+
{
270+
return;
271+
}
272+
273+
// don't decrease the number of timers if the specified slot is already empty (zero period, invalid)
274+
if ( (PWM[channelNum].pin != INVALID_TEENSY_PIN) && (PWM[channelNum].period != 0) )
275+
{
276+
memset((void*) &PWM[channelNum], 0, sizeof (PWM_t));
277+
278+
PWM[channelNum].pin = INVALID_TEENSY_PIN;
279+
280+
// update number of timers
281+
numChannels--;
282+
}
283+
}
284+
285+
///////////////////////////////////////////////////
286+
287+
void TEENSY_SLOW_PWM_ISR::restartChannel(const uint8_t& channelNum)
288+
{
289+
if (channelNum >= MAX_NUMBER_CHANNELS)
290+
{
291+
return;
292+
}
293+
294+
PWM[channelNum].prevTime = timeNow();
295+
}
296+
297+
///////////////////////////////////////////////////
298+
299+
bool TEENSY_SLOW_PWM_ISR::isEnabled(const uint8_t& channelNum)
300+
{
301+
if (channelNum >= MAX_NUMBER_CHANNELS)
302+
{
303+
return false;
304+
}
305+
306+
return PWM[channelNum].enabled;
307+
}
308+
309+
///////////////////////////////////////////////////
310+
311+
void TEENSY_SLOW_PWM_ISR::enable(const uint8_t& channelNum)
312+
{
313+
if (channelNum >= MAX_NUMBER_CHANNELS)
314+
{
315+
return;
316+
}
317+
318+
PWM[channelNum].enabled = true;
319+
}
320+
321+
///////////////////////////////////////////////////
322+
323+
void TEENSY_SLOW_PWM_ISR::disable(const uint8_t& channelNum)
324+
{
325+
if (channelNum >= MAX_NUMBER_CHANNELS)
326+
{
327+
return;
328+
}
329+
330+
PWM[channelNum].enabled = false;
331+
}
332+
333+
///////////////////////////////////////////////////
334+
335+
void TEENSY_SLOW_PWM_ISR::enableAll()
336+
{
337+
// Enable all timers with a callbackStart assigned (used)
338+
339+
for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++)
340+
{
341+
if (PWM[channelNum].period != 0)
342+
{
343+
PWM[channelNum].enabled = true;
344+
}
345+
}
346+
}
347+
348+
///////////////////////////////////////////////////
349+
350+
void TEENSY_SLOW_PWM_ISR::disableAll()
351+
{
352+
// Disable all timers with a callbackStart assigned (used)
353+
for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++)
354+
{
355+
if (PWM[channelNum].period != 0)
356+
{
357+
PWM[channelNum].enabled = false;
358+
}
359+
}
360+
}
361+
362+
///////////////////////////////////////////////////
363+
364+
void TEENSY_SLOW_PWM_ISR::toggle(const uint8_t& channelNum)
365+
{
366+
if (channelNum >= MAX_NUMBER_CHANNELS)
367+
{
368+
return;
369+
}
370+
371+
PWM[channelNum].enabled = !PWM[channelNum].enabled;
372+
}
373+
374+
///////////////////////////////////////////////////
375+
376+
int8_t TEENSY_SLOW_PWM_ISR::getnumChannels()
377+
{
378+
return numChannels;
379+
}
380+
381+
#endif // TEENSY_SLOW_PWM_ISR_IMPL_H
382+

‎src/Teensy_Slow_PWM_Impl.h

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/****************************************************************************************************************************
2+
Teensy_Slow_PWM_Impl.h
3+
For Teensy boards (Teensy 2.x, Teensy LC, Teensy 3.x, Teensy 4.x, etc.)
4+
Written by Khoi Hoang
5+
6+
Built by Khoi Hoang https://github.com/khoih-prog/Teensy_Slow_PWM
7+
Licensed under MIT license
8+
9+
Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
10+
unsigned long miliseconds), you just consume only one RP2040-based timer and avoid conflicting with other cores' tasks.
11+
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
12+
Therefore, their executions are not blocked by bad-behaving functions / tasks.
13+
This important feature is absolutely necessary for mission-critical tasks.
14+
15+
Version: 1.2.0
16+
17+
Version Modified By Date Comments
18+
------- ----------- ---------- -----------
19+
1.0.0 K.Hoang 28/09/2021 Initial coding for Teensy 2.x, Teensy LC, Teensy 3.x, Teensy 4.x, etc.
20+
1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly
21+
1.2.0 K.Hoang 07/02/2022 Fix `multiple-definitions` linker error. Improve accuracy. Optimize code. Fix bug
22+
*****************************************************************************************************************************/
23+
24+
#pragma once
25+
26+
#ifndef TEENSY_SLOW_PWM_IMPL_H
27+
#define TEENSY_SLOW_PWM_IMPL_H
28+
29+
#if ( defined(__arm__) && defined(TEENSYDUINO) && defined(__IMXRT1062__) )
30+
31+
// Nothing here yet
32+
33+
///////////////////////////////////////////////////////////////////////////////
34+
///////////////////////////////////////////////////////////////////////////////
35+
// For Teensy 3.x and LC
36+
#elif defined(__arm__) && defined(TEENSYDUINO) && (defined(KINETISK) || defined(KINETISL))
37+
38+
void ftm1_isr()
39+
{
40+
uint32_t sc = FTM1_SC;
41+
42+
#ifdef KINETISL
43+
if (sc & 0x80)
44+
FTM1_SC = sc;
45+
#else
46+
if (sc & 0x80)
47+
FTM1_SC = sc & 0x7F;
48+
#endif
49+
50+
(*(TeensyTimers[TEENSY_TIMER_1]->getCallback()))();
51+
}
52+
53+
void ftm2_isr()
54+
{
55+
uint32_t sc = FTM2_SC;
56+
57+
#ifdef KINETISL
58+
if (sc & 0x80)
59+
FTM2_SC = sc;
60+
#else
61+
if (sc & 0x80)
62+
FTM2_SC = sc & 0x7F;
63+
#endif
64+
65+
(*(TeensyTimers[TEENSY_TIMER_3]->getCallback()))();
66+
}
67+
68+
///////////////////////////////////////////////////////////////////////////////
69+
///////////////////////////////////////////////////////////////////////////////
70+
// For Teensy 2.0 and Teensy++ 2.0
71+
72+
#elif ( defined(ARDUINO_ARCH_AVR) || defined(__AVR__) )
73+
74+
ISR(TIMER1_OVF_vect)
75+
{
76+
(*(TeensyTimers[TEENSY_TIMER_1]->getCallback()))();
77+
}
78+
79+
ISR(TIMER3_OVF_vect)
80+
{
81+
(*(TeensyTimers[TEENSY_TIMER_3]->getCallback()))();
82+
}
83+
84+
////////////////////////////////////////////////////////////////////////////////
85+
////////////////////////////////////////////////////////////////////////////////
86+
87+
#else
88+
89+
#error Not support board
90+
91+
#endif
92+
93+
94+
////////////////////////////////////////////////////////////////////////////////
95+
96+
97+
98+
#endif // TEENSY_SLOW_PWM_IMPL_H
99+

0 commit comments

Comments
 (0)
This repository has been archived.