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

Commit f26b6dc

Browse files
authored
v1.7.0 to add new PushPull mode
### Releases v1.7.0 1. Add functions `setPWMPushPull_Int`, `setPWMPushPull` and `setPWMPushPull_Period` for the new `PushPull` mode. Check [pwm_set_output_polarity #21](#21) 2. Add these examples to demo the new `PushPull` mode - [PWM_PushPull](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/PWM_PushPull) - [PWM_PushPull_DynamicDC](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/PWM_PushPull_DynamicDC) - [PWM_PushPull_DynamicFreq](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/PWM_PushPull_DynamicFreq) 3. Fix bug of half frequency when using `phaseCorrect` mode 4. Improve `README.md` so that links can be used in other sites, such as `PIO`
1 parent 078551c commit f26b6dc

7 files changed

+424
-66
lines changed

README.md

+223-54
Large diffs are not rendered by default.

changelog.md

+12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
## Table of Contents
1919

2020
* [Changelog](#changelog)
21+
* [Releases v1.7.0](#Releases-v170)
2122
* [Releases v1.6.0](#Releases-v160)
2223
* [Releases v1.5.0](#Releases-v150)
2324
* [Releases v1.4.1](#Releases-v141)
@@ -39,6 +40,17 @@
3940

4041
## Changelog
4142

43+
### Releases v1.7.0
44+
45+
1. Add functions `setPWMPushPull_Int`, `setPWMPushPull` and `setPWMPushPull_Period` for the new `PushPull` mode. Check [pwm_set_output_polarity #21](https://github.com/khoih-prog/RP2040_PWM/discussions/21)
46+
2. Add these examples to demo the new `PushPull` mode
47+
- [PWM_PushPull](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/PWM_PushPull)
48+
- [PWM_PushPull_DynamicDC](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/PWM_PushPull_DynamicDC)
49+
- [PWM_PushPull_DynamicFreq](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/PWM_PushPull_DynamicFreq)
50+
3. Fix bug of half frequency when using `phaseCorrect` mode
51+
4. Improve `README.md` so that links can be used in other sites, such as `PIO`
52+
53+
4254
### Releases v1.6.0
4355

4456
1. Optimize speed with new `setPWM_manual_Fast` function to improve almost 50% compared to `setPWM_manual`. Check [setPWM latency #19](https://github.com/khoih-prog/RP2040_PWM/issues/19)

keywords.txt

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ PWM_slice KEYWORD1
1313
# Class RP2040_PWM
1414
###################################
1515

16+
setPWMPushPull_Int KEYWORD2
17+
setPWMPushPull KEYWORD2
18+
setPWMPushPull_Period KEYWORD2
1619
setPWM_Int KEYWORD2
1720
setPWM KEYWORD2
1821
setPWM_manual KEYWORD2

library.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "RP2040_PWM",
3-
"version": "1.6.0",
3+
"version": "1.7.0",
44
"keywords": "timing, device, control, timer, pwm, pwm-slice, hardware-based-pwm, high-frequency-pwm, hardware-pwm, mission-critical, accuracy, non-blocking, mbed, mbed-nano, mbed-rp2040, rpi-pico, rp2040, nano-rp2040-connect, duty-cycle, hardware",
55
"description": "This library enables you to use Hardware-based PWM channels on RP2040-based boards, such as Nano_RP2040_Connect, RASPBERRY_PI_PICO, with either Arduino-mbed (mbed_nano or mbed_rp2040) or arduino-pico core to create and output PWM any GPIO pin. The most important feature is they're purely hardware-based PWM channels, supporting very high PWM frequencies. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using ISR, millis() or micros(). That's necessary if you need to control devices requiring high precision. New efficient setPWM_manual function to facilitate waveform creation using PWM",
66
"authors":

library.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=RP2040_PWM
2-
version=1.6.0
2+
version=1.7.0
33
author=Khoi Hoang <[email protected]>
44
maintainer=Khoi Hoang <[email protected]>
55
sentence=his library enables you to use Hardware-based PWM channels on RP2040-based boards, such as Nano_RP2040_Connect, RASPBERRY_PI_PICO, with either Arduino-mbed (mbed_nano or mbed_rp2040) or arduino-pico core to create and output PWM to any GPIO pin.

src/PWM_Generic_Debug.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Built by Khoi Hoang https://github.com/khoih-prog/RP2040_PWM
77
Licensed under MIT license
88
9-
Version: 1.6.0
9+
Version: 1.7.0
1010
1111
Version Modified By Date Comments
1212
------- ----------- ---------- -----------
@@ -25,6 +25,7 @@
2525
1.4.1 K Hoang 21/01/2023 Add `PWM_StepperControl` example
2626
1.5.0 K Hoang 24/01/2023 Add `PWM_manual` example and functions
2727
1.6.0 K Hoang 26/01/2023 Optimize speed with new `setPWM_manual_Fast` function
28+
1.7.0 K Hoang 31/01/2023 Add PushPull mode and related examples
2829
*****************************************************************************************************************************/
2930

3031
#pragma once

src/RP2040_PWM.h

+182-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Built by Khoi Hoang https://github.com/khoih-prog/RP2040_PWM
77
Licensed under MIT license
88
9-
Version: 1.6.0
9+
Version: 1.7.0
1010
1111
Version Modified By Date Comments
1212
------- ----------- ---------- -----------
@@ -25,6 +25,7 @@
2525
1.4.1 K Hoang 21/01/2023 Add `PWM_StepperControl` example
2626
1.5.0 K Hoang 24/01/2023 Add `PWM_manual` example and functions
2727
1.6.0 K Hoang 26/01/2023 Optimize speed with new `setPWM_manual_Fast` function
28+
1.7.0 K Hoang 31/01/2023 Add PushPull mode and related examples
2829
*****************************************************************************************************************************/
2930

3031
#pragma once
@@ -68,13 +69,13 @@
6869
///////////////////////////////////////////////////////////////////
6970

7071
#ifndef RP2040_PWM_VERSION
71-
#define RP2040_PWM_VERSION "RP2040_PWM v1.6.0"
72+
#define RP2040_PWM_VERSION "RP2040_PWM v1.7.0"
7273

7374
#define RP2040_PWM_VERSION_MAJOR 1
74-
#define RP2040_PWM_VERSION_MINOR 6
75+
#define RP2040_PWM_VERSION_MINOR 7
7576
#define RP2040_PWM_VERSION_PATCH 0
7677

77-
#define RP2040_PWM_VERSION_INT 1006000
78+
#define RP2040_PWM_VERSION_INT 1007000
7879
#endif
7980

8081
///////////////////////////////////////////////////////////////////
@@ -312,6 +313,8 @@ class RP2040_PWM
312313
{
313314
_pin = pin;
314315

316+
_phaseCorrect = phaseCorrect;
317+
315318
_PWM_config.top = top;
316319
_PWM_config.div = div;
317320

@@ -389,13 +392,163 @@ class RP2040_PWM
389392

390393
///////////////////////////////////////////
391394

395+
// dutycycle from 0-100,000 for 0%-100% to make use of 16-bit top register
396+
// dutycycle = real_dutycycle * 1000 for better accuracy
397+
// pinA and pinB must belong to the same channel. Check https://github.com/khoih-prog/RP2040_PWM#programmers-model
398+
// Must use phasecorrect mode here
399+
bool setPWMPushPull_Int(const uint8_t& pinA, const uint8_t& pinB, const float& frequency, const uint32_t& dutycycle)
400+
{
401+
bool newFreq = false;
402+
bool newDutyCycle = false;
403+
404+
_pin = pinA;
405+
406+
_slice_num = pwm_gpio_to_slice_num(pinA);
407+
408+
if ( pwm_gpio_to_slice_num(pinB) != _slice_num )
409+
{
410+
PWM_LOGERROR3("Error, not correct PWM push-pull pair of pins = ", pinA, "and", pinB);
411+
412+
return false;
413+
}
414+
415+
if ( (frequency <= ( (float) MAX_PWM_FREQUENCY * freq_CPU / 125000000))
416+
&& (frequency >= ( (float) MIN_PWM_FREQUENCY * freq_CPU / 125000000) ) )
417+
{
418+
if ( (_frequency != frequency) || !_phaseCorrect )
419+
{
420+
// Must change before calling calc_TOP_and_DIV()
421+
_phaseCorrect = true;
422+
423+
// To compensate phasecorrect half frequency
424+
if (!calc_TOP_and_DIV(frequency))
425+
{
426+
_frequency = 0;
427+
}
428+
else
429+
{
430+
_frequency = frequency;
431+
_dutycycle = dutycycle;
432+
433+
newFreq = true;
434+
435+
PWM_LOGINFO3("Changing PWM frequency to", frequency, "and dutyCycle =", (float) _dutycycle / 1000);
436+
}
437+
}
438+
else if (_enabled)
439+
{
440+
if (_dutycycle != dutycycle)
441+
{
442+
_dutycycle = dutycycle;
443+
newDutyCycle = true;
444+
445+
PWM_LOGINFO3("Changing PWM DutyCycle to", (float) _dutycycle / 1000, "and keeping frequency =", _frequency);
446+
447+
}
448+
else
449+
{
450+
PWM_LOGINFO3("No change, same PWM frequency =", frequency, "and dutyCycle =", (float) _dutycycle / 1000);
451+
}
452+
}
453+
454+
if ( (!_enabled) || newFreq || newDutyCycle )
455+
{
456+
gpio_set_function(pinA, GPIO_FUNC_PWM);
457+
gpio_set_function(pinB, GPIO_FUNC_PWM);
458+
459+
pwm_config config = pwm_get_default_config();
460+
461+
pwm_config_set_clkdiv_int(&config, _PWM_config.div);
462+
pwm_config_set_wrap(&config, _PWM_config.top);
463+
464+
if ( newDutyCycle )
465+
{
466+
// KH, to fix glitch when changing dutycycle from v1.4.0
467+
// Check https://github.com/khoih-prog/RP2040_PWM/issues/10
468+
// From pico-sdk/src/rp2_common/hardware_pwm/include/hardware/pwm.h
469+
// Only take effect after the next time the PWM slice wraps
470+
// (or, in phase-correct mode, the next time the slice reaches 0).
471+
// If the PWM is not running, the write is latched in immediately
472+
//pwm_set_wrap(uint slice_num, uint16_t wrap)
473+
pwm_set_wrap(_slice_num, _PWM_config.top);
474+
}
475+
else
476+
{
477+
// auto start running once configured
478+
pwm_init(_slice_num, &config, true);
479+
}
480+
481+
uint32_t PWM_level = ( _PWM_config.top * (_dutycycle / 2) ) / 50000;
482+
483+
// To avoid uint32_t overflow and still keep accuracy as _dutycycle max = 100,000 > 65536 of uint16_t
484+
pwm_set_gpio_level(pinA, PWM_level);
485+
pwm_set_gpio_level(pinB, PWM_level);
486+
487+
// From v1.1.0
488+
////////////////////////////////
489+
// Update PWM_slice_data[]
490+
PWM_slice_data[_slice_num].freq = _frequency;
491+
492+
// Set phaseCorrect
493+
pwm_set_phase_correct(_slice_num, true);
494+
495+
pwm_set_output_polarity(_slice_num, false, true);
496+
497+
if ( ( (pwm_gpio_to_channel(_pin)) == PWM_CHAN_A) || ( (pwm_gpio_to_channel(_pin)) == PWM_CHAN_B) )
498+
{
499+
PWM_slice_data[_slice_num].channelA_div = PWM_level;
500+
PWM_slice_data[_slice_num].channelB_div = _PWM_config.top - PWM_level;
501+
502+
PWM_slice_data[_slice_num].channelA_Active = true;
503+
PWM_slice_data[_slice_num].channelB_Active = true;
504+
505+
// If A is active, set the data now
506+
if (PWM_slice_data[_slice_num].channelA_Active)
507+
{
508+
pwm_set_chan_level(_slice_num, PWM_CHAN_A, PWM_slice_data[_slice_num].channelA_div);
509+
}
510+
511+
// If B is active, set the data now
512+
if (PWM_slice_data[_slice_num].channelB_Active)
513+
{
514+
pwm_set_chan_level(_slice_num, PWM_CHAN_B, PWM_slice_data[_slice_num].channelB_div);
515+
}
516+
}
517+
else
518+
{
519+
PWM_LOGERROR1("Error, not correct PWM pin = ", _pin);
520+
521+
return false;
522+
}
523+
524+
pwm_set_enabled(_slice_num, true);
525+
526+
PWM_LOGINFO5("pinA = ", pinA, ", pinB = ", pinB, ", PWM_CHAN =", pwm_gpio_to_channel(_pin));
527+
528+
////////////////////////////////
529+
530+
_enabled = true;
531+
532+
PWM_LOGINFO3("PWM enabled, slice = ", _slice_num, ", _frequency = ", _frequency);
533+
}
534+
535+
return true;
536+
}
537+
else
538+
return false;
539+
}
540+
541+
///////////////////////////////////////////
542+
392543
// dutycycle from 0-100,000 for 0%-100% to make use of 16-bit top register
393544
// dutycycle = real_dutycycle * 1000 for better accuracy
394545
bool setPWM_Int(const uint8_t& pin, const float& frequency, const uint32_t& dutycycle, bool phaseCorrect = false)
395546
{
396547
bool newFreq = false;
397548
bool newDutyCycle = false;
398549

550+
_phaseCorrect = phaseCorrect;
551+
399552
if ( (frequency <= ( (float) MAX_PWM_FREQUENCY * freq_CPU / 125000000))
400553
&& (frequency >= ( (float) MIN_PWM_FREQUENCY * freq_CPU / 125000000) ) )
401554
{
@@ -464,8 +617,10 @@ class RP2040_PWM
464617
pwm_init(_slice_num, &config, true);
465618
}
466619

620+
uint32_t PWM_level = ( _PWM_config.top * (_dutycycle / 2) ) / 50000;
621+
467622
// To avoid uint32_t overflow and still keep accuracy as _dutycycle max = 100,000 > 65536 of uint16_t
468-
pwm_set_gpio_level(_pin, ( _PWM_config.top * (_dutycycle / 2) ) / 50000 );
623+
pwm_set_gpio_level(_pin, PWM_level );
469624

470625
// From v1.1.0
471626
////////////////////////////////
@@ -474,7 +629,7 @@ class RP2040_PWM
474629

475630
if ( (pwm_gpio_to_channel(_pin)) == PWM_CHAN_A)
476631
{
477-
PWM_slice_data[_slice_num].channelA_div = ( _PWM_config.top * (_dutycycle / 2) ) / 50000;
632+
PWM_slice_data[_slice_num].channelA_div = PWM_level;
478633
PWM_slice_data[_slice_num].channelA_Active = true;
479634

480635
// If B is active, set the data now
@@ -485,7 +640,7 @@ class RP2040_PWM
485640
}
486641
else if ( (pwm_gpio_to_channel(_pin)) == PWM_CHAN_B)
487642
{
488-
PWM_slice_data[_slice_num].channelB_div = ( _PWM_config.top * (_dutycycle / 2) ) / 50000;
643+
PWM_slice_data[_slice_num].channelB_div = PWM_level;
489644
PWM_slice_data[_slice_num].channelB_Active = true;
490645

491646
// If A is active, set the data now
@@ -517,7 +672,7 @@ class RP2040_PWM
517672
else
518673
return false;
519674
}
520-
675+
521676
///////////////////////////////////////////
522677

523678
bool setPWM(const uint8_t& pin, const float& frequency, const float& dutycycle, bool phaseCorrect = false)
@@ -532,6 +687,20 @@ class RP2040_PWM
532687
return setPWM_Int(pin, 1000000.0f / period_us, dutycycle * 1000, phaseCorrect);
533688
}
534689

690+
///////////////////////////////////////////
691+
692+
bool setPWMPushPull(const uint8_t& pinA, const uint8_t& pinB, const float& frequency, const float& dutycycle)
693+
{
694+
return setPWMPushPull_Int(pinA, pinB, frequency, dutycycle * 1000);
695+
}
696+
697+
///////////////////////////////////////////
698+
699+
bool ssetPWMPushPull_Period(const uint8_t& pinA, const uint8_t& pinB, const float& period_us, const float& dutycycle)
700+
{
701+
return setPWMPushPull_Int(pinA, pinB, 1000000.0f / period_us, dutycycle * 1000);
702+
}
703+
535704
///////////////////////////////////////////
536705

537706
void enablePWM()
@@ -649,9 +818,13 @@ class RP2040_PWM
649818

650819
// Formula => PWM_Freq = ( F_CPU ) / [ ( TOP + 1 ) * ( DIV + DIV_FRAC/16) ]
651820
_PWM_config.top = ( freq_CPU / freq / _PWM_config.div ) - 1;
652-
821+
653822
_actualFrequency = ( freq_CPU ) / ( (_PWM_config.top + 1) * _PWM_config.div );
654823

824+
// Compensate half freq if _phaseCorrect
825+
if (_phaseCorrect)
826+
_PWM_config.top /= 2;
827+
655828
PWM_LOGINFO3("_PWM_config.top =", _PWM_config.top, ", _actualFrequency =", _actualFrequency);
656829

657830
return true;

0 commit comments

Comments
 (0)