Forum: Mikrocontroller und Digitale Elektronik ESP32 Clock-Signal ausgeben


von Stefan Z. (stefan_z)


Lesenswert?

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.

von Christopher J. (christopher_j23)


Lesenswert?

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.

von Stefan Z. (stefan_z)


Lesenswert?

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.

von Christopher J. (christopher_j23)


Lesenswert?

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.

von Stefan Z. (stefan_z)


Lesenswert?

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!

von Christopher J. (christopher_j23)


Lesenswert?

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
}

von Stefan Z. (stefan_z)


Lesenswert?

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]

von Christopher J. (christopher_j23)


Lesenswert?

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.

von Stefan Z. (stefan_z)


Lesenswert?

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
Noch kein Account? Hier anmelden.