diff options
| author | IhorNehrutsa <Ihor.Nehrutsa@gmail.com> | 2024-12-16 14:44:37 +0200 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-05-16 12:34:32 +1000 |
| commit | c310301f27e351e0dc9f966556cab512b3c82615 (patch) | |
| tree | 79dff427df44b2556f5ba2a4fa510b22a1f24453 | |
| parent | 6d74b4e3c15d0b677247ce02c459095d3cf03fc5 (diff) | |
docs/esp32: Improve PWM documentation and examples.
This reduces inconsitencies between esp32 and other ports.
According to the discussion in #10817.
Signed-off-by: Ihor Nehrutsa <Ihor.Nehrutsa@gmail.com>
| -rw-r--r-- | docs/esp32/quickref.rst | 35 | ||||
| -rw-r--r-- | docs/esp32/tutorial/pwm.rst | 220 | ||||
| -rw-r--r-- | docs/library/machine.PWM.rst | 14 |
3 files changed, 209 insertions, 60 deletions
diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index ccc01099d..707ae27c0 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -393,7 +393,7 @@ Use the :ref:`machine.PWM <machine.PWM>` class:: pwm0.duty(256) # set duty cycle from 0 to 1023 as a ratio duty/1023, (now 25%) duty_u16 = pwm0.duty_u16() # get current duty cycle, range 0-65535 - pwm0.duty_u16(2**16*3//4) # set duty cycle from 0 to 65535 as a ratio duty_u16/65535, (now 75%) + pwm0.duty_u16(65536*3//4) # set duty cycle from 0 to 65535 as a ratio duty_u16/65535, (now 75%) duty_ns = pwm0.duty_ns() # get current pulse width in ns 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:: pwm2 = PWM(Pin(2), freq=20000, duty=512) # create and configure in one go print(pwm2) # view PWM settings + pwm2.deinit() # turn off PWM on the pin + + pwm0 = PWM(Pin(0), duty_u16=16384) # The output is at a high level 25% of the time. + pwm2 = PWM(Pin(2), duty_u16=16384, invert=1) # The output is at a low level 25% of the time. + + pwm4 = PWM(Pin(4), lightsleep=True) # Allow PWM during light sleep mode ESP chips have different hardware peripherals: -===================================================== ======== ======== ======== -Hardware specification ESP32 ESP32-S2 ESP32-C3 ------------------------------------------------------ -------- -------- -------- -Number of groups (speed modes) 2 1 1 -Number of timers per group 4 4 4 -Number of channels per group 8 8 6 ------------------------------------------------------ -------- -------- -------- -Different PWM frequencies (groups * timers) 8 4 4 -Total PWM channels (Pins, duties) (groups * channels) 16 8 6 -===================================================== ======== ======== ======== +======================================================= ======== ========= ========== +Hardware specification ESP32 ESP32-S2, ESP32-C2, + ESP32-S3, ESP32-C3, + ESP32-P4 ESP32-C5, + ESP32-C6, + ESP32-H2 +------------------------------------------------------- -------- --------- ---------- +Number of groups (speed modes) 2 1 1 +Number of timers per group 4 4 4 +Number of channels per group 8 8 6 +------------------------------------------------------- -------- --------- ---------- +Different PWM frequencies = (groups * timers) 8 4 4 +Total PWM channels (Pins, duties) = (groups * channels) 16 8 6 +======================================================= ======== ========= ========== + +In light sleep, the ESP32 PWM can only operate in low speed mode, so only 4 timers and +8 channels are available. A maximum number of PWM channels (Pins) are available on the ESP32 - 16 channels, but only 8 different PWM frequencies are available, the remaining 8 channels must diff --git a/docs/esp32/tutorial/pwm.rst b/docs/esp32/tutorial/pwm.rst index 2650284d3..82d43b36f 100644 --- a/docs/esp32/tutorial/pwm.rst +++ b/docs/esp32/tutorial/pwm.rst @@ -11,16 +11,20 @@ compared with the length of a single period (low plus high time). Maximum duty cycle is when the pin is high all of the time, and minimum is when it is low all of the time. -* More comprehensive example with all 16 PWM channels and 8 timers:: +* More comprehensive example with all **16 PWM channels and 8 timers**:: + from time import sleep from machine import Pin, PWM try: - f = 100 # Hz - d = 1024 // 16 # 6.25% - pins = (15, 2, 4, 16, 18, 19, 22, 23, 25, 26, 27, 14 , 12, 13, 32, 33) + F = 10000 # Hz + D = 65536 // 16 # 6.25% + pins = (2, 4, 12, 13, 14, 15, 16, 18, 19, 22, 23, 25, 26, 27, 32, 33) pwms = [] for i, pin in enumerate(pins): - pwms.append(PWM(Pin(pin), freq=f * (i // 2 + 1), duty= 1023 if i==15 else d * (i + 1))) + f = F * (i // 2 + 1) + d = min(65535, D * (i + 1)) + pwms.append(PWM(pin, freq=f, duty_u16=d)) + sleep(2 / f) print(pwms[i]) finally: for pwm in pwms: @@ -31,65 +35,100 @@ low all of the time. Output is:: - PWM(Pin(15), freq=100, duty=64, resolution=10, mode=0, channel=0, timer=0) - PWM(Pin(2), freq=100, duty=128, resolution=10, mode=0, channel=1, timer=0) - PWM(Pin(4), freq=200, duty=192, resolution=10, mode=0, channel=2, timer=1) - PWM(Pin(16), freq=200, duty=256, resolution=10, mode=0, channel=3, timer=1) - PWM(Pin(18), freq=300, duty=320, resolution=10, mode=0, channel=4, timer=2) - PWM(Pin(19), freq=300, duty=384, resolution=10, mode=0, channel=5, timer=2) - PWM(Pin(22), freq=400, duty=448, resolution=10, mode=0, channel=6, timer=3) - PWM(Pin(23), freq=400, duty=512, resolution=10, mode=0, channel=7, timer=3) - PWM(Pin(25), freq=500, duty=576, resolution=10, mode=1, channel=0, timer=0) - PWM(Pin(26), freq=500, duty=640, resolution=10, mode=1, channel=1, timer=0) - PWM(Pin(27), freq=600, duty=704, resolution=10, mode=1, channel=2, timer=1) - PWM(Pin(14), freq=600, duty=768, resolution=10, mode=1, channel=3, timer=1) - PWM(Pin(12), freq=700, duty=832, resolution=10, mode=1, channel=4, timer=2) - PWM(Pin(13), freq=700, duty=896, resolution=10, mode=1, channel=5, timer=2) - PWM(Pin(32), freq=800, duty=960, resolution=10, mode=1, channel=6, timer=3) - PWM(Pin(33), freq=800, duty=1023, resolution=10, mode=1, channel=7, timer=3) - -* Example of a smooth frequency change:: + PWM(Pin(2), freq=10000, duty_u16=4096) + PWM(Pin(4), freq=10000, duty_u16=8192) + PWM(Pin(12), freq=20000, duty_u16=12288) + PWM(Pin(13), freq=20000, duty_u16=16384) + PWM(Pin(14), freq=30030, duty_u16=20480) + PWM(Pin(15), freq=30030, duty_u16=24576) + PWM(Pin(16), freq=40000, duty_u16=28672) + PWM(Pin(18), freq=40000, duty_u16=32768) + PWM(Pin(19), freq=50000, duty_u16=36864) + PWM(Pin(22), freq=50000, duty_u16=40960) + PWM(Pin(23), freq=60060, duty_u16=45056) + PWM(Pin(25), freq=60060, duty_u16=49152) + PWM(Pin(26), freq=69930, duty_u16=53248) + PWM(Pin(27), freq=69930, duty_u16=57344) + PWM(Pin(32), freq=80000, duty_u16=61440) + PWM(Pin(33), freq=80000, duty_u16=65535) + + +* Example of a **smooth frequency change**:: from time import sleep from machine import Pin, PWM - F_MIN = 500 - F_MAX = 1000 + F_MIN = 1000 + F_MAX = 10000 f = F_MIN - delta_f = 1 + delta_f = F_MAX // 50 - p = PWM(Pin(5), f) - print(p) + pwm = PWM(Pin(27), f) while True: - p.freq(f) - - sleep(10 / F_MIN) + pwm.freq(f) + sleep(1 / f) + sleep(0.1) + print(pwm) f += delta_f - if f >= F_MAX or f <= F_MIN: + if f > F_MAX or f < F_MIN: delta_f = -delta_f + print() + if f > F_MAX: + f = F_MAX + elif f < F_MIN: + f = F_MIN - See PWM wave at Pin(5) with an oscilloscope. + See PWM wave on Pin(27) with an oscilloscope. + + Output is:: -* Example of a smooth duty change:: + PWM(Pin(27), freq=998, duty_u16=32768) + PWM(Pin(27), freq=1202, duty_u16=32768) + PWM(Pin(27), freq=1401, duty_u16=32768) + PWM(Pin(27), freq=1598, duty_u16=32768) + ... + PWM(Pin(27), freq=9398, duty_u16=32768) + PWM(Pin(27), freq=9615, duty_u16=32768) + PWM(Pin(27), freq=9804, duty_u16=32768) + PWM(Pin(27), freq=10000, duty_u16=32768) + + PWM(Pin(27), freq=10000, duty_u16=32768) + PWM(Pin(27), freq=9804, duty_u16=32768) + PWM(Pin(27), freq=9615, duty_u16=32768) + PWM(Pin(27), freq=9398, duty_u16=32768) + ... + PWM(Pin(27), freq=1598, duty_u16=32768) + PWM(Pin(27), freq=1401, duty_u16=32768) + PWM(Pin(27), freq=1202, duty_u16=32768) + PWM(Pin(27), freq=998, duty_u16=32768) + + +* Example of a **smooth duty change**:: from time import sleep from machine import Pin, PWM - DUTY_MAX = 2**16 - 1 + DUTY_MAX = 65535 duty_u16 = 0 - delta_d = 16 + delta_d = 256 - p = PWM(Pin(5), 1000, duty_u16=duty_u16) - print(p) + pwm = PWM(Pin(27), freq=1000, duty_u16=duty_u16) while True: - p.duty_u16(duty_u16) + pwm.duty_u16(duty_u16) + sleep(2 / pwm.freq()) + print(pwm) - sleep(1 / 1000) + if duty_u16 >= DUTY_MAX: + print() + sleep(2) + elif duty_u16 <= 0: + print() + sleep(2) duty_u16 += delta_d if duty_u16 >= DUTY_MAX: @@ -99,9 +138,106 @@ low all of the time. duty_u16 = 0 delta_d = -delta_d - See PWM wave at Pin(5) with an oscilloscope. + PWM wave on Pin(27) with an oscilloscope. + + Output is:: + + PWM(Pin(27), freq=998, duty_u16=0) + PWM(Pin(27), freq=998, duty_u16=256) + PWM(Pin(27), freq=998, duty_u16=512) + PWM(Pin(27), freq=998, duty_u16=768) + PWM(Pin(27), freq=998, duty_u16=1024) + ... + PWM(Pin(27), freq=998, duty_u16=64512) + PWM(Pin(27), freq=998, duty_u16=64768) + PWM(Pin(27), freq=998, duty_u16=65024) + PWM(Pin(27), freq=998, duty_u16=65280) + PWM(Pin(27), freq=998, duty_u16=65535) + + PWM(Pin(27), freq=998, duty_u16=65279) + PWM(Pin(27), freq=998, duty_u16=65023) + PWM(Pin(27), freq=998, duty_u16=64767) + PWM(Pin(27), freq=998, duty_u16=64511) + ... + PWM(Pin(27), freq=998, duty_u16=1023) + PWM(Pin(27), freq=998, duty_u16=767) + PWM(Pin(27), freq=998, duty_u16=511) + PWM(Pin(27), freq=998, duty_u16=255) + PWM(Pin(27), freq=998, duty_u16=0) + + +* Example of a **smooth duty change and PWM output inversion**:: + + from utime import sleep + from machine import Pin, PWM + + try: + DUTY_MAX = 65535 + + duty_u16 = 0 + delta_d = 65536 // 32 + + pwm = PWM(Pin(27)) + pwmi = PWM(Pin(32), invert=1) + + while True: + pwm.duty_u16(duty_u16) + pwmi.duty_u16(duty_u16) + + duty_u16 += delta_d + if duty_u16 >= DUTY_MAX: + duty_u16 = DUTY_MAX + delta_d = -delta_d + elif duty_u16 <= 0: + duty_u16 = 0 + delta_d = -delta_d + + sleep(.01) + print(pwm) + print(pwmi) + + finally: + try: + pwm.deinit() + except: + pass + try: + pwmi.deinit() + except: + pass + + Output is:: + + PWM(Pin(27), freq=5000, duty_u16=0) + PWM(Pin(32), freq=5000, duty_u16=32768, invert=1) + PWM(Pin(27), freq=5000, duty_u16=2048) + PWM(Pin(32), freq=5000, duty_u16=2048, invert=1) + PWM(Pin(27), freq=5000, duty_u16=4096) + PWM(Pin(32), freq=5000, duty_u16=4096, invert=1) + PWM(Pin(27), freq=5000, duty_u16=6144) + PWM(Pin(32), freq=5000, duty_u16=6144, invert=1) + PWM(Pin(27), freq=5000, duty_u16=8192) + PWM(Pin(32), freq=5000, duty_u16=8192, invert=1) + ... + + + See PWM waves on Pin(27) and Pin(32) with an oscilloscope. + +Note: New PWM parameters take effect in the next PWM cycle. + + pwm = PWM(2, duty=512) + print(pwm) + >>> PWM(Pin(2), freq=5000, duty=1023) # the duty is not relevant + pwm.init(freq=2, duty=64) + print(pwm) + >>> PWM(Pin(2), freq=2, duty=16) # the duty is not relevant + time.sleep(1 / 2) # wait one PWM period + print(pwm) + >>> PWM(Pin(2), freq=2, duty=64) # the duty is actual + +Note: machine.freq(20_000_000) reduces the highest PWM frequency to 10 MHz. -Note: the Pin.OUT mode does not need to be specified. The channel is initialized +Note: the Pin.OUT mode does not need to be specified. The channel is initialized to PWM mode internally once for each Pin that is passed to the PWM constructor. The following code is wrong:: diff --git a/docs/library/machine.PWM.rst b/docs/library/machine.PWM.rst index 5f592b8df..c2b606aff 100644 --- a/docs/library/machine.PWM.rst +++ b/docs/library/machine.PWM.rst @@ -11,20 +11,20 @@ Example usage:: from machine import PWM pwm = PWM(pin, freq=50, duty_u16=8192) # create a PWM object on a pin - # and set freq and duty - pwm.duty_u16(32768) # set duty to 50% + # and set freq 50 Hz and duty 12.5% + pwm.duty_u16(32768) # set duty to 50% # reinitialise with a period of 200us, duty of 5us pwm.init(freq=5000, duty_ns=5000) - pwm.duty_ns(3000) # set pulse width to 3us + pwm.duty_ns(3000) # set pulse width to 3us pwm.deinit() Constructors ------------ -.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert) +.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert=False) Construct and return a new PWM object using the following parameters: @@ -40,7 +40,7 @@ Constructors Setting *freq* may affect other PWM objects if the objects share the same underlying PWM generator (this is hardware specific). Only one of *duty_u16* and *duty_ns* should be specified at a time. - *invert* is not available at all ports. + *invert* is available only on the esp32, mimxrt, nrf, rp2, samd and zephyr ports. Methods ------- @@ -116,10 +116,10 @@ Limitations of PWM resolution of 8 bit, not 16-bit as may be expected. In this case, the lowest 8 bits of *duty_u16* are insignificant. So:: - pwm=PWM(Pin(13), freq=300_000, duty_u16=2**16//2) + pwm=PWM(Pin(13), freq=300_000, duty_u16=65536//2) and:: - pwm=PWM(Pin(13), freq=300_000, duty_u16=2**16//2 + 255) + pwm=PWM(Pin(13), freq=300_000, duty_u16=65536//2 + 255) will generate PWM with the same 50% duty cycle. |
