Wie muss ich den ESP32 konfigurieren damit ich ein PWM von 1Hz mit 50 % Tastverhältnis erreiche? Ich bekomme entweder Fehler für div_param oder der GPIO bleibt einfach HIGH. https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/ledc.html#arduino-esp32-ledc-api
Alexander schrieb: > Ich bekomme entweder Fehler für div_param oder > der GPIO bleibt einfach HIGH. Wie hast du es denn probiert und welchen Wert gibt ledcAttach() zurück?
Ohne den ESP im Detail zu kennen, manchmal reicht der interne Vorteiler einfach nicht aus für diese extrem niedrigen Raten.
Aus dem setup() heraus. Dachte schon es liegt an falschen Datentypen, habe auch mal direkt alle Werte hardcoded eingetragen. Hab schon ein paar verschiedene Auflösungen ausprobiert. Alles unter res < 10 gibt Fehlermeldung mit Return Code false, ab res = 10 true. Duty immer 2^res / 2 eingesetzt, auch wenn das die Doku nicht so explizit hergibt. E (622) ledc: requested frequency and duty resolution can not be achieved, try reducing freq_hz or duty_resolution. div_param=0
1 | #define WO GPIO_NUM_32
|
2 | void startWatchdog(gpio_num_t wpin) { |
3 | const unsigned long wfreq = 1; // 1 Hz |
4 | const unsigned char wres = 11; // resolution 2048 |
5 | const unsigned long wduty = 1024; // 50% |
6 | ledcAttach((uint8_t) wpin, wfreq, wres); |
7 | ledcWrite((uint8_t) wpin, wduty); |
8 | }
|
9 | void setup() { |
10 | pinMode(WO, OUTPUT); |
11 | startWatchdog(WO); |
12 | }
|
13 | void loop() { |
14 | // main
|
15 | }
|
ledcRead() und ledcReadFreq() geben den gesetzen Wert zurück bei höheren Frequenzen, aber mit 1 Hz kommt Wert 0 trotz Returncode true. Kann es sein dass ledcWrite() in die main loop() muss?
:
Bearbeitet durch User
Alexander schrieb: > Kann es sein dass ledcWrite() in die main loop() muss? Nein, warum willst du das Tastverhältnis mehrere tausend Mal pro Sekunde neu setzen? > ledcRead() und ledcReadFreq() geben den gesetzen Wert zurück bei höheren > Frequenzen, aber mit 1 Hz kommt Wert 0 trotz Returncode true. Worauf werden die übergebenen Parameter denn überprüft, um den Returncode zu generieren? Wie sieht das erzeugte Signal bei freq=2 und eingestelltem Tastverhältnis 50% aus? Sonst probiere einfach einmal eine für LEDs angemessene PWM-Frequenz, z.B. 1 kHz.
:
Bearbeitet durch User
Hallo Veit, danke, mit 5 kHz hab ich kein Problem (meine 4 kHz PWM funktionieren bei mir). Rainer W. schrieb: > Wie sieht das erzeugte Signal bei freq=2 und eingestelltem > Tastverhältnis 50% aus? Je nach resolution entweder return false oder Pin hängt auf HIGH. Ab 10 Hz funktioniert es. Rainer W. schrieb: > Worauf werden die übergebenen Parameter denn überprüft, um den > Returncode zu generieren? Die Funktionen ledcRead() und ledcReadFreq() geben die gesetzte wfreq = ledc_get_freq(group, timer); und wduty = ledc_get_duty(group, channel); aus, so wie ich es mit ledcAttach() und ledcWrite() eingegeben habe. ledcAttach() ist ein Wrapper und reicht den Returncode von ledcAttachChannel() durch. Wenn Du dort durchblickst sag Bescheid. https://github.com/espressif/arduino-esp32/blob/release/v3.0.x/cores/esp32/esp32-hal-ledc.c#L94 Norbert schrieb: > Ohne den ESP im Detail zu kennen, manchmal reicht der interne Vorteiler > einfach nicht aus für diese extrem niedrigen Raten. Angeblich soll die Clock automatisch umschalten, es stehen 80 MHz und 1 Mhz zur Auswahl (LEDC_REF_TICK). Der GPIO32 ist ein RTC Pin und kann nur LEDC_LOW_SPEED_MODE. https://github.com/espressif/esp-idf/blob/632e0c2a/components/hal/include/hal/ledc_types.h#L75 Leider habe ich diesen `div_param` noch nicht in den Sources gefunden. Ich lass dann mal grep über den gesamten Rechner laufen..
1 | E (622) ledc: requested frequency and duty resolution can not be |
2 | achieved, try reducing freq_hz or duty_resolution. div_param=0 |
:
Bearbeitet durch User
Nüscht gefunden. Dafür so eine Art Bruteforce Sketch. Fängt mit 1 Hz an. https://github.com/espressif/arduino-esp32/blob/release/v3.0.x/libraries/ESP32/examples/AnalogOut/ledcFrequency/ledcFrequency.ino
1 | Bit resolution | Min Frequency [Hz] | Max Frequency [Hz] |
2 | 1 | 489 | 40078277 |
3 | 2 | 245 | 20039138 |
4 | 3 | 123 | 10019569 |
5 | 4 | 62 | 5009784 |
6 | 5 | 31 | 2504892 |
7 | 6 | 16 | 1252446 |
8 | 7 | 8 | 626223 |
9 | 8 | 4 | 313111 |
10 | 9 | 2 | 156555 |
11 | 10 | 1 | 78277 |
12 | 11 | 1 | 39138 |
13 | 12 | 1 | 19569 |
14 | 13 | 1 | 9784 |
15 | 14 | 1 | 4892 |
16 | 15 | 1 | 2446 |
17 | 16 | 1 | 1223 |
18 | 17 | 1 | 611 |
19 | 18 | 1 | 305 |
20 | 19 | 1 | 152 |
21 | 20 | 1 | 76 |
:
Bearbeitet durch User
Basierend auf den structs aus <driver/ledc.h> habe ich nun die Clock auf LEDC_REF_TICK manuell gesetzt. Das funktioniert soweit auch mit 10 Bit Auflösung und nun endlich mit 1 Hz. https://github.com/espressif/esp-idf/blob/632e0c2a/components/driver/ledc/include/driver/ledc.h#L61
1 | void startWatchdog(gpio_num_t wpin, const unsigned long wfreq) { |
2 | ledc_timer_config_t ledc_timer = { |
3 | .speed_mode = LEDC_LOW_SPEED_MODE, |
4 | .duty_resolution = LEDC_TIMER_10_BIT, |
5 | .timer_num = LEDC_TIMER_0, |
6 | .freq_hz = wfreq, |
7 | .clk_cfg = (ledc_clk_cfg_t) LEDC_REF_TICK |
8 | };
|
9 | ledc_timer_config(&ledc_timer); |
10 | ledc_channel_config_t ledc_channel = { |
11 | .gpio_num = wpin, |
12 | .speed_mode = LEDC_LOW_SPEED_MODE, |
13 | .channel = LEDC_CHANNEL_0, |
14 | .intr_type = LEDC_INTR_DISABLE, |
15 | .timer_sel = LEDC_TIMER_0, |
16 | .duty = 512, |
17 | .hpoint = 0, |
18 | .flags = { .output_invert = 0 } |
19 | };
|
20 | ledc_channel_config(&ledc_channel); |
21 | ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel); |
22 | }
|
Allerdings kann ich nicht aus Arduino heraus auf die Logik mit ledc_handle.used_channels zugreifen. Mir ist nicht klar ob ledcAttachChannel() überhaupt erkennen kann dass ich bereits einen C̶h̶a̶n̶n̶e̶l̶ a̶t̶t̶a̶c̶h̶e̶d̶ habe / Kanal belegt (für Falk)... neues Problem. ledcAttach() würde ich ja für die übrigen PWM (4 kHz) trotzdem gerne nutzen. Wo das div_param=0 herkommt ist mir noch ein Rätsel.
:
Bearbeitet durch User
Hm, war da nicht irgendwas, daß bei den LEDC Funktionen irgendwas beim neuen Arduino-Core geändert wurde?
Ich hab mal bisschen gesucht, vielleicht bin ich auf dieses Problem gestoßen und der Pin war gar nicht HIGH. https://forum.arduino.cc/t/ledcattach-on-esp32-for-different-frequencies/1331942 https://forum.arduino.cc/t/fighting-ledc-on-esp32/1359165
:
Bearbeitet durch User
Alexander schrieb: > Ich hab mal bisschen gesucht, vielleicht bin ich auf dieses Problem > gestoßen und der Pin war gar nicht HIGH. Der Zustand eines Pins sollte sich doch herausfinden lassen. Hänge einfach einen Pull-Up und einen gleich großen Pull-Down Widerstand dran (z.B. 4k7) und miss die Spannung.
Ich hatte auch das neue Alexander schrieb: > Tooltop ET828Pro für €27,99 kurz dran aber das Gerät ist irgendwie komisch, da muss man jedesmal die Range Taste drücken und dann analysiert es erstmal 2 Sekunden bevor es skaliert. Hab dann auf LED_BUILTIN gewechselt da ich keine Hand frei hatte für die Taste und zu faul es ordentlich aufzubauen, und die LED hatte halt nur geleuchtet nicht geblinkt. Ich hab das nun nachgeholt, aber kann nicht bestätigen dass es sich um o.g. genanntes Problem, der Channel gehöre zur selben Group und würde daher die Frequenz durch nachfolgende ledcAttach() mit 4 kHz überschreiben, handelt; der Pin bleibt wohl tatsächlich einfach HIGH. Oder das Oszi rafft es nicht. Das Gute ist, zumindest funktioniert die manuelle Methode mit hardcoded timer=0 und channel=0 für 1 Hz in einem Sketch zusammen mit den ledcAttach() mit 4 kHz. Ob das nun Zufall ist oder Glück, zufrieden bin ich damit noch nicht. Laut Log wird der Channel angeblich überschrieben. Aber man weiß ja nicht wie die Debugausgaben zusammengeschustert sind, oder ob `ledc_handle.used_channels` einfach woanders anfängt mit zählen.
1 | #define WO GPIO_NUM_32
|
2 | #define PWM1 GPIO_NUM_12
|
3 | #define PWM2 GPIO_NUM_13
|
4 | #define PWM3 GPIO_NUM_14
|
5 | #define PWM4 GPIO_NUM_27
|
6 | void startWatchdog(...) {...} // s.o. www.mikrocontroller.net/topic/goto_post/7953261 |
7 | void setup() { |
8 | const int freq = 4000; |
9 | const uint8_t res = 8; |
10 | uint8_t duty = 128; |
11 | startWatchdog(WO, 1); |
12 | ledcAttach(PWM1, freq, res); |
13 | ledcAttach(PWM2, freq, res); |
14 | ledcAttach(PWM3, freq, res); |
15 | ledcAttach(PWM4, freq, res); |
16 | ledcWrite(PWM1, duty); |
17 | ledcWrite(PWM2, duty); |
18 | ledcWrite(PWM3, duty); |
19 | ledcWrite(PWM4, duty); |
20 | }
|
1 | [ 1699][V][esp32-hal-periman.c:235] perimanSetBusDeinit(): Deinit function for type LEDC (12) successfully set to 0x400ecdf0 |
2 | [ 1710][V][esp32-hal-periman.c:160] perimanSetPinBus(): Pin 12 successfully set to type LEDC (12) with bus 0x3ffb5744 |
3 | [ 1721][I][esp32-hal-ledc.c:166] ledcAttachChannel(): LEDC attached to pin 12 (channel 0, resolution 8) |
4 | [ 1730][V][esp32-hal-periman.c:235] perimanSetBusDeinit(): Deinit function for type LEDC (12) successfully set to 0x400ecdf0 |
5 | [ 1741][V][esp32-hal-periman.c:160] perimanSetPinBus(): Pin 13 successfully set to type LEDC (12) with bus 0x3ffb5764 |
6 | [ 1751][I][esp32-hal-ledc.c:166] ledcAttachChannel(): LEDC attached to pin 13 (channel 1, resolution 8) |
7 | [ 1761][V][esp32-hal-periman.c:235] perimanSetBusDeinit(): Deinit function for type LEDC (12) successfully set to 0x400ecdf0 |
8 | [ 1772][V][esp32-hal-periman.c:160] perimanSetPinBus(): Pin 14 successfully set to type LEDC (12) with bus 0x3ffb5784 |
9 | [ 1782][I][esp32-hal-ledc.c:166] ledcAttachChannel(): LEDC attached to pin 14 (channel 2, resolution 8) |
10 | [ 1791][V][esp32-hal-periman.c:235] perimanSetBusDeinit(): Deinit function for type LEDC (12) successfully set to 0x400ecdf0 |
11 | [ 1802][V][esp32-hal-periman.c:160] perimanSetPinBus(): Pin 27 successfully set to type LEDC (12) with bus 0x3ffb57a4 |
12 | [ 1813][I][esp32-hal-ledc.c:166] ledcAttachChannel(): LEDC attached to pin 27 (channel 3, resolution 8) |
13 | ------------------------------------------ |
14 | GPIO Info: |
15 | ------------------------------------------ |
16 | GPIO : BUS_TYPE[bus/unit][chan] |
17 | -------------------------------------- |
18 | 12 : LEDC[0][0] |
19 | 13 : LEDC[0][1] |
20 | 14 : LEDC[0][2] |
21 | 27 : LEDC[0][3] |
22 | ============ After Setup End ============= |
:
Bearbeitet durch User
Ich hab mir jetzt die Kommentare in den Sources noch mal durchgelesen. In `esp32-hal-ledc.c` steht zum einen "Need to be fixed" das ist schon mal etwas beunruhigend.
1 | //Use XTAL clock if possible to avoid timer frequency error when setting APB clock < 80 Mhz
|
2 | //Need to be fixed in ESP-IDF
|
3 | #ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
|
4 | #define LEDC_DEFAULT_CLK LEDC_USE_XTAL_CLK
|
5 | #else
|
6 | #define LEDC_DEFAULT_CLK LEDC_AUTO_CLK
|
7 | #endif
|
Dazu hab ich noch die passende Doku gefunden (ist nicht der gleiche Link wie im Eröffnungspost) da gibt es noch eine Funktion zur Auswahl der Clock, wobei da nicht steht auf was das angewendet wird: `ledcSetCLockSource(LEDC_REF_CLK);` https://docs.espressif.com/projects/arduino-esp32/en/latest/api/ledc.html Aus den Kommentaren in `ledc.h` geht hervor dass die Clock für alle Timer gilt, also global, aber so ganz eindeutig ist das noch nicht da wiederum steht "except esp32"
1 | /*!< Configure LEDC source clock from ledc_clk_cfg_t.
|
2 | Note that LEDC_USE_RC_FAST_CLK and LEDC_USE_XTAL_CLK are
|
3 | non-timer-specific clock sources. You can not have one LEDC timer uses
|
4 | RC_FAST_CLK as the clock source and have another LEDC timer uses XTAL_CLK
|
5 | as its clock source. All chips except esp32 and esp32s2 do not have
|
6 | timer-specific clock sources, which means clock source for all timers
|
7 | must be the same one. */
|
In `ledc_types.h` ist allerdings auch von "LEDC timer-specific clock sources" die Rede, weswegen ich unsicher bin ob meine Einstellung nun global gilt oder nicht.
1 | /**
|
2 | * @brief LEDC timer-specific clock sources
|
3 | *
|
4 | * Note: Setting numeric values to match ledc_clk_cfg_t values are a hack to avoid collision with
|
5 | * LEDC_AUTO_CLK in the driver, as these enums have very similar names and user may pass
|
6 | * one of these by mistake.
|
7 | */
|
Auf jeden Fall ist erstmal geklärt dass es keinen Konflikt mit dem Channel 0 gibt, da es nur zweimal 8x Channels gibt (0-7) und nicht einmal 16x Channels (0-15) so wie der Wrapper `ledcAttach()` das in ledc_handle.used_channels für sich trackt. Mein hardcoded LEDC_LOW_SPEED_MODE + LEDC_CHANNEL_0 entspricht also dem Arduino Channel 8. Das erklärt warum es überhaupt funktioniert. In den in `ledc.h` "brief LEDC update channel parameters" liest man noch, nicht alle GPIO können den Highspeed Mode. Ich nehme an das bezieht sich nur auf GPIO32 + GPIO33, nicht auf alle RTC Pins? Wären sonst ziemlich viele..
1 | /**
|
2 | * @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
|
3 | */
|
Die Wrapper Funktion `ledcAttach()` unterscheidet jedenfalls nicht nach High speed / Low Speed, die weißt einfach den nächstbesten freien Channel 0-15 zu, ohne Einschränkung des GPIO. Das ist meiner Meinung nach ein Bug.
1 | bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution) { |
2 | int free_channel = ~ledc_handle.used_channels & (ledc_handle.used_channels + 1); |
3 | }
|
Das Channel Mapping in `ledcAttachChannel()` ist wiederum ziemlich eindeutig. Das ergibt eine fixe Zuordungstabelle.
1 | bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel) { |
2 | uint8_t group = (channel / 8), timer = ((channel / 2) % 4); |
3 | }
|
Ich dachte daran eine eigene `ledcAttach()` Wrapper-Funktion als Override im Sketch zu hinterlegen, da ich ja ein eigenes Channel-Tracking brauche wenn ich da die GPIO nach Lowspeed/Highspeed korrekt zuordnen möchte, aber da ich nicht weiß welche GPIO das betrifft und ich nur den einen GPIO32 als Lowspeed brauche, lasse ich das. Ich werde mich nun also mit `ledcAttachChannel()` begnügen und den Channel 8 selbst zuweisen, notfalls noch dazu mit `ledcSetCLockSource()`
:
Bearbeitet durch User
Rainer W. schrieb: > Wie hast du es denn probiert und welchen Wert gibt ledcAttach() zurück? keine Rückfragen, der To möchte nur Antworten und nicht lernen.
mußt du nicht extra ansagen, weiß man sofort wenn man deinen Nick liest.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.