PWM-Signal mit STM32 messen

#6652375
Lesenswert?

Gibt es beim STM32F103C8(Blue Pill) (ich verwende derzeit die Arduino 
IDE Toolchain von STM32duino) die Möglichkeit den Dutycycle eines 3,3V 
PWM-Signals mit "bliebiger" Frequenz (irgendwas zwischen 100 Hz und 1 
kHz) direkt als Messwert zu erhalten, oder muss man das selbst 
programmieren?

Bei letzterem, wie wäre der Ansatz? Über die steigende/fallende Flanke 
eines Interrupt-Pins eine ISR auslösen lassen und dazwischen jeweils die 
CPU-Zyklen zählen um ein Ratio zu erhalten? Und dann eine Messreihe 
integrieren um einen stabilen Wert zu erhalten?

Am Ende hätte ich gern einen DC von 0-100% als Integer Wert. Besondere 
Präzision ist nicht gefragt, 10% Messfehler sind kein Problem.
#6652413
Lesenswert?

Die elegante Variante wäre über einen Timer, allerdings bezweifle ich, 
dass es da für Arduino was Out of the Box gibt. Im Arduinoumfeld wird 
das einfachste vermutlich ein Pinchange Interrupt sein, allerdings kann 
ich dir nicht sagen wie genau das ganze ist. Der Arduino Kram hat doch 
recht viel Overhead.

Grundsätzlich bringt der STM alles mit um das mit hoher Präzision 
komplett in Hardware zu machen, aber da wirst du selbst was schreiben 
müssen.
#6652721
Lesenswert?

Also aufs falsche Pferd gesetzt?! I wurde bislang mit STMCube oder Mbed 
nicht so richtig warm... bzw. die Lernkurve ist sehr steil für den 
Basteleinsatz.

Aber unabhängig davon, vielleicht kann mir einer der Herren doch nochmal 
etwas genauer erklären wie das mit einem Timer funktioniert? Das habe 
ich bei der kurzen Erklärung noch nicht ganz verstanden.

Wer mag, gern auch ein kleines Codebeispiel zum besseren Verständnis :-)
Gast #6652734
Lesenswert?

Olli Z. schrieb:
> Also aufs falsche Pferd gesetzt?! I wurde bislang mit STMCube oder Mbed
> nicht so richtig warm

Niemand zwingt dich dazu, überhaupt ein Framework zu verwenden. Es geht 
auch ohne. Hier hats du was für den Anfang: 
http://stefanfrings.de/stm32/stm32f1.html

Das Reference Manul ist dein Freund und Helfer.

> Aber unabhängig davon, vielleicht kann mir einer der Herren doch
> nochmal etwas genauer erklären wie das mit einem Timer funktioniert?
> Das habe ich bei der kurzen Erklärung noch nicht ganz verstanden.

Das kann man auch nicht aml eben kurz erklären. Dafür sind die Timer zu 
komplex. Du musst die Beschreibung schon selbst lesen und verstehen. Es 
hilft nichts, wenn wenn dir hier das entsprechende Kapitel des Reference 
Manual abschreiben.

Du brauchst unabhängig davon ein Lösungskonzept. Wie kann man mit einem 
Timer die Pulsbreite messen? Zum Beispiel so:

Du lässt eine Timer mit einer festen Taktfrequenz (z.B. 100kHz) hoch 
zählen. Den Timer konfigurierst du so, dass er bei jeder Flanke des PWM 
Signals einen Interrupt auslöst und sich den dann gerade aktuellen 
Zählerstand merkt (input capture mode).

In der ISR fragst du ab, ob das gerade die steigende oder fallende 
Flanke war. Daraus ergibt sich, ob er gerade die HIGH oder LOW Phase des 
PWM Signals gemessen hat. Außerdem subtrahierst du den vorherigen 
capture-Wert vom aktuellen, um die Breite des PWM Pulses zu ermitteln.

Nun hast du zwei Zahlen: Breite des LOW-Anteils und Breite des 
HIGH-Anteils. Diese dividierst du, dann hast du den Faktor im Bereich 
0-1. Multipliziert mit hundert ergibt den gewünschten Wert in Prozent.
Gast #6652748
Lesenswert?

So, wie das hier vielfach vorgeschlagen wird, produziert das aber einen 
großen Fehler. Der Abtastfehler kommt 2x rein, der Jitter des 
abtastenden Taktes auch und vor allem hat man das Problem eine 
jitternden Eingangssignals und das Verhalten des Pins nicht im Griff.

Sauber ist es die Phasen der jeweils korrespondierenden Flanken zu 
messen und den sich bildenen Versatz über mehrere Phasen zu mitteln und 
zufiltern. Dann kriegt man den Abtastfehler und Jitter nur 1x rein und 
hat zudem mit N Phasen den Taktfehler des Eingangs um den Faktor 1/N 
reduziert.
Moderator (Firma: Titel) Persönliche Seite #6652752
Lesenswert?

Olli Z. schrieb:
> die Möglichkeit den Dutycycle eines 3,3V PWM-Signals mit "bliebiger"
> Frequenz (irgendwas zwischen 100 Hz und 1 kHz) direkt als Messwert zu
> erhalten
Wie genau brauchst du den Wert?
Reicht es auf 1/100 genau (z.B. 23%) oder musst du es auf drei 
Nachkommastellen genau wissen (z.B. 23,456%)?

Wie schnell brauchst du den Wert?
Kannst du da schon mal 100 PWM-Zyklen abwarten oder muss unbedingt in 
jedem einzelnen Zyklus ein Wert herauskommen?

Wenn du nämlich ein wenig Zeit hast, dann kannst du auch einfach die 
Mote-Carlo-Methode anwenden und in einem Timertick den Pegel des Signals 
einlesen. Nach ein paar Samples kannst du dann recht gut das Verhältnis 
zwischen high und low in den PWM-Wert umrechnen.

Das geht für ganz Mutige oft auch "hinreichend genau" ganz Monte-Carlo 
mäßig einfach nur per Sampling in der Mainloop. Probiers mal aus...  ;-)
#6652772
Lesenswert?

Stefan ⛄ F. schrieb:
> Niemand zwingt dich dazu, überhaupt ein Framework zu verwenden. Es geht
Natürlich nicht :-) Beim Arduino fand ich es einfach, installieren, 
loslegen. Beim STM32 muss man sich erstmal zwischen zig Plattformen, 
IDE, Libraries, entscheiden... Nutzt man CMSIS oder HAL?
Ohne "Starthilfe" verliert man sich da ganz schnell.

In Assembler möchte ich das nicht unbedingt machen und ein klein wenig C 
kann ich.


> Wie kann man mit einem Timer die Pulsbreite messen? Zum Beispiel so:

Das aber ist doch im grunde exakt was ich oben in Theorie beschrieben 
hab :-) Es geht darum bei jedem Flankenwechsel die Zeit bis zum nächsten 
zu messen. Die Taktfrequenz des Timers entscheidet ja dann auch über die 
Auflösung des Messergebnisses als auch die Frage wie lang die zu 
messenden Pulsbreiten wohl sein werden. Ich denke das ich das auf eine 
Abstrakte weise schon verstehe, schön wäre halt wenn mir jemand bei der 
Realisierung helfen könnte damit ich die richtigen Schritte tue.

Dabei können wir gern auch mit STM32Cube IDE arbeiten, oder sonst was. 
Ich wollte halt auch sicher gehen das ich mir nicht eine Mordsarbeit 
mache und dann gibt es nachher sowas wie einen HAL_getPWM() oder so ;-)
#6652774
Lesenswert?

Ich empfehle aus dem Reference Manual von Deinem STM32 mal das Kapitel 
zu den Advanced Timern (z.B. TIM1) durchzulesen. Den Teil zu PWM-Output 
kannst Du überfliegen, Dich interessiert hier das Input Capture, und 
zwar über 2 gekoppelte Kanäle um Rising und Falling-Events gleichzeitig 
zu bekommen. Soo kompliziert und viel zu lesen ist das dann auch nicht.

So, und jetzt könntest Du Dein restliches Arduino-Framework einfach 
lassen wie es bisher ist und nur in einer Funktion den Timer über die 
entsprechenden Register entsprechend initialisieren und abfragen.

Ich vermute mal stark daß Arduino die CMSIS-Header bereitstellt, so daß 
Du die einfach includen kannst und nicht alle Registeradressen von Hand 
berechnen und eintragen musst.
#6652793
Lesenswert?

Ich bin wirklich dankbar für Eure Hinweise und Beiträge, fürchte nur das 
zwischen Euren und meinem Know-How hier eine kleine Welt liegt. Ich 
glaube zwar weder faul noch doof zu sein, aber manchmal braucht man 
einfach etwas tatkräftigen Anschub... ich finde das der Einstieg in 
STM32 deutlich schwieriger ist als beim AVR, dennoch glaube ich das der 
STM die bessere Plattform ist, weshalb ich daran schon festhalten 
möchte.

Ich versuche mich jetzt erstmal mit Stefans Links und den dahinter 
liegenden Infos.
Gast #6652825
Lesenswert?

Olli Z. schrieb:
> Gern, allein ein Beispiel wäre gut...

Ach Olli. Wenn dir das Reference Manual zu viel ist, dann ist dir der 
Mikrocontroller zu viel. Einfacher sind nur die 8bit Controller, aber 
auch da musst du lesen.

Ich werde dir das Kapitel jedenfalls nicht übersetzen und vorkauen.

Den einfacheren weg über R/C Filter und analogem Eingang habe ich dir 
schon genannt. Du musst dein Hobby aber selbst durchführen.

Alternativ: Fernsehgucken. Da machen andere was für dich und du kannst 
zugucken.
#6652919
Lesenswert?

Hallo,

@ Olli Z.
Die Microchip megaAVR0 und AVRxDB Serie haben auch Messtimer. Auf den 
Teilen kann ich dir das erklären, weil das ganz simpel ist. Ein Arduino 
Nano Every hat einen ATmega4809 (megaAVR0) drauf. Wäre ein 16Bit Timer. 
Wenn millis benötigt wird gibts nur Prescaler 1 oder 2. Wenn auf millis 
(bzw. das gesamte Arduino Framework) verzichtet werden kann gibts mehr 
Prescaler um den Messbereich anzupassen. Wobei man millis noch 
korrigieren könnte, ist auch möglich, wenn millis doch benötigt wird.
Ansonsten musste dich beim STM durchbeißen. Das Prinzip wird ähnlich 
sein, nur das die viel viel mehr Register haben.
Das wäre die hochgenaue Variante.

Wie schon gesagt wurde kannste das auch alles im Pin Interrupt 
erledigen, mit minimaler Messungenauigkeit. Musst dir nur immer merken 
welche Flanke du gerade gemessen hast und den Timerwert dafür 
hinterlegen.

PW:
1. steigende Flanke > Timercounter merken
2. fallende Flanke  > Timercounter merken
3. PW vorbei, PW berechnen.
4. auf nächste steigende Flanke warten und bei 1. beginnen.

PW und Frequenz:
1. steigende Flanke > Timercounter merken
2. fallende Flanke  > Timercounter merken
3. steigende Flanke > Timercounter merken
4. Periode vorbei, PW und Frequenz berechnen.
5. auf nächste steigende Flanke warten und bei 1. beginnen.
Hierbei kann nur jede 2. Periode vermessen werden.
Bauste dir ein schönes switch case zusammen. Das läuft.

Du kannst das auch ohne speziellen Timer machen. Nimmst dafür millis 
oder micros. Dann schauste wie genau das wird und ob es dir reicht.
#6667183
Lesenswert?

Ich habe mir nun ein kleines Beispiel zusammengeschrieben und einen 
PWM-Generator angeschlossen. Die Frequenz wird optimal angezeigt, 
allerdings ist der Duty-Cycle immer "0". Irgendwie finde ich den Fehler 
nicht...

Ich verwende den externen Quarz mit 8 MHz auf dem Bluepill Board und 
teile ihn so das am Timer 4 MHz ankommen. Damit erreiche ich eine 
ausreichend grobe Auflösung um so niedrige Frequenzen wie 250 Hz messen 
zu können.
Angehängte Dateien:
Gast #6667225
Lesenswert?

Ein Beispiel mit F407 für 0,05 Hz - 500 kHz:
http://www.mino-elektronik.de/FM_407/fmeter_407.htm#a5
Ein Programm für ATmega88, wobei nur eine Pulsweite ermittelt wird:
http://www.mino-elektronik.de/fmeter/fm_software.htm#bsp6

Sofern Dir 10% Genauigkeit reichen kann man das Signal auch über ein 
RC-Glied filtern, skalieren 3,3 V -> 1 V und mit einem Multimeter in 
Prozent anzeigen.
Bei einem Drehspulinstrument kann man sich das RC-Glied sparen :-)
Gast #6668990
Lesenswert?

Olli Z. schrieb:
> Evtl. ein Hinweis auf einen Programmierfehler in meinem Code?
1
uint32_t Frequency = 0;
2
uint32_t Duty_Cycle = 0;

Diese Variablen werden im Interrupt-Kontext berechnet und im
normalen Programmlauf angezeigt. Deswegen empfiehlt es sich
die Variablen als volatile zu deklarieren. Sonst könnte der
Compiler auf die Idee kommen zu optimieren und dadurch fälsch-
licherweise jeweils zwei verschiedene Werte der Variablen
parallel zu halten. Das Ganze erübrigt sich natürlich wenn
die Compiler-Optimierungsstufe auf Null gesetzt ist.
#6669679
Lesenswert?

jo mei schrieb:
> Diese Variablen werden im Interrupt-Kontext berechnet und im
> normalen Programmlauf angezeigt. Deswegen empfiehlt es sich
> die Variablen als volatile zu deklarieren. Sonst könnte der

Guter Tipp, hat aber leider nichts am Problem geändert. Die Frequenz 
wird einwandfrei angezeigt, der Dutycycle bleibt immer 0. Er wird auch 
als 0 berechnet, sprich wenn ich den Startwert anders setzte, gibt es im 
Display trotzdem immer eine 0.
Mir scheint als würde IC_Val2 überhaupt keinen anderen Wert erhalten, 
außer 0. Wenn ich Duty_Cycle = IC_Val1; in der Sub mache, erhalte ich 
Werte, wenn ich Duty_Cycle = IC_Val2; setze nicht. D.h. die Falling-Edge 
wird nicht gemessen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren