Hallo zusammen,
ich bin gerade dabei ein Relais in eimen zyklisch Zeitabstand zu
schalten.
Hierzu zähle ich Timerinterruptimpulse. Als Zählvariable verwende ich
eine unsigned Long Variable. Hierbei gibt es aber Probleme.
Wenn ich zwischen 5m-30m Zähle gibt es keine Probleme (Abweichung
<100ms).
Bei 4h funktioniert es nicht. Hier schaltet das Relais erst nach 8h.
Den Code habe ich etwas gekürzt, damit es nicht so überfrachtet ist.
rest_spuelzeit wird über einen Timerinterrupt alle 100ms decrementiert.
zeit_nicht_gespuelt wird über einen Timerinterrupt alle 100m
incrementiert.
Wenn ich die Zeiten 3m-30m einstelle, dann funktioniert dies.
max_Zwangsspuelzeit=18000L;
min_Zwangsspuelzeit=1800L;
Bei 25m-250m klappt es mit 25m aber die 250m funktionieren nicht
max_Zwangsspuelzeit=150000L;
min_Zwangsspuelzeit=15000L;
Hierbei verhält es sich, als wäre die Zähler Variable irgendwann
negativ.
Wenn ich das Poti nach 4h auf 0 drehe, dann bleibt das Relais trotzem
aus.
Gibt es bei Code Composer Studio irgendwelche Bugs, die dieses erklären
würden?
Festlegung des Intervalls: P2 wird über ein Poti verstellt. Ist getestet
und liefert Werte zwischen 0-1023.
Sebastian R. schrieb:> Hallo zusammen,>> ich bin gerade dabei ein Relais in eimen zyklisch Zeitabstand zu> schalten.> Hierzu zähle ich Timerinterruptimpulse. Als Zählvariable verwende ich> eine unsigned Long Variable. Hierbei gibt es aber Probleme.
Hast du das Thema volatile und atomar beachtet? Siehe Interrutpt.
> Bei 4h funktioniert es nicht. Hier schaltet das Relais erst nach 8h.> Den Code habe ich etwas gekürzt, damit es nicht so überfrachtet ist.
Irrtum! Deine Fragmente sind nichtssagend, der Fehler steckt oft nicht
im gezeigten Ausschnitt.
Zeige uns deinen GESAMTEN Quelltext als Anhang.
Sebastian R. schrieb:> Den Code habe ich etwas gekürzt, damit es nicht so überfrachtet ist.
Du solltest einen Code posten, der den Fehler zeigt. Dazu solltest du
den Code im Idealsfall auf einen "Dreizeiler" zusammenkürzen, also den
kürzestmöglichen Code, der den Fehler noch zeigt.
Der Witz dabei: ganz oft findet man den Fehler bei deratigem Vorgehen
schon selber.
> Hierzu zähle ich Timerinterruptimpulse.
Womit? Was ist die Zählervariable?
Warum nachst du es nicht so wie die Arduinos beim millis() Zähler? Denn
die zeigen, wie Zeitvergleiche auch mit einem Zählerüberlauf zuverlässig
funktionieren: der millis-Zähler läuft dauernd hoch, die zugehörigen
Vergleichswerte sind Zeitstempel dieses millis-Zählers und die Differenz
zwischen beiden ist die inzwischen abgelaufene Zeit. Und der Überlauf
des millis-Zählers wird durch eine gechickte Berechnung im Vergleich
elegant abgefangen:
- https://electric-junkie.de/2020/08/arduino-millis-anstatt-von-delay
Ich hab gerade paar Sachen rausgelöscht.
ADC ist uninteressant hier habe ich einfach den vollausschlag von 1023
eingegeben.
Ich habe volatile beachtet, aber nicht atomar. Dieses sollte aber
eigentlich keinen effekt haben oder?
Die Bedingung sollte ja normalerweise, wenn P2value auf 0 gedreht wird.
zeit_nicht_gespuelt>intervall_zwangsspuelung
Sebastian R. schrieb:> Ich habe volatile beachtet, aber nicht atomar. Dieses sollte aber> eigentlich keinen effekt haben oder?
Nun 32Bit dürfte auf nem 16Bitter wohl 2 Zugriffe benötigen, wo
natürlich ein Interrupt zwischen grätschen kann.
Auch lustiges mehrfaches Lesen und Setzen im Main ist zutiefst
unbestimmt. Die komplette Sequenz von ersten bis zum letzten Zugriff
müßte dann atomar sein.
Ich setze mir daher im Interrupt nur ein 100ms Flag, was dann an
kontrollierter Stelle im Main die Variablen ändert.
Peter D. schrieb:> Sebastian R. schrieb:>> Ich habe volatile beachtet, aber nicht atomar. Dieses sollte aber>> eigentlich keinen effekt haben oder?>> Nun 32Bit dürfte auf nem 16Bitter wohl 2 Zugriffe benötigen, wo> natürlich ein Interrupt zwischen grätschen kann.> Auch lustiges mehrfaches Lesen und Setzen im Main ist zutiefst> unbestimmt. Die komplette Sequenz von ersten bis zum letzten Zugriff> müßte dann atomar sein.>> Ich setze mir daher im Interrupt nur ein 100ms Flag, was dann an> kontrollierter Stelle im Main die Variablen ändert.
Also das der Interrupt zwischen der Bearbeitung des 32Bit Wertes
aufgerufen wird, kann ich glauben.
Das es für einen Zyklus dann ggf. falsche Werte hat auch, aber
spätestens bei der nächsten Abarbeitung müsste der Zähler wieder
synchron sein.
Es ist ja nur ein lesender Zugriff an der Stelle. Der Schreibzugriff
findet ja nicht statt.
Ich werde mein Programm kurz abändern und ein Flag benutzen um die Werte
zu bearbeiten. Das Programm ist ja schnell genug und wird nicht länger
als 100ms brauchen. Dementsprechen wird es zu keinem doppelten Aufruf
kommen.
Sebastian R. schrieb:> Ich hab gerade paar Sachen rausgelöscht.
Ob das so gut ist?
> ADC ist uninteressant hier habe ich einfach den vollausschlag von 1023> eingegeben.
Ist der Effekt noch da?
> Ich habe volatile beachtet, aber nicht atomar. Dieses sollte aber> eigentlich keinen effekt haben oder?
Oder.
Einfach will alles volatile zu deklarieren ist Unsinn. Nur Variablen,
welche sowohl in einer ISR als auch im Hauptprogramm verwendet werden.
> Die Bedingung sollte ja normalerweise, wenn P2value auf 0 gedreht wird.>> zeit_nicht_gespuelt>intervall_zwangsspuelung> #include <msp430.h>> #include <msp430f2012.h>> void io_init(void);> volatile unsigned long rest_spuelzeit;> volatile unsigned long zeit_nicht_gespuelt;> unsigned int P2value = 0;> int main(void)> {> int jumper_Spuelzeit;>> volatile unsigned long intervall_zwangsspuelung;> volatile unsigned long max_spuelzeit;> volatile unsigned long min_Zwangsspuelzeit;> volatile unsigned long max_Zwangsspuelzeit;
Die volatile sind hier Unsinn, auch wenn sie nicht schaden.
Du greifst auf rest_spuelzeit und zeit_nicht_gespuelt mehrfach in deinem
Hauptprgramm zu, außerdem in der ISR. Deine Hauptprogramm rast wie ein
Wahnsinniger ohne Bremse im Kreis und wird schätzungsweise 10.000mal und
mehr pro Sekunde ausgeführt. Die Chance, einen nichtatomaren
Zugriffskonflikt zu erzeugen ist sehr hoch, um nicht zu sagen sicher.
Sowas macht man nicht.
Peter hat das deutlich bessere Konzept genannt. Der Timer aktiviert alle
x ms ein Flag, welches in der Hauptschleife abgefragt und ausgewertet
wird. Dann wird deine Funktion EINMAL aufgerufen. Dann kann sie CPU
sonstwas machen oder wieder schlafen gehen. Damit vermeidet man u.a.,
daß in der ISR große Variablen verändert werden müssen. Dein Programm
ist soooo langsam, daß das nicht nötig ist.
Alternativ muss der Durchlauf deines Programmabschnitts atomar gemacht
werden, dann ist es sicher. Siehe Interrupt, jetzt mit korrekter
Schreibweise ;-)
Die Einrückungen deines Programm sind sehr schlecht, vermutlich TABs,
welche hier in der Forumssoftware vermurkst wurden. Formatiere die
Einrückungen sauber mit Leerzeichen.
if (rest_spuelzeit>0L)
{
P2OUT |= 0x40;
zeit_nicht_gespuelt=0L;
}
else
{
P2OUT &= ~0x40;
rest_spuelzeit=0L;
}
Sowas ist Unsinn. Im else Zweig ist rest_spuelzeit == 0, denn das wurde
in der Bedingung geprüft. Das nochmal zuzuweisen ist Unsinn und
irreführend.
Was ich irreführend finde, dass überall im Program 'signed long' Werte
benutzt werden um 'unsigned long' Variablen zu initialisieren.
Z.B. 'rest_spuelzeit=300L' anstelle von 'rest_spuelzeit=300UL'.
Hast du dir die Compiler Warnings mal genauer angeschaut?
Loco M. schrieb:> Was ich irreführend finde, dass überall im Program 'signed long' Werte> benutzt werden um 'unsigned long' Variablen zu initialisieren.> Z.B. 'rest_spuelzeit=300L' anstelle von 'rest_spuelzeit=300UL'.>> Hast du dir die Compiler Warnings mal genauer angeschaut?
Ich habe den Code übernommen und dort waren die schon mit einem L
initialisiert. Es gibt keine Warnung, sonst hätte ich diese schon
geschrieben.
Mein Fehler war definitiv, dass ich keinen Atomaren Datenzugriff
verwendet habe.
Ich verwende jetzt ein Timepulse Flag und ich schalte Global die
Interrupts aus, wenn ich auf Daten worauf die Interupts schreiben.
Test lasse ich gerade laufen.
Die Unordnung im Programm ist ein wenig der Versuche geschuldet.
Ich hatte mit dem Volatile schon gelesen gehabt und habe irgendwann
gesagt, driss drauf lieber vor allem als irgendwo was zu vergessen.
Das gleiche gilt dem "L".
Kann man beim Code Composer Studio die Einrückungen Global auf
Leerzeilen stellen?
Obwohl ich mit Code Composer Studio absolut nicht zufrieden bin.
Könnt ihr einen anderen IDE bzw. Compiler empfehlen?
Ich würde sonst schauen ob ich nicht auf Eclispe gehe.
Macht es Sinn die stdint.h zuverwenden und mit den int32_t generell zu
arbeiten? Prinzipiell ist es ja nichts anderes als meine declarationen.
Sebastian R. schrieb:> Mein Fehler war definitiv, dass ich keinen Atomaren Datenzugriff> verwendet habe.> Ich verwende jetzt ein Timepulse Flag und ich schalte Global die> Interrupts aus, wenn ich auf Daten worauf die Interupts schreiben.> Test lasse ich gerade laufen.
Zeig mal deinen Quelltext.
Sebastian R. schrieb:> Macht es Sinn die stdint.h zuverwenden und mit den int32_t generell zu> arbeiten?
Ja sicher, die gibt es seit 25 Jahren in C99!
> Prinzipiell ist es ja nichts anderes als meine declarationen.
Ja, nur allgemeingültiger, kürzer und eindeutiger.
Siehe Anhang, etwa so. Man muss nicht immer die Endung L hinschreiben.
Das braucht man nur in bestimmten Situationen, wo man ein Formel
hinschreibt und möchte, daß die mit 32 Bit gerechnet wird. Für einfache
Zuweisungen braucht man das nicht.
Hallo Zusammen,
ich habe noch etwas weiter getestet. An dem Interrupt liegt es definitiv
nicht, aber ich habe das trotzdem alles Atomar getrennt.
Wenn ich mit kleinen Zahlen arbeite funktioniert es, aber sobald ich
z.B. 18000 als intervall verwende geht es nicht mehr.
Könnt ihr ein gutes Board inkl. Programmer fürs debuggen empfehlen?
Mit 1800UL klappt es:
1
intervall_zwangsspuelung=1800UL;
Wenn ich das Poti direkt mit einbinde, dann kommt die passende Zeit.
Sebastian R. schrieb:> 1023UL
Das ist falsch. Richtig wären 1024.
> Aber ich habe das trotzdem alles Atomar getrennt.> rest_spuelzeit=100UL;
Das hier ist aber noch nicht atomar. Da kann irgendwas in die Zuweisung
reinfunken.
> Mit 1800UL klappt es>> Mit 18000UL klappt es nicht
Mach mal eine sukzessive Approximation(**) an den Wert, der grade noch
geht und den, mit dem es dann nicht mehr geht. Würde mich nicht arg
wundern, wenn da 16383 und 16384 (oder irgendeine andere Zweierpotenz
zwischen 1800 und 18000) herauskommen würden.
(**) nächster Test mit (18000+1800)/2 = 9900.
Wenn der Wert 9900 noch funktioniert, dann mit (18000+9900)/2 = 13950
weiter.
Wenn der 9900 nicht funktioniert, dann mit (9900+1800)/2 = 5850 weiter.
Und dann immer weiter mit dem Mittelwert bis hin zum Übergang von "gut
nach schlecht".
Sebastian R. schrieb:> Aber ich habe das trotzdem alles Atomar getrennt.>> rest_spuelzeit=100UL;
Das hier ist aber noch nicht atomar. Da kann irgendwas in die Zuweisung
reinfunken.
Lothar M. schrieb:> Sebastian R. schrieb:>> 1023UL> Das ist falsch. Richtig wären 1024.>>> Aber ich habe das trotzdem alles Atomar getrennt.>> rest_spuelzeit=100UL;> Das hier ist aber noch nicht atomar. Da kann irgendwas in die Zuweisung> reinfunken.>>> Mit 1800UL klappt es>>>> Mit 18000UL klappt es nicht> Mach mal eine sukzessive Approximation(**) an den Wert, der grade noch> geht und den, mit dem es dann nicht mehr geht. Würde mich nicht arg> wundern, wenn da 16383 und 16384 (oder irgendeine andere Zweierpotenz> zwischen 1800 und 18000) herauskommen würden.>> (**) nächster Test mit (18000+1800)/2 = 9900.> Wenn der Wert 9900 noch funktioniert, dann mit (18000+9900)/2 = 13950> weiter.> Wenn der 9900 nicht funktioniert, dann mit (9900+1800)/2 = 5850 weiter.> Und dann immer weiter mit dem Mittelwert bis hin zum Übergang von "gut> nach schlecht".>> Sebastian R. schrieb:>> Aber ich habe das trotzdem alles Atomar getrennt.>>>> rest_spuelzeit=100UL;> Das hier ist aber noch nicht atomar. Da kann irgendwas in die Zuweisung> reinfunken.
Mit dem Annähern bin ich schon dran.
hatte vorhin schon einmal mit 16382 getestet gehabt. Das hat noch
funktioniert.
ich habe den Zugriff auf die Zähler Variablen nur noch im Main. Das
sollte doch ausreichen sein.
Die TimerPulse Variable wird von der Interrupt Variable auf 1 gesetzt.
Wenn nur die Main Variable auf "zeit_nicht_gespuelt" zugreift, dann kann
doch die Bearbeitung nicht manipuliert werden.
Oder kann es passieren, dass der Interrupt die Register in die die
Variablen geladen wurde manipuliert und ich davon nichts mitbekomme?
Sebastian R. schrieb:> Hallo Zusammen,> ich habe noch etwas weiter getestet. An dem Interrupt liegt es definitiv> nicht, aber ich habe das trotzdem alles Atomar getrennt.
Zeig uns deinen KOMPLETTEN, AKTUELLEN Quelltext!
Hab den letzten Fehler gefunden. In einem Beitrag hatte ich gelesen,
dass man die Optimierungen abschalten sollte. Dieses hatte ich getestet,
aber vergessen wieder einzuschalten.
Nachdem die Optimierung wieder eingeschaltet war, funktionierte auch der
Rest vom Programm wieder.
geändert habe ich von der Ausgangssituation:
Interrupt Routine gekürzt. Hier wird nur noch ein Flag gesetzt was im
Main ausgewertet wird.
ADC Interrupt setzt ebenfalls ein Flag und wird im Main umkopiert.
Volatile vor Variablen geschrieben die im Interrupt und im Main
verwendet werden.
Vielen Dank für die Unterstützung.
Sebastian R. schrieb:> Nachdem die Optimierung wieder eingeschaltet war, funktionierte auch der> Rest vom Programm wieder.
(M)ein Programm muss mit und ohne Optimierung funktionieren.
Denn wenn die einen Unterschied macht, der sich nicht erklären lässt,
dann hat man noch einen Fehler im Programm, der einen sicher wieder
einholt.
Und wie schon gesagt: die Verwaltung von Zeiten wird viel einfacher,
wenn man nicht zig Zähler verwalten muss, sondern nur 1 einzigen, der
pünktlich und garantiert jede ms um 1 hochzählt. Wie das geht und sicher
kein Überlaufproblem bringt, habe ich schon erwähnt.
Sebastian R. schrieb:> Hab den letzten Fehler gefunden. In einem Beitrag hatte ich gelesen,> dass man die Optimierungen abschalten sollte. Dieses hatte ich getestet,> aber vergessen wieder einzuschalten.> Nachdem die Optimierung wieder eingeschaltet war, funktionierte auch der> Rest vom Programm wieder.
Das ist eine große Warnlampe! Wenn das Ein oder Ausschalten der
Optimierung solche Änderung in der Reaktion eines Programms erzeugt, ist
grundlegend was faul!
> geändert habe ich von der Ausgangssituation:> Interrupt Routine gekürzt. Hier wird nur noch ein Flag gesetzt was im> Main ausgewertet wird.> ADC Interrupt setzt ebenfalls ein Flag und wird im Main umkopiert.> Volatile vor Variablen geschrieben die im Interrupt und im Main> verwendet werden.
Bla Bla Bla!
Bis du so ignorant oder was ist sonst mit dir los? Warum haben MEHRERE
Leute geschrieben, daß du den aktuellen, VOLLSTÄNDIGEN Quelltext hier
hochladen sollst?
Du hast weder das Problem verstanden noch gelöst, nur solange planlos
rumgefummelt bis es SCHEINBAR funktioniert.
Lothar M. schrieb:> Sebastian R. schrieb:>> Nachdem die Optimierung wieder eingeschaltet war, funktionierte auch der>> Rest vom Programm wieder.> (M)ein Programm muss mit und ohne Optimierung funktionieren.>> Denn wenn die einen Unterschied macht, der sich nicht erklären lässt,> dann hat man noch einen Fehler im Programm, der einen sicher wieder> einholt.>> Und wie schon gesagt: die Verwaltung von Zeiten wird viel einfacher,> wenn man nicht zig Zähler verwalten muss, sondern nur 1 einzigen, der> pünktlich und garantiert jede ms um 1 hochzählt. Wie das geht und sicher> kein Überlaufproblem bringt, habe ich schon erwähnt.
Wie schon erwähnt habe ich das schon so abgeändert.
Es gibt nur einen der per Flag aus dem Interrupt einmal im Main
inkrementiert wird. Auch das Zurücksetzen findet nur einmal im Main
statt.
Problem gab es mit der Berechnung der Zwangsspülzeit.
Falk B. schrieb:> Sebastian R. schrieb:>> Hab den letzten Fehler gefunden. In einem Beitrag hatte ich gelesen,>> dass man die Optimierungen abschalten sollte. Dieses hatte ich getestet,>> aber vergessen wieder einzuschalten.>> Nachdem die Optimierung wieder eingeschaltet war, funktionierte auch der>> Rest vom Programm wieder.>> Das ist eine große Warnlampe! Wenn das Ein oder Ausschalten der> Optimierung solche Änderung in der Reaktion eines Programms erzeugt, ist> grundlegend was faul!>>> geändert habe ich von der Ausgangssituation:>> Interrupt Routine gekürzt. Hier wird nur noch ein Flag gesetzt was im>> Main ausgewertet wird.>> ADC Interrupt setzt ebenfalls ein Flag und wird im Main umkopiert.>> Volatile vor Variablen geschrieben die im Interrupt und im Main>> verwendet werden.>> Bla Bla Bla!>> Bis du so ignorant oder was ist sonst mit dir los? Warum haben MEHRERE> Leute geschrieben, daß du den aktuellen, VOLLSTÄNDIGEN Quelltext hier> hochladen sollst?>> Du hast weder das Problem verstanden noch gelöst, nur solange planlos> rumgefummelt bis es SCHEINBAR funktioniert.
Daumen hoch für die Freundlichkeit.
Ich habe jetzt noch draus gelernt, dass wenn was nachher
funktioniert/was gefunden hat, man am besten die Klappe hält.
Ich habe ein paar Fehler gefunden und behoben. Nur zur Korrektur, dass
das Problem nicht verstanden wurde. Es waren leider mehrere Fehler.
Sebastian schrieb:> Ich habe jetzt noch draus gelernt, dass wenn was nachher> funktioniert/was gefunden hat, man am besten die Klappe hält.
Vielleicht hast du das falsche gelernt.