Skip to content

Commit c310301

Browse files
IhorNehrutsadpgeorge
authored andcommitted
docs/esp32: Improve PWM documentation and examples.
This reduces inconsitencies between esp32 and other ports. According to the discussion in micropython#10817. Signed-off-by: Ihor Nehrutsa <[email protected]>
1 parent 6d74b4e commit c310301

File tree

3 files changed

+209
-60
lines changed

3 files changed

+209
-60
lines changed

docs/esp32/quickref.rst

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ Use the :ref:`machine.PWM <machine.PWM>` class::
393393
pwm0.duty(256) # set duty cycle from 0 to 1023 as a ratio duty/1023, (now 25%)
394394

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

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

403403
pwm2 = PWM(Pin(2), freq=20000, duty=512) # create and configure in one go
404404
print(pwm2) # view PWM settings
405+
pwm2.deinit() # turn off PWM on the pin
406+
407+
pwm0 = PWM(Pin(0), duty_u16=16384) # The output is at a high level 25% of the time.
408+
pwm2 = PWM(Pin(2), duty_u16=16384, invert=1) # The output is at a low level 25% of the time.
409+
410+
pwm4 = PWM(Pin(4), lightsleep=True) # Allow PWM during light sleep mode
405411

406412
ESP chips have different hardware peripherals:
407413

408-
===================================================== ======== ======== ========
409-
Hardware specification ESP32 ESP32-S2 ESP32-C3
410-
----------------------------------------------------- -------- -------- --------
411-
Number of groups (speed modes) 2 1 1
412-
Number of timers per group 4 4 4
413-
Number of channels per group 8 8 6
414-
----------------------------------------------------- -------- -------- --------
415-
Different PWM frequencies (groups * timers) 8 4 4
416-
Total PWM channels (Pins, duties) (groups * channels) 16 8 6
417-
===================================================== ======== ======== ========
414+
======================================================= ======== ========= ==========
415+
Hardware specification ESP32 ESP32-S2, ESP32-C2,
416+
ESP32-S3, ESP32-C3,
417+
ESP32-P4 ESP32-C5,
418+
ESP32-C6,
419+
ESP32-H2
420+
------------------------------------------------------- -------- --------- ----------
421+
Number of groups (speed modes) 2 1 1
422+
Number of timers per group 4 4 4
423+
Number of channels per group 8 8 6
424+
------------------------------------------------------- -------- --------- ----------
425+
Different PWM frequencies = (groups * timers) 8 4 4
426+
Total PWM channels (Pins, duties) = (groups * channels) 16 8 6
427+
======================================================= ======== ========= ==========
428+
429+
In light sleep, the ESP32 PWM can only operate in low speed mode, so only 4 timers and
430+
8 channels are available.
418431

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

docs/esp32/tutorial/pwm.rst

Lines changed: 178 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,20 @@ 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:
18-
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)
19+
F = 10000 # Hz
20+
D = 65536 // 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+
f = F * (i // 2 + 1)
25+
d = min(65535, D * (i + 1))
26+
pwms.append(PWM(pin, freq=f, duty_u16=d))
27+
sleep(2 / f)
2428
print(pwms[i])
2529
finally:
2630
for pwm in pwms:
@@ -31,65 +35,100 @@ low all of the time.
3135

3236
Output is::
3337

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::
38+
PWM(Pin(2), freq=10000, duty_u16=4096)
39+
PWM(Pin(4), freq=10000, duty_u16=8192)
40+
PWM(Pin(12), freq=20000, duty_u16=12288)
41+
PWM(Pin(13), freq=20000, duty_u16=16384)
42+
PWM(Pin(14), freq=30030, duty_u16=20480)
43+
PWM(Pin(15), freq=30030, duty_u16=24576)
44+
PWM(Pin(16), freq=40000, duty_u16=28672)
45+
PWM(Pin(18), freq=40000, duty_u16=32768)
46+
PWM(Pin(19), freq=50000, duty_u16=36864)
47+
PWM(Pin(22), freq=50000, duty_u16=40960)
48+
PWM(Pin(23), freq=60060, duty_u16=45056)
49+
PWM(Pin(25), freq=60060, duty_u16=49152)
50+
PWM(Pin(26), freq=69930, duty_u16=53248)
51+
PWM(Pin(27), freq=69930, duty_u16=57344)
52+
PWM(Pin(32), freq=80000, duty_u16=61440)
53+
PWM(Pin(33), freq=80000, duty_u16=65535)
54+
55+
56+
* Example of a **smooth frequency change**::
5257

5358
from time import sleep
5459
from machine import Pin, PWM
5560

56-
F_MIN = 500
57-
F_MAX = 1000
61+
F_MIN = 1000
62+
F_MAX = 10000
5863

5964
f = F_MIN
60-
delta_f = 1
65+
delta_f = F_MAX // 50
6166

62-
p = PWM(Pin(5), f)
63-
print(p)
67+
pwm = PWM(Pin(27), f)
6468

6569
while True:
66-
p.freq(f)
67-
68-
sleep(10 / F_MIN)
70+
pwm.freq(f)
71+
sleep(1 / f)
72+
sleep(0.1)
73+
print(pwm)
6974

7075
f += delta_f
71-
if f >= F_MAX or f <= F_MIN:
76+
if f > F_MAX or f < F_MIN:
7277
delta_f = -delta_f
78+
print()
79+
if f > F_MAX:
80+
f = F_MAX
81+
elif f < F_MIN:
82+
f = F_MIN
7383

74-
See PWM wave at Pin(5) with an oscilloscope.
84+
See PWM wave on Pin(27) with an oscilloscope.
85+
86+
Output is::
7587

76-
* Example of a smooth duty change::
88+
PWM(Pin(27), freq=998, duty_u16=32768)
89+
PWM(Pin(27), freq=1202, duty_u16=32768)
90+
PWM(Pin(27), freq=1401, duty_u16=32768)
91+
PWM(Pin(27), freq=1598, duty_u16=32768)
92+
...
93+
PWM(Pin(27), freq=9398, duty_u16=32768)
94+
PWM(Pin(27), freq=9615, duty_u16=32768)
95+
PWM(Pin(27), freq=9804, duty_u16=32768)
96+
PWM(Pin(27), freq=10000, duty_u16=32768)
97+
98+
PWM(Pin(27), freq=10000, duty_u16=32768)
99+
PWM(Pin(27), freq=9804, duty_u16=32768)
100+
PWM(Pin(27), freq=9615, duty_u16=32768)
101+
PWM(Pin(27), freq=9398, duty_u16=32768)
102+
...
103+
PWM(Pin(27), freq=1598, duty_u16=32768)
104+
PWM(Pin(27), freq=1401, duty_u16=32768)
105+
PWM(Pin(27), freq=1202, duty_u16=32768)
106+
PWM(Pin(27), freq=998, duty_u16=32768)
107+
108+
109+
* Example of a **smooth duty change**::
77110

78111
from time import sleep
79112
from machine import Pin, PWM
80113

81-
DUTY_MAX = 2**16 - 1
114+
DUTY_MAX = 65535
82115

83116
duty_u16 = 0
84-
delta_d = 16
117+
delta_d = 256
85118

86-
p = PWM(Pin(5), 1000, duty_u16=duty_u16)
87-
print(p)
119+
pwm = PWM(Pin(27), freq=1000, duty_u16=duty_u16)
88120

89121
while True:
90-
p.duty_u16(duty_u16)
122+
pwm.duty_u16(duty_u16)
123+
sleep(2 / pwm.freq())
124+
print(pwm)
91125

92-
sleep(1 / 1000)
126+
if duty_u16 >= DUTY_MAX:
127+
print()
128+
sleep(2)
129+
elif duty_u16 <= 0:
130+
print()
131+
sleep(2)
93132

94133
duty_u16 += delta_d
95134
if duty_u16 >= DUTY_MAX:
@@ -99,9 +138,106 @@ low all of the time.
99138
duty_u16 = 0
100139
delta_d = -delta_d
101140

102-
See PWM wave at Pin(5) with an oscilloscope.
141+
PWM wave on Pin(27) with an oscilloscope.
142+
143+
Output is::
144+
145+
PWM(Pin(27), freq=998, duty_u16=0)
146+
PWM(Pin(27), freq=998, duty_u16=256)
147+
PWM(Pin(27), freq=998, duty_u16=512)
148+
PWM(Pin(27), freq=998, duty_u16=768)
149+
PWM(Pin(27), freq=998, duty_u16=1024)
150+
...
151+
PWM(Pin(27), freq=998, duty_u16=64512)
152+
PWM(Pin(27), freq=998, duty_u16=64768)
153+
PWM(Pin(27), freq=998, duty_u16=65024)
154+
PWM(Pin(27), freq=998, duty_u16=65280)
155+
PWM(Pin(27), freq=998, duty_u16=65535)
156+
157+
PWM(Pin(27), freq=998, duty_u16=65279)
158+
PWM(Pin(27), freq=998, duty_u16=65023)
159+
PWM(Pin(27), freq=998, duty_u16=64767)
160+
PWM(Pin(27), freq=998, duty_u16=64511)
161+
...
162+
PWM(Pin(27), freq=998, duty_u16=1023)
163+
PWM(Pin(27), freq=998, duty_u16=767)
164+
PWM(Pin(27), freq=998, duty_u16=511)
165+
PWM(Pin(27), freq=998, duty_u16=255)
166+
PWM(Pin(27), freq=998, duty_u16=0)
167+
168+
169+
* Example of a **smooth duty change and PWM output inversion**::
170+
171+
from utime import sleep
172+
from machine import Pin, PWM
173+
174+
try:
175+
DUTY_MAX = 65535
176+
177+
duty_u16 = 0
178+
delta_d = 65536 // 32
179+
180+
pwm = PWM(Pin(27))
181+
pwmi = PWM(Pin(32), invert=1)
182+
183+
while True:
184+
pwm.duty_u16(duty_u16)
185+
pwmi.duty_u16(duty_u16)
186+
187+
duty_u16 += delta_d
188+
if duty_u16 >= DUTY_MAX:
189+
duty_u16 = DUTY_MAX
190+
delta_d = -delta_d
191+
elif duty_u16 <= 0:
192+
duty_u16 = 0
193+
delta_d = -delta_d
194+
195+
sleep(.01)
196+
print(pwm)
197+
print(pwmi)
198+
199+
finally:
200+
try:
201+
pwm.deinit()
202+
except:
203+
pass
204+
try:
205+
pwmi.deinit()
206+
except:
207+
pass
208+
209+
Output is::
210+
211+
PWM(Pin(27), freq=5000, duty_u16=0)
212+
PWM(Pin(32), freq=5000, duty_u16=32768, invert=1)
213+
PWM(Pin(27), freq=5000, duty_u16=2048)
214+
PWM(Pin(32), freq=5000, duty_u16=2048, invert=1)
215+
PWM(Pin(27), freq=5000, duty_u16=4096)
216+
PWM(Pin(32), freq=5000, duty_u16=4096, invert=1)
217+
PWM(Pin(27), freq=5000, duty_u16=6144)
218+
PWM(Pin(32), freq=5000, duty_u16=6144, invert=1)
219+
PWM(Pin(27), freq=5000, duty_u16=8192)
220+
PWM(Pin(32), freq=5000, duty_u16=8192, invert=1)
221+
...
222+
223+
224+
See PWM waves on Pin(27) and Pin(32) with an oscilloscope.
225+
226+
Note: New PWM parameters take effect in the next PWM cycle.
227+
228+
pwm = PWM(2, duty=512)
229+
print(pwm)
230+
>>> PWM(Pin(2), freq=5000, duty=1023) # the duty is not relevant
231+
pwm.init(freq=2, duty=64)
232+
print(pwm)
233+
>>> PWM(Pin(2), freq=2, duty=16) # the duty is not relevant
234+
time.sleep(1 / 2) # wait one PWM period
235+
print(pwm)
236+
>>> PWM(Pin(2), freq=2, duty=64) # the duty is actual
237+
238+
Note: machine.freq(20_000_000) reduces the highest PWM frequency to 10 MHz.
103239

104-
Note: the Pin.OUT mode does not need to be specified. The channel is initialized
240+
Note: the Pin.OUT mode does not need to be specified. The channel is initialized
105241
to PWM mode internally once for each Pin that is passed to the PWM constructor.
106242

107243
The following code is wrong::

docs/library/machine.PWM.rst

Lines changed: 7 additions & 7 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 only on the esp32, mimxrt, nrf, rp2, samd and zephyr ports.
4444

4545
Methods
4646
-------
@@ -116,10 +116,10 @@ Limitations of PWM
116116
resolution of 8 bit, not 16-bit as may be expected. In this case, the lowest
117117
8 bits of *duty_u16* are insignificant. So::
118118

119-
pwm=PWM(Pin(13), freq=300_000, duty_u16=2**16//2)
119+
pwm=PWM(Pin(13), freq=300_000, duty_u16=65536//2)
120120

121121
and::
122122

123-
pwm=PWM(Pin(13), freq=300_000, duty_u16=2**16//2 + 255)
123+
pwm=PWM(Pin(13), freq=300_000, duty_u16=65536//2 + 255)
124124

125125
will generate PWM with the same 50% duty cycle.

0 commit comments

Comments
 (0)