Der ESP32 ist ein fettes Teil, aber die (Arduino) Doku / Beispiele sind bisher dürftig. Derzeit suche ich eine Möglichkeit am ESP32 ein externes Clock-Signal auszugeben. Beim AVR ja simpel - Timer einstellen, anschalten, Pin hüpft. Beim ESP32 habe ich bisher probiert: #1 - RepeatTimer aus den beigefügten Arduino Beispielen. Hebt und senkt im Sekundentakt fein das Bein - am Prescaler drehen geht auch (40 statt 80 sind 500ms, usw.). Den Zähler (Microsekunden) im Timer-Call von 1000000 auf 500000 stellen macht das selbe. Aber enn man zu weit runter geht wird erst die Linie auf dem Oszi "unscharf" - die Rechtecke verlagern sich periodisch, es entstehen Geister. Und ab einem gewissen Punkt kommt einfach nur noch eine Guru Meditation über UART. (Ich gebe übrigens nix auf der UART aus, nur Pin an/aus). Vermutlich funkt hier RTOS dazwischen. #2 - LEDC PWM. Scheinbar auch nicht geeignet so schnelle Signale zu erzeugen - es werden bisher auch nur 10+ Bit PWM-Frequenzen angeboten. Mit weniger Bits könnte es gehen. Hat jemand eine Idee? Das muss doch gehen.
Wenn dir das LEDC-Modul zu langsam ist, probier es mal mit dem MCPWM-Modul. Das ist eigentlich für die Ansteuerung von Motortreibern gedacht, sollte aber einen ganzen zacken schneller laufen als das LEDC-Modul. Keine Ahnung wie schnell genau. Du hast auch nicht gesagt wie schnell es zuckeln soll. Das was du gemacht hast ist sehr wahrscheinlich Software-PWM per Interrupt. Da der Guru eingesprungen ist, tippe ich mal darauf, dass FreeRTOS mit diesem IRQ-Storm nicht klar kam und sich verschluckt hat. Was die Timer und andere "spezielle" Peripherie des ESP32 angeht, vergiss den Arduino-Kram. Selbst das ESP-IDF ist noch dermaßen unausgegoren und unvollständig, dass es selbst da oft haaresträubend ist. Wenn du mit dem ESP32 derzeit irgendwas machen willst, was über das Dimmen einer LED hinausgeht, dann musst du schon mindestens mal das IDF hernehmen und im Zweifelsfall auch mal einen Blick ins Handbuch werfen und zur größten Not auch ein paar Register von Hand ziehen. Das geht aber alles auch aus der Arduino-Umgebung heraus und du musst nicht komplett auf das IDF umsatteln. Ist in gewisser Weise wie bei den AVRs.
OK, MCPWM ist wieder was neues, auf dem board bei ESP32.com wurde mir die I2C Clock vorgeschlagen. Kreativ, aber nicht unbedingt das was man erwartet... Das mit den IDF Namespaces in Arduino hab ich schon - teilweise - mitbekommen. Manche Sachen tuns auch 1:1 aus der Dokumentation raus. Z.B. das hier:
1 | adc1_config_width(ADC_WIDTH_12Bit); |
2 | adc1_config_channel_atten(ADC1_CHANNEL_0,ADC_ATTEN_0db); |
3 | int val = adc1_get_voltage(ADC1_CHANNEL_0); |
Aber so genau gecheckt was wie wo geht und nicht hab ich noch nicht.
Stefan Z. schrieb: > OK, MCPWM ist wieder was neues Ja bis vor zwei Monaten gab es den auch noch nicht :D Also im Silizium war er drin aber eben nirgendwo dokumentiert. Angeblich gibt es auch noch ein BPWM-, also "Basic PWM-Modul" und angeblich arbeiten sie da gerade daran. Es gibt also immer was neues und man kann sich quasi alle paar Monate über ein neues Feature freuen, als hätte man einen neuen Chip.
Hmm und ich dachte, dass bei € 6,– für das Modul jetzt der Punkt wäre wo es langsam Resourcen für Normalsterbliche gibt… Naja lassen wir das gute Stück noch etwas im Regal reifen. Danke auf jeden Fall!
Stefan Z. schrieb: > Naja lassen wir das gute Stück noch etwas im Regal reifen. Ich bezweifle ob das z.B. bei den Timern so schnell so viel besser wird. Dafür gibt es bei dem Teil einfach noch zu viele Baustellen. Der CAN-Treiber fehlt meines Wissens nach z.B. noch komplett. Eigentlich ist die Sache nicht so wild. Ein simples PWM-Dingens mit dem IDF sieht z.B. so aus:
1 | #include "driver/mcpwm.h" |
2 | #include "soc/mcpwm_reg.h" |
3 | #include "soc/mcpwm_struct.h" |
4 | |
5 | |
6 | #define GPIO_MCPWM0_0A_OUT 19 //Set GPIO 19 as PWM0A of MCPWM0
|
7 | |
8 | void app_main() |
9 | {
|
10 | //1. mcpwm gpio initialization
|
11 | // configure channel A of timer 0 of MCPWM0
|
12 | mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_MCPWM0_0A_OUT); |
13 | |
14 | //2. initialize mcpwm configuration
|
15 | mcpwm_config_t pwm_config; |
16 | pwm_config.frequency = 5000; //frequency = 5kHz |
17 | pwm_config.cmpr_a = 20.0; //duty cycle of PWMxA = 20.0% |
18 | pwm_config.counter_mode = MCPWM_UP_COUNTER; |
19 | // set the output polarity
|
20 | // DUTY_MODE_0 means output is 20% high and 80% low, DUTY_MODE_1 means 20% low and 80% high
|
21 | pwm_config.duty_mode = MCPWM_DUTY_MODE_0; |
22 | |
23 | mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings |
24 | |
25 | while(1); |
26 | }
|
Das hatte ich so in der Art auch selber schonmal am laufen. Funktioniert an sich ganz gut aber die Angabe des Dutycycles als Float stinkt nach Fisch. Stelle ich die Frequenz auf 500kHz und Duty-Cycle auf 50% bekomme ich 3µs Periodendauer und 1µs Impuls auf High, 2µs auf Low. Guckt man in den Quelltext der mcpwm.c stellt man dann fest, das alle Timer grundsätzlich auf 1MHz getaktet werden, nur steht das nirgendwo. Das ist das, was ich oben mit "haaresträubend" meinte. Wie gesagt: Man kann den ESP32 auch direkt per Register programmieren. Im IDF selbst wird das auch so gemacht. Man kann sich also einen prinzipiellen Ablauf in den Funktionen des IDF abschauen. Ich hab mir mal den Spaß gemacht und so ein Beispiel zusammengetippt/kopiert. Für einen Überblick über das MCPWM-Modul empfehle ich unbedingt einen Blick ins (wirklich nicht so schlechte) Reference Manual. Ansonsten viel Spaß beim Pin togglen.
1 | #include "driver/mcpwm.h" |
2 | #include "soc/mcpwm_struct.h" |
3 | |
4 | |
5 | #define GPIO_MCPWM1_2B_OUT 19
|
6 | |
7 | void app_main() |
8 | {
|
9 | // configure GPIO for output B of operator 2 of MCPWM module 1
|
10 | mcpwm_gpio_init(MCPWM_UNIT_1, MCPWM2B, GPIO_MCPWM1_2B_OUT); |
11 | |
12 | // enable the mcpwm module 1
|
13 | periph_module_enable(PERIPH_PWM0_MODULE + 1); |
14 | |
15 | // set timer 2 as the timer for operator 2
|
16 | MCPWM1.timer_sel.operator2_sel = 2; |
17 | |
18 | // set mcpwm prescaler
|
19 | MCPWM1.clk_cfg.prescale = 1; // PWM_CLK = 160MHz / (prescaler+1) => 80MHz |
20 | |
21 | // set timer prescaler
|
22 | MCPWM1.timer[2].period.prescale = 1; // TIM_CLK = PWM_CLK / (prescaler+1) => 40MHz |
23 | |
24 | // set timer period
|
25 | MCPWM1.timer[2].period.period = 19; // f = TIM_CLK / (period+1) = 2MHz, T = 1/f = 500ns |
26 | |
27 | // set compare value of output B of operator 2, output A would be ...cmpr_value[0].cmpr_val
|
28 | MCPWM1.channel[2].cmpr_value[1].cmpr_val = 10; // duty_cycle = cmpr_val / (period+1) = 50% |
29 | |
30 | // set the counter to upcounter mode
|
31 | MCPWM1.timer[2].mode.mode = MCPWM_UP_COUNTER; |
32 | |
33 | // set the output polarity so that output is high if counter < cmpr_val
|
34 | mcpwm_set_duty_type(MCPWM_UNIT_1, MCPWM_TIMER_2, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); |
35 | |
36 | // start the timer by writing 0b10 to the start bit field in the mode register
|
37 | MCPWM1.timer[2].mode.start = 2; |
38 | |
39 | while(1); |
40 | }
|
Nice! Vielen Danke! Ich habs mal in Arduino reingeworfen - macht leider Fehler beim Kompilieren. der Fehler hier kommt ständig - was bedeutet er in diesem Kontext?
1 | ESP32_clock_1:17: error: invalid conversion from 'int' to 'periph_module_t' [-fpermissive] |
Hmmm, ich hab es auch selber gerade mal mit Arduino getestet und dem scheinen die sowohl die Struct-Deklarationen aus dem Header, sowie so manche Typedefs nicht zu schmecken. Das liegt wohl vermutlich an C++. Ich bin leider weder bei Arduino, noch bei C++ sonderlich bewandert und nutze selber nur das IDF. Du könntest auch den Karren von hinten aufziehen und im IDF das Arduino-Framework einbinden: https://github.com/espressif/arduino-esp32/blob/master/docs/esp-idf_component.md Ansonsten müsstest du denke ich die Funktion in eine C-Datei packen und eine Arduino-Library (in C++) als Wrapper basteln aber da bin ich ehrlich gesagt der falsche Ansprechpartner. Mit dem IDF funktioniert es jedenfalls einwandfrei.
Ich werde mal auf Zeit setzen und die Library-Arbeit den Pros überlassen... Ging ja dann beim alten ESP auch irgendwann alles recht gut. Vielen Dank trotzdem!
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.