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.
:
Bearbeitet durch User
Es gibt ein Beispielprojekt unter: ~/STM32Cube/Repository/STM32Cube_FW_F1_V1.8.3/Projects/STM32F103RB-Nucle o/Examples_MIX/TIM/TIM_PWMInput
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.
Hallo, mit Pin Interrupt und freien Timer spielt der "Arduino Kram" keine Rolle. Hat der STM keinen speziellen Messtimer?
Das wird gemacht mit ein Timer und ein "Input capture". Bei den Pin change wird die actuelle Timercounter wert gespeichert und ein Interrupt getriggert. So ist die "latenz"nur noch abhangig von Timerfrequenz.
Veit D. schrieb: > Hat der STM keinen speziellen Messtimer? Ja schon (jeder Timer kann das), aber Arduino nutzt sie nicht so.
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 :-)
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.
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.
Man könnte es auch pragmatisch angehen und das PWM Signal mittels R/C Filter in eine analoge Spannung umwandeln, die man dann wiederum mit Arduino ganz einfach auslesen kann. Den digitalen Filter kann man sich damit auch gleich einsparen. Kostet halt 20 Cent für Bauteile.
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... ;-)
Bei Mbed läuft ein Systemtimer mit 1 MHz, also 1 μs. Die Zeitstempel kann man mit einem InterruptIn bei rise/fall erfassen und Pulsweite und Frequenz ausrechnen. Ein 1 ms Signal bekommt man damit in der gewünschten Genauigkeit aufgelöst.
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 ;-)
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.
Herr Bert schrieb: > Sauber ist es die Phasen der jeweils korrespondierenden Flanken zu > messen und den sich bildenen Versatz über mehrere Phasen zu mitteln Und das ist doch genau der von mir oben beschriebene Integral?! Eben um kleine Signal- oder Messungenauigkeiten zu unterdrücken.
Lothar M. schrieb: > 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... ;-) Gern, allein ein Beispiel wäre gut...
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.
:
Bearbeitet durch User
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.
Stefan ⛄ F. schrieb: > Alternativ: Fernsehgucken. Da machen andere was für dich und du kannst > zugucken. Falsch, die machen nie was ICH will! ;-) Stefan, mit solchen Sätzen schadest Du nur unnötig Deinem Image... Und wenn Du glaubst das ich hier nur sitze und F5 drücke, dann täuschst Du dich gewaltig.
:
Bearbeitet durch User
Man kann doch einen 100us Interrupt anlegen und den Input einfach pollen. Ist zwar eine grandiose Verschwendung/Verachtung von gegebenen Systemressourcen aber es passt evtl. zum eigenen Programmiervermögen.
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.
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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 :-)
Gibt es auch noch einen sinnvollen Kommentar? Evtl. ein Hinweis auf einen Programmierfehler in meinem Code?
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.
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.
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.
:
Bearbeitet durch User
Olli Z. schrieb: > Mir scheint als würde IC_Val2 überhaupt keinen anderen Wert erhalten, > außer 0. Dann lass doch bitte diese ganze HAL-Gestrüpp in der Kiste und programmiere die Timerregister direkt. Da sieht man dann, was passieren soll und kann überprüfen, ob es so auch funktioniert.
Du verwendet TIM_ICSELECTION_DIRECTTI für beide Channel. Damit muss das Signal auch an beiden Timer Eingängen (Channel 2 und Channel 3) anliegen. Ist das so verdrahtet? Es ist einfacher einen Kanal mit TIM_ICSELECTION_DIRECTTI und einen mit TIM_ICSELECTION_INDIRECTTI zu verwenden. (Pin reusen)
c.w. schrieb: > Ist das so verdrahtet? AAAAAARGH! DAS wars, ich war einen Pin daneben gerutscht weil das Bluepill board ein wenig bescheiden beschriftet ist an den pins. GRRR, ok jetzt läuft es wie es soll. Danke für den entscheidenden Hinweis, ich hatte die Verdrahtung nicht in Zweifel gezogen.
Olli Z. schrieb: > ...jetzt läuft es wie es soll. Ohh, das ist aber interessant! Wenn du jetzt noch im Programm die Pulsweite mit 0,6 multiplizierst, hättest du ganz nebenbei eine Klartext-Ladestromanzeige für Ladesäulen entwickelt (die Frequenz ist bei den Ladesäulen konstant 1000 Hz).
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.