Skip to content

esp32/machine_pwm: Enhancement of PWM: Add features light_sleep_enable. #16102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

yoann-darche
Copy link

@yoann-darche yoann-darche commented Oct 28, 2024

Summary

This PR will add new features of the PWM ESP32 port to enable the use of PWM in ligth sleep mode of the ESP32 series as reported in https://github.com/orgs/micropython/discussions/16033
After some exchange, we have decided to simplify the API and remove the exposer of the clock parameters. We have identified that this parameter dores not bring any adventage and complexify the code.
To perform this we need to add clock source selection of the timer used by the PWM Channel, this selection is totaly transparent and automated.

Adding 1 new keywords to the PWM constructor :

  • ligth_sleep_enable (optional) = True | Flase # Indicate if the PWM Channel will persit in ligth sleep mode

Enhancement of the print PWM object to add information relating the clock source and light sleep enable flag value:

PWM(Pin(7), freq=2997, duty=640, resolution=12, (duty=62.50%, resolution=0.024%), mode=0, channel=0, timer=0, clock=RC_FAST_CLK, light sleep enabled)

Some parts of the code have been rewritten to improve the precision of duty calculations, Many previously hardcoded instructions have been replaced with IDF utility functions, such as ledc_find_suitable_duty_resolution(), to replace fixed calculations. Alos improved the PWM.deinit() function to correctly unset the timer module.
The code adress SoC hardware limitation for ESP32-S3/C3/C6 that accept only one source of clock for all timer.
Please note dispite of the fact that ESP32 and ESP32-S2 accept multi-clock setup, in our case and with the simplicity in mind, we can not use these capabilites as the APB_CLK is multiplexed with the RC_FAST_CLK. So all SoC does not support multi-clock.

Testing

Test board : ESP32 (high speed mode, and ESP32-S2, ESP32-S3 mono-clock)
Compile test on ESP32-C6

Test concerning API:

# Test on ESP32-S3 (mono source clock, LowSpeed only)
MPY: soft reboot
MicroPython v1.25.0-preview.23.g56f820bd5.dirty on 2024-10-31; Generic ESP32S3 module with ESP32S3
Type "help()" for more information.

>>> from machine import PWM, Pin

>>> a=PWM(Pin(5), freq=1000, duty=640)
>>> print(a)
            PWM(Pin(5), freq=1000, duty=640, resolution=14, (duty=62.50%, resolution=0.006%), mode=0, channel=0, timer=0, clock=APB_CLK)

>>> b=PWM(Pin(5), freq=2000, duty=640) # Same PIN
>>> print(b)
            PWM(Pin(5), freq=2000, duty=640, resolution=14, (duty=62.50%, resolution=0.006%), mode=0, channel=0, timer=0, clock=APB_CLK)
>>> print(a)
            PWM(Pin(5), freq=2000, duty=640, resolution=14, (duty=62.50%, resolution=0.006%), mode=0, channel=0, timer=0, clock=APB_CLK)
			
>>> b=PWM(Pin(6), freq=2000, duty=640) # same freq => same timer
>>> print(b)
            PWM(Pin(6), freq=2000, duty=640, resolution=14, (duty=62.50%, resolution=0.006%), mode=0, channel=1, timer=0, clock=APB_CLK)
			
>>> c=PWM(Pin(6), freq=3000, duty=640) # same pin but diff freq => new Timer
>>> print(c)
            PWM(Pin(6), freq=2998, duty=640, resolution=14, (duty=62.50%, resolution=0.006%), mode=0, channel=1, timer=1, clock=APB_CLK)
			
>>> d=PWM(Pin(7), freq=3000, duty=640, light_sleep_enable=True) # ERROR multiple clock source
            Traceback (most recent call last):
              File "<stdin>", line 1, in <module>
            ValueError: one or more active timers use a different clock source, not supported by the current SoC.
			
>>> a.deinit()
>>> b.deinit()
>>> c.deinit()
>>> d=PWM(Pin(7), freq=3000, duty=640, light_sleep_enable=True)
>>> print(d)
            PWM(Pin(7), freq=2997, duty=640, resolution=12, (duty=62.50%, resolution=0.024%), mode=0, channel=0, timer=0, clock=RC_FAST_CLK, light sleep enabled)
			
>>> e=PWM(Pin(8), freq=4000, duty=128)
>>> print(e)
            PWM(Pin(8), freq=3996, duty=128, resolution=12, (duty=12.50%, resolution=0.024%), mode=0, channel=1, timer=1, clock=RC_FAST_CLK)
>>> 

Testing light sleep feature

from machine import Pin, I2C, ADC, Timer, PWM, deepsleep, lightsleep, mem32
from neopixel import NeoPixel
import time

# This script test the ligthSleepEnable parameters
# It will create 2 PWM signal, one active during the ligth sleep, the second without this parameter


S3 = const(1)
S2 = const(2)

TARGET = S3

# PWM

if TARGET == S2:
    PWM_1 = 7
    INTERNAL_STD_LED = 15
    TST_PIN = 12
elif TARGET == S3:   
    PWM_1 = 4
    INTERNAL_LED = 47
    TST_PIN = 10
    
PWM_2 = const(13)


O_PWM1 = None
O_PWM2 = None
LED = None


def setLed(status):
    
    if TARGET == S3:
        if status == True:
            neoLed(True, (32,0,0))
        else:
            neoLed(True, (0,64,0))
    elif TARGET == S2 :   
        stdLed(status)
        

def stdLed(status):
    
    global STD_LED
    
    if status == True:
        STD_LED.value(1)
    else:
        STD_LED.value(0)

def neoLed(status=False, cGRB=(0,125,0)):
        
        global LED
        
        if LED is None:
            return
        
        if status:
            LED[0] = cGRB
        else:
            LED[0] = (0,0,0)
            
        LED.write()  

def init_hardware():
    
    global O_PWM1, O_PWM2, LED, STD_LED, O_TST
    
    O_PWM1 = PWM(Pin(PWM_1,  Pin.OUT ), freq=1000, duty=1023, light_sleep_enable=True)
    O_PWM2 = PWM(Pin(PWM_2,  Pin.OUT ), freq=1000, duty=1023)
    O_TST =  Pin(TST_PIN, Pin.OUT, value=1, hold=True)
    #O_TST.value(1)
    if TARGET == S2:
        STD_LED = Pin(INTERNAL_STD_LED, Pin.OUT)
    elif TARGET == S3:
        LED = NeoPixel(Pin(INTERNAL_LED, Pin.OUT), 1)
        


def Main():
    
    global O_PWM1, O_PWM2
    
    
    init_hardware()
        
    
    V = 50

    while True:
        
        if V == 50:
            V = 25
        else:
            V = 50
            
        # Set Fan PWM:
        
        duty_pct = (100-V) * 1023 // 100    
    
        O_PWM1.duty(duty_pct)
        O_PWM2.duty(duty_pct)
        print('OPWM1',O_PWM1)
        print('OPWM2',O_PWM2)
                
        time.sleep(10)
        
        print("[i] Activation du someil légé (20s) ! zZz ")
        setLed(False)
        
        lightsleep(20*1000)
        print("[i] WakeUp !")
        
        setLed(True)

# Global Start
Main()

Result:
image

@yoann-darche yoann-darche force-pushed the esp32-dev branch 2 times, most recently from a60f5bb to 4bf685f Compare October 29, 2024 02:09
@robert-hh
Copy link
Contributor

robert-hh commented Oct 29, 2024

Please have a look at PR #16090 for setting the frequency and duty cycle. That PR fixes a current bug with ESP32S3 and simplifies the setting.

@yoann-darche
Copy link
Author

yoann-darche commented Oct 29, 2024

Yes I've seen it and I've checked that I use the same approach of this PR. I'ven't the chance to test on ESP32-C6 (for PLL_CLOCK) as I've not this Soc in my lab.
The things that differ from this PR is about estimation of the frequency, the PR #16090 use ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, in this PR I use ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX (that seems required for the RC_FAST_CLK).
Also add the support to the RC_FAST_CLK, that is not implemented in the PR-16090 and needed to support the light_sleep option.

@robert-hh
Copy link
Contributor

You could add class constants for the clock argument. For that you have to change extmod/machine_pwm.c as well and insert into the local dictionary table a line like:

    // A port may add PWM class constants defining the following macro.
    // It can be defined to nothing if there are no constants.
    MICROPY_PY_MACHINE_PWM_CLASS_CONSTANTS

and then define MICROPY_PY_MACHINE_PWM_CLASS_CONSTANTS in esp32/machine_pwm.c with the class constants. Just like it is available for UART.

PLL_CLK works at a ESP32C6.

@yoann-darche
Copy link
Author

Thanks for your test on ESP32-C6 :-)
Yes great idea, I porpose a slidy approach that differ from the UART source conde extmod/machine_uart.c, I see the follwing:

    // A port must add UART class constants defining the following macro.
    // It can be defined to nothing if there are no constants.
    MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS

when adding the define in the table machine_uart_locals_dict_table[] (line: 138 & 164). Does it mean that I've to add a dummy #define in each port ?

my proposal is the follwing:

    // A port may add PWM class constants defining the following macro.
    // It can be defined to nothing if there are no constants.
    #ifdef MICROPY_PY_MACHINE_PWM_CLASS_CONSTANTS
    MICROPY_PY_MACHINE_PWM_CLASS_CONSTANTS
    #endif

I've pushed the new version.

Copy link

codecov bot commented Oct 29, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.57%. Comparing base (0e490b7) to head (93591d8).

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #16102   +/-   ##
=======================================
  Coverage   98.57%   98.57%           
=======================================
  Files         164      164           
  Lines       21345    21345           
=======================================
  Hits        21040    21040           
  Misses        305      305           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

github-actions bot commented Oct 29, 2024

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +9 +0.002% VIRT_RV32

@projectgus
Copy link
Contributor

projectgus commented Oct 29, 2024

@yoann-darche Thanks for the long discussion ahead of implementing this, and for submitting it as a PR. The main concern is really about adding complexity (there's a lot of code here) but also that where possible we try to keep the machine APIs similar between ports.

I've read the discussion and the PR and I understand the extra functionality from adding the light_sleep_enable parameter to the API (with the necessary trade-off of a lower frequency range), but I don't quite understand exposing the explicit clock selection parameter. Is there a use that this enables, separate from the lightsleep feature?

@yoann-darche
Copy link
Author

yoann-darche commented Oct 30, 2024

@projectgus Good point, and it is main raison why I've implemented and auto clock mode to not borrow the developper with that. With it on mind, I think you are right. If look at the table of available clock vs SoC:

Clock Source                      ``clock=``  ESP32/S2   ESP32-S3/C3  ESP32-C6
--------------------------------  ----------  ---------  -----------  ---------
(AUTO) select the best one             0      Available   Available   Available
APB_CLK (40Mhz or less)                1      Available   Available
RC_FAST_CLK (8Mhz) (lightsleep)        2      Available   Available   Available
REF_TICK                               3      Available        
XTAL_CLOCK (40Mhz)                     4                  Available   Available
PLL_DIV_CLK (80Mhz)                    5                              Available
================================  ==========  =========  ===========  =========

The two main options are the "CPU_CLK" (sometimes called APB_CLK or PLL_DIV_CLK) and the RC_FAST_CLK. Clearly, the best choice for precision is the CPU_CLK, which is derived from XTAL_CLK. And the only clock that continues to function in light sleep mode is the RC_FAST_CLK. The other clocks will be used only in very specific cases.
Now, about the code, we need to keep the main part that tracks which clock has been selected to control the multi/mono source for the timer. I've kept the code footprint as small as possible. The only memory increase is due to tracking activated GPIOs in light sleep mode.

I'm fine to keep the clock exposed in order to let the developper to fine tune if necessary
or not.....

@robert-hh : what do you think ?

Edit: After some research about the case where we need use XTAL_CLOCK (for example) as the source is very specific and relevant when we change the CPU clock to low frequency (<80Mhz). And in our context it is not relevant. So aggree with @projectgus to remove the clock parameter, and refactor the code to simply it :-)

@yoann-darche
Copy link
Author

yoann-darche commented Oct 31, 2024

I've update the esp32/machine_pwm.c file to remove the clock parameter and constante definition. But I've also to rebase my branch, and I've maybe done some bad things. So now I get the message "This branch cannot be rebased safely".
In the merge branch step, I seen some other files that not in my update.... I don't now how to solve it.
Shall I have to make a new PR ?

@projectgus
Copy link
Contributor

In the merge branch step, I seen some other files that not in my update.... I don't now how to solve it.
Shall I have to make a new PR ?

You don't need to make a new PR, but there's a merge conflict because we merged #16090. You'll need to update your local working copy and rebase again. There will be some local merge conflicts ("rebase conflicts" I guess) for you to resolve as part of the rebase.

If you use the Git CLI then the first steps are something like:

git checkout esp32-dev  # if not already your current branch
git fetch origin  # get the new commits from GitHub
git rebase origin/master  # rebase your local branch on the latest master

However you might want to read up on resolving conflicts before you dive in. Jimmo has some tips here.

If you get totally stuck and think the only way out is to make a new PR then please ping me first and tell me what branch you were going to use for the new PR, I can show you how to update this PR instead of creating a new one.

@yoann-darche yoann-darche changed the title esp32/machine_pwm: Enhancement of PWM: Add features light_sleep_enable and clock source. esp32/machine_pwm: Enhancement of PWM: Add features light_sleep_enable. Oct 31, 2024
@yoann-darche
Copy link
Author

yoann-darche commented Oct 31, 2024

Rebase successfull, thank to the Jimmo tips :-)
Code updated
Update and simplify test script, rewrite description
Test pass on ESP32-S3, todo: ESP32-S2 and ESP32

@robert-hh
Copy link
Contributor

You have now 16 commits in your PR with 5 commits repeated thrice. Better clean that up using
git rebase -i and the squash option. You can end up with a single commit.

@yoann-darche
Copy link
Author

yoann-darche commented Nov 2, 2024

Test pass on ESP32-S3/ESP32-S2 and ESP32
I will study and try to the rebase and squash to clean the repository.

@robert-hh
Copy link
Contributor

Compiling fails with ESP32C6.

/home/robert/Downloads/MicroPython/micropython/ports/esp32/machine_pwm.c:287:284: error: 'LEDC_USE_APB_CLK' undeclared (first use in this function); did you mean 'LEDC_USE_XTAL_CLK'?
  287 |                     MP_ERROR_TEXT("ledc_timer_config::Bad ARG.. frequency %d, precision=%d\n Timer# %d,\n New clk:%d\n HighSpeed:%d\n LED_AUTO_CLK:%d\n LEDC_APB:%d\n LEDC_FAST_CLK:%d"), freq, timer->duty_resolution, timer->timer_num, led_src_clock, timer->speed_mode, LEDC_AUTO_CLK, LEDC_USE_APB_CLK, LEDC_USE_RC_FAST_CLK);

@robert-hh
Copy link
Contributor

robert-hh commented Nov 3, 2024

It still runs into a bug when compiling. There are duplicated lines. When erasing one of the set, compile succeeds.

/home/robert/Downloads/MicroPython/micropython/ports/esp32/machine_pwm.c:535:13: error: redefinition of 'resolution'
  535 |         int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution;
      |             ^~~~~~~~~~
/home/robert/Downloads/MicroPython/micropython/ports/esp32/machine_pwm.c:532:13: note: previous definition of 'resolution' with type 'int'
  532 |         int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution;
      |             ^~~~~~~~~~

Edit: It is the last commit which duplicates the lines.

@yoann-darche
Copy link
Author

yoann-darche commented Nov 5, 2024

I've done some tests on ESP32/S2/S3 with the following scenario. And all pass as expected.

Compilation Ok for ESP32-C3/C6,

Test Script:

import os
from machine import PWM, Pin, lightsleep,
from time import sleep_ms

IS_ESP32GEN = 'ESP32' in os.uname().machine
IS_ESP32S2 = 'ESP32-S2' in os.uname().machine
IS_ESP32S3 = 'ESP32S3' in os.uname().machine

# List available pin in function of the SOC

print("\n=====================")
if IS_ESP32S2:
    # Support only 8 channels
    pinList = (15, 2, 3, 4, 5, 6, 7, 8)
    print("ESP32-S2 detected")
elif IS_ESP32S3:
    # Support only 8 channels
    pinList = (2, 3, 4, 5, 6, 7, 8, 9)
    print("ESP32-S3 detected")
elif IS_ESP32GEN:
    # support 16 channels (8 in LowSpeed and 8 in HightSpeed)
    pinList = (15, 2, 4, 16, 18, 19, 22, 23, 25, 26, 27, 14 , 12, 13, 32, 33)
    print("ESP32-GENERIC detected")
    

if pinList == None:
    print("Test Fail: Board not detetected !")
    exit(1)
print("=====================\n")
pwms = []

print("\nTest #1: Create all available channels")
# From test in the documentation
try:
    f = 100  # Hz
    d = 1024 // 16  # 6.25%
        
    for i, pin in enumerate(pinList):        
        pwms.append(PWM(Pin(pin), freq=f * (i // 2 + 1), duty= 1023 if i==15 else d * (i + 1)))
        sleep_ms(200)
        print(pwms[i])        
finally:
    while len(pwms) > 0:   
        p = pwms.pop()
        try:
            p.deinit()
        except:
            print("Error while deinit :",p)
           


print("\nTest #2: Create all available channels in sleep mode")
try:
    for i, pin in enumerate(pinList):
        pwms.append(PWM(Pin(pin), freq=f * (i // 2 + 1), duty= 1023 if i==15 else d * (i + 1), light_sleep_enable=True))
        sleep_ms(200)
        print(pwms[i])        
except:
    if i>7:
        print("In case of ESP32, only low speed mode can use RC_FAST_CLK, so only 8 channels available.")
finally:
    while len(pwms) > 0:   
        p = pwms.pop()
        try:
            p.deinit()
        except:
            print("Error while deinit :",p)

print("\nTest #3: Create 2 channel, one in light_sleep mode, second default both shloud run with clock=RC_FAST_CLK")
try:
    pLS = PWM(Pin(pinList[0]), freq=10_000, duty=512, light_sleep_enable=True)
    sleep_ms(200)
    print(pLS)
    pDEF = PWM(Pin(pinList[1]), freq=10_000, duty=512)
    sleep_ms(200)
    print(pDEF)        
finally:
    pLS.deinit()
    pDEF.deinit()
    
print("\nTest #4: Create 2 channel, one default, second in light_sleep mode shloud generate an exception on the second one")
try:
    pLS = PWM(Pin(pinList[0]), freq=10_000, duty=512)
    sleep_ms(200)
    print(pLS)
    try:
        pDEF = PWM(Pin(pinList[1]), freq=10_000, duty=512,light_sleep_enable=True)
    except:
        print("Clock conflict detected !")           
finally:
    pLS.deinit()

Result (concat from output of ESP32, ESP32-S2 and ESP32-S3):

MPY: soft reboot

=====================
ESP32-S2 detected
=====================


Test #1: Create all available channels
PWM(Pin(15), freq=100, duty=64, (duty=6.25%, resolution=0.006%), mode=low_speed, channel=0, timer=0, clock=APB_CLK)
PWM(Pin(2), freq=100, duty=128, (duty=12.50%, resolution=0.006%), mode=low_speed, channel=1, timer=0, clock=APB_CLK)
PWM(Pin(3), freq=200, duty=192, (duty=18.75%, resolution=0.006%), mode=low_speed, channel=2, timer=1, clock=APB_CLK)
PWM(Pin(4), freq=200, duty=256, (duty=25.00%, resolution=0.006%), mode=low_speed, channel=3, timer=1, clock=APB_CLK)
PWM(Pin(5), freq=300, duty=320, (duty=31.25%, resolution=0.006%), mode=low_speed, channel=4, timer=2, clock=APB_CLK)
PWM(Pin(6), freq=300, duty=384, (duty=37.50%, resolution=0.006%), mode=low_speed, channel=5, timer=2, clock=APB_CLK)
PWM(Pin(7), freq=400, duty=448, (duty=43.75%, resolution=0.006%), mode=low_speed, channel=6, timer=3, clock=APB_CLK)
PWM(Pin(8), freq=400, duty=512, (duty=50.00%, resolution=0.006%), mode=low_speed, channel=7, timer=3, clock=APB_CLK)

Test #2: Create all available channels in sleep mode
PWM(Pin(15), freq=100, duty=64, (duty=6.25%, resolution=0.006%), mode=low_speed, channel=0, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(2), freq=100, duty=128, (duty=12.50%, resolution=0.006%), mode=low_speed, channel=1, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(3), freq=200, duty=192, (duty=18.75%, resolution=0.006%), mode=low_speed, channel=2, timer=1, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(4), freq=200, duty=256, (duty=25.00%, resolution=0.006%), mode=low_speed, channel=3, timer=1, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(5), freq=300, duty=320, (duty=31.25%, resolution=0.006%), mode=low_speed, channel=4, timer=2, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(6), freq=300, duty=384, (duty=37.50%, resolution=0.006%), mode=low_speed, channel=5, timer=2, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(7), freq=400, duty=448, (duty=43.75%, resolution=0.006%), mode=low_speed, channel=6, timer=3, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(8), freq=400, duty=512, (duty=50.00%, resolution=0.006%), mode=low_speed, channel=7, timer=3, clock=RC_FAST_CLK, light sleep enabled)

Test #3: Create 2 channel, one in light_sleep mode, second default both shloud run with clock=RC_FAST_CLK
PWM(Pin(15), freq=10004, duty=512, (duty=50.00%, resolution=0.195%), mode=low_speed, channel=0, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(2), freq=10004, duty=512, (duty=50.00%, resolution=0.195%), mode=low_speed, channel=1, timer=0, clock=RC_FAST_CLK)

Test #4: Create 2 channel, one default, second in light_sleep mode shloud generate an exception on the second one
PWM(Pin(15), freq=10000, duty=512, (duty=50.00%, resolution=0.024%), mode=low_speed, channel=0, timer=0, clock=APB_CLK)
Clock conflict detected !


=====================
ESP32-S3 detected
=====================


Test #1: Create all available channels
PWM(Pin(2), freq=100, duty=64, (duty=6.25%, resolution=0.006%), mode=low_speed, channel=0, timer=0, clock=APB_CLK)
PWM(Pin(3), freq=100, duty=128, (duty=12.50%, resolution=0.006%), mode=low_speed, channel=1, timer=0, clock=APB_CLK)
PWM(Pin(4), freq=200, duty=192, (duty=18.75%, resolution=0.006%), mode=low_speed, channel=2, timer=1, clock=APB_CLK)
PWM(Pin(5), freq=200, duty=256, (duty=25.00%, resolution=0.006%), mode=low_speed, channel=3, timer=1, clock=APB_CLK)
PWM(Pin(6), freq=300, duty=320, (duty=31.25%, resolution=0.006%), mode=low_speed, channel=4, timer=2, clock=APB_CLK)
PWM(Pin(7), freq=300, duty=384, (duty=37.50%, resolution=0.006%), mode=low_speed, channel=5, timer=2, clock=APB_CLK)
PWM(Pin(8), freq=400, duty=448, (duty=43.75%, resolution=0.006%), mode=low_speed, channel=6, timer=3, clock=APB_CLK)
PWM(Pin(9), freq=400, duty=512, (duty=50.00%, resolution=0.006%), mode=low_speed, channel=7, timer=3, clock=APB_CLK)

Test #2: Create all available channels in sleep mode
PWM(Pin(2), freq=100, duty=64, (duty=6.25%, resolution=0.006%), mode=low_speed, channel=0, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(3), freq=100, duty=128, (duty=12.50%, resolution=0.006%), mode=low_speed, channel=1, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(4), freq=200, duty=192, (duty=18.75%, resolution=0.006%), mode=low_speed, channel=2, timer=1, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(5), freq=200, duty=256, (duty=25.00%, resolution=0.006%), mode=low_speed, channel=3, timer=1, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(6), freq=300, duty=320, (duty=31.25%, resolution=0.006%), mode=low_speed, channel=4, timer=2, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(7), freq=300, duty=384, (duty=37.50%, resolution=0.006%), mode=low_speed, channel=5, timer=2, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(8), freq=400, duty=448, (duty=43.75%, resolution=0.006%), mode=low_speed, channel=6, timer=3, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(9), freq=400, duty=512, (duty=50.00%, resolution=0.006%), mode=low_speed, channel=7, timer=3, clock=RC_FAST_CLK, light sleep enabled)

Test #3: Create 2 channel, one in light_sleep mode, second default both shloud run with clock=RC_FAST_CLK
PWM(Pin(2), freq=9997, duty=512, (duty=50.00%, resolution=0.098%), mode=low_speed, channel=0, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(3), freq=9997, duty=512, (duty=50.00%, resolution=0.098%), mode=low_speed, channel=1, timer=0, clock=RC_FAST_CLK)

Test #4: Create 2 channel, one default, second in light_sleep mode shloud generate an exception on the second one
PWM(Pin(2), freq=10000, duty=512, (duty=50.00%, resolution=0.024%), mode=low_speed, channel=0, timer=0, clock=APB_CLK)
Clock conflict detected !



=====================
ESP32-GENERIC detected
=====================


Test #1: Create all available channels
PWM(Pin(15), freq=100, duty=64, (duty=6.25%, resolution=0.000%), mode=high_speed, channel=0, timer=0, clock=APB_CLK)
PWM(Pin(2), freq=100, duty=128, (duty=12.50%, resolution=0.000%), mode=high_speed, channel=1, timer=0, clock=APB_CLK)
PWM(Pin(4), freq=200, duty=192, (duty=18.75%, resolution=0.000%), mode=high_speed, channel=2, timer=1, clock=APB_CLK)
PWM(Pin(16), freq=200, duty=256, (duty=25.00%, resolution=0.000%), mode=high_speed, channel=3, timer=1, clock=APB_CLK)
PWM(Pin(18), freq=300, duty=320, (duty=31.25%, resolution=0.000%), mode=high_speed, channel=4, timer=2, clock=APB_CLK)
PWM(Pin(19), freq=300, duty=384, (duty=37.50%, resolution=0.000%), mode=high_speed, channel=5, timer=2, clock=APB_CLK)
PWM(Pin(22), freq=400, duty=448, (duty=43.75%, resolution=0.001%), mode=high_speed, channel=6, timer=3, clock=APB_CLK)
PWM(Pin(23), freq=400, duty=512, (duty=50.00%, resolution=0.001%), mode=high_speed, channel=7, timer=3, clock=APB_CLK)
PWM(Pin(25), freq=499, duty=576, (duty=56.25%, resolution=0.001%), mode=low_speed, channel=0, timer=0, clock=APB_CLK)
PWM(Pin(26), freq=499, duty=640, (duty=62.50%, resolution=0.001%), mode=low_speed, channel=1, timer=0, clock=APB_CLK)
PWM(Pin(27), freq=601, duty=704, (duty=68.75%, resolution=0.001%), mode=low_speed, channel=2, timer=1, clock=APB_CLK)
PWM(Pin(14), freq=601, duty=768, (duty=75.00%, resolution=0.001%), mode=low_speed, channel=3, timer=1, clock=APB_CLK)
PWM(Pin(12), freq=701, duty=832, (duty=81.25%, resolution=0.002%), mode=low_speed, channel=4, timer=2, clock=APB_CLK)
PWM(Pin(13), freq=701, duty=896, (duty=87.50%, resolution=0.002%), mode=low_speed, channel=5, timer=2, clock=APB_CLK)
PWM(Pin(32), freq=799, duty=960, (duty=93.75%, resolution=0.002%), mode=low_speed, channel=6, timer=3, clock=APB_CLK)
PWM(Pin(33), freq=799, duty=1023, (duty=99.90%, resolution=0.002%), mode=low_speed, channel=7, timer=3, clock=APB_CLK)

Test #2: Create all available channels in sleep mode
PWM(Pin(15), freq=100, duty=64, (duty=6.25%, resolution=0.002%), mode=low_speed, channel=0, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(2), freq=100, duty=128, (duty=12.50%, resolution=0.002%), mode=low_speed, channel=1, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(4), freq=200, duty=192, (duty=18.75%, resolution=0.003%), mode=low_speed, channel=2, timer=1, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(16), freq=200, duty=256, (duty=25.00%, resolution=0.003%), mode=low_speed, channel=3, timer=1, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(18), freq=300, duty=320, (duty=31.25%, resolution=0.006%), mode=low_speed, channel=4, timer=2, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(19), freq=300, duty=384, (duty=37.50%, resolution=0.006%), mode=low_speed, channel=5, timer=2, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(22), freq=400, duty=448, (duty=43.75%, resolution=0.006%), mode=low_speed, channel=6, timer=3, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(23), freq=400, duty=512, (duty=50.00%, resolution=0.006%), mode=low_speed, channel=7, timer=3, clock=RC_FAST_CLK, light sleep enabled)
In case of ESP32, only low speed mode can use RC_FAST_CLK, so only 8 channels available.

Test #3: Create 2 channel, one in light_sleep mode, second default both shloud run with clock=RC_FAST_CLK
PWM(Pin(15), freq=10011, duty=512, (duty=50.00%, resolution=0.195%), mode=low_speed, channel=0, timer=0, clock=RC_FAST_CLK, light sleep enabled)
PWM(Pin(2), freq=10011, duty=512, (duty=50.00%, resolution=0.195%), mode=low_speed, channel=1, timer=0, clock=RC_FAST_CLK)

Test #4: Create 2 channel, one default, second in light_sleep mode shloud generate an exception on the second one
PWM(Pin(15), freq=10000, duty=512, (duty=50.00%, resolution=0.024%), mode=high_speed, channel=0, timer=0, clock=APB_CLK)
Clock conflict detected !

@yoann-darche
Copy link
Author

Rebase with latest master
Test done ESP32/S2/S3
Compile check for ESP32-C6
I've not the chance to compile under IDF v5.0x or v5.1x (maybe @projectgus could do that ?): In order to get the fequency for RC_FAST_CLK, I've add an include file : soc/clk_tree_defs.h that define SOC_CLK_RC_FAST_FREQ_APPROX.

@yoann-darche
Copy link
Author

Ok, the code is now ready (and clean). Do I need to provide something else ?

@yoann-darche
Copy link
Author

Ok, the code is now ready (and clean). Do I need to provide something else ?

@projectgus @robert-hh does something need to be done in order to integrate this PR in the next release ?

@robert-hh
Copy link
Contributor

There are a few other PRs about ESP32 PWM which may have to get merged first. Then the maintainer may have a look at this PR. So for now you "just" have to wait.

@yoann-darche
Copy link
Author

I'm facing to a complex situation, I've try to follow update on ESP32 to keep teh PR fine, but now I have a mistake somewhere, and I don't how to solve it. The file esp32/machine_pwm is up to date, and it is the only one that I've touch.
I've try rebase and pull... but It is not ok.
Should I restart from scratch ?

@robert-hh
Copy link
Contributor

The history of commits looks pretty crowded. So yes, If you changed only a single file, put that aside, start over from the current master and apply the changed machine_pwm.c to that state.
Note:

  • Look into the Git documentation how to update an existing commit.
  • Look at the GIT documentation for git rebase and git rebase -i. For making changes to existsing commits this is very powerful.
  • Consider to use git push -f to push changes to open PRs.

Performing a rebase and squash.

Enhancement of the PWM constructor by adding a new keywords:
light_sleep_enable.
Review the precision (of the duty parameter) determination by using
the ESP-IDF native function ledc_find_suitable_duty_resolution().

Add LEDC_USE_PLL_DIV_CLK clock source for ESP32-C6 support.
Reordering clock source list.

Fix some issue inside the PWM.deint(), particulary about the
deconfiguration of the timer.

Signed-off-by: Yoann Darche <yoannd@hotmail.com>
@yoann-darche
Copy link
Author

Ok, I've clean my PR... I stil have some errors but not related to the ESP32 target witch I've changed ?

@robert-hh
Copy link
Contributor

Do not worry about these.

@IhorNehrutsa
Copy link
Contributor

IhorNehrutsa commented Dec 18, 2024

@yoann-darche
I have add your light_sleep_enable feature to the #10854
You might be interested in testing it.

Sorry, I was unable to add you as a co-author by means of GitHubDesktop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants