2010-05-02 8 views
7

Ich hätte etwas Code, der als Ergebnis eines bestimmten ablaufenden Interrupts ausgeführt werden muss.Welche Cortex-M3 Interrupts kann ich für allgemeine Arbeiten verwenden?

Ich möchte es nicht im Kontext des Interrupts selbst ausführen, aber ich möchte auch nicht, dass es im Thread-Modus ausgeführt wird.

Ich möchte es mit einer Priorität ausführen, die niedriger ist als die High-Level-Interrupt, der seine Ausführung ausgelöst hat, sondern auch eine Priorität, die höher als Thread-Ebene (und einige andere Interrupts als auch).

Ich denke, ich muss einen der anderen Interrupt-Handler verwenden.

Welche sind am besten zu verwenden und wie kann man sie am besten aufrufen?

Im Moment plane ich nur die Interrupt-Handler für einige Peripheriegeräte zu verwenden, die ich nicht benutze, und sie durch Setzen von Bits direkt über die NVIC aufzurufen, aber ich hoffte, dass es einen besseren, offiziellen Weg gibt.

Danke,

Antwort

15

ARM Cortex unterstützt eine spezielle Ausnahme namens PendSV. Es scheint, dass Sie diese Ausnahme genau verwenden könnten, um Ihre Arbeit zu erledigen. Praktisch alle präemptiven RTOS für ARM Cortex verwenden PendSV, um den Kontextwechsel zu implementieren.

Damit es funktioniert, müssen Sie PendSV niedrig priorisieren (schreiben Sie 0xFF in das PRI_14-Register in der NVIC). Sie sollten auch alle IRQs über dem PendSV priorisieren (schreiben Sie niedrigere Nummern in die jeweiligen Prioritätsregister in der NVIC). Wenn Sie bereit sind, die ganze Nachricht zu verarbeiten, lösen die PendSV von hoher Priorität ISR:

*((uint32_t volatile *)0xE000ED04) = 0x10000000; // trigger PendSV 

Der ARM Cortex-CPU wird dann ISR beenden und alle anderen ISRs, der möglicherweise durch sie verdrängt wurden, und es schließlich wird an die PendSV-Ausnahme angehängt. Hier sollte der Code zum Parsen der Nachricht sein.

Bitte beachten Sie, dass PendSV anderen ISRs vorgreifen könnte. Das ist alles in Ordnung, aber Sie müssen sich natürlich daran erinnern, alle freigegebenen Ressourcen durch einen kritischen Codeabschnitt zu schützen (kurzes Deaktivieren und Aktivieren von Interrupts). In ARM Cortex deaktivieren Sie Interrupts, indem Sie __asm ​​("cpsid i") ausführen und Interrupts durch __asm ​​("cpsie i") aktivieren. (Die meisten C-Compiler bieten integrierte intrinsische Funktionen oder Makros für diesen Zweck.)

+0

Danke, Miro! Ich habe überall nach Informationen darüber gesucht, wie ich den PendSV-Interrupt auslösen kann, und hier habe ich ihn endlich gefunden! Auch wenn ich mein Cortex M3 r1p1 Technisches Referenzhandbuch sorgfältiger gelesen hätte, hätte ich es auf der Seite gefunden, die das Interrupt Control State Register beschreibt: http://infocenter.arm.com/help/topic/com.arm .doc.ddi0337e/DDI0337E_cortex_m3_r1p1_trm.pdf –

+0

Aus meiner Sicht verwenden fast alle RTOS auf CortexM3 den Systick Timer Interrupt (45 dezimal, 2D hex), um ihren Scheduler zu starten. Dann ist der PendSV der Weg, um eine Art besonderen "Syscall" zu initiieren? Hoffe ich verstehe das. –

+1

@WarrenP: PendSV wird verwendet, um den Kontextwechsel tatsächlich zu behandeln. Der System-Tick wird zum Round-Robin-Task verwendet - sys tick setzt wiederum PendSV. –

3

Verwenden Sie ein RTOS? Im Allgemeinen würde diese Art von Sache gehandhabt werden, indem man einen Thread mit hoher Priorität hat, der signalisiert wird, etwas Arbeit durch den Interrupt zu machen.

Wenn Sie kein RTOS verwenden, haben Sie nur wenige Aufgaben, und die Arbeit, die durch den Interrupt ausgelöst wird, ist nicht zu ressourcenintensiv. Es kann am einfachsten sein, Ihre Arbeit mit hoher Priorität im Kontext von der Unterbrechungshandler. Wenn diese Bedingungen nicht erfüllt sind, dann wäre das Implementieren, von dem Sie sprechen, der Beginn eines grundlegenden Multitasking-Betriebssystems selbst. Das kann ein interessantes Projekt sein, aber wenn Sie nur Arbeit erledigen wollen, sollten Sie ein einfaches RTOS in Betracht ziehen.

Da Sie einige Besonderheiten über die Arbeit erwähnt Sie tun, ist hier ein Überblick darüber, wie ich ein ähnliches Problem in der Vergangenheit behandelt haben:

Für die Handhabung empfangenen Daten über eine UART eine Methode, die ich habe verwendet, wenn es sich um ein einfacheres System handelt, das keine volle Unterstützung für das Tasking hat (dh die Tasks sind in einer einfachen while-Schleife round-robined), um eine gemeinsame Warteschlange für Daten zu haben, die vom UART empfangen werden. Wenn ein UART-Interrupt ausgelöst wird, werden die Daten vom RDR (Receive Data Register) des UART gelesen und in die Warteschlange gestellt. Der Trick, dies so zu behandeln, dass die Warteschlangenzeiger nicht beschädigt sind, besteht darin, die Warteschlangenzeiger vorsichtig flüchtig zu machen und sicherzustellen, dass nur der Interrupt-Handler den Endzeiger und nur die Vordergrundaufgabe, die Daten liest, modifiziert Aus der Warteschlange wurde der Kopfzeiger geändert. Ein High-Level-Übersicht:

  • Produzent (die UART-Interrupt-Handler):

    1. queue.head und queue.tail in Einheimischen lesen;
    2. Inkrementieren Sie den lokalen Tail-Zeiger (nicht der tatsächliche Zeiger queue.tail). Wickeln Sie es an den Anfang des Warteschlangenpuffers, wenn Sie nach dem Ende des Warteschlangenpuffers inkrementiert haben.
    3. vergleichen local.tail und local.head - wenn sie gleich sind, ist die Warteschlange voll, und Sie müssen tun, was auch immer Fehlerbehandlung angemessen ist.
    4. sonst können Sie die neuen Daten an, wo local.tail Punkte
    5. jetzt nur schreiben können Sie einstellen, queue.tail == local.tail
    6. Rückkehr von der Unterbrechung (oder andere UART Aufgaben im Zusammenhang behandeln, gegebenenfalls wie das Lesen aus einer Sendewarteschlange)
  • Verbraucher (die 'Aufgabe' Vordergrund)

    1. lesen queue.head und queue.tail in einheimischen;
    2. wenn local.head == local.tail die Warteschlange leer ist; kehrt die nächste Aufgabe tun einige Arbeit
    3. lesen das Byte, auf den local.head
    4. Schritt local.head und wickeln Sie es bei Bedarf zu lassen;
    5. gesetzt queue.head = local.head
    6. goto Schritt 1

Vergewissern Sie sich, dass queue.head und queue.tailvolatile sind (oder diese Bits in der Montage schreiben), um sicherzustellen, dass keine Sequenzierung Probleme gibt.

Jetzt stellen Sie nur sicher, dass Ihre UART empfangen Datenwarteschlange groß genug ist, dass es alle Bytes enthalten wird, die empfangen werden konnten, bevor die Vordergrundaufgabe eine Chance bekommt, zu laufen. Die Vordergrundaufgabe muss die Daten aus der Warteschlange in ihre eigenen Puffer ziehen, um die Nachrichten aufzubauen, die der Aufgabe "Nachrichtenprozessor" übergeben werden sollen.

+0

Ich verwende kein RTOS. Was ich habe, ist eine hohe Priorität Interrupt, die jedes Mal erlischt, wenn ich Bytes über serielle empfangen. Es läuft mit einer hohen (niedrige Priorität Nummer) Priorität, so dass ich keine Bytes vermisse. Wenn eine vollständige Nachricht erhalten wurde, dann muss ich etwas arbeiten, und während ich vorhabe, dass diese Arbeit vollständig nicht blockierend sein soll, möchte ich mich nicht darauf beschränken, eine maximale Zeit dafür zu haben (Ich werde diesen Code nicht schreiben). Ich mache keine preemptive Multitasking. –

+0

Wie hoch ist die Geschwindigkeit der seriellen Daten? Ist die Rate wirklich so hoch oder sind andere Interrupt-Handler so schlecht ausgelegt, dass die Interrupt-Prioritätsstufe einen Unterschied macht? Warum nicht DMA-Übertragungen verwenden, um die Unterbrechungsfrequenz zu reduzieren oder sie vollständig zu vermeiden? – Clifford

+0

Wir laufen bei 57600, werden aber wahrscheinlich auf 115200 umstellen. Ich kann DMA-Übertragungen auf dem Weg nicht verwenden, weil das, was das Ende einer Nachricht anzeigt, die Zeit zwischen Bytes ist, die einen bestimmten Wert überschreitet. Ich habe drei Aufgaben, die ich ausführen muss: 1. ein serieller Nachrichtenempfänger/-sender 2. ein Nachrichtenprozessor 3. etwas schweres Heben im Hintergrund. Die mittlere ist eine Zustandsmaschine, so dass _supposed_ fast keine Arbeit machen kann (nur den Zustand aktualisieren). Ich möchte, dass sie in verschiedenen Interrupt-Levels laufen, weil es weniger schwierig ist, Deadlines auf niedrigeren Ebenen zu debuggen (für mich). –

1

Der "offiziellere Weg" oder eher die herkömmliche Methode besteht darin, einen prioritätsbasierten präemptiven Multitasking-Scheduler und das "Deferred Interrupt Handler" -Muster zu verwenden.

+0

@Clifford: Haben Sie einen Link zu einer Beschreibung des "Deferred Interrupt Pattern"? – simon

0

Überprüfen Sie die Dokumentation Ihres Prozessors. Einige Prozessoren werden unterbrochen, wenn Sie das Bit schreiben, das Sie normalerweise innerhalb des Interrupts löschen müssen.Ich verwende derzeit ein SiLabs c8051F344 und im Datenblatt Abschnitt 9.3.1:

"Software kann einen Interrupt simulieren, indem Sie ein Interrupt-Pending-Flag auf logisch 1 setzen. Wenn Interrupts für das Flag aktiviert sind, wird eine Interrupt-Anfrage generiert werden und die CPU wird zu der ISR-Adresse, die dem Flag für anstehende Interrupts zugeordnet ist, vektorisieren.

1

Was Sie fragen, ist auf dem Cortex-M3 ziemlich einfach. Sie müssen das STIR-Register aktivieren, damit Sie die ISR mit niedriger Priorität mit Software auslösen können. Wenn der ISR mit hoher Priorität mit dem kritischen Zeug fertig ist, löst er nur den Interrupt mit niedriger Priorität aus und beendet sich. Die NVIC wird dann an den Handler mit niedriger Priorität angehängt, wenn nichts Wichtigeres passiert.

Verwandte Themen