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.
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.
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:
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.
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.
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.
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;
1
volatile unsigned long intervall_zwangsspuelung;
2
volatile unsigned long max_spuelzeit;
3
volatile unsigned long min_Zwangsspuelzeit;
4
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.
1
if (rest_spuelzeit>0L)
2
{
3
P2OUT |= 0x40;
4
zeit_nicht_gespuelt=0L;
5
}
6
else
7
{
8
P2OUT &= ~0x40;
9
rest_spuelzeit=0L;
10
}
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?
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.
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.
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.
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".
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".
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?
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.
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.
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.
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.
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.