Skip to content

Commit 59da885

Browse files
committed
esp32\machine_pwm: PWM reduce inconsist.
Signed-off-by: IhorNehrutsa <[email protected]>
1 parent c1c25de commit 59da885

File tree

4 files changed

+634
-426
lines changed

4 files changed

+634
-426
lines changed

docs/esp32/quickref.rst

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,11 @@ Use the :ref:`machine.PWM <machine.PWM>` class::
306306
freq = pwm0.freq() # get current frequency
307307
pwm0.freq(1000) # set PWM frequency from 1Hz to 40MHz
308308

309-
duty = pwm0.duty() # get current duty cycle, range 0-1023 (default 512, 50%)
310-
pwm0.duty(256) # set duty cycle from 0 to 1023 as a ratio duty/1023, (now 25%)
309+
duty = pwm0.duty() # get current duty cycle, range 0-1024 (default 512, 50%)
310+
pwm0.duty(256) # set duty cycle from 0 to 1024 as a ratio duty/1024, (now 25%)
311311

312-
duty_u16 = pwm0.duty_u16() # get current duty cycle, range 0-65535
313-
pwm0.duty_u16(2**16*3//4) # set duty cycle from 0 to 65535 as a ratio duty_u16/65535, (now 75%)
312+
duty_u16 = pwm0.duty_u16() # get current duty cycle, range 0-65536
313+
pwm0.duty_u16(2**16*3//4) # set duty cycle from 0 to 65536 as a ratio duty_u16/65536, (now 75%)
314314

315315
duty_ns = pwm0.duty_ns() # get current pulse width in ns
316316
pwm0.duty_ns(250_000) # set pulse width in nanoseconds from 0 to 1_000_000_000/freq, (now 25%)
@@ -319,19 +319,24 @@ Use the :ref:`machine.PWM <machine.PWM>` class::
319319

320320
pwm2 = PWM(Pin(2), freq=20000, duty=512) # create and configure in one go
321321
print(pwm2) # view PWM settings
322+
pwm2.deinit() # turn off PWM on the pin
323+
324+
pwm0 = PWM(Pin(0), duty_u16=16384) # The output is at a high level 25% of the time.
325+
pwm2 = PWM(Pin(2), duty_u16=16384, invert=1) # The output is at a low level 25% of the time.
322326

323327
ESP chips have different hardware peripherals:
324328

325-
===================================================== ======== ======== ========
326-
Hardware specification ESP32 ESP32-S2 ESP32-C3
327-
----------------------------------------------------- -------- -------- --------
328-
Number of groups (speed modes) 2 1 1
329-
Number of timers per group 4 4 4
330-
Number of channels per group 8 8 6
331-
----------------------------------------------------- -------- -------- --------
332-
Different PWM frequencies (groups * timers) 8 4 4
333-
Total PWM channels (Pins, duties) (groups * channels) 16 8 6
334-
===================================================== ======== ======== ========
329+
======================================================= ======== ======== ========
330+
Hardware specification ESP32 ESP32-S2 ESP32-C3
331+
ESP32-S3 ESP32-H2
332+
------------------------------------------------------- -------- -------- --------
333+
Number of groups (speed modes) 2 1 1
334+
Number of timers per group 4 4 4
335+
Number of channels per group 8 8 6
336+
------------------------------------------------------- -------- -------- --------
337+
Different PWM frequencies = (groups * timers) 8 4 4
338+
Total PWM channels (Pins, duties) = (groups * channels) 16 8 6
339+
======================================================= ======== ======== ========
335340

336341
A maximum number of PWM channels (Pins) are available on the ESP32 - 16 channels,
337342
but only 8 different PWM frequencies are available, the remaining 8 channels must

docs/esp32/tutorial/pwm.rst

Lines changed: 161 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ compared with the length of a single period (low plus high time). Maximum
1111
duty cycle is when the pin is high all of the time, and minimum is when it is
1212
low all of the time.
1313

14-
* More comprehensive example with all 16 PWM channels and 8 timers::
14+
* More comprehensive example with all **16 PWM channels and 8 timers**::
1515

16+
from time import sleep
1617
from machine import Pin, PWM
1718
try:
1819
f = 100 # Hz
19-
d = 1024 // 16 # 6.25%
20-
pins = (15, 2, 4, 16, 18, 19, 22, 23, 25, 26, 27, 14 , 12, 13, 32, 33)
20+
d = 2**16 // 16 # 6.25%
21+
pins = (2, 4, 12, 13, 14, 15, 16, 18, 19, 22, 23, 25, 26, 27, 32, 33)
2122
pwms = []
2223
for i, pin in enumerate(pins):
23-
pwms.append(PWM(Pin(pin), freq=f * (i // 2 + 1), duty= 1023 if i==15 else d * (i + 1)))
24+
pwms.append(PWM(Pin(pin), freq=f * (i // 2 + 1), duty_u16=min(2**16 - 1, d * (i + 1))))
2425
print(pwms[i])
26+
sleep(60)
2527
finally:
2628
for pwm in pwms:
2729
try:
@@ -31,75 +33,198 @@ low all of the time.
3133

3234
Output is::
3335

34-
PWM(Pin(15), freq=100, duty=64, resolution=10, mode=0, channel=0, timer=0)
35-
PWM(Pin(2), freq=100, duty=128, resolution=10, mode=0, channel=1, timer=0)
36-
PWM(Pin(4), freq=200, duty=192, resolution=10, mode=0, channel=2, timer=1)
37-
PWM(Pin(16), freq=200, duty=256, resolution=10, mode=0, channel=3, timer=1)
38-
PWM(Pin(18), freq=300, duty=320, resolution=10, mode=0, channel=4, timer=2)
39-
PWM(Pin(19), freq=300, duty=384, resolution=10, mode=0, channel=5, timer=2)
40-
PWM(Pin(22), freq=400, duty=448, resolution=10, mode=0, channel=6, timer=3)
41-
PWM(Pin(23), freq=400, duty=512, resolution=10, mode=0, channel=7, timer=3)
42-
PWM(Pin(25), freq=500, duty=576, resolution=10, mode=1, channel=0, timer=0)
43-
PWM(Pin(26), freq=500, duty=640, resolution=10, mode=1, channel=1, timer=0)
44-
PWM(Pin(27), freq=600, duty=704, resolution=10, mode=1, channel=2, timer=1)
45-
PWM(Pin(14), freq=600, duty=768, resolution=10, mode=1, channel=3, timer=1)
46-
PWM(Pin(12), freq=700, duty=832, resolution=10, mode=1, channel=4, timer=2)
47-
PWM(Pin(13), freq=700, duty=896, resolution=10, mode=1, channel=5, timer=2)
48-
PWM(Pin(32), freq=800, duty=960, resolution=10, mode=1, channel=6, timer=3)
49-
PWM(Pin(33), freq=800, duty=1023, resolution=10, mode=1, channel=7, timer=3)
50-
51-
* Example of a smooth frequency change::
36+
PWM(Pin(2), freq=100, duty_u16=4096) # resolution=16, (duty=6.25%, resolution=0.002%), mode=0, channel=0, timer=0
37+
PWM(Pin(4), freq=100, duty_u16=8192) # resolution=16, (duty=12.50%, resolution=0.002%), mode=0, channel=1, timer=0
38+
PWM(Pin(12), freq=199, duty_u16=12288) # resolution=16, (duty=18.75%, resolution=0.002%), mode=0, channel=2, timer=1
39+
PWM(Pin(13), freq=199, duty_u16=16384) # resolution=16, (duty=25.00%, resolution=0.002%), mode=0, channel=3, timer=1
40+
PWM(Pin(14), freq=299, duty_u16=20480) # resolution=16, (duty=31.25%, resolution=0.002%), mode=0, channel=4, timer=2
41+
PWM(Pin(15), freq=299, duty_u16=24576) # resolution=16, (duty=37.50%, resolution=0.002%), mode=0, channel=5, timer=2
42+
PWM(Pin(16), freq=400, duty_u16=28672) # resolution=16, (duty=43.75%, resolution=0.002%), mode=0, channel=6, timer=3
43+
PWM(Pin(18), freq=400, duty_u16=32768) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=7, timer=3
44+
PWM(Pin(19), freq=500, duty_u16=36864) # resolution=16, (duty=56.25%, resolution=0.002%), mode=1, channel=0, timer=0
45+
PWM(Pin(22), freq=500, duty_u16=40960) # resolution=16, (duty=62.50%, resolution=0.002%), mode=1, channel=1, timer=0
46+
PWM(Pin(23), freq=599, duty_u16=45056) # resolution=16, (duty=68.75%, resolution=0.002%), mode=1, channel=2, timer=1
47+
PWM(Pin(25), freq=599, duty_u16=49152) # resolution=16, (duty=75.00%, resolution=0.002%), mode=1, channel=3, timer=1
48+
PWM(Pin(26), freq=700, duty_u16=53248) # resolution=16, (duty=81.25%, resolution=0.002%), mode=1, channel=4, timer=2
49+
PWM(Pin(27), freq=700, duty_u16=57344) # resolution=16, (duty=87.50%, resolution=0.002%), mode=1, channel=5, timer=2
50+
PWM(Pin(32), freq=799, duty_u16=61440) # resolution=16, (duty=93.75%, resolution=0.002%), mode=1, channel=6, timer=3
51+
PWM(Pin(33), freq=799, duty_u16=65536) # resolution=16, (duty=100.00%, resolution=0.002%), mode=1, channel=7, timer=3
52+
53+
54+
* Example of a **smooth frequency change**::
5255

5356
from time import sleep
5457
from machine import Pin, PWM
5558

56-
F_MIN = 500
59+
F_MIN = 100
5760
F_MAX = 1000
5861

5962
f = F_MIN
60-
delta_f = 1
63+
delta_f = 100
6164

62-
p = PWM(Pin(5), f)
63-
print(p)
65+
p = PWM(Pin(27), f)
6466

6567
while True:
6668
p.freq(f)
69+
print(p)
6770

68-
sleep(10 / F_MIN)
71+
sleep(.2)
6972

7073
f += delta_f
71-
if f >= F_MAX or f <= F_MIN:
74+
if f > F_MAX or f < F_MIN:
7275
delta_f = -delta_f
76+
print()
77+
if f > F_MAX:
78+
f = F_MAX
79+
elif f < F_MIN:
80+
f = F_MIN
81+
82+
`See PWM wave on Pin(27) with an oscilloscope. <https://user-images.githubusercontent.com/70886343/224013926-73953f7b-9b75-4e32-9595-83236c76ca1f.mp4>`_
7383

74-
See PWM wave at Pin(5) with an oscilloscope.
84+
Output is::
7585

76-
* Example of a smooth duty change::
86+
PWM(Pin(27), freq=100, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
87+
PWM(Pin(27), freq=199, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
88+
PWM(Pin(27), freq=299, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
89+
PWM(Pin(27), freq=400, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
90+
PWM(Pin(27), freq=500, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
91+
PWM(Pin(27), freq=599, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
92+
PWM(Pin(27), freq=700, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
93+
PWM(Pin(27), freq=799, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
94+
PWM(Pin(27), freq=900, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
95+
PWM(Pin(27), freq=998, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
96+
97+
PWM(Pin(27), freq=998, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
98+
PWM(Pin(27), freq=900, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
99+
PWM(Pin(27), freq=799, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
100+
PWM(Pin(27), freq=700, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
101+
PWM(Pin(27), freq=599, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
102+
PWM(Pin(27), freq=500, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
103+
PWM(Pin(27), freq=400, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
104+
PWM(Pin(27), freq=299, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
105+
PWM(Pin(27), freq=199, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
106+
PWM(Pin(27), freq=100, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0
107+
...
108+
109+
110+
* Example of a **smooth duty change**::
77111

78112
from time import sleep
79113
from machine import Pin, PWM
80114

81115
DUTY_MAX = 2**16 - 1
82116

83117
duty_u16 = 0
84-
delta_d = 16
118+
delta_d = 256
85119

86-
p = PWM(Pin(5), 1000, duty_u16=duty_u16)
87-
print(p)
120+
pwm = PWM(Pin(27), 1000, duty_u16=duty_u16)
121+
print(pwm)
88122

89123
while True:
90-
p.duty_u16(duty_u16)
124+
pwm.duty_u16(duty_u16)
91125

92-
sleep(1 / 1000)
126+
sleep(.001)
127+
128+
print(pwm)
93129

94130
duty_u16 += delta_d
95131
if duty_u16 >= DUTY_MAX:
96132
duty_u16 = DUTY_MAX
97133
delta_d = -delta_d
134+
print()
98135
elif duty_u16 <= 0:
99136
duty_u16 = 0
100137
delta_d = -delta_d
138+
print()
139+
140+
See `PWM wave on Pin(27) with an oscilloscope. <https://user-images.githubusercontent.com/70886343/224020123-1c958e85-0c91-4ca6-8b4c-b3bb956892b1.mp4>`_
141+
142+
Output is::
143+
144+
PWM(Pin(27), freq=998, duty_u16=0) # resolution=16, (duty=0.00%, resolution=0.002%), mode=0, channel=0, timer=0
145+
PWM(Pin(27), freq=998, duty_u16=256) # resolution=16, (duty=0.39%, resolution=0.002%), mode=0, channel=0, timer=0
146+
PWM(Pin(27), freq=998, duty_u16=512) # resolution=16, (duty=0.78%, resolution=0.002%), mode=0, channel=0, timer=0
147+
PWM(Pin(27), freq=998, duty_u16=768) # resolution=16, (duty=1.17%, resolution=0.002%), mode=0, channel=0, timer=0
148+
PWM(Pin(27), freq=998, duty_u16=1024) # resolution=16, (duty=1.56%, resolution=0.002%), mode=0, channel=0, timer=0
149+
...
150+
PWM(Pin(27), freq=998, duty_u16=64256) # resolution=16, (duty=98.05%, resolution=0.002%), mode=0, channel=0, timer=0
151+
PWM(Pin(27), freq=998, duty_u16=64512) # resolution=16, (duty=98.44%, resolution=0.002%), mode=0, channel=0, timer=0
152+
PWM(Pin(27), freq=998, duty_u16=64768) # resolution=16, (duty=98.83%, resolution=0.002%), mode=0, channel=0, timer=0
153+
PWM(Pin(27), freq=998, duty_u16=65024) # resolution=16, (duty=99.22%, resolution=0.002%), mode=0, channel=0, timer=0
154+
PWM(Pin(27), freq=998, duty_u16=65280) # resolution=16, (duty=99.61%, resolution=0.002%), mode=0, channel=0, timer=0
155+
156+
PWM(Pin(27), freq=998, duty_u16=65536) # resolution=16, (duty=100.00%, resolution=0.002%), mode=0, channel=0, timer=0
157+
PWM(Pin(27), freq=998, duty_u16=65279) # resolution=16, (duty=99.61%, resolution=0.002%), mode=0, channel=0, timer=0
158+
PWM(Pin(27), freq=998, duty_u16=65023) # resolution=16, (duty=99.22%, resolution=0.002%), mode=0, channel=0, timer=0
159+
PWM(Pin(27), freq=998, duty_u16=64767) # resolution=16, (duty=98.83%, resolution=0.002%), mode=0, channel=0, timer=0
160+
PWM(Pin(27), freq=998, duty_u16=64511) # resolution=16, (duty=98.44%, resolution=0.002%), mode=0, channel=0, timer=0
161+
...
162+
PWM(Pin(27), freq=998, duty_u16=1279) # resolution=16, (duty=1.95%, resolution=0.002%), mode=0, channel=0, timer=0
163+
PWM(Pin(27), freq=998, duty_u16=1024) # resolution=16, (duty=1.56%, resolution=0.002%), mode=0, channel=0, timer=0
164+
PWM(Pin(27), freq=998, duty_u16=767) # resolution=16, (duty=1.17%, resolution=0.002%), mode=0, channel=0, timer=0
165+
PWM(Pin(27), freq=998, duty_u16=511) # resolution=16, (duty=0.78%, resolution=0.002%), mode=0, channel=0, timer=0
166+
PWM(Pin(27), freq=998, duty_u16=255) # resolution=16, (duty=0.39%, resolution=0.002%), mode=0, channel=0, timer=0
167+
168+
PWM(Pin(27), freq=998, duty_u16=0) # resolution=16, (duty=0.00%, resolution=0.002%), mode=0, channel=0, timer=0
169+
PWM(Pin(27), freq=998, duty_u16=256) # resolution=16, (duty=0.39%, resolution=0.002%), mode=0, channel=0, timer=0
170+
PWM(Pin(27), freq=998, duty_u16=512) # resolution=16, (duty=0.78%, resolution=0.002%), mode=0, channel=0, timer=0
171+
PWM(Pin(27), freq=998, duty_u16=768) # resolution=16, (duty=1.17%, resolution=0.002%), mode=0, channel=0, timer=0
172+
PWM(Pin(27), freq=998, duty_u16=1024) # resolution=16, (duty=1.56%, resolution=0.002%), mode=0, channel=0, timer=0
173+
174+
175+
* Example of a **smooth duty change and PWM output inversion**::
176+
177+
from utime import sleep
178+
from machine import Pin, PWM
179+
180+
try:
181+
DUTY_MAX = 2**16 - 1
182+
183+
duty_u16 = 0
184+
delta_d = 2**16 // 32
185+
186+
pwm = PWM(Pin(27), 5000)
187+
pwmi = PWM(Pin(32), 5000, invert=1)
188+
189+
while True:
190+
pwm.duty_u16(duty_u16)
191+
pwmi.duty_u16(duty_u16)
192+
193+
duty_u16 += delta_d
194+
if duty_u16 >= DUTY_MAX:
195+
duty_u16 = DUTY_MAX
196+
delta_d = -delta_d
197+
elif duty_u16 <= 0:
198+
duty_u16 = 0
199+
delta_d = -delta_d
200+
201+
sleep(.01)
202+
print(pwm)
203+
print(pwmi)
204+
205+
finally:
206+
try:
207+
pwm.deinit()
208+
except:
209+
pass
210+
try:
211+
pwmi.deinit()
212+
except:
213+
pass
214+
215+
Output is::
216+
217+
...
218+
PWM(Pin(27), freq=5000, duty_u16=24576) # resolution=13, (duty=37.50%, resolution=0.012%), mode=0, channel=0, timer=0
219+
PWM(Pin(32), freq=5000, duty_u16=24576, invert=1) # resolution=13, (duty=37.50%, resolution=0.012%), mode=0, channel=1, timer=0
220+
PWM(Pin(27), freq=5000, duty_u16=26624) # resolution=13, (duty=40.63%, resolution=0.012%), mode=0, channel=0, timer=0
221+
PWM(Pin(32), freq=5000, duty_u16=26624, invert=1) # resolution=13, (duty=40.63%, resolution=0.012%), mode=0, channel=1, timer=0
222+
PWM(Pin(27), freq=5000, duty_u16=28672) # resolution=13, (duty=43.75%, resolution=0.012%), mode=0, channel=0, timer=0
223+
PWM(Pin(32), freq=5000, duty_u16=28672, invert=1) # resolution=13, (duty=43.75%, resolution=0.012%), mode=0, channel=1, timer=0
224+
...
225+
226+
See `PWM waves on Pin(27) and Pin(32) <https://user-images.githubusercontent.com/70886343/222743883-dca25aa8-681d-471c-933a-6f9beacb6eee.mp4>`_ with an oscilloscope.
101227

102-
See PWM wave at Pin(5) with an oscilloscope.
103228

104229
Note: the Pin.OUT mode does not need to be specified. The channel is initialized
105230
to PWM mode internally once for each Pin that is passed to the PWM constructor.

docs/library/machine.PWM.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,20 @@ Example usage::
1111
from machine import PWM
1212

1313
pwm = PWM(pin, freq=50, duty_u16=8192) # create a PWM object on a pin
14-
# and set freq and duty
15-
pwm.duty_u16(32768) # set duty to 50%
14+
# and set freq 50 Hz and duty 12.5%
15+
pwm.duty_u16(32768) # set duty to 50%
1616

1717
# reinitialise with a period of 200us, duty of 5us
1818
pwm.init(freq=5000, duty_ns=5000)
1919

20-
pwm.duty_ns(3000) # set pulse width to 3us
20+
pwm.duty_ns(3000) # set pulse width to 3us
2121

2222
pwm.deinit()
2323

2424
Constructors
2525
------------
2626

27-
.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert)
27+
.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert=False)
2828

2929
Construct and return a new PWM object using the following parameters:
3030

@@ -40,7 +40,7 @@ Constructors
4040
Setting *freq* may affect other PWM objects if the objects share the same
4141
underlying PWM generator (this is hardware specific).
4242
Only one of *duty_u16* and *duty_ns* should be specified at a time.
43-
*invert* is not available at all ports.
43+
*invert* is available at RP2, i.MXRT, SAMD, nRF, ESP32 ports.
4444

4545
Methods
4646
-------
@@ -73,6 +73,14 @@ Methods
7373
With a single *value* argument the duty cycle is set to that value, measured
7474
as the ratio ``value / 65535``.
7575

76+
Use functions like these to convert percentages to u16 and back::
77+
78+
def percents_to_u16(percents:int)->int:
79+
return (percents * 2**16 + 50) // 100
80+
81+
def u16_to_percents(u16:int)->int:
82+
return (u16 * 100 + 2**15) // 2**16
83+
7684
.. method:: PWM.duty_ns([value])
7785

7886
Get or set the current pulse width of the PWM output, as a value in nanoseconds.

0 commit comments

Comments
 (0)