Ich war bisher der Meinung, dass ein Interrupt ein laufendes Programm unterbricht, der µC sich den letzten Befehl merkt und nach Abarbeitung des Interruptunterprogramms an der Stelle die Programmabarbeitung fortsetzt, wo es unterbrochen wurde. Folgende Problemstellung: Mit dem TimerA wird jede Sekunde ein Programm angestoßen, mit welchem der AD-Wandler abgefragt wird. Die Messwerte werden auf einer Smart Media Karte gespeichert. Dazu werden 512Bytes gesammelt und in einem Rutsch auf die Karte übertragen. Läuft problemlos. Zur Auswertung der Messwerte wird die Karte entnommen und in den PC eingespielt. Jetzt sollen die Daten, ohne die Karte herauszunehmen, mit RS232 an USART1 simultan gelesen werden. Dazu wird vom PC ein Zeichen gesendet, um das Auslesen zu starten. Dies funktioniert nur soweit, dass zwar die Daten übertragen werden, aber die Übertragung nicht, wie gehofft, vom Timerinterrupt unterbrochen wird. Erst wenn meine Schleife (Zeile: //100 Sektoren werden gelesen) vollständig abgearbeitet ist, wird das Timerprogramm wieder abgearbeitet. Was mache ich falsch? Lt. Datenblatt hat Timer A die Priorität 6 und USART1 senden die Priorität 2. Oder hat die Priorität überhaupt hierfür eine Bedeutung? Programmausschnitte im Anhang. Wolfgang
Wenn der MSP einen Interrupt bearbeitet, werden beim Kontextwechsel automatisch die Interrupts gesperrt. So wird verhindert, daß die laufende ISR von einem anderen Interrupt unterbrochen werden kann. Man kann aber die Interrupts in der ISR wieder freigeben, muß aber dann evtl. verhindern, daß eine ISR in Rekursion gerät.
Interrupt Routinen sollten kurz gehalten werden. Bei dir steckt da ja das ganze Programm drin. Transfer von 50KB in der Rx Routine! Man kann zwar Interrupts verschachteln, aber hier ist das kaum der richtige Weg. Ein Rx Interrupt hat das Byte zu lesen und irgendwo abzuspeichern und damit hat es sich. Alles andere gehört da nicht rein. Besserer Ansatz: In der Rx Routine das Steuerzeichen einlesen und global speichern, oder vergleichen und ein Flag setzen. Im Hauptprogramm wird dies dann abgefragt und ggf. der Transfer durchgeführt. Dann kollidieren Rx und Timer Interrupt nicht mehr. Das gleiche gilt natürlich auch für den Timer Interrupt. Auch da gehört nicht der komplette Transfer hinein, sondern der sollte dem Hauptprogramm nur signalisieren, dass es wieder mal soweit ist.
Aus den Antworten entnehme ich, dass mein Problem lösbar ist, wenn man richtig programmieren kann. Mit „Kontextwechsel“ und „in Rekursion gerät“ kann ich noch nichts anfangen. Vielleicht kann mir jemand erklären, was damit gemeint ist. @Andreas >Besserer Ansatz: In der Rx Routine das Steuerzeichen einlesen und global >speichern, oder vergleichen und ein Flag setzen. Ich hatte mir schon gedacht, dass es eine bessere Lösung geben könnte. Wie setzt man ein Flag? Im überarbeiteten Anhang habe ich versucht, zunächst Rx umzubauen. Nur weiß ich nicht, wie man im Hauptprogramm erkennen kann, ob ein interrupt sich gemeldet hat. Für Timer Interrupt ist mir noch nichts eingefallen. Ich würde mich freuen, wenn Du mir helfen und die dafür notwendigen Zeilen als Beispiel formulieren könntest. Es kann natürlich auch jeder andere mitreden. MfG Wolfgang
Globale Variablen: Flag für "Timer war da" Empfangenes Zeichen Timer Interrupt: Flag setzen: "Timer war da" UART Rx Interrupt: in "Empfangenes Zeichen" abspeichern Hauptprogramm: Schleife: wenn Flag gesetzt "Timer war da": Flag zurücksetzen tu was getan werden muss wenn der Timer abgelaufen ist wenn Zeichen empfangen: tu was getan werden muss wenn ein Zeichen empfangen wurde "Empfangenes Zeichen" löschen Ein Flag ist hier schlicht eine globale Variable. Schlag aber diesbezüglich mal unter "volatile" nach. Jede Warteschleife in einer Interrupt-Routine ist ein potentieller Kandidat für einen Denkfehler. Warum wird im Rx Interrupt auf den Tx Status gewartet?
>Warum wird im Rx Interrupt auf den Tx Status gewartet?
Diese Zeile entstammt einem TI-Beispiel. War dort als Echo gedacht. Hab
die Zeile herausgenommen.
Ich habe mein Programm noch einmal nach deinen Hinweisen umgebaut.
Würde mein Hauptprogramm im Ansatz funktionieren?
Wenn ja, wie merkt das Hauptprogramm, wenn gerade „lesen_Karte“ läuft,
dass "Timer war da" ausgelöst wurde?
MfG
Wolfgang
Nö, so sicher nicht, da ist so ziemlich alles verkehrt. Aber ich schreib meinen Text oben nicht nochmal ab. Anfängerkurs in Programmieren liegt mir nicht. EDIT: Die Interrupt-Routinen sind ok. Das Hauptprogramm nicht. Bischen über die Schleifen und was da wann wie passiert nachdenken würde nicht schaden.
„Kontextwechsel“: Wenn z.B. ein Interrupt ausgelöst wird, wird das laufende Programm unterbrochen und der Prozessor wechselt vom Kontext der unterbrochenen Routine in den der ISR. Hinterher wird das unterborchene Programm an der Stelle fortgesetzt, an der es unterbrochen wurde - der Prozessor schaltet zurück in den unterbrochenen Kontext. „in Rekursion gerät“: Wenn man in einer zu lang laufenden ISR die Interrupts wieder freigibt und derselbe Interrupt nocheinmal ausgelöst wird, dann wird die laufende ISR unterbrochen und eine neue wird gestartet. Damit ist die ISR in Rekursion geraten. Da ISRs in aller Regel so geschrieben sind, daß sie Rekursion nicht vertragen, muß man diese Situation unbedingt vermeiden.
@ Wolfgang (Gast) >Ich habe mein Programm noch einmal nach deinen Hinweisen umgebaut. Naja, die Formatierung lässt noch zu wünschen übrig. >Würde mein Hauptprogramm im Ansatz funktionieren? Nein. >Wenn ja, wie merkt das Hauptprogramm, wenn gerade „lesen_Karte“ läuft, >dass "Timer war da" ausgelöst wurde? Die Frage solltest DU als Programmierer beantworten können! Hirnloses Copy & Past bringt da nix. Da der Andreas den Anfängerkurs verweiget, hier mal ein Crash-Kurs. Was macht dein Programm in der Hauptschleife? Die gar keine ist! >while (!(An=='L'))lesen_Karte(); Solange das Zeichen An nicht 'L' ist, liest du die Karte. ??? Was soll das? Willst du nicht eher dann die Karte lesen, WENN es 'L' ist? Das hätte man sinnvollerweise besser geschrieben als if (An=='L') lesen_Karte(); Wenn An eben nicht 'L' ist, was es die meiste Zeit ist, geht das Programm weiter. Dann die nächste Schote. while (!(Timer_war_da==1))Messwert_speichern(); Solange wie Timer_war_da NICHT ==1 ist speicherst du Messwerte, ohne Pause!. Und du fragst aber die ganze Zeit NICHT nach, ob denn vielleicht ein 'L' angekommen ist. Hmmm? Wie wäre es damit? while (1) { // Hauptschleife if (An=='L') lesen_Karte(); if (Timer_war_da==1) Messwert_speichern(); } Dann gibt es noch das kleine Problem, dass du in dem jetzigen Quelltext die Augabe per UART ohne Interrupt machst. D.H. solange die Übertragung der Daten zum PC läuft wird keine neue Messung gestartet! Das willst du nicht. Also muss das Senden der Daten in den UART TX Interrupt! Messwert_speichern() bereitet nur alles vor, sprich Daten von der Karte in den Buffer einlesen, Zähler setzen, UART TX-Interrupt einschalten. Dann klappts auch mit dem Timer. MfG Falk
Zunächst mein Dank an alle. >Naja, die Formatierung lässt noch zu wünschen übrig. Ich möchte mich bessern. Worauf kommt es besonders an? Ich orientierte mich an den TI-Beispielen. > Hirnloses Copy & Past bringt da nix. Gerade mein Hauptprogramm war auf meinem Mist gewachsen, auch wenn es offensichtlich zumeist aus Mist besteht. >Was macht dein Programm in der Hauptschleife? Die gar keine ist! Das hatte ich mir schon fast gedacht, dass mein Ansatz falsch ist, aber eine andere "Lösung" für eine Endlosschleife ist mir nicht eingefallen. > DU als Programmierer Das wird wohl nichts mehr mit mir, aber ich werde mir Mühe geben. >Dann gibt es noch das kleine Problem,... Für mich aber das große Problem. Diesen Absatz habe ich nicht richtig kapiert. Vielleicht hat jemand noch Geduld mit mir. Habe ich es richtig verstanden: mit ME2 |= UTXE1 + URXE1; werden die Module UART für senden und empfangen zugeschaltet. Mit IE2 |= URXIE1; wird der interrupt für Empfang und mit Mit IE2 |= UTXIE1; für Senden freigegeben. Ein ankommendes Zeichen an URXD1 löst den interrupt für Empfang aus. Wann wird ein interrupt für Senden ausgelöst? Vielleicht bekomm ich dann die nächsten Schritte noch hin. Wolfgang
> Für mich aber das große Problem. Diesen Absatz habe ich nicht richtig > kapiert. Ich bin auch nicht sicher, ob du den Schritt, den Falk hier empfiehlt, gleich am Anfang gehen solltest. Immer hübsch der Reihe nach kann anfangs sinnvoller sein.
@ Wolfgang (Gast) >Ich möchte mich bessern. Worauf kommt es besonders an? Ich orientierte >mich an den TI-Beispielen. Gleichmässig einrücken, Zeilen nicht zu lange (80 bis 100 Zeichen), auch mal ein paar Leerzeichen verwenden, etc. >Für mich aber das große Problem. Diesen Absatz habe ich nicht richtig >kapiert. >Vielleicht hat jemand noch Geduld mit mir. >Habe ich es richtig verstanden: mit ME2 |= UTXE1 + URXE1; werden die >Module UART für senden und empfangen zugeschaltet. Ja. > Mit IE2 |= URXIE1; >wird der interrupt für Empfang und mit Mit IE2 |= UTXIE1; für Senden >freigegeben. Ja. >Ein ankommendes Zeichen an URXD1 löst den interrupt für Empfang aus. Ja. >Wann wird ein interrupt für Senden ausgelöst? Wenn der Sendepuffer leer ist. Der UART ist in beide Richtungen gepuffert. D.h. beim Senden schreibst du ein Byte in das Datenregister. Das wird sofort vom UART ins Schiebergister übernommen. Nun ist das Datenregister leer, ein TX Interrupt wird generiert. Dort muss dein MSP ein nues Datenbyte reinschreiben. Nun dauert es ne Weile, bis das erste Byte übertragen ist, abhängig von der Baudrate. Dann wird das zweite Byte, welches im Puffer steht, wieder automatisch ins Schieberegister übernommen und gesendet. Nach der Übernahme kommt der nächste Interrupt. Deine TX Interruptroutine muss also nur byteweise die Daten aus deinem Array lesen und ins TX Datenregister schreiben. Dann prüft sie noch, ob alle Daten gesendet wurden. Ist das der Fall, wird der TX-Interrupt wieder ausgeschaltet. >Vielleicht bekomm ich dann die nächsten Schritte noch hin. Mit etwas Geduld wird das schon. MfG Falk
>Immer hübsch der Reihe nach kann anfangs sinnvoller sein.
Dann versuch ich mal zu sortieren.
a) Meine Interrupt-Routinen enthalten nur noch ein Flag, welches als
globale volatile Variable deklariert wurde, damit es durch eine ISR
veränderbar wird.
b) das Hauptprogramm läuft als Endlosschleife und fragt die beiden Flag
„Timer_war_da“ und 'L' ab.
c) die Schoten der while-Schleifen wurden korrigiert
d)das Erscheinungsbild wurde teilweise verbessert
e)die Erläuterung zum Sendepuffer habe ich soweit verstanden, aber was
ist der Vorteil gegenüber der Kontrolle mit
while (!(IFG2 & UTXIFG1)); // USART1 TX buffer ready
hier wird doch m.E. auch geprüft, ob der Sendepuffer leer ist.
Wäre das überarbeitete Programm im Ansatz möglich?
Für bessere Lösungen bin ich immer offen.
Wolfgang
@ Wolfgang (Gast) >a) Meine Interrupt-Routinen enthalten nur noch ein Flag, welches als >globale volatile Variable deklariert wurde, damit es durch eine ISR >veränderbar wird. Das volatile sagt dem Compiler, dass er keinerlei Optimierungen beim Zugriff auf diese Variablen machen darf. >b) das Hauptprogramm läuft als Endlosschleife und fragt die beiden Flag >„Timer_war_da“ und 'L' ab. Richtig. >c) die Schoten der while-Schleifen wurden korrigiert ;-) >d)das Erscheinungsbild wurde teilweise verbessert Du bewegst dich in die richtige Richtung. Aber es fehlen noch ein paar Schritte. >e)die Erläuterung zum Sendepuffer habe ich soweit verstanden, aber was >ist der Vorteil gegenüber der Kontrolle mit >while (!(IFG2 & UTXIFG1)); // USART1 TX buffer ready >hier wird doch m.E. auch geprüft, ob der Sendepuffer leer ist. Ja, aber während der Prüfung kann die CPU NICHTS anderes machen, sie rennt wie wild in der Schleife rum! Bei 9600 Baud für ~ 1ms. Das ist eine Ewigkeit! Und das dann 512 mal hintereinander, (~530ms) und das dann noch 100 mal hintereinander! Macht schlappe 50 Sekunden! In denen die Routine nicht einmal verlassen wird! >Wäre das überarbeitete Programm im Ansatz möglich? Im Ansatz ja. Aber du denkst teilweis noch zu kompliziert. >Für bessere Lösungen bin ich immer offen. Siehe Anhang. Das Ganze läuft erstmal noch ohne TX Interupt. Allerdings werden pro Aufruf nur 512 Bytes gelesen und zum PC gesendet. Wie oft kommt denn dein Timerinterrupt? Du hast ja leider nicht den vollständigen Quelltext reingestellt, da kann man das nicht erkennen. Die Übertragung von 512 Byte bei 9600 Baud dauert 533ms. Wenn das Schreiben von einem Sektor AusgabeZW32(), das Lesen eines Sektors mit MB32_zuruecklesenSektor () plus die Übertragung (533ms) weniger Zeit braucht als ein Timerintervall dann läuft das Programm. Ich nehme mal an, der Timer klingelt 1 mal pro Sekunde, oder? Dann sollte es passen (wenn nicht das Lesen/Schreiben der SD-Karte tierisch lange dauert). MfG Falk
In einem Punkt möchte ich meinem obigen Ansatz etwas korrigieren. Im Timer-Interrupt den ADC auszulesen und in einen Puffer zu speichern, wie du ursprünglich gemacht hast, ist völlig ok. Der kritische Punkt kam dort, wo du diesen Puffer, wenn voll, noch innerhalb des Interrupts auf Karte gespeichert hast. Das ist erstens aus Zeitgründen nicht sinnvoll und führt zweitens zu Chaos, wenn der Interrupt grad das Auslesen der Karte unterbricht.
@ Andreas Kaiser (a-k) >In einem Punkt möchte ich meinem obigen Ansatz etwas korrigieren. Im der >Timer-Interrupt den ADC auszulesen und in einen Puffer zu speichern, wie >du ursprünglich gemacht hast, ist völlig ok. Der kritische Punkt kam Yep, die Version hab ich aber nicht mitbekommen. Bin erst bei B eingestiegen ;-) Aber so ist es auch erstmal OK. MFG Falk
Kleine Anmerkung, in der Version _d darf der UART TX interrupt nicht aktiviert werden. Hab ich vergessen rauszumachen. Hier jetzt noch als Sahnehäubchen die Lösung mit Interrupt, damit läuft die Datenübertragung wirklich parallel zum Rest, innerhalb eines Timerintervalls muss nun nur noch einmal ein Sektor gelesen und geschrieben werden können. Die Baudrate ist vollkommen egal. MfG Falk
>Ja, aber während der Prüfung kann die CPU NICHTS anderes machen, sie >rennt wie wild in der Schleife rum! Bei 9600 Baud für ~ 1ms. Kann ich zwar nicht richtig beurteilen, denke aber, es ist fast egal, ob der Sendepuffer einen interrupt auslöst, wenn 10? Bit für ein Byte gesendet wurden oder ob vor dem Sendevorgang geprüft wird, ob der Sendepuffer leer ist. >noch innerhalb des Interrupts auf Karte gespeichert hast. Das ist erstens >aus Zeitgründen nicht sinnvoll und führt zweitens zu Chaos, wenn der >Interrupt grad das Auslesen der Karte unterbricht. Wie in meinem ersten Beitrag schon geschrieben, hat bei mir auch nicht die Unterbrechung des Auslesens funktioniert. Zur Lösung des Problems hatte ich deshalb im Forum um Hilfe gebeten. Durch die Anzahl der Beiträge kann man leicht den Überblick verlieren. Deshalb mal kurz die Beschreibung meines Datenspeichers: µC: MSP430F1611 es werden je nach Programmierung die internen AD- Wandler ausgelesen bzw. über SPI oder I2C diverse Temperatur- oder Druckfühler abgefragt, in einem 512 Bytes großen [Feld] zwischengespeichert und wenn es voll ist, auf einer 32MB oder64MB Smart Media- Karte abgespeichert. Es müssen also bis zu 65000 Sektoren (bei 32MB) ausgelesen werden. Z.Zt. entnehme ich die Karte in gewissen Abständen dem Datenspeicher und spiele sie an der parallelen Schnittstelle in den PC ein. Dadurch entsteht oftmals Datenverlust. Je nach Anwendung werden 1, 2, 4, 8 Messwerte in frei wählbaren Abständen (zumeist 1s, 2s, 4s, 6s kann auch mal der ms Bereich sein) gesammelt. Auch GPS-Daten wurden gespeichert, um die Geschwindigkeit beim Skifahren zu ermitteln. Zuletzt kam eine Flüssigkristallanzeige dazu. Inzwischen hat sich das Programm tüchtig aufgebläht, sodass ich mit IAR Kickstart nicht mehr das volle Programm compilieren kann. Deshalb habe ich mspgcc verwendet. Beim compilieren mit mspgcc funktioniert bei mir der I2C Bus nicht. !! Wichtig !! kennt jemand dieses Problem und hat eine passende Lösung? Teile des Sahnehäubchens „Datenspeicherkorrigiert_d_int.c „ werde ich in mein Programm in Ruhe einarbeiten und testen und zu gegebener Zeit berichten. Schon jetzt mal vielen Dank. Wie schon oben gesagt: Ich bin für alle Vorschläge offen. Wolfgang
@ Wolfgang (Gast) >Kann ich zwar nicht richtig beurteilen, denke aber, es ist fast egal, ob >der Sendepuffer einen interrupt auslöst, wenn 10? Bit für ein Byte >gesendet wurden oder ob vor dem Sendevorgang geprüft wird, ob der >Sendepuffer leer ist. Du hast das Problem noch nicht verstanden. Denk nochmal drüber nach! >Zur Lösung des Problems hatte ich deshalb im Forum um Hilfe gebeten. Die hast du ja reichlich bekommen, oder? MfG Falk
>Die hast du ja reichlich bekommen, oder?
Da kann ich nicht meckern, kann nicht besser sein!!
MfG
Wolfgang
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.