Hallo,
ich möchte eine neues Projekt starten.
hierzu erzeugt ein Timer jese millisekunde ein Interrupt.
In diesem Interrupt wird eine volatile varriable um 1 hochgezählt.
Dass der Interrupt jede ms ausgeführt wird, habe ich überprüft, indem ich eine LED immer toggle und das Signal mit einem Oszzi überprüft habe. Passt, jede ms ein Flankenwechsel high>low oder low>high.
Dann erfolgt im zyklischen Programmteil eine Logic, die nach 1000 ms eine andere LED toggelt.
Was nicht stimmt, dass die Zeit zum Toggeln 2 Sekunden dauert, es sollte aber 1 Sekunde sein.
Jetzt könnte man denken, dass irgendwo anders im Zyklus eine Sekunde gewartet wird. Ist es aber nicht, weil wenn ich den Zähler (COUNT) von 1000 auf 500 reduziere, dass beträgt die Toggelzeit exact 1 Sekunde. Also Fehler Faktor 2.
Bin für jeden Hinweis dankbar......
Also Fehler Faktor 2.
Bin für jeden Hinweis dankbar......
Du macht es auch maximal kompliziert und auch resourcenhungrig. Du zählst DOPPELT! In der ISR UND im Hauptprogramm! Setz doch einfach nach 500ms ein Flag, da im Hauptprogramm erkannt wird und die LED schaltet. Das ist DEUTLICH einfacher und braucht weniger CPU-Last.
Dein Zugriff auf zeit bei "if (zeit>=1)" ist nicht atomar und könnte, weil int zeit aus mehreren Bytes besteht, inmitten des Vergleichs unterbrochen werden.
Dein Zugriff auf zeit bei "if (zeit>=1)" ist nicht atomar und
könnte,
weil int zeit aus mehreren Bytes besteht, inmitten des Vergleichs
unterbrochen werden.
LG, Sebastian
Hallo, es ist ein 32 Bit MCU.
Habe den teil deaktiviert:
// if (zeit >=1)
// {
Trotzdem ist die Zeit doppelt so lange wie sie sein sollte....
Beim Lesen deines Codes bekomme ich einen Knoten im Hirn. Aber er ist sicher nicht fehlerhaft.
Die "zeit" wird jede ms um 1 erhöht. Du toggelst die LED nach 500ms. Sie ist also 500ms AN und dann 500ms AUS. Macht zusammen 1s pro Zyklus, oder 1 Hz.
Die "zeit" wird jede ms um 1 erhöht. Du toggelst die LED nach 500ms. Sie
ist also 500ms AN und dann 500ms AUS. Macht zusammen 1s pro Zyklus, oder
1 Hz.
Istzustand:
Bei COUNT 1000 : LED 2s an 2s aus
Bei COUNT 100 : LED 200 ms an 200 ms aus
Tja, hier liegt vielleicht der Fehler. Die LED "toggelt" nach 500ms, sprich wechselt den Pegel. Damit ist die Pulsbreite für LOW und HIGH 500ms. Die Periodendauer aus LOW+HIGH ist aber 1000ms, logisch. VErmutlich hat das der OP verwechselt.
Istzustand:
Bei COUNT 1000 : LED 2s an 2s aus
Bei COUNT 100 : LED 200 ms an 200 ms aus
Hmm, das glauben wir einfach mal. Dann läuft irgendwas nur halb so schnell wie es soll. Oder es wird immer ein Takt "verdoppelt" ? Komisch. Möglichwerweise kommt der Timer mit dem dauernden Reset auf 0 aus dem Takt, das kann mit dem Vorteiler zusammen hängen. Wenn der auch einen Reset bekommt, kann sowas passieren. So oder so ist dein Ansatz unsinnig. Pack die Uhr in den Timer-Interrupt und generiere dort Flags für das Hauptprogramm. Dabei läuft der Zähler durch und alles ist gut.
Schreibe mal ein neues Programm, wo nur die für diesen Effekt relevanten Teile drin sind. Tritt der Fehler dann noch auf, dann zeige und den kompletten Quelltext dieses Testprogramms. Tritt er nicht mehr auf, finde den Unterschied.
Und zeige uns dein Oszilloskop-Bild von den 1ms. Vielleicht sind es doch 2ms und du hast dich nur verguckt.
Hier als JPG, wenn gleich das auch nur ein Workaround ist. Wie schafft man es, ein 9,3MB PNG zu erzeugen? OK, bei 4000x2300 Pixel geht das anscheinend! Und das bei einem Digitaloszilloskop, das saubere Screenshots erzeugen kann. FAIL!
@Falk:
Und wie soll ich dann die Zykluszeit in ms ermitteln ???
Welche Zykluszeit? In deinem Beispiel zählst du nur eine Uhr hoch. Und wenn man die Zeitdifferenz zweier Ereignisse messen will, macht man das wie der Rest der Welt.
Differenz = Endzeit - Startzeit.
Und wenn man periodisch ein Ereignis auslösen will, kann man das auch über den Interrupt machen. Anstatt des #define für die 1000ms nutzt man eine Variable. Damit kann man variable Blinkzeiten oder was auch immer generieren.
Man muss den Interrupt gar nicht sperren wenn man das so macht:
volatile int zeit1;
volatile int zeit2;
int failsafe_exit = 0;
while (1) {
zeit1 = zeit;
zeit2 = zeit;
if (zeit1 == zeit2) {
break;
}
failsafe_exit++;
if (failsafe_exit > 10) {
break;
}
}
failsafe_exit braucht man nicht unbedingt. Ist aber besser. Wobei wenn der greift, dann gibt es keine konsistente Zeit. Da muss man dann richtung Fehlerbehandlung gehen.
Die While schleife sollte max 2 mal laufen, wenn der Interrupt ca 1ms kommt.
Der wechselt einmalig am Anfang (Statusmaschine) vom State Init in
Event.
Wenn der Interrupt jede 1ms triggert, wie auf dem Oszi zu sehen, und also jede 1ms "zeit" um 1 erhöht, und du häufig genug in der Hauptschleife die in "zeit" gezählten 1ms in appData überträgst und auf das Erreichen von 1000 überprüfst, dann sollte dieses Erreichen von 1000 jede Sekunde passieren. Wenn es nur alle zwei Sekunden passiert, bzw. nur jede Sekunde passiert indem du statt auf das Erreichen von 1000 auf das Erreichen von 500 prüfst, dann ist irgendwo tatsächlich der Wurm drin.
Hast du eine Möglichkeit, Testausgaben zu generieren? Dann gib an der Stelle "//---------jede Sekunde------------------------------------" mal appData.msec_cnt aus.
Hast du eine Möglichkeit, Testausgaben zu generieren? Dann gib an der
Stelle "//---------jede Sekunde------------------------------------" mal
appData.msec_cnt aus.
Ja, habe ich. Über CDC (USB-Serial) COM Port kann ich Meldungen ausgeben.
Werde dann das Programm anpassen und diese Werte jede Sekunde ausgeben:
appData.msec_cnt
appData.sec_cnt
Das Programm ist mir viel zu sehr von hinten durch die Brust ins Auge, da sieht doch keiner durch.
Wenn Du einen Oszi hast, dann setze das Toggle der LED einfach direkt in den Interrupthandler. Die 500Hz sollten gut zu sehen sein.
Das Programm ist mir viel zu sehr von hinten durch die Brust ins Auge,
da sieht doch keiner durch.
Dann will ich die Programm Logic mal so erklären:
Im Interrupt wird eine Varriable (32 Bit) jede ms um 1 erhöht.
Der normalen Zyklus (Superloop) kann einige us bis einige ms dauern.
Im normalen Zyklus wird die bisher vergangene Zeit vom letzten Aufruf in ms übertragen (= aktuelle Zykluszeit in ms).
Wenn diese 0 ist, dann ist noch keine ganze ms vergangen >>> mach nix
Wenn diese 1 oder mehr ist, dann addiere die vergangene Zeit in ms auf den ms-Zähler.
Wenn der ms Zähler dann z.B 1003 ist (letzter Zyklus dauerte z.B. 3 ms), dann ziehe 1000 ab, bleibt Rest 3 für nächsten Durchlauf, damit keine 3ms verloren gehen.
4.Erhöhe den Sekundenzähler.
War meine Posting zu subtil? Versuche -= statt =-, oder einfach =0 in der Zeile. Wenn appData.msec_cnt = -500, dann bis 500 braucht die doppelte Zeit als von 0, oder lege ich falsch?
Wenn Du das wirklich so: =- in Deinem Programm stehen hast, dann kann Ozvald nur recht geben; das erste Toggeln kommt zwar eventuell noch zum richtigen Zeitpunkt, aber das übersieht man natürlich leicht, alle folgenden Intervalle sind immer das doppelte von COUNT.
Sollte das nur ein Tippfehler hier im Post sein, dann weißt Du jetzt, warum alle nach einem compilierenden Minimalbeispiel verlangen.
Verstehe ich nicht. Klar geht das. Wenn Du die Differenz zwischen zwei Zeitpunkten messen willst wird es sogar noch einfacher. Du brauchst nur eine einzige "ms" Variable (unsigned) im Interrupt hochzuzählen, und zwar ohne auf 0 zurückzusetzen. Du merkst Dir den Stand am Anfang und den Stand am Ende deines Zyklus. Deine Zyklusdauer ist "Ende - Anfang" Und zwar immer, Overflows usw. werden wegen der Subtraktion implizit berücksichtigt.
Ein Tickzähler läuft immer nur hoch. Er wird nie zurückgesetzt. Das Zurücksetzen passiert implizit nur dann, wenn der Zähler überläuft.
Mein Tipp: sieh dir die Implementierung von millis() beim Arduino an. Damit können sogar blutige Laien mit Zeitdifferenzen ohne Überlaufprobleme arbeiten.
Passiert die Magie der Integerarithmetik. Solange die Differenz kleiner
als der Zählumfang des Timers ist, ist die Differenz IMMER korrekt!
Also wenn ich einen 16 Bit Timer, der jede µs um 1 erhöht wird, frei laufen lasse, und ihn an zwei unterschiedlichen Zeitpunkten auslese, dann ist das Ergebnis auch noch richtig, auch wenn zwischendurch ein Überlauf von 0xFFFF auf 0x0000 stattgefunden hat ?
Also Jungs, das war der best Hinweis überhaupt.
Durch das neue System wird der Prozessor gar nicht mehr mit Interrupts belastet. Und die Messung der aktuellen Zykluszeit hat eine Auflösung von 1 µs.
Hier ein Teil des Codes:
1
voidCLKS_Initialize(void)
2
{
3
// Timer 2/3 (32 Bit) läuft endlos mit 25 MHz
4
// Konfiguriert mit Harmony für PIC32MZ
5
TMR2_PeriodSet(0xffffffff);
6
TMR2_Start();// Start the timer
7
LogMsg("The 1 microsecond timer has started");
8
}
9
10
11
voidCLKS_SM(void)
12
{
13
staticuint32_tzeit_alt;
14
staticuint32_tzeit_neu;
15
zeit_neu=TMR2_CounterGet();// Aktuelle Zeit holen
16
17
// Vergangene Zeit in us * 25, weil Timer2/3 mit 25 MHz läuft
Also Jungs, das war der best Hinweis überhaupt.
Durch das neue System wird der Prozessor gar nicht mehr mit Interrupts
belastet. Und die Messung der aktuellen Zykluszeit hat eine Auflösung
von 1 µs.
Schöner Unfug. Vor allem, da dein Timer mit 25 MHz läuft. Der hat auch einen Vorteiler, den kann man einstellen, dann läuft der direkt mit 1MHz, was immer noch Käse ist, aber etwas weniger Käse.
1
static uint32_t zeit_alt ;
2
static uint32_t zeit_neu ;
3
zeit_neu = TMR2_CounterGet(); // Aktuelle Zeit holen
4
5
// Vergangene Zeit in us * 25, weil Timer2/3 mit 25 MHz läuft
Ja und? Dann hättest du 25MHz/128=195,3125kHz (5,12us) Auflösung. Immer
noch mehr als sinnvoll, vor allem für solche Messungen.
Woher willst Du das wissen? Wir machen hier z.B. synchrone Automatisierungstechnik, da messe ich die Laufzeit meiner Software in "ns". 2us Jitter liegen bei uns schon außerhalb der Spezifikation. 5us sind ne halbe Ewigkeit.
"Vielen Dank an alle. Ich habe es kapiert.
Bin halt nur Gelegenheitsprogrammierer...."
Das ist nur Hobbyspielerei.
Wir machen hier z.B. synchrone
Automatisierungstechnik, da messe ich die Laufzeit meiner Software in
"ns". 2us Jitter liegen bei uns schon außerhalb der Spezifikation. 5us
sind ne halbe Ewigkeit.
Von dir und deiner professionellen Anwendung war gar nicht die Rede.
Und darauf lassen die einen Gelegenheitsprogrammierer los. Sehr
professionell!
Falk, ich glaube Du bist etwas frustriert.
Es gibt auch Leute, de neben Programmieren auch noch etwas anders beruflich machen.
Fachidiot: Kann eine Sache zu 100 %
Guter Mitarbeiter: Kann mehrere Sachen, nicht zu 100 % aber ist bereit sich neuen Aufgaben zu stellen und sich einzuarbeiten....
Auf den kann man sich also verlassen - liefert stets korrekte Ergebnisse.
Guter Mitarbeiter: Kann mehrere Sachen, nicht zu 100 % aber ist bereit
sich neuen Aufgaben zu stellen und sich einzuarbeiten....
Kann also nichts richtig - liefert stets zusammengeschusterten Laienpfusch und wird in Kürze von ChatGPT/Copilot ersetzt. Diese Einstellung, dass sowas ein "Guter Mitarbeiter" ist, scheint bei den Personalern leider weit verbreitet zu sein (er ist halt billiger). Es wird übersehen, dass das "sich einarbeiten" in Fachgebiete gerne mal Jahre dauert. Da dafür die Zeit nicht reicht, gibt man sich mit Pfusch zufrieden - entsprechend ist der aktuelle Stand der Technik.
Ich wüsste auf jeden Fall, wen ich also Mitarbeiter haben wollte ...
Guter Mitarbeiter: Kann mehrere Sachen, nicht zu 100 % aber ist bereit
sich neuen Aufgaben zu stellen und sich einzuarbeiten....
Kann also nichts richtig - liefert stets zusammengeschusterten
Laienpfusch
[Noch mehr Unsinn]
Nö, genau die Leute die breit Aufgestellt sind werden die einzigen sein, die zukünftig noch Arbeit haben werden. Denn Gerade die "Fachidioten" sind die, die man mit KI ersetzen wird. Bisher hat man solche Arbeiten gerne nach Fernost abgeschoben, aber die Leute dort haben auch keinen Bock mehr da drauf.
Die Ergebnisse von Entwicklern, die nur in Ihrer eigenen kleinen Welt leben, darf ich jeden Tag erleben.
Schöner Unfug. Vor allem, da dein Timer mit 25 MHz läuft.
Hab ich mir sofort auch gedacht, als ich /25 und /1000 gesehen habe. Arg viel ineffizienter kann man nicht prorammieren, denn auch heute noch sind Prozessoren im tiefsten Inneren binär. Eine dezimale Rechnung (oder allgemeiner einer Rechnung jenseits von Zweierpotenzen) macht ihnen Aufwand.
Sag ichs noch: ein µC ist binär.
Für eine effiziente Programmierung musst du dich dem Controller anpassen, denn der Witz ist: du schreibst das Programm nur ein einziges Mal, der Controller muss es aber zigtausendmal pro Sekunde ausführen.
Ich mache es so: ein Zeitzähler (Timertic) läuft immer hoch. Er wird niemals von Hand zurückgesetzt. Nur am "Ende" der läuft er implizit über. Und basierend auf diesem Zähler dann werden alle Zeiten per Differenz verwaltet. Ein Tipp dazu: sieh dir die Arduino millis() Funktion und die Arbeit mit Zeitdifferenz mal genauer an.
2us Jitter liegen bei uns schon außerhalb der Spezifikation. 5us sind ne
halbe Ewigkeit.
Ja, mag sein. Allerdings bin ich mir absolut sicher, dass daran dann aber auch keine "fortgeschrittenen Laien" arbeiten.
Im Fall hier würde ich mir überhaupt erst mal klar machen, welche Zeitauflösung denn nötig und welcher Jitter erlaubt ist. Und dann eine geeignete Zeitbasis aufbauen, die dem Controller "gut liegt" und mit der er einfach arbeiten kann.
Dann hättest du 25MHz/128=195,3125kHz (5,12us) Auflösung
Also könnte ich da einfach einen "hundertstel µs"-Zähler bei jedem Interrupt um 512 hochzählen. Und wenn der dann über 100000 hinausläuft, dann würde ich den ms-Zähler um 1 hochzählen und den "hundertstel µs"-Zähler atomar(!) um 100000 verringern.
Also etwa so:
1
longmillis,lasttoggle;
2
:
3
TimerInterrupt(){// 5.12µs
4
staticlongus100cnt;
5
us100cnt+=512;
6
if(us100cnt>100000){
7
millis++;
8
us100cnt-=100000;
9
}
10
}
11
:
12
// Arbeiten mit Zeitdifferenzen
13
if(millis-lasttoggle>1000){
14
toggleLED();
15
lasttoggle=millis;// Umschaltzeitpunkt merken
16
}
17
:
18
// oder besser so, dann summieren sich Verzögerungen nicht auf,
19
// sondern "verpasste" Toggles werden "nachgeholt"
20
if(millis-lasttoggle>1000){
21
toggleLED();
22
lasttoggle+=1000;// Umschaltzeitpunkte jeweils alle 1000 ms