diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index d17040277167..18fcd9986712 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -307,11 +307,11 @@ Use the :ref:`machine.PWM ` class:: freq = pwm0.freq() # get current frequency pwm0.freq(1000) # set PWM frequency from 1Hz to 40MHz - duty = pwm0.duty() # get current duty cycle, range 0-1024 (default 512, 50%) - pwm0.duty(256) # set duty cycle from 0 to 1024 as a ratio duty/1024, (now 25%) + duty = pwm0.duty() # get current duty cycle, range 0-1023 (default 512, 50%) + 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-65536 - pwm0.duty_u16(2**16*3//4) # set duty cycle from 0 to 65536 as a ratio duty_u16/65536, (now 75%) + 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%) 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%) @@ -351,7 +351,7 @@ Note: New PWM parameters take effect in the next PWM cycle. pwm = PWM(2, duty=512) print(pwm) - >>>PWM(Pin(2), freq=5000, duty=1024) # the duty is not relevant + >>>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 diff --git a/docs/esp32/tutorial/pwm.rst b/docs/esp32/tutorial/pwm.rst index 2e1b82a58510..9fa8553bdf70 100644 --- a/docs/esp32/tutorial/pwm.rst +++ b/docs/esp32/tutorial/pwm.rst @@ -16,14 +16,16 @@ low all of the time. from time import sleep from machine import Pin, PWM try: - f = 100 # Hz - d = 2**16 // 16 # 6.25% + F = 10000 # Hz + D = 2**16 // 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_u16=min(2**16 - 1, d * (i + 1)))) + f = F * (i // 2 + 1) + d = min(2**16 - 1, D * (i + 1)) + pwms.append(PWM(pin, freq=f, duty_u16=d)) + sleep(1/f) print(pwms[i]) - sleep(60) finally: for pwm in pwms: try: @@ -33,22 +35,22 @@ low all of the time. Output is:: - PWM(Pin(2), freq=100, duty_u16=4096) # resolution=16, (duty=6.25%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(4), freq=100, duty_u16=8192) # resolution=16, (duty=12.50%, resolution=0.002%), mode=0, channel=1, timer=0 - PWM(Pin(12), freq=199, duty_u16=12288) # resolution=16, (duty=18.75%, resolution=0.002%), mode=0, channel=2, timer=1 - PWM(Pin(13), freq=199, duty_u16=16384) # resolution=16, (duty=25.00%, resolution=0.002%), mode=0, channel=3, timer=1 - PWM(Pin(14), freq=299, duty_u16=20480) # resolution=16, (duty=31.25%, resolution=0.002%), mode=0, channel=4, timer=2 - PWM(Pin(15), freq=299, duty_u16=24576) # resolution=16, (duty=37.50%, resolution=0.002%), mode=0, channel=5, timer=2 - PWM(Pin(16), freq=400, duty_u16=28672) # resolution=16, (duty=43.75%, resolution=0.002%), mode=0, channel=6, timer=3 - PWM(Pin(18), freq=400, duty_u16=32768) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=7, timer=3 - PWM(Pin(19), freq=500, duty_u16=36864) # resolution=16, (duty=56.25%, resolution=0.002%), mode=1, channel=0, timer=0 - PWM(Pin(22), freq=500, duty_u16=40960) # resolution=16, (duty=62.50%, resolution=0.002%), mode=1, channel=1, timer=0 - PWM(Pin(23), freq=599, duty_u16=45056) # resolution=16, (duty=68.75%, resolution=0.002%), mode=1, channel=2, timer=1 - PWM(Pin(25), freq=599, duty_u16=49152) # resolution=16, (duty=75.00%, resolution=0.002%), mode=1, channel=3, timer=1 - PWM(Pin(26), freq=700, duty_u16=53248) # resolution=16, (duty=81.25%, resolution=0.002%), mode=1, channel=4, timer=2 - PWM(Pin(27), freq=700, duty_u16=57344) # resolution=16, (duty=87.50%, resolution=0.002%), mode=1, channel=5, timer=2 - PWM(Pin(32), freq=799, duty_u16=61440) # resolution=16, (duty=93.75%, resolution=0.002%), mode=1, channel=6, timer=3 - PWM(Pin(33), freq=799, duty_u16=65536) # resolution=16, (duty=100.00%, resolution=0.002%), mode=1, channel=7, timer=3 + PWM(Pin(2), freq=10000, duty_u16=4096) # duty=6.25%, raw_duty=256, resolution=12, mode=0, channel=0, timer=0 + PWM(Pin(4), freq=10000, duty_u16=8192) # duty=12.50%, raw_duty=512, resolution=12, mode=0, channel=1, timer=0 + PWM(Pin(12), freq=20000, duty_u16=12288) # duty=18.75%, raw_duty=384, resolution=11, mode=0, channel=2, timer=1 + PWM(Pin(13), freq=20000, duty_u16=16384) # duty=25.00%, raw_duty=512, resolution=11, mode=0, channel=3, timer=1 + PWM(Pin(14), freq=30030, duty_u16=20480) # duty=31.25%, raw_duty=640, resolution=11, mode=0, channel=4, timer=2 + PWM(Pin(15), freq=30030, duty_u16=24576) # duty=37.50%, raw_duty=768, resolution=11, mode=0, channel=5, timer=2 + PWM(Pin(16), freq=40000, duty_u16=28672) # duty=43.75%, raw_duty=448, resolution=10, mode=0, channel=6, timer=3 + PWM(Pin(18), freq=40000, duty_u16=32768) # duty=50.00%, raw_duty=512, resolution=10, mode=0, channel=7, timer=3 + PWM(Pin(19), freq=50000, duty_u16=36864) # duty=56.25%, raw_duty=576, resolution=10, mode=1, channel=0, timer=0 + PWM(Pin(22), freq=50000, duty_u16=40960) # duty=62.50%, raw_duty=640, resolution=10, mode=1, channel=1, timer=0 + PWM(Pin(23), freq=60060, duty_u16=45056) # duty=68.75%, raw_duty=704, resolution=10, mode=1, channel=2, timer=1 + PWM(Pin(25), freq=60060, duty_u16=49152) # duty=75.00%, raw_duty=768, resolution=10, mode=1, channel=3, timer=1 + PWM(Pin(26), freq=69930, duty_u16=53248) # duty=81.25%, raw_duty=832, resolution=10, mode=1, channel=4, timer=2 + PWM(Pin(27), freq=69930, duty_u16=57344) # duty=87.50%, raw_duty=896, resolution=10, mode=1, channel=5, timer=2 + PWM(Pin(32), freq=80000, duty_u16=61440) # duty=93.75%, raw_duty=480, resolution=9, mode=1, channel=6, timer=3 + PWM(Pin(33), freq=80000, duty_u16=65535) # duty=100.00%, raw_duty=0, resolution=9, mode=1, channel=7, timer=3 * Example of a **smooth frequency change**:: @@ -56,17 +58,18 @@ low all of the time. from time import sleep from machine import Pin, PWM - F_MIN = 100 - F_MAX = 1000 + F_MIN = 1000 + F_MAX = 10000 f = F_MIN - delta_f = 100 + delta_f = F_MAX // 50 - p = PWM(Pin(27), f) + pwm = PWM(Pin(27), f) while True: - p.freq(f) - print(p) + pwm.freq(f) + sleep(1/f) + print(pwm) sleep(.2) @@ -83,28 +86,25 @@ low all of the time. Output is:: - PWM(Pin(27), freq=100, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=199, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=299, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=400, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=500, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=599, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=700, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=799, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=900, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - - PWM(Pin(27), freq=998, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=900, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=799, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=700, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=599, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=500, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=400, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=299, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=199, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=100, duty=512) # resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0 - ... +PWM(Pin(27), freq=998, duty_u16=32768) # duty=50.00%, raw_duty=32768, resolution=16, mode=0, channel=0, timer=2 +PWM(Pin(27), freq=1202, duty_u16=32768) # duty=50.00%, raw_duty=32768, resolution=16, mode=0, channel=0, timer=2 +PWM(Pin(27), freq=1401, duty_u16=65536) # duty=100.00%, raw_duty=32768, resolution=15, mode=0, channel=0, timer=2 +PWM(Pin(27), freq=1598, duty_u16=32768) # duty=50.00%, raw_duty=16384, resolution=15, mode=0, channel=0, timer=2 +... +PWM(Pin(27), freq=9398, duty_u16=32768) # duty=50.00%, raw_duty=4096, resolution=13, mode=0, channel=0, timer=0 +PWM(Pin(27), freq=9615, duty_u16=32768) # duty=50.00%, raw_duty=4096, resolution=13, mode=0, channel=0, timer=0 +PWM(Pin(27), freq=9804, duty_u16=32768) # duty=50.00%, raw_duty=2048, resolution=12, mode=0, channel=0, timer=0 +PWM(Pin(27), freq=10000, duty_u16=32768) # duty=50.00%, raw_duty=2048, resolution=12, mode=0, channel=0, timer=1 + +PWM(Pin(27), freq=10000, duty_u16=32768) # duty=50.00%, raw_duty=2048, resolution=12, mode=0, channel=0, timer=1 +PWM(Pin(27), freq=9804, duty_u16=32768) # duty=50.00%, raw_duty=2048, resolution=12, mode=0, channel=0, timer=0 +PWM(Pin(27), freq=9615, duty_u16=32768) # duty=50.00%, raw_duty=4096, resolution=13, mode=0, channel=0, timer=0 +PWM(Pin(27), freq=9398, duty_u16=32768) # duty=50.00%, raw_duty=4096, resolution=13, mode=0, channel=0, timer=0 +... +PWM(Pin(27), freq=1598, duty_u16=32768) # duty=50.00%, raw_duty=16384, resolution=15, mode=0, channel=0, timer=2 +PWM(Pin(27), freq=1401, duty_u16=32768) # duty=50.00%, raw_duty=16384, resolution=15, mode=0, channel=0, timer=2 +PWM(Pin(27), freq=1202, duty_u16=16384) # duty=25.00%, raw_duty=16384, resolution=16, mode=0, channel=0, timer=2 +PWM(Pin(27), freq=998, duty_u16=32768) # duty=50.00%, raw_duty=32768, resolution=16, mode=0, channel=0, timer=2 * Example of a **smooth duty change**:: @@ -117,59 +117,54 @@ low all of the time. duty_u16 = 0 delta_d = 256 - pwm = PWM(Pin(27), 1000, duty_u16=duty_u16) - print(pwm) + pwm = PWM(Pin(27), freq=1000, duty_u16=duty_u16) while True: pwm.duty_u16(duty_u16) - - sleep(.001) - + sleep(2/pwm.freq()) print(pwm) + if duty_u16 >= DUTY_MAX: + print() + sleep(2) + elif duty_u16 <= 0: + print() + sleep(2) + duty_u16 += delta_d if duty_u16 >= DUTY_MAX: duty_u16 = DUTY_MAX delta_d = -delta_d - print() elif duty_u16 <= 0: duty_u16 = 0 delta_d = -delta_d - print() See `PWM wave on Pin(27) with an oscilloscope. `_ Output is:: - PWM(Pin(27), freq=998, duty_u16=0) # resolution=16, (duty=0.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=256) # resolution=16, (duty=0.39%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=512) # resolution=16, (duty=0.78%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=768) # resolution=16, (duty=1.17%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=1024) # resolution=16, (duty=1.56%, resolution=0.002%), mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=0) # duty=0.00%, raw_duty=0, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=256) # duty=0.39%, raw_duty=256, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=512) # duty=0.78%, raw_duty=512, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=768) # duty=1.17%, raw_duty=768, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=1024) # duty=1.56%, raw_duty=1024, resolution=16, mode=0, channel=0, timer=0 ... - PWM(Pin(27), freq=998, duty_u16=64256) # resolution=16, (duty=98.05%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=64512) # resolution=16, (duty=98.44%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=64768) # resolution=16, (duty=98.83%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=65024) # resolution=16, (duty=99.22%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=65280) # resolution=16, (duty=99.61%, resolution=0.002%), mode=0, channel=0, timer=0 - - PWM(Pin(27), freq=998, duty_u16=65536) # resolution=16, (duty=100.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=65279) # resolution=16, (duty=99.61%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=65023) # resolution=16, (duty=99.22%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=64767) # resolution=16, (duty=98.83%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=64511) # resolution=16, (duty=98.44%, resolution=0.002%), mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=64512) # duty=98.44%, raw_duty=64512, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=64768) # duty=98.83%, raw_duty=64768, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=65024) # duty=99.22%, raw_duty=65024, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=65280) # duty=99.61%, raw_duty=65280, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=65535) # duty=100.00%, raw_duty=0, resolution=16, mode=0, channel=0, timer=0 + + PWM(Pin(27), freq=998, duty_u16=65279) # duty=99.61%, raw_duty=65279, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=65023) # duty=99.22%, raw_duty=65023, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=64767) # duty=98.83%, raw_duty=64767, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=64511) # duty=98.44%, raw_duty=64511, resolution=16, mode=0, channel=0, timer=0 ... - PWM(Pin(27), freq=998, duty_u16=1279) # resolution=16, (duty=1.95%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=1024) # resolution=16, (duty=1.56%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=767) # resolution=16, (duty=1.17%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=511) # resolution=16, (duty=0.78%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=255) # resolution=16, (duty=0.39%, resolution=0.002%), mode=0, channel=0, timer=0 - - PWM(Pin(27), freq=998, duty_u16=0) # resolution=16, (duty=0.00%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=256) # resolution=16, (duty=0.39%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=512) # resolution=16, (duty=0.78%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=768) # resolution=16, (duty=1.17%, resolution=0.002%), mode=0, channel=0, timer=0 - PWM(Pin(27), freq=998, duty_u16=1024) # resolution=16, (duty=1.56%, resolution=0.002%), mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=1023) # duty=1.56%, raw_duty=1023, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=767) # duty=1.17%, raw_duty=767, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=511) # duty=0.78%, raw_duty=511, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=255) # duty=0.39%, raw_duty=255, resolution=16, mode=0, channel=0, timer=0 + PWM(Pin(27), freq=998, duty_u16=0) # duty=0.00%, raw_duty=0, resolution=16, mode=0, channel=0, timer=0 * Example of a **smooth duty change and PWM output inversion**:: @@ -183,8 +178,8 @@ low all of the time. duty_u16 = 0 delta_d = 2**16 // 32 - pwm = PWM(Pin(27), 5000) - pwmi = PWM(Pin(32), 5000, invert=1) + pwm = PWM(Pin(27)) + pwmi = PWM(Pin(32), invert=1) while True: pwm.duty_u16(duty_u16) @@ -214,19 +209,23 @@ low all of the time. Output is:: + PWM(Pin(27), freq=5000, duty_u16=0) # duty=0.00%, raw_duty=0, resolution=13, mode=0, channel=0, timer=3 + PWM(Pin(32), freq=5000, duty_u16=32768, invert=1) # duty=50.00%, raw_duty=4096, resolution=13, mode=0, channel=1, timer=3 + PWM(Pin(27), freq=5000, duty_u16=2048) # duty=3.13%, raw_duty=256, resolution=13, mode=0, channel=0, timer=3 + PWM(Pin(32), freq=5000, duty_u16=2048, invert=1) # duty=3.13%, raw_duty=256, resolution=13, mode=0, channel=1, timer=3 + PWM(Pin(27), freq=5000, duty_u16=4096) # duty=6.25%, raw_duty=512, resolution=13, mode=0, channel=0, timer=3 + PWM(Pin(32), freq=5000, duty_u16=4096, invert=1) # duty=6.25%, raw_duty=512, resolution=13, mode=0, channel=1, timer=3 + PWM(Pin(27), freq=5000, duty_u16=6144) # duty=9.38%, raw_duty=768, resolution=13, mode=0, channel=0, timer=3 + PWM(Pin(32), freq=5000, duty_u16=6144, invert=1) # duty=9.38%, raw_duty=768, resolution=13, mode=0, channel=1, timer=3 + PWM(Pin(27), freq=5000, duty_u16=8192) # duty=12.50%, raw_duty=1024, resolution=13, mode=0, channel=0, timer=3 + PWM(Pin(32), freq=5000, duty_u16=8192, invert=1) # duty=12.50%, raw_duty=1024, resolution=13, mode=0, channel=1, timer=3 ... ... - PWM(Pin(27), freq=5000, duty_u16=24576) # resolution=13, (duty=37.50%, resolution=0.012%), mode=0, channel=0, timer=0 - PWM(Pin(32), freq=5000, duty_u16=24576, invert=1) # resolution=13, (duty=37.50%, resolution=0.012%), mode=0, channel=1, timer=0 - PWM(Pin(27), freq=5000, duty_u16=26624) # resolution=13, (duty=40.63%, resolution=0.012%), mode=0, channel=0, timer=0 - PWM(Pin(32), freq=5000, duty_u16=26624, invert=1) # resolution=13, (duty=40.63%, resolution=0.012%), mode=0, channel=1, timer=0 - PWM(Pin(27), freq=5000, duty_u16=28672) # resolution=13, (duty=43.75%, resolution=0.012%), mode=0, channel=0, timer=0 - PWM(Pin(32), freq=5000, duty_u16=28672, invert=1) # resolution=13, (duty=43.75%, resolution=0.012%), mode=0, channel=1, timer=0 - ... + See `PWM waves on Pin(27) and Pin(32) `_ with an oscilloscope. -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/drivers/dht/dht.c b/drivers/dht/dht.c index e6b71542dabc..47d114fed784 100644 --- a/drivers/dht/dht.c +++ b/drivers/dht/dht.c @@ -71,8 +71,9 @@ static mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in) { } } + #define WAIT_OPPOSITE false // time pulse, should be 80us - ticks = machine_time_pulse_us(pin, 1, 150); + ticks = machine_time_pulse_us(pin, 1, 150, WAIT_OPPOSITE); if ((mp_int_t)ticks < 0) { goto timeout; } @@ -80,7 +81,7 @@ static mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in) { // time 40 pulses for data (either 26us or 70us) uint8_t *buf = bufinfo.buf; for (int i = 0; i < 40; ++i) { - ticks = machine_time_pulse_us(pin, 1, 100); + ticks = machine_time_pulse_us(pin, 1, 100, WAIT_OPPOSITE); if ((mp_int_t)ticks < 0) { goto timeout; } diff --git a/extmod/machine_pulse.c b/extmod/machine_pulse.c index 7ffe78481b2b..6077ca3b166a 100644 --- a/extmod/machine_pulse.c +++ b/extmod/machine_pulse.c @@ -30,8 +30,14 @@ #if MICROPY_PY_MACHINE_PULSE -MP_WEAK mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) { +MP_WEAK mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us, bool wait_opposite) { mp_uint_t start = mp_hal_ticks_us(); + while (wait_opposite && (mp_hal_pin_read(pin) == pulse_level)) { + if ((uint64_t)(mp_hal_ticks_us() - start) >= (uint64_t)timeout_us) { + return (mp_uint_t)-3; + } + } + start = mp_hal_ticks_us(); while (mp_hal_pin_read(pin) != pulse_level) { if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) { return (mp_uint_t)-2; @@ -56,11 +62,15 @@ static mp_obj_t machine_time_pulse_us_(size_t n_args, const mp_obj_t *args) { if (n_args > 2) { timeout_us = mp_obj_get_int(args[2]); } - mp_uint_t us = machine_time_pulse_us(pin, level, timeout_us); - // May return -1 or -2 in case of timeout + bool wait_opposite = false; + if (n_args > 3) { + wait_opposite = mp_obj_is_true(args[3]); + } + mp_uint_t us = machine_time_pulse_us(pin, level, timeout_us, wait_opposite); + // May return -1 or -2 or -3 in case of timeout return mp_obj_new_int(us); } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj, 2, 3, machine_time_pulse_us_); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj, 2, 4, machine_time_pulse_us_); MP_WEAK mp_uint_t/*uint64_t*/ machine_time_hardware_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) { /*mp_uint_t*/uint64_t start = mp_hal_ticks_us(); @@ -87,11 +97,9 @@ MP_WEAK mp_uint_t/*uint64_t*/ machine_time_hardware_pulse_us(mp_hal_pin_obj_t pi } start = mp_hal_ticks_us(); while (mp_hal_pin_read(pin) == pulse_level) { - /* if ((uint64_t)(mp_hal_ticks_us() - start) >= (uint64_t)timeout_us) { return (mp_uint_t)-1; } - */ } return (mp_uint_t)(mp_hal_ticks_us() - start); } diff --git a/extmod/modmachine.h b/extmod/modmachine.h index a33925a49e35..7825ff0b6428 100644 --- a/extmod/modmachine.h +++ b/extmod/modmachine.h @@ -244,7 +244,7 @@ uintptr_t MICROPY_MACHINE_MEM_GET_WRITE_ADDR(mp_obj_t addr_o, uint align); NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args); void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len); -mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us); +mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us, bool wait_opposite); mp_uint_t machine_time_hardware_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us); MP_DECLARE_CONST_FUN_OBJ_0(machine_unique_id_obj); diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 1471d4b287d3..d78cbd10df7e 100755 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -44,99 +44,84 @@ #include "py/mpprint.h" #define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, ", LINE=%d\n", __LINE__); -#define debug_printf0(...)// mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, ", LINE=%d\n", __LINE__); -#define debug_printf2(...) //mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, ", LINE=%d\n", __LINE__); -#define debug_printf3(...) //mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, ", LINE=%d\n", __LINE__); -#define debug_printf4(...)// mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, ", LINE=%d\n", __LINE__); -#define debug_printf5(...) //mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, ", LINE=%d\n", __LINE__); - -typedef struct _chan_t { - // Which channel has which GPIO pin assigned? - // (-1 if not assigned) - gpio_num_t pin; - // Which channel has which timer assigned? - // (-1 if not assigned) - int timer; -} chan_t; -// List of PWM channels -static chan_t chans[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; - -// List of timer configs -static ledc_timer_config_t timers[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX]; - -// Params for PWM operation // 5khz is default frequency #define PWM_FREQ (5000) +// default duty 50% +#define PWM_DUTY ((1UL << UI_RES_16_BIT) / 2) -// 10-bit user interface resolution compatible with esp8266 PWM.duty(0..1024) +// 10-bit user interface resolution compatible with esp8266 PWM.duty() #define UI_RES_10_BIT (10) - // Maximum duty value on 10-bit resolution -#define UI10_DUTY ((1 << UI_RES_10_BIT) - 1) +#define UI10_DUTY ((1UL << UI_RES_10_BIT) - 1) -// Duty resolution of user interface in `duty_u16(0..2^16)` and `duty_u16` parameter in constructor/initializer +// 16-bit user interface resolution used in PWM.duty_u16() #define UI_RES_16_BIT (16) -// Maximum duty value on highest user interface resolution -#define UI16_DUTY ((1 << UI_RES_16_BIT) - 1) - -#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) -//#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1 - 1) +// Maximum duty value on 16-bit resolution +#define UI16_DUTY ((1UL << UI_RES_16_BIT) - 1) // timer_duty is the MAX value of a channel_duty -#define timer_duty ((1 << timer->duty_resolution) - 1) +#define timer_duty ((1UL << timers[self->mode][self->timer].duty_resolution) - 1) // All chips except esp32 and esp32s2 do not have timer-specific clock sources, which means clock source for all timers must be the same one. #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 // If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used //#define EMPIRIC_FREQ (10) // Hz #endif + // Config of timer upon which we run all PWM'ed GPIO pins static bool pwm_inited = false; // MicroPython PWM object struct typedef struct _machine_pwm_obj_t { mp_obj_base_t base; - gpio_num_t pin; - int mode; - int channel; - int timer; - int duty_x; // UI_RES_10_BIT if duty(), UI_RES_16_BIT if duty_u16(), -UI_RES_16_BIT if duty_ns() + int8_t pin; + int8_t mode; + int8_t channel; + int8_t timer; + int32_t freq; + int8_t duty_x; // UI_RES_10_BIT if duty(), UI_RES_16_BIT if duty_u16(), -UI_RES_16_BIT if duty_ns() int duty_ui; // saved values of UI duty int channel_duty; // saved values of UI duty, calculated to raw channel->duty uint8_t output_invert; } machine_pwm_obj_t; -static void register_channel(int mode, int channel, int pin, int timer) { +typedef struct _chans_t { + // Which channel has which GPIO pin assigned? (-1 if not assigned) + int8_t pin; + // Which channel has which timer assigned? (-1 if not assigned) + int8_t timer; +} chans_t; + +// List of PWM channels +static chans_t chans[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; + +typedef struct _timers_t { + int32_t freq; + int8_t duty_resolution; +} timers_t; + +// List of PWM timers +static timers_t timers[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX]; + +// register-unregister channel +static void set_channel(int mode, int channel, int pin, int timer) { if ((mode >= 0) && (mode < LEDC_SPEED_MODE_MAX) && (channel >= 0) && (channel < LEDC_CHANNEL_MAX)) { chans[mode][channel].pin = pin; chans[mode][channel].timer = timer; } } -static void unregister_channel(int mode, int channel) { - if ((mode >= 0) && (mode < LEDC_SPEED_MODE_MAX) && (channel >= 0) && (channel < LEDC_CHANNEL_MAX)) { - chans[mode][channel].pin = -1; - chans[mode][channel].timer = -1; - } -} - static void pwm_init(void) { - // Initial condition: no channels assigned for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + // Initial condition: no channels assigned for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { - unregister_channel(mode, channel); + set_channel(mode, channel, -1, -1); // unregister } - - // Prepare all timers config // Initial condition: no timers assigned for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { - timers[mode][timer].duty_resolution = HIGHEST_PWM_RES; - timers[mode][timer].freq_hz = -1; // unset timer is -1 - timers[mode][timer].speed_mode = mode; - timers[mode][timer].timer_num = timer; - timers[mode][timer].clk_cfg = LEDC_AUTO_CLK; // will reinstall later - timers[mode][timer].deconfigure = false; + timers[mode][timer].freq = -1; // unset timer is -1 + timers[mode][timer].duty_resolution = 0; } } } @@ -151,8 +136,8 @@ static bool is_timer_in_use(int mode, int current_channel, int timer) { return false; } -// Deinit channel and timer if the timer is unused, detach pin -static void pwm_deinit(int mode, int channel) { +// Deinit channel and timer if the timer is unused +static void pwm_deinit(int mode, int channel, int level) { // Is valid channel? if ((mode >= 0) && (mode < LEDC_SPEED_MODE_MAX) && (channel >= 0) && (channel < LEDC_CHANNEL_MAX)) { // Clean up timer if necessary @@ -161,28 +146,17 @@ static void pwm_deinit(int mode, int channel) { if (!is_timer_in_use(mode, channel, timer)) { check_esp_err(ledc_timer_rst(mode, timer)); // Flag it unused - timers[mode][timer].freq_hz = -1; + timers[mode][timer].freq = -1; + timers[mode][timer].duty_resolution = 0; } } int pin = chans[mode][channel].pin; if (pin >= 0) { - // Mark it unused, and tell the hardware to stop routing - check_esp_err(ledc_stop(mode, channel, 0)); - /* - // Disable ledc signal for the pin - if (mode == LEDC_LOW_SPEED_MODE) { - esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + channel, false, false); - } else { - #if SOC_LEDC_SUPPORT_HS_MODE - esp_rom_gpio_connect_out_signal(pin, LEDC_HS_SIG_OUT0_IDX + channel, false, false); - #endif - } - // reconfigure as GPIO - //gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT); - */ + // Disable LEDC output, and set idle level + check_esp_err(ledc_stop(mode, channel, level)); } - unregister_channel(mode, channel); + set_channel(mode, channel, -1, -1); // unregister } } @@ -191,7 +165,7 @@ void machine_pwm_deinit_all(void) { if (pwm_inited) { for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { - pwm_deinit(mode, channel); + pwm_deinit(mode, channel, 0); } } pwm_inited = false; @@ -207,8 +181,7 @@ static void pwm_is_active(machine_pwm_obj_t *self) { // Calculate the duty parameters based on an ns value static int ns_to_duty(machine_pwm_obj_t *self, int ns) { pwm_is_active(self); - ledc_timer_config_t *timer = &timers[self->mode][self->timer]; - int64_t duty = ((int64_t)ns * UI16_DUTY * timer->freq_hz + 500000000LL) / 1000000000LL; + int64_t duty = ((int64_t)ns * UI16_DUTY * self->freq + 500000000LL) / 1000000000LL; if ((ns > 0) && (duty == 0)) { duty = 1; } else if (duty > UI16_DUTY) { @@ -219,26 +192,27 @@ static int ns_to_duty(machine_pwm_obj_t *self, int ns) { static int duty_to_ns(machine_pwm_obj_t *self, int duty) { pwm_is_active(self); - ledc_timer_config_t *timer = &timers[self->mode][self->timer]; - int64_t ns = ((int64_t)duty * 1000000000LL + (int64_t)timer->freq_hz * UI16_DUTY / 2) / ((int64_t)timer->freq_hz * UI16_DUTY); - return ns; + return ((int64_t)duty * 1000000000LL + (int64_t)self->freq * UI16_DUTY / 2) / ((int64_t)self->freq * UI16_DUTY); } -static void configure_pin(machine_pwm_obj_t *self) { - // reconfigure PWM pin output as input/output +// Reconfigure PWM pin output as input/output. This allows to read the pin level. +static void reconfigure_pin(machine_pwm_obj_t *self) { + int invert = self->output_invert; + if (self->channel_duty && (self->channel_duty == timer_duty)) { + invert = invert ^ 1; + } gpio_set_direction(self->pin, GPIO_MODE_INPUT_OUTPUT); if (self->mode == LEDC_LOW_SPEED_MODE) { - esp_rom_gpio_connect_out_signal(self->pin, LEDC_LS_SIG_OUT0_IDX + self->channel, self->output_invert, false); + esp_rom_gpio_connect_out_signal(self->pin, LEDC_LS_SIG_OUT0_IDX + self->channel, invert, false); #if SOC_LEDC_SUPPORT_HS_MODE } else if (self->mode == LEDC_HIGH_SPEED_MODE) { - esp_rom_gpio_connect_out_signal(self->pin, LEDC_HS_SIG_OUT0_IDX + self->channel, self->output_invert, false); + esp_rom_gpio_connect_out_signal(self->pin, LEDC_HS_SIG_OUT0_IDX + self->channel, invert, false); #endif } } -#define apply_duty(self) debug_printf0("apply_duty(self->channel=%d,self->channel_duty=%d,self->pin=%d,self->mode=%d,self->timer=%d)", self->channel, self->channel_duty, self->pin, self->mode, self->timer); _apply_duty(self); -static void _apply_duty(machine_pwm_obj_t *self) { - ledc_timer_config_t *timer = &timers[self->mode][self->timer]; +static void apply_duty(machine_pwm_obj_t *self) { + pwm_is_active(self); int duty = 0; if (self->duty_x == UI_RES_16_BIT) { @@ -249,21 +223,14 @@ static void _apply_duty(machine_pwm_obj_t *self) { duty = ns_to_duty(self, self->duty_ui); } + int duty_resolution = timers[self->mode][self->timer].duty_resolution; int channel_duty; - if (timer->duty_resolution <= UI_RES_16_BIT) { - channel_duty = duty >> (UI_RES_16_BIT - timer->duty_resolution); + if (duty_resolution <= UI_RES_16_BIT) { + channel_duty = duty >> (UI_RES_16_BIT - duty_resolution); } else { - channel_duty = duty << (timer->duty_resolution - UI_RES_16_BIT); - } - /* - if (channel_duty < 0) { - channel_duty = 0; - } else if (channel_duty >= timer_duty) { - // When channel's binded timer selects its maximum duty resolution, - // the duty cycle value cannot be set to (2 ** duty_resolution) - channel_duty = timer_duty - 1; // Ok! + channel_duty = duty << (duty_resolution - UI_RES_16_BIT); } - */ + self->channel_duty = channel_duty; ledc_channel_config_t cfg = { @@ -275,116 +242,46 @@ static void _apply_duty(machine_pwm_obj_t *self) { .timer_sel = self->timer, .flags.output_invert = self->output_invert, }; - debug_printf4("apply_duty():self->channel_duty=%d, timer_duty=%d, cfg.flags.output_invert=%d", self->channel_duty, timer_duty, cfg.flags.output_invert) - /* - if (self->channel_duty == timer_duty) { + if (self->channel_duty && (self->channel_duty == timer_duty)) { cfg.duty = 0; cfg.flags.output_invert = self->output_invert ^ 1; } - */ + debug_printf("ledc_channel_config() self->pin=%d, self->channel=%d, self->mode=%d, self->timer=%d, cfg.duty=%d, cfg.flags.output_invert=%d", + self->pin, self->channel, self->mode, self->timer, cfg.duty, cfg.flags.output_invert); check_esp_err(ledc_channel_config(&cfg)); - /* - #define USE_THREAD_SAFE_LEDC_API 0 - #if USE_THREAD_SAFE_LEDC_API - check_esp_err(ledc_set_duty_and_update(self->mode, self->channel, channel_duty, timer_duty - 1)); - esp_err_t err = ESP_OK; // ledc_set_duty_and_update(self->mode, self->channel, channel_duty, timer_duty - 1); - if (err != ESP_OK) - #endif - { - check_esp_err(ledc_set_duty(self->mode, self->channel, channel_duty)); - check_esp_err(ledc_update_duty(self->mode, self->channel)); - } - */ -} -/* -static unsigned int calc_divider(uint32_t src_clk_freq, uint32_t timer_freq) { - unsigned int divider = (src_clk_freq + timer_freq / 2) / timer_freq; // rounded - if (divider == 0) { - divider = 1; - } - return divider; -} -*/ -/* -static uint32_t calc_divider(uint32_t src_clk_freq, uint32_t timer_freq) { - uint32_t divider = (uint32_t)(((uint64_t)src_clk_freq + (uint64_t)timer_freq / 2ULL) / (uint64_t)timer_freq); // rounded - return divider == 0 ? 1 : divider; + debug_printf("bind self->mode=%d, self->channel=%d, self->timer=%d", self->mode, self->channel, self->timer); + check_esp_err(ledc_bind_channel_timer(self->mode, self->channel, self->timer)); + reconfigure_pin(self); + set_channel(self->mode, self->channel, self->pin, self->timer); // register } -*/ + +// Temporary workaround for ledc_find_suitable_duty_resolution function only being added in IDF V5.2 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0) static uint32_t calc_divider(uint32_t src_clk_freq, uint32_t timer_freq) { uint32_t divider = (uint32_t)(((uint64_t)src_clk_freq + timer_freq / 2) / timer_freq); // rounded -/* - uint32_t divider2 = (uint32_t)((uint64_t)src_clk_freq / timer_freq); // tracate - if (divider != divider2) { - debug_printf5("divider=%d, divider2=%d", divider, divider2); - } -*/ return divider == 0 ? 1 : divider; } -// Temporary workaround for ledc_find_suitable_duty_resolution function only being added in IDF V5.2 -#if 1 // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0) -/* -static uint32_t _ledc_find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { - // This implementation is based on the one used in Micropython v1.23 - - // Find the highest bit resolution for the requested frequency - unsigned int freq = src_clk_freq; - unsigned int divider = calc_divider(freq, timer_freq); - float f = (float)freq / divider; // actual frequency - if (f <= 1.0) { - f = 1.0; - } - freq = (unsigned int)roundf((float)freq / f); - - unsigned int res = 0; - for (; freq > 1; freq >>= 1) { - ++res; - } - return res; -} -*/ -/* - unsigned int freq = src_clk_freq; - unsigned int divider = calc_divider(src_clk_freq, timer_freq); - float f = (float)src_clk_freq / divider; // actual frequency - if (f <= 1.0) { - f = 1.0; - } - freq = (unsigned int)roundf((float)src_clk_freq + (src_clk_freq / divider /2) / (src_clk_freq / divider)); -*/ -static uint32_t _ledc_find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { - // Find the highest bit resolution for the requested frequency +// Find the highest bit resolution for the requested frequency +static uint32_t ledc_find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { uint32_t divider = calc_divider(src_clk_freq, timer_freq); uint32_t f = src_clk_freq / divider; // actual frequency - uint32_t freq = src_clk_freq + (f / 2) / f; // rounded - uint32_t freq1 = freq; - + uint32_t freq = src_clk_freq + (f / 2) / f; // rounded frequency uint32_t resolution = 0; for (; freq > 1; freq >>= 1) { ++resolution; } - //if (src_clk_freq != freq1) { - if (1) { - debug_printf5("\nsrc_clk_freq=%d, timer_freq=%d, divider=%d, freq=%d, resolution=%d", src_clk_freq, timer_freq, divider, freq1, resolution); - } return resolution; } #endif static uint32_t find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { - unsigned int ledc_resolution = _ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq); - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) unsigned int resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq); - //if (resolution != resolution2) { - debug_printf5("resolution=%d, ledc_resolution=%d", resolution, ledc_resolution); - //} - #endif if (resolution == 0) { - //resolution = 1; - } else if (resolution > HIGHEST_PWM_RES) { - // Limit resolution to HIGHEST_PWM_RES to match units of our duty - resolution = HIGHEST_PWM_RES; + resolution = 1; + } else if (resolution > UI_RES_16_BIT) { + // limit resolution to user interface + resolution = UI_RES_16_BIT; } return resolution; } @@ -393,18 +290,10 @@ static uint32_t find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t ti static uint32_t get_duty_u16(machine_pwm_obj_t *self) { pwm_is_active(self); - //ledc_timer_config_t *timer = &timers[self->mode][self->timer]; - debug_printf("MAX_TIMER_DUTY=%d timer->duty_resolution=%d self->channel_duty=%d ledc_duty()=%d", MAX_TIMER_DUTY, timer->duty_resolution, self->channel_duty, ledc_duty()) - - debug_printf2("get_duty_u16():self->output_invert=%d, ledc_duty()=%d", self->output_invert, ledc_duty()) - #if 0 // UI16_DUTY == 65536 if (self->channel_duty == timer_duty) { return UI16_DUTY; - } else - #endif - { + } else { int duty_resolution = timers[self->mode][self->timer].duty_resolution; - debug_printf2("duty_resolution=%d", duty_resolution) if (duty_resolution <= UI_RES_16_BIT) { return ledc_duty() << (UI_RES_16_BIT - duty_resolution); } else { @@ -414,7 +303,8 @@ static uint32_t get_duty_u16(machine_pwm_obj_t *self) { } static uint32_t get_duty_u10(machine_pwm_obj_t *self) { - return get_duty_u16(self) >> (UI_RES_16_BIT - UI_RES_10_BIT); // Scale down from 16 bit to 10 bit resolution + // Scale down from 16 bit to 10 bit resolution + return get_duty_u16(self) >> (UI_RES_16_BIT - UI_RES_10_BIT); } static uint32_t get_duty_ns(machine_pwm_obj_t *self) { @@ -467,18 +357,26 @@ static void check_freq_ranges(int freq, int upper_freq) { // Set timer frequency static void set_freq(machine_pwm_obj_t *self, unsigned int freq) { - ledc_timer_config_t *timer = &timers[self->mode][self->timer]; - if (timer->freq_hz != freq) { - timer->clk_cfg = LEDC_AUTO_CLK; + self->freq = freq; + if (timers[self->mode][self->timer].freq != freq) { + timers[self->mode][self->timer].freq = freq; + + ledc_timer_config_t timer = {}; + timer.speed_mode = self->mode; + timer.timer_num = self->timer; + timer.freq_hz = freq; + timer.deconfigure = false; + + timer.clk_cfg = LEDC_AUTO_CLK; uint32_t src_clk_freq = 0; #if SOC_LEDC_SUPPORT_APB_CLOCK - timer->clk_cfg = LEDC_USE_APB_CLK; + timer.clk_cfg = LEDC_USE_APB_CLK; src_clk_freq = APB_CLK_FREQ; // 80 MHz #elif SOC_LEDC_SUPPORT_PLL_DIV_CLOCK - timer->clk_cfg = LEDC_USE_PLL_DIV_CLK; + timer.clk_cfg = LEDC_USE_PLL_DIV_CLK; #elif SOC_LEDC_SUPPORT_XTAL_CLOCK - timer->clk_cfg = LEDC_USE_XTAL_CLK; + timer.clk_cfg = LEDC_USE_XTAL_CLK; src_clk_freq = XTAL_CLK_FREQ; // 40 MHz #else #error No supported PWM / LEDC clocks. @@ -486,47 +384,33 @@ static void set_freq(machine_pwm_obj_t *self, unsigned int freq) { #ifdef EMPIRIC_FREQ // ESP32 and ESP32S2 only if (freq < EMPIRIC_FREQ) { - timer->clk_cfg = LEDC_USE_REF_TICK; + timer.clk_cfg = LEDC_USE_REF_TICK; src_clk_freq = REF_CLK_FREQ; // 1 MHz } #endif #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) - esp_err_t err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT, &src_clk_freq); + esp_err_t err = esp_clk_tree_src_get_freq_hz(timer.clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT, &src_clk_freq); if (err != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg); + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer.clk_cfg); } // machine.freq(20_000_000) reduces APB_CLK_FREQ to 20MHz and the highest PWM frequency to 10MHz check_freq_ranges(freq, src_clk_freq / 2); #endif - timer->freq_hz = freq; - // Configure the new resolution and frequency - timer->duty_resolution = find_suitable_duty_resolution(src_clk_freq, timer->freq_hz); - debug_printf5("SOC_MOD_CLK_APB=%d, LEDC_USE_APB_CLK=%d, LEDC_AUTO_CLK=%d, LEDC_USE_REF_TICK=%d", SOC_MOD_CLK_APB, LEDC_USE_APB_CLK, LEDC_AUTO_CLK, LEDC_USE_REF_TICK); - debug_printf5("src_clk_freq=%d, timer->freq_hz=%d, timer->clk_cfg=%d, timer->duty_resolution=%d, timer->speed_mode=%d, timer->timer_num=%d", src_clk_freq, timer->freq_hz, timer->clk_cfg, timer->duty_resolution, timer->speed_mode, timer->timer_num) + // Configure the new resolution + timer.duty_resolution = find_suitable_duty_resolution(src_clk_freq, self->freq); + timers[self->mode][self->timer].duty_resolution = timer.duty_resolution; + + debug_printf("ledc_timer_config() timer.freq_hz=%d, timer.clk_cfg=%d, timer.duty_resolution=%d, timer.speed_mode=%d, timer.timer_num=%d", + timer.freq_hz, timer.clk_cfg, timer.duty_resolution, timer.speed_mode, timer.timer_num); // Configure timer - Set frequency - check_esp_err(ledc_timer_config(timer)); - /* - err = ledc_timer_config(timer); - if (1) { - //if (ESP_OK != (err = ledc_timer_config(timer))) { - uint32_t divider = calc_divider(src_clk_freq, timer->freq_hz); - debug_printf5("divider=%d, timer->clk_cfg=%d, err=0x%X", divider, timer->clk_cfg, err); - check_esp_err(ledc_timer_set(timer->speed_mode, timer->timer_num, divider, timer->duty_resolution, timer->clk_cfg)); - if (freq >= EMPIRIC_FREQ) { - check_esp_err(ledc_set_freq(timer->speed_mode, timer->timer_num,timer->freq_hz)); - } - } - */ + check_esp_err(ledc_timer_config(&timer)); // Reset the timer if low speed if (self->mode == LEDC_LOW_SPEED_MODE) { check_esp_err(ledc_timer_rst(self->mode, self->timer)); } - - // Save the same duty cycle when frequency is changed - set_duty(self); } } @@ -536,112 +420,83 @@ static bool is_free_channels(int mode, int pin) { return true; } } - return false; } -// Find self channel or free channel in the mode -static int find_channel(int mode, int pin) { - int avail_channel = -1; - for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { - if (chans[mode][channel].pin == pin) { - return channel; +// Find self channel or free channel +static void find_channel(int pin, int *ret_mode, int *ret_channel) { + // Try to find self channel first + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if (chans[mode][channel].pin == pin) { + *ret_mode = mode; + *ret_channel = channel; + return; + } } - if ((avail_channel < 0) && (chans[mode][channel].pin < 0)) { - avail_channel = channel; + } + // Find free channel + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if (chans[mode][channel].pin < 0) { + *ret_mode = mode; + *ret_channel = channel; + return; + } } } - return avail_channel; } -// Returns timer with the same mode and frequency, freq == -1 means free timer -#define find_timer(mode, freq) _find_timer(mode, freq); debug_printf0("find_timer(mode=%d, freq=%d)", mode, freq); -static int _find_timer(int mode, unsigned int freq) { - for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { - if (timers[mode][timer].freq_hz == freq) { - return timer; +// Returns timer with the same frequency, freq == -1 means free timer +static void find_tmr(int pin, int freq, int *ret_mode, int *ret_timer) { + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + if (is_free_channels(mode, pin)) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if (timers[mode][timer].freq == freq) { + *ret_mode = mode; + *ret_timer = timer; + return; + } + } } } - return -1; } // Try to find a timer with the same frequency in the current mode, otherwise in the next mode. // If no existing timer and channel was found, then try to find free timer in any mode. // If the mode or channel is changed, release the channel and select(bind) a new channel in the next mode. -#define select_a_timer(self, freq) debug_printf0("select_a_timer(freq=%d)", freq); _select_a_timer(self, freq); -static void _select_a_timer(machine_pwm_obj_t *self, int freq) { +static void select_timer(machine_pwm_obj_t *self, int freq) { // mode, channel, timer may be -1(not defined) or actual values - int save_mode = self->mode; - int save_channel = self->channel; - // int save_timer = self->timer; - - int mode = MAX(self->mode, 0); - - // Check if an already running timer with the required frequency is running in the current mode + int mode = -1; int timer = -1; - if (is_free_channels(mode, self->pin)) { - timer = find_timer(mode, freq); - } - // If no existing timer and channel was found in the current mode, then find a new one in another mode - if (timer < 0) { - // Calc next mode - int mode_ = mode; - if (mode > 0) { - --mode; - } else if (mode < (LEDC_SPEED_MODE_MAX - 1)) { - ++mode; - } - - if (mode_ != mode) { - if (is_free_channels(mode, self->pin)) { - timer = find_timer(mode, freq); - } - } - } - // If the timer is found, then bind and set the duty - debug_printf0("timer=%d, timers[mode][timer].freq_hz=%d, self->channel=%d, self->mode=%d", timer, timers[mode][timer].freq_hz, self->channel, self->mode); - if ((timer >= 0) - && (timers[mode][timer].freq_hz != 0) - && (timers[mode][timer].freq_hz != freq) - && (self->channel >= 0) - && (self->mode >= 0)) { - // Bind the channel to the timer - self->mode = mode; - self->timer = timer; - check_esp_err(ledc_bind_channel_timer(self->mode, self->channel, self->timer)); - register_channel(self->mode, self->channel, self->pin, self->timer); - } - if (timer >= 0) { - self->timer = timer; - set_duty(self); - } - + // Check if an already running timer with the required frequency is running + find_tmr(self->pin, freq, &mode, &timer); + debug_printf("1. freq=%d, mode=%d, timer=%d", freq, mode, timer) if (timer < 0) { // Try to reuse self timer if ((self->mode >= 0) && (self->channel >= 0)) { if (!is_timer_in_use(self->mode, self->channel, self->timer)) { mode = self->mode; timer = self->timer; + debug_printf("2. freq=%d, mode=%d, timer=%d", freq, mode, timer) } } // If no existing timer and channel was found, then try to find free timer in any mode if (timer < 0) { - mode = -1; - while ((timer < 0) && (mode < (LEDC_SPEED_MODE_MAX - 1))) { - ++mode; - if (is_free_channels(mode, self->pin)) { - timer = find_timer(mode, -1); // find free timer - } - } - if (timer < 0) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), LEDC_SPEED_MODE_MAX * LEDC_TIMER_MAX); - } + find_tmr(self->pin, -1, &mode, &timer); + debug_printf("3. freq=%d, mode=%d, timer=%d", freq, mode, timer) } + } + if (timer < 0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), LEDC_SPEED_MODE_MAX * LEDC_TIMER_MAX); + } + // If the timer is found, then bind + if (self->timer != timer) { + set_channel(self->mode, self->channel, -1, -1); // unregister + // Bind the channel to the timer self->mode = mode; self->timer = timer; - } - if ((save_mode != self->mode) || (save_channel != self->channel)) { - unregister_channel(save_mode, save_channel); + set_channel(self->mode, self->channel, self->pin, self->timer); // register } } @@ -666,10 +521,10 @@ static void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p } mp_printf(print, ")"); #if 1//MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_NORMAL - int duty_resolution = timers[self->mode][self->timer].duty_resolution; mp_printf(print, " #"); mp_printf(print, " duty=%.2f%%", 100.0 * get_duty_u16(self) / UI16_DUTY); // percents - mp_printf(print, ", raw_duty()=%d, duty_resolution=%d", ledc_duty(), duty_resolution); + int duty_resolution = timers[self->mode][self->timer].duty_resolution; + mp_printf(print, ", raw_duty=%d, resolution=%d", ledc_duty(), duty_resolution); mp_printf(print, ", mode=%d, channel=%d, timer=%d", self->mode, self->channel, self->timer); #endif } else { @@ -708,16 +563,6 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, int duty = args[ARG_duty].u_int; int duty_u16 = args[ARG_duty_u16].u_int; int duty_ns = args[ARG_duty_ns].u_int; - /* - if (((duty != -1) && (duty_u16 != -1)) || ((duty != -1) && (duty_ns != -1)) || ((duty_u16 != -1) && (duty_ns != -1))) { - mp_raise_ValueError(MP_ERROR_TEXT("only one of parameters 'duty', 'duty_u16' or 'duty_ns' is allowed")); - } - */ - /* - if ((duty < 0) && (duty_u16 < 0) && (duty_ns < 0)) { - mp_raise_ValueError(MP_ERROR_TEXT("one of parameters 'duty', 'duty_u16', or 'duty_ns' is required")); - } - */ if (duty_u16 >= 0) { self->duty_x = UI_RES_16_BIT; self->duty_ui = duty_u16; @@ -729,7 +574,7 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, self->duty_ui = duty; } else if (self->duty_x == 0) { self->duty_x = UI_RES_16_BIT; - self->duty_ui = (1 << UI_RES_16_BIT) / 2; // default 50% + self->duty_ui = PWM_DUTY; } int output_invert = args[ARG_invert].u_int; @@ -737,17 +582,10 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, self->output_invert = output_invert == 0 ? 0 : 1; } - int save_mode = self->mode; - int save_channel = self->channel; - int save_timer = self->timer; - // Check the current mode and channel int mode = -1; int channel = -1; - while ((channel < 0) && (mode < (LEDC_SPEED_MODE_MAX - 1))) { - ++mode; - channel = find_channel(mode, self->pin); - } + find_channel(self->pin, &mode, &channel); if (channel < 0) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX); // in all modes } @@ -761,36 +599,27 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, // pwm = PWM(pin, freq=1000, duty=256) // pwm = PWM(pin, duty=128) if (chans[mode][channel].timer >= 0) { - freq = timers[mode][chans[mode][channel].timer].freq_hz; + freq = timers[mode][chans[mode][channel].timer].freq; } if (freq <= 0) { freq = PWM_FREQ; } } - select_a_timer(self, freq); - - // New PWM assignment - if ((chans[mode][channel].pin < 0) - || ((save_mode != self->mode)) - || ((save_channel != self->channel)) - || ((save_timer != self->timer))) { - debug_printf("mp_machine_pwm_init_helper()") - apply_duty(self); - configure_pin(self); - register_channel(self->mode, self->channel, self->pin, self->timer); - } else { - set_freq(self, freq); - } + select_timer(self, freq); + set_freq(self, freq); + set_duty(self); } static void self_reset(machine_pwm_obj_t *self) { + self->pin = -1; self->mode = -1; self->channel = -1; self->timer = -1; + self->freq = -1; self->duty_x = 0; self->duty_ui = 0; - self->channel_duty = 0; + self->channel_duty = -1; self->output_invert = 0; } @@ -798,23 +627,20 @@ static void self_reset(machine_pwm_obj_t *self) { static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 2, true); - gpio_num_t pin = machine_pin_get_id(args[0]); - - // create PWM object from the given pin - machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); - self->pin = pin; - self_reset(self); // start the PWM subsystem if it's not already running if (!pwm_inited) { - #if USE_THREAD_SAFE_LEDC_API - ledc_fade_func_install(ESP_INTR_FLAG_INTRDISABLED); - #endif pwm_init(); pwm_inited = true; } - // start the PWM running for this channel + // create PWM object from the given pin + machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); + self_reset(self); + + self->pin = machine_pin_get_id(args[0]); + + // Process the remaining parameters. mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); @@ -824,30 +650,26 @@ static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, // This called from pwm.deinit() method static void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { - pwm_deinit(self->mode, self->channel); + pwm_deinit(self->mode, self->channel, self->output_invert ? 1 : 0); self_reset(self); } // Set and get methods of PWM class static mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { - if (self->timer < 0) { - return MP_OBJ_NEW_SMALL_INT(0); - } else { - return MP_OBJ_NEW_SMALL_INT(ledc_get_freq(self->mode, self->timer)); - } + pwm_is_active(self); + return MP_OBJ_NEW_SMALL_INT(ledc_get_freq(self->mode, self->timer)); } static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { pwm_is_active(self); check_freq_ranges(freq, 40000000); - if (freq == timers[self->mode][self->timer].freq_hz) { + if (freq == timers[self->mode][self->timer].freq) { return; } - // Set new PWM frequency - select_a_timer(self, freq); - // Set the frequency + select_timer(self, freq); set_freq(self, freq); + set_duty(self); } static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) {