Skip to content

esp32: PWM duty will be changed when changing the frequency if more than one channel is used. #8345

Description

@Dreagonmon

After upgrading mpy from 1.17 to 1.18, my buzzer can't play low frequency sounds any more.

With further test, it shows that the pwm has no output when frequencies < 611Hz.

from machine import PWM, Pin
pwm = PWM(Pin(5)) # also tested with Pin(2)
pwm.duty(512)
pwm.freq(611) # it works, digital multimeters shows freq 1222Hz
pwm.freq(610) # no pwm output, digital multimeters says freq is 0

And after switching back to mpy 1.17, pwm.freq(610) works fine. I'v tested with another board and I still got the same result. I'm sure this is an issue introduced by v1.18.

Activity

  1. xky183 commented on Feb 26, 2022

    @xky183

    meet same problem by v1.18,work fine by v1.17

  2. robert-hh commented on Feb 26, 2022

    @robert-hh
    Contributor

    Fixed since a while. Please use a recent nightly build, and ignore the "unstable" label. The nightly builds are tested the same way than the release build.

  3. xky183 commented on Feb 26, 2022

    @xky183

    Fixed since a while. Please use a recent nightly build, and ignore the "unstable" label. The nightly builds are tested the same way than the release build.

    Thank you very much,I just tested the version "esp32-20220226-unstable-v1.18-160-g0a217624e.bin", it can work in 1Hz now, but... the duty will be changed when changing the freq.

    >>> 
    >>> pwm0
    PWM(Pin(0), freq=2, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(200)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=1024, resolution=15, (duty=100.00%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.duty(512)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=20000, duty=8192, resolution=11, (duty=800.00%, resolution=0.049%), mode=0, channel=0, timer=1)
    >>> pwm0.duty(512)
    >>> pwm0.freq(20000)
    >>> pwm0
    PWM(Pin(0), freq=20000, duty=512, resolution=11, (duty=50.00%, resolution=0.049%), mode=0, channel=0, timer=1)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=32, resolution=15, (duty=3.13%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.duty(512)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.freq(200)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=256, resolution=16, (duty=25.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.duty(512)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(20)
    >>> pwm0
    PWM(Pin(0), freq=20, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2)
    >>> pwm0
    PWM(Pin(0), freq=2, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> 
    
  4. Dreagonmon commented on Feb 26, 2022

    @Dreagonmon
    Author

    maybe #8306 is the related issue.

    I created this issue and closed it soon when I realized that this is fixed in the nightly build. but it seems not really fixed.

  5. robert-hh commented on Feb 26, 2022

    @robert-hh
    Contributor

    No. It is not. That is something which show up only once after hard reset. I just tested you sequence with my own builds, and it works fine. I just downloaded the nightly build and verify, which esp-idf and version is used.

  6. robert-hh commented on Feb 26, 2022

    @robert-hh
    Contributor

    I cannot verify your findings. Everything looks as intended. Both the reporting in the print and the actual signal are fine.

    MicroPython v1.18 on 2022-02-26; ESP32 module (spiram) with ESP32
    Type "help()" for more information.
    >>> p0=PWM(Pin(0), 2, duty=512)
    >>> p0
    PWM(Pin(0), freq=2, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> p0.freq(200)
    >>> p0
    PWM(Pin(0), freq=200, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> p0.freq(2000)
    >>> p0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=0)
    >>> p0.freq(20000)
    >>> p0
    PWM(Pin(0), freq=20000, duty=512, resolution=11, (duty=50.00%, resolution=0.049%), mode=0, channel=0, timer=0)
    >>> p0.freq(2000)
    >>> p0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=0)
    >>> p0.freq(200)
    >>> p0
    PWM(Pin(0), freq=200, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> p0.freq(2)
    >>> p0
    PWM(Pin(0), freq=2, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> import platform
    >>> platform.platform()
    'MicroPython-1.18.0-xtensa-IDFv4.2.2-with-newlib3.0.0'
    >>> 
    
  7. xky183 commented on Feb 27, 2022

    @xky183

    I test twice,
    at the first time,only use one PWM0 channel,it works well。
    at the second time,use PWM0,PWM2,PWM4 channel,it seems timers for each pwm channel will be changed when changing the freq, and it will cause the changing of duty , maybe the problem is the counter of the timer is not set right value,

    `>>> 
    >>> from machine import PWM
    >>> pwm0 = PWM(Pin(0), freq=2, duty=512)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'Pin' isn't defined
    >>> from machine import Pin
    >>> pwm0 = PWM(Pin(0), freq=2, duty=512)
    >>> pwm0
    PWM(Pin(0), freq=2, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(20)
    >>> pwm0
    PWM(Pin(0), freq=20, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(200)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(20000)
    >>> pwm0
    PWM(Pin(0), freq=20000, duty=512, resolution=11, (duty=50.00%, resolution=0.049%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(200000)
    >>> pwm0
    PWM(Pin(0), freq=200000, duty=512, resolution=8, (duty=50.00%, resolution=0.391%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(20000)
    >>> pwm0
    PWM(Pin(0), freq=20000, duty=512, resolution=11, (duty=50.00%, resolution=0.049%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(200)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(20)
    >>> pwm0
    PWM(Pin(0), freq=20, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2)
    >>> pwm0
    PWM(Pin(0), freq=2, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> ets Jun  8 2016 00:22:57
    
    rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:2
    load:0x3fff0030,len:4540
    ho 0 tail 12 room 4
    load:0x40078000,len:12344
    ho 0 tail 12 room 4
    load:0x40080400,len:4124
    entry 0x40080680
    MicroPython v1.18 on 2022-02-26; ESP32 module with ESP32
    Type "help()" for more information.
    >>> from machine import Pin
    >>> from machine import PWM
    >>> pwm0 = PWM(Pin(0), freq=2, duty=512)
    >>> pwm2 = PWM(Pin(2), freq=200, duty=512)
    >>> pwm4 = PWM(Pin(4), freq=2000, duty=512)
    >>> pwm0
    PWM(Pin(0), freq=2, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(20)
    >>> pwm0
    PWM(Pin(0), freq=20, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(200)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=1)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=1024, resolution=15, (duty=100.00%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.duty(512)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.freq(20000)
    >>> pwm0
    PWM(Pin(0), freq=20000, duty=512, resolution=11, (duty=50.00%, resolution=0.049%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(200000)
    >>> pwm0
    PWM(Pin(0), freq=200000, duty=512, resolution=8, (duty=50.00%, resolution=0.391%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(20000)
    >>> pwm0
    PWM(Pin(0), freq=20000, duty=512, resolution=11, (duty=50.00%, resolution=0.049%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=32, resolution=15, (duty=3.13%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.duty(512)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.freq(200)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=256, resolution=16, (duty=25.00%, resolution=0.002%), mode=0, channel=0, timer=1)
    >>> pwm0.duty(512)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=1)
    >>> pwm0.freq(20)
    >>> pwm0
    PWM(Pin(0), freq=20, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2)
    >>> pwm0
    PWM(Pin(0), freq=2, duty=512, resolution=16, (duty=50.00%, resolution=0.002%), mode=0, channel=0, timer=0)
    >>> import platform
    >>> platform.platform()
    'MicroPython-1.18.0-xtensa-IDFv4.2.2-with-newlib3.0.0'
    >>> 
    >>> 
    >>> `
    
    
    
  8. xky183 commented on Feb 27, 2022

    @xky183

    maybe #8306 is the related issue.

    I created this issue and closed it soon when I realized that this is fixed in the nightly build. but it seems not really fixed.

    could you reopen this issue? thanks

  9. robert-hh commented on Feb 27, 2022

    @robert-hh
    Contributor

    You are right. If more than one channel is used, there will be issues with the duty cycle. The basic PR which changed that behavior is #7817. The PR message explains the function and one can see the root of the problem, which is also visible in your log. If a channel's frequency is set to a frequency used by the other channel, it uses this channels timer. That does not take care of potentially different duty cycles.
    Dropping the timer re-use mechanism makes the PWM work as intended. However, it is then again limited to 8 channels.

  10. Dreagonmon commented on Feb 27, 2022

    @Dreagonmon
    Author

    could you reopen this issue? thanks

    sure

  11. changed the title [-]esp32: PWM has no output when frequencies < 611Hz.[/-] [+]esp32: PWM duty will be changed when changing the frequency if more than one channel is used.[/+] on Feb 27, 2022
  12. robert-hh commented on Feb 27, 2022

    @robert-hh
    Contributor

    So I made a small change to the code which forces the duty rate to be set on frequency changes. That was omitted when setting to a frequency which was already used in a different channel. The timer for that channel was reused, but still the duty rate had to be adapted. The change is based on a different PR I made earlier, and by chance it fixes #8306 as well. Output from going though the same series as @xky183 above:

    MicroPython v1.18-160-g0a217624e-dirty on 2022-02-27; ESP32 module (spiram) with ESP32
    Type "help()" for more information.
    >>> import platform
    >>> platform.platform()
    'MicroPython-1.18.0-xtensa-IDFv4.2.2-dirty-with-newlib3.0.0'
    >>> pwm0 = PWM(Pin(0), freq=2, duty=512)
    >>> pwm2 = PWM(Pin(22), freq=200, duty=512)
    >>> pwm4 = PWM(Pin(4), freq=2000, duty=512)
    >>> pwm0
    PWM(Pin(0), freq=2, duty=512, resolution=18, (duty=50.00%, resolution=0.000%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(20)
    >>> pwm0
    PWM(Pin(0), freq=20, duty=512, resolution=20, (duty=50.00%, resolution=0.000%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(200)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=512, resolution=18, (duty=50.00%, resolution=0.000%), mode=0, channel=0, timer=1)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.freq(20000)
    >>> pwm0
    PWM(Pin(0), freq=20000, duty=512, resolution=11, (duty=50.00%, resolution=0.049%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2000)
    >>> pwm0
    PWM(Pin(0), freq=2003, duty=512, resolution=15, (duty=50.00%, resolution=0.003%), mode=0, channel=0, timer=2)
    >>> pwm0.freq(200)
    >>> pwm0
    PWM(Pin(0), freq=200, duty=512, resolution=18, (duty=50.00%, resolution=0.000%), mode=0, channel=0, timer=1)
    >>> pwm0.freq(20)
    >>> pwm0
    PWM(Pin(0), freq=20, duty=512, resolution=20, (duty=50.00%, resolution=0.000%), mode=0, channel=0, timer=0)
    >>> pwm0.freq(2)
    >>> pwm0
    PWM(Pin(0), freq=2, duty=512, resolution=18, (duty=50.00%, resolution=0.000%), mode=0, channel=0, timer=0)
    >>> 
    
  13. dpgeorge commented on Mar 7, 2022

    @dpgeorge
    Member

    Fixed by 55a0125

  14. added a commit that references this issue on Sep 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions