Die ganze Wahrheit über RTOS. Artikel 29. Unterbrechungen in Nucleus SE

Alle modernen Mikroprozessoren und Mikrocontroller enthalten eine Art Interrupt-Mechanismus. Diese Mechanismen sind erforderlich, um die für viele Anwendungen erforderliche Reaktionsfähigkeit bereitzustellen. Reaktionsfähigkeit und Vorhersehbarkeit sind natürlich das Hauptziel bei der Verwendung von RTOS, aber gleichzeitig sind sie einander entgegengesetzt. Die Verwendung von Interrupts kann die Integrität des Echtzeitbetriebssystems beeinträchtigen. Dieses Problem und seine Lösung wurden in einem der vorherigen Artikel (Nr. 4 und Nr. 6) erwähnt. In diesem Artikel werden wir uns die Interrupt-Behandlungsstrategie ansehen, die in Nucleus SE verwendet wird. In allen Fällen werden Interrupts NICHT von Nucleus SE gesteuert: Sie werden verarbeitet, wenn sie gemäß der Priorität und den Vektoren auf die übliche Weise auftreten. Ihre Ausführungszeit wird einfach aus der verfügbaren Zeit im Code der Hauptanwendung und des Schedulers "gestohlen". Daraus folgt natürlich, dass alle Interrupt-Handler einfach, kurz und schnell sein sollten.




Regelmäßige und kontrollierte Interrupts


Nucleus SE bietet zwei Möglichkeiten, um Interrupts zu behandeln: "native" oder "regulär" (native), bei denen Interrupts nichts Besonderes sind und zum Teil nur eine begrenzte Interaktion mit dem Betriebssystem haben (zumindest bei Verwendung des Prioritätsplaners), und Verwaltet, in dem Sie über den Interrupt-Handler auf eine viel größere Anzahl von API-Aufrufen zugreifen können.

Mit den E / A-Makros kann der Nucleus SE-Interrupt-Handler entweder im Standardmodus oder im verwalteten Modus verwendet werden.

Mitarbeiter unterbricht


Nucleus SE Staff Interrupts sind der Standard-Interrupt-Handler und können als "nicht verwaltet" betrachtet werden. Sie werden normalerweise verwendet, wenn eine Unterbrechung mit hoher Frequenz auftreten kann und eine Verarbeitung mit geringem Rechenaufwand erfordert. Ein solcher Handler ist höchstwahrscheinlich in C geschrieben, da viele moderne eingebettete Compiler die Entwicklung von Interrupt-Handlern mithilfe des Interrupt-Schlüsselworts unterstützen. Es werden nur die Kontextinformationen gespeichert, die der Compiler für erforderlich hält. Dies führt zu erheblichen Einschränkungen der Standard-Interrupt-Handler, die wir bald sehen werden.

Um einen regulären Interrupt-Handler in Nucleus SE zu erstellen, reicht es aus, einfach einen regulären Interrupt-Handler zu schreiben, einschließlich des Aufrufs des Makros NUSE_NISR_Enter () am Anfang und des Aufrufs von NUSE_NISR_Exit () am Ende. Diese Makros sind in der Datei nuse_types definiert . h und setzen Sie die globale Variable NUSE_Task_State auf NUSE_NISR_CONTEXT .

Geführte Interrupts


Wenn Sie mehr Flexibilität für Interrupt-Handler-Vorgänge benötigen, sind möglicherweise von Nucleus SE verwaltete Interrupts die Lösung. Der Hauptunterschied zum Standard-Interrupt besteht darin, den Kontext beizubehalten. Anstatt dem Compiler zu erlauben, mehrere Register auf dem Stapel zu speichern, speichert ein verwalteter Interrupt den gesamten Aufgabenkontext (in einem eigenen Kontextblock) an der Eingabe. Anschließend wird der Kontext der aktuellen Aufgabe aus dem Kontextblock am Ausgang wiederhergestellt. Dies bietet die Möglichkeit, die aktuelle Aufgabe durch den Interrupt-Handler-Code zu ändern, was bei Verwendung des Prioritätsplaners möglich ist. Eine vollständige Beschreibung der Kontexterhaltung und -wiederherstellung in Nucleus SE wurde in einem früheren Artikel ( Nr. 10 ) bereitgestellt.

Offensichtlich bedeutet die vollständige Erhaltung des Kontexts eine Zunahme des Einsatzes von Rechenressourcen im Vergleich zur Speicherung mehrerer Register auf dem Stapel, die während einer Standardunterbrechung auftritt. Dieser Preis muss für zusätzliche Flexibilität gezahlt werden, und genau aus diesem Grund wird die Wahl des Interrupt-Handling-Ansatzes getroffen.

Ein verwalteter Interrupt wird mit dem in nuse_types.h beschriebenen Makro NUSE_MANAGED_ISR () erstellt . Dieses Makro erstellt eine Funktion, die die folgenden Aktionen enthält:

  • Aufrechterhaltung des Kontextes der Aufgabe;
  • Zuweisen von NUSE_Task_State zu NUSE_MISR_CONTEXT ;
  • vom Benutzer bereitgestellter Interrupt-Handler-Funktionscode;
  • Wiederherstellen des vorherigen Status von NUSE_Task_State ;
  • Stellen Sie den Kontext der Aufgabe wieder her.

Das Makro verwendet zwei Parameter: den Interruptnamen, der als Funktionsname für den generierten Handler verwendet wird, und den Namen der Funktion, die die Benutzerlogik für den Interrupthandler enthält.

API-Aufrufe vom Interrupt-Handler


Die Menge der API-Funktionen, die von einem Standard- oder verwalteten Interrupt-Handler aufgerufen werden können, hängt davon ab, welcher Scheduler verwendet wird. Im Allgemeinen bietet die Verwendung des Prioritätsplaners viele Optionen für den Zugriff auf den Planer über einen API-Funktionsaufruf, was bei Verwendung des Standard-Interrupt-Handlers schwierig ist.

API-Aufrufe an den Standard-Interrupt-Handler bei Verwendung des Prioritätsplaners

Bei Verwendung des Prioritätsplaners ist eine begrenzte Anzahl von API-Funktionsaufrufen vom Standard-Interrupt-Handler zulässig. Diese Einschränkung ist das Ergebnis der Flexibilität der Nucleus SE-API: Viele Aufrufe können dazu führen, dass die Aufgabe bereit ist und der Scheduler vom Standard-Interrupt-Handler nicht aufgerufen werden kann (da der Aufgabenkontext nicht gespeichert wird). Das Deaktivieren von Task-Sperren bietet noch mehr Flexibilität.

Die folgenden API-Aufrufe sind immer zulässig:

NUSE_Task_Current() NUSE_Task_Check_Stack() NUSE_Task_Information() NUSE_Task_Count() NUSE_Partition_Pool_Information() NUSE_Partition_Pool_Count() NUSE_Mailbox_Information() NUSE_Mailbox_Count() NUSE_Queue_Information() NUSE_Queue_Count() NUSE_Pipe_Information() NUSE_Pipe_Count() NUSE_Semaphore_Information() NUSE_Semaphore_Count() NUSE_Event_Group_Information() NUSE_Event_Group_Count() NUSE_Signals_Send() NUSE_Timer_Control() NUSE_Timer_Get_Remaining() NUSE_Timer_Reset() NUSE_Timer_Information() NUSE_Timer_Count() NUSE_Clock_Set() NUSE_Clock_Retrieve() NUSE_Release_Information() 

Es ist jedoch nur NUSE_Signals_Send () nützlich, da es eine bequeme Möglichkeit bietet, der Aufgabe anzuzeigen, dass eine Aktion erforderlich ist.

Wenn die Sperre deaktiviert ist, dh Aufgaben von vielen API-Aufrufen nicht in den Bereitschaftszustand versetzt werden können, werden zusätzliche API-Aufrufe verfügbar:

 NUSE_Partition_Allocate() NUSE_Partition_Deallocate() NUSE_Mailbox_Send() NUSE_Mailbox_Receive() NUSE_Mailbox_Reset() NUSE_Queue_Send() NUSE_Queue_Receive() NUSE_Queue_Jam() NUSE_Queue_Reset() NUSE_Pipe_Send() NUSE_Pipe_Receive() NUSE_Pipe_Jam() NUSE_Pipe_Reset() NUSE_Semaphore_Obtain() NUSE_Semaphore_Release() NUSE_Semaphore_Reset() NUSE_Event_Group_Set() NUSE_Event_Group_Retrieve() 

Einige API-Aufrufe sind für Standard-Interrupt-Handler immer nicht zugänglich, da sie zwangsläufig die Arbeit eines Schedulers erfordern:
 NUSE_Task_Suspend() NUSE_Task_Resume() NUSE_Task_Sleep() NUSE_Task_Relinquish() NUSE_Task_Reset() NUSE_Signals_Receive() 

API-Aufrufe an den verwalteten Interrupt-Handler oder den Standard-Interrupt-Handler, wenn ein anderer Scheduler als der Prioritätsplaner verwendet wird

Viele weitere API-Funktionen können vom Interrupt-Handler aufgerufen werden, wenn die Scheduler "Run to Completion", "Round Robin" oder "Time Slice" verwendet werden. Wenn ein Prioritätsplaner verwendet wird, haben verwaltete Interrupt-Handler ähnliche Funktionen. Dies liegt daran, dass Anrufe zulässig sind, was dazu führen kann, dass eine andere Aufgabe geplant wird. Diese Funktion wird durch den Code NUSE_Reschedule () bereitgestellt, der den Aufrufkontext im Interrupt-Handler erkennt und die Kontextänderung unterdrückt (dies kann am Ende des Interrupt-Handlers geschehen). Eine vollständige Analyse der Arbeit des Planers wurde in einem der vorherigen Artikel ( Nr. 9 ) gegeben.

Die Hauptanforderung besteht darin, dass API-Aufrufe im Interrupt-Handler nicht zu einer Unterbrechung der aktuellen Aufgabe führen dürfen, z. B. das Warten auf die Freigabe einer Ressource.

Mit anderen Worten, solche Aufrufe müssen mit der Pausenoption NUSE_NO_SUSPEND erfolgen .

In diesem Sinne können die folgenden API-Aufrufe verwendet werden:

 NUSE_Task_Current() NUSE_Task_Check_Stack() NUSE_Task_Information() NUSE_Task_Count() NUSE_Task_Suspend() NUSE_Task_Resume() NUSE_Task_Reset() NUSE_Partition_Allocate() NUSE_Partition_Deallocate() NUSE_Partition_Pool_Information() NUSE_Partition_Pool_Count() NUSE_Mailbox_Send() NUSE_Mailbox_Receive() NUSE_Mailbox_Reset() NUSE_Mailbox_Information() NUSE_Mailbox_Count() NUSE_Queue_Send() NUSE_Queue_Receive() NUSE_Queue_Jam() NUSE_Queue_Reset() NUSE_Queue_Information() NUSE_Queue_Count() NUSE_Pipe_Send() NUSE_Pipe_Receive() NUSE_Pipe_Jam() NUSE_Pipe_Reset() NUSE_Pipe_Information() NUSE_Pipe_Count() NUSE_Semaphore_Obtain() NUSE_Semaphore_Release() NUSE_Semaphore_Reset() NUSE_Semaphore_Information() NUSE_Semaphore_Count() NUSE_Event_Group_Set() NUSE_Event_Group_Retrieve() NUSE_Event_Group_Information() NUSE_Event_Group_Count() NUSE_Signals_Send() NUSE_Timer_Control() NUSE_Timer_Get_Remaining() NUSE_Timer_Reset() NUSE_Timer_Information() NUSE_Timer_Count() NUSE_Clock_Set() NUSE_Clock_Retrieve() NUSE_Release_Information() 

Einige Anrufe sind immer gesperrt, da sie sich direkt auf die aktuelle Aufgabe beziehen:
 NUSE_Task_Relinquish() NUSE_Signals_Receive() NUSE_Task_Sleep() 

Echtzeituhr-Interrupt-Handler


Der RTC-Interrupt-Handler (Real Time Clock) ist der einzige vollständige Interrupt-Handler in Nucleus SE. Neben der Bereitstellung aller erforderlichen Funktionen für das Zeitmanagement in Nucleus SE dient es auch als Beispiel für das Schreiben eines verwalteten Interrupt-Handlers.

RTC-Interrupt-Handler-Betrieb


Die vom RTC-Interrupt-Handler bereitgestellten Funktionen wurden in einem der vorherigen Artikel aufgeführt, die sich mit dem allgemeinen Thema der Systemzeit in Nucleus SE ( Nr. 27 ) befassten. Die beschriebene Funktionalität ist je nach Konfiguration der Anwendung optional.

Das Folgende ist der vollständige RTC-Interrupt-Handler-Code.
 #if NUSE_TIMER_NUMBER != 0 { U8 timer; for (timer=0; timer<NUSE_TIMER_NUMBER; timer++) { if (NUSE_Timer_Status[timer]) { if (--NUSE_Timer_Value[timer] == 0) { NUSE_Timer_Expirations_Counter[timer]++; #if NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT || NUSE_INCLUDE_EVERYTHING if (NUSE_Timer_Expiration_Routine_Address[timer] != NULL) { ((PF1)NUSE_Timer_Expiration_Routine_Address[timer]) NUSE_Timer_Expiration_Routine_Parameter[timer]); } #endif /* reschedule? */ if (NUSE_Timer_Reschedule_Time[timer] != 0) { /* yes: set up time */ NUSE_Timer_Value[timer] = NUSE_Timer_Reschedule_Time[timer]; } else { /* no: disable */ NUSE_Timer_Status[timer] = FALSE; } } } } } #endif #if NUSE_SYSTEM_TIME_SUPPORT || NUSE_INCLUDE_EVERYTHING NUSE_Tick_Clock++; #endif #if NUSE_TASK_SLEEP || NUSE_INCLUDE_EVERYTHING { U8 task; for (task=0; task<NUSE_TASK_NUMBER; task++) { if (NUSE_Task_Timeout_Counter[task] != 0) { NUSE_Task_Timeout_Counter[task]--; if (NUSE_Task_Timeout_Counter[task] == 0) { NUSE_Wake_Task(task); } } } } #endif #if NUSE_SCHEDULER_TYPE == NUSE_TIME_SLICE_SCHEDULER if (--NUSE_Time_Slice_Ticks == 0) { NUSE_Reschedule(); } #endif 


Als nächstes betrachten wir vier Hauptfunktionsbereiche für den RTC-Interrupt-Handler.

Timer
Wenn Anwendungszeitgeber konfiguriert sind, geht der Interrupt-Handler in eine Schleife, um jeden Zeitgeber zu verarbeiten, indem sein Zähler um 1 verringert wird. Wenn der Zeitgeber die Zählung beendet (d. H. Der Zähler erreicht 0), sind zwei Aktionen möglich:

  • Wenn der Timer-Abschluss-Handler konfiguriert ist und der Timer über einen korrekten (nicht NULL ) Funktionszeiger verfügt (in NUSE_Timer_Expiration_Routine_Address [] ), wird der Handler ausgeführt, indem ein Parameter aus NUSE_Timer_Expiration_Routine_Parameter [] übernommen wird .
  • Wenn der Timer so konfiguriert ist, dass er nach Abschluss initialisiert wird ( dh NUSE_Timer_Reschedule_Time [] hat einen Wert ungleich Null), wird der Timer mit diesem Wert neu geladen .

Anwendungszeitgeber wurden in einem früheren Artikel (Nr. 28) ausführlich beschrieben.

Systemuhr
Wenn ein System-Timer konfiguriert ist, erhöht sich der Wert von NUSE_Tick_Cloc k einfach um 1. Weitere Informationen finden Sie in Artikel 28.

Unterbrechen einer Aufgabe (Task Sleep)
Wenn die Unterstützung für das Anhalten von Aufgaben aktiviert ist ( dh der API-Aufruf NUSE_Task_Sleep () konfiguriert ist), wird der Zeitüberschreitungszähler jeder Aufgabe (der Wert in NUSE_Task_Timeout_Counter [] ) überprüft, und wenn er ungleich Null ist, wird er um 1 verringert. Wenn er Null erreicht, wird die entsprechende Aufgabe fortgesetzt .

Zeitscheibenplanung
Wenn ein Time Slice-Scheduler verwendet wird, wird der Scheduler-Zähler ( NUSE_Time_Slice_Ticks ) dekrementiert. Wenn es Null erreicht, wird der Scheduler aufgerufen. Der Aufruf von NUSE_Reschedule () ist für das Zurücksetzen des Zählers verantwortlich.

Kontrollierte Unterbrechung


Es muss erklärt werden, warum der RTC-Interrupt-Handler steuerbar ist, da der Benutzer unter bestimmten Umständen beschließen kann, ihn als Standard-Interrupt neu zu schreiben, um den Einsatz von Rechenressourcen zu reduzieren. Wenn beispielsweise nur eine Systemzeitfunktion verwendet wird (d. H. Es gibt keine Anwendungszeitgeber, keine Aufgabensperrungen und keinen Zeitscheibenplaner), funktioniert eine regelmäßige Unterbrechung. In folgenden Fällen ist eine geführte Unterbrechung erforderlich:

  • Wenn Timer verwendet werden und Handler für deren Abschluss konfiguriert sind, können diese Handler API-Aufrufe (aus dem Interrupt-Kontext) ausführen, was zu einer neuen Planung führt. Sie haben dieselben Einschränkungen wie API-Aufrufe von Interrupt-Handlern (siehe weiter oben in diesem Artikel).
  • Wenn ein Prioritätsplaner verwendet wird, kann das Abschließen einer Aufgabensperre das Aufwecken einer Aufgabe mit einer höheren Priorität erfordern.
  • Wenn der Time Slice-Scheduler verwendet wird, wird er vom RTC-Interrupt-Handler aufgerufen. Daher ist ein gesteuerter Interrupt erforderlich.

Nucleus RTOS-kompatibel


Da sich die Implementierung von Nucleus SE-Interrupts stark von Nucleus RTOS unterscheidet, sollten Sie diesbezüglich keine Kompatibilität erwarten. Nucleus RTOS verfügt über ein Standard- / Low-Level- / High-Level-Interrupt-Schema, das dem Standard- / gesteuerten Interrupt-Schema in Nucleus SE ähnelt.

Interrupt-Handler auf niedriger und hoher Ebene


Low Level Interrupt Handler
Ein Low-Level Interrupt Service Routin (LISR) wird auf die gleiche Weise wie ein regulärer Handler ausgeführt, einschließlich der Verwendung des aktuellen Stapels. Nucleus RTOS behält den Kontext bei, bis der Interrupt-Handler auf niedriger Ebene aufgerufen wird, und stellt den Kontext nach Abschluss des Handlers wieder her. Daher kann ein Interrupt-Handler auf niedriger Ebene in C geschrieben werden und andere Handler in C aufrufen. Dem Handler auf niedriger Ebene stehen jedoch nur wenige Nucleus RTOS-Dienste zur Verfügung. Wenn für die Interrupt-Behandlung zusätzliche Nucleus RTOS-Dienste erforderlich sind, müssen Sie einen Interrupt-Handler auf hoher Ebene aktivieren. Nucleus RTOS unterstützt die Verwendung mehrerer Interrupt-Handler auf niedriger Ebene.

High Level Interrupt Handler
High-Level Interrupt Service Routin (HISR) werden dynamisch erstellt und gelöscht. Jeder High-Level-Prozessor verfügt über einen eigenen Stapelspeicher und eine eigene Steuereinheit. Der Speicher wird von der Anwendung zugewiesen. Und natürlich muss ein Interrupt-Handler auf hoher Ebene erstellt werden, bevor ein Interrupt-Handler auf niedriger Ebene ihn aktivieren kann.

Da der Interrupt-Handler auf hoher Ebene über einen eigenen Stapel und eine eigene Steuereinheit verfügt, kann er vorübergehend blockiert werden, wenn versucht wird, auf die derzeit verwendete Nucleus RTOS-Datenstruktur zuzugreifen.

Für einen Interrupt-Handler auf hoher Ebene stehen drei Prioritätsstufen zur Verfügung. Wenn während der Arbeit eines Handlers mit niedrigerer Priorität ein übergeordneter Handler mit höherer Priorität aktiviert wird, wird nach Abschluss der Aufgabe ein Handler mit niedrigerer Priorität ausgeführt. Übergeordnete Interrupt-Handler mit derselben Priorität werden in der Reihenfolge ausgeführt, in der sie aktiviert sind. Alle aktivierten Interrupt-Handler auf hoher Ebene müssen abgeschlossen sein, bevor weitere Aufgaben im normalen Modus geplant werden können.

Das Nucleus RTOS API-Dienstprogramm fordert eine Unterbrechung an


Nucleus RTOS verfügt über mehrere API-Aufrufe zur Unterstützung von Interrupts. Keiner von ihnen ist in Nucleus SE implementiert.

Für Standard-Interrupts bieten API-Aufrufe die folgenden Funktionen:

  • Kontrolle (Aktivierung / Deaktivierung) der Unterbrechung (lokal und global);
  • Einstellen des Interrupt-Vektors.

Für Low-Level-Interrupts:

  • Registrieren eines Interrupt-Handlers auf niedriger Ebene im Kernel.

Für Interrupts auf hoher Ebene:

  • Schaffung / Beseitigung von Unterbrechungen auf hoher Ebene;
  • Interrupt-Aktivierung auf hoher Ebene;
  • Abrufen der Anzahl der Interrupts auf hoher Ebene in der Anwendung (im Moment);
  • Erhalten von Zeigern auf Steuereinheiten aller Interrupts auf hoher Ebene;
  • Erhalten von Zeigern auf Steuereinheiten des aktuellen Interrupts auf hoher Ebene;
  • Abrufen von Interrupt-Informationen auf hoher Ebene.

Globale Interrupt-Steuerung

Dieser Aufruf aktiviert oder deaktiviert Interrupts unabhängig von der Aufgabe. Daher bleibt der durch diesen Anruf deaktivierte Interrupt so lange bestehen, bis er durch Wiederverwendung dieses Anrufs aktiviert wird.

Prototyp eines Serviceabrufs:

 INT NU_Control_Interrupts (INT new_level); 

Parameter:

new_level - eine neue Interrupt-Ebene für das System. Es kann immer die Werte NU_DISABLE_INTERRUPTS (deaktiviert alle Interrupts) und NU_ENABLE_INTERRUPTS (aktiviert alle Interrupts) annehmen . Abhängig von der Architektur sind möglicherweise andere Werte verfügbar.

Rückgabewert:

Dieser Serviceabruf gibt die vorherige Ebene der aktivierten Interrupts zurück.

Lokale Interrupt-Steuerung

Mit diesem Serviceabruf können Sie Interrupts je nach Aufgabe aktivieren oder deaktivieren. Dieser Aufruf ändert das Statusregister auf den angegebenen Wert. Das Statusregister wird auf den Wert zurückgesetzt, der beim letzten Aufruf von NU_Control_Interrupts () angegeben wurde, wenn der Kontext das nächste Mal geändert wird.

Prototyp eines Serviceabrufs:

 INT NU_Local_Control_Interrupts (INT new_level); 

Parameter:

new_level - eine neue Interrupt-Ebene für die aktuelle Aufgabe. Es kann immer die Werte NU_DISABLE_INTERRUPTS (deaktiviert alle Interrupts) und NU_ENABLE_INTERRUPTS (aktiviert alle Interrupts) annehmen . Abhängig von der Architektur sind möglicherweise andere Werte verfügbar.

Rückgabewert:
Dieser Serviceabruf gibt die vorherige Ebene der aktivierten Interrupts zurück.

Einstellen des Interrupt-Vektors

Dieser Overhead ersetzt den Interrupt-Vektor, der durch den vom Interrupt-Handler gesteuerten Vektor angegeben wird.

Prototyp eines Serviceabrufs:

 VOID *NU_Setup_Vector (INT vector, VOID *new); 

Parameter:

Vektor - Interrupt-Vektor, für den der Interrupt registriert wird;
Neu ist der Interrupt-Handler, der für den Vektor geschrieben wurde.

Rückgabewert:

Dieser Dienstprogrammaufruf gibt einen Zeiger auf den Interrupt-Handler zurück, der zuvor für den Interrupt-Vektor registriert wurde.

Low Level Interrupt Logging

Dieser Overhead ruft die Funktion des Low-Level-Interrupt-Handlers mit dem Interrupt-Vektor auf. Der Systemkontext wird automatisch gespeichert, bevor der angegebene Interrupt-Handler auf niedriger Ebene aufgerufen und nach Abschluss des Interrupt-Handlers wiederhergestellt wird.

Prototyp eines Serviceabrufs:

 STATUS NU_Register_LISR (INT vector, VOID (*lisr_entry) (INT), VOID (**old_lisr) (INT); 

Parameter:

Vektor - Interrupt-Vektor, für den der Interrupt registriert wird;
lisr_entry - die Funktion, die für den Vektor registriert wird. Der Wert NU_NULL löscht den Vektor.
old_lisr ist eine Funktion, die zuvor für den angegebenen Vektor registriert wurde.

Rückgabewert:

NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_INVALID_VECTOR - ungültiger Vektor;
NU_NOT_Rector - Im Moment ist der Vektor nicht registriert, da die Abmeldung in l isr_entry angegeben wurde .
NO_MORE_LISRS - Die maximale Anzahl registrierter Interrupt-Handler auf niedriger Ebene wurde erreicht.

Erstellen eines Interrupt-Handlers auf hoher Ebene
Dieser Dienstprogrammaufruf erstellt einen Interrupt-Handler auf hoher Ebene.

Prototyp eines Serviceabrufs:

 STATUS NU_Create_HISR (NU_HISR *hisr, CHAR *name, VOID (*hisr_entry) (VOID), OPTION priority, VOID *stack_pointer, UNSIGNED stack_size); 

Parameter:

hisr - ein Zeiger auf einen vom Benutzer bereitgestellten Steuerblock für einen Interrupt-Handler auf hoher Ebene;
name - Zeiger auf einen 7-stelligen Namen für einen Interrupt-Handler auf hoher Ebene mit einer abschließenden Null;
hisr_entry - der Einstiegspunkt der Interrupt-Handler-Funktion auf hoher Ebene;
Priorität - Es gibt drei Prioritäten für Interrupt-Handler auf hoher Ebene (0-2). Priorität 0 ist die höchste;
stack_pointer - Zeiger auf den Stapelbereich des High-Level-Interrupt-Handlers;
stack_size - Die Anzahl der Bytes im Stapel des Interrupt- Handlers auf hoher Ebene.

Rückgabewert:

NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_INVALID_HISR - Ein Nullzeiger auf die Steuereinheit des High-Level-Interrupt-Handlers ( NULL ) oder die Steuereinheit wird bereits verwendet.
NU_INVALID_ENTRY - ein Nullzeiger auf den Einstiegspunkt eines Interrupt- Handlers auf hoher Ebene ( NULL );
NU_INVALID_PRIORITY - falsche Priorität für einen Interrupt-Handler auf hoher Ebene;
NU_INVALID_MEMORY - ungültiger Stapelzeiger;
NU_INVALID_SIZE - Die Stapelgröße ist zu klein.

Entfernen eines Interrupt-Handlers auf hoher Ebene
Dieser Dienstprogrammaufruf entfernt den zuvor erstellten Interrupt-Handler auf hoher Ebene.

Prototyp eines Serviceabrufs:

 STATUS NU_Delete_HISR (NU_HISR *hisr); 

Parameter:

hisr ist ein Zeiger auf den vom Benutzer bereitgestellten Steuerblock des Interrupt- Handlers auf hoher Ebene.

Rückgabewert:

NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_INVALID_HISR - Ungültiger Zeiger auf einen Interrupt-Handler auf hoher Ebene.

Aktivieren eines Interrupt-Handlers auf hoher Ebene

Dieser Dienstprogrammaufruf aktiviert einen Interrupt-Handler auf hoher Ebene. Wenn der angegebene High-Level-Interrupt-Handler gerade ausgeführt wird, wird die Aktivierungsanforderung erst ausgeführt, wenn der Handler nicht mehr funktioniert. Ein Interrupt-Handler auf hoher Ebene wird für jede Aktivierungsanforderung einmal ausgeführt.

Prototyp eines Serviceabrufs:

 STATUS NU_Activate_HISR (NU_HISR *hisr); 

Parameter:

hisr ist ein Zeiger auf den Steuerblock eines Interrupt- Handlers auf hoher Ebene.
Rückgabewert:
NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_INVALID_HISR - ungültiger Zeiger auf die Steuereinheit eines Interrupt- Handlers auf hoher Ebene.

Abrufen der Anzahl der Interrupt-Handler auf hoher Ebene in einem System
Dieser Dienstprogrammaufruf gibt die Anzahl der installierten Interrupt-Handler auf hoher Ebene zurück. Alle erstellten Interrupt-Handler auf hoher Ebene gelten als installiert. Remote-Interrupt-Handler auf hoher Ebene gelten nicht als installiert.

Prototyp eines Serviceabrufs:

 UNSIGNED NU_Established_HISRs(VOID); 

Parameter:
Sind abwesend.

Rückgabewert:
Dieser Dienstprogrammaufruf gibt die Anzahl der im System installierten Interrupt-Handler auf hoher Ebene zurück.

Abrufen von Zeigern zum Steuern von Blöcken von Interrupt-Handlern auf hoher Ebene

Dieser Serviceabruf bildet eine sequentielle Liste von Zeigern auf alle im System installierten Interrupt-Handler auf hoher Ebene.

Prototyp eines Serviceabrufs:

 UNSIGNED NU_HISR_Pointers(NU_HISR **pointer_list, UNSIGNED maximum_pointers); 

Parameter:

pointer_list - Zeiger auf ein Array von Zeigern NU_HISR ; Dieses Array wird mit Zeigern auf die im System installierten Interrupt-Handler auf hoher Ebene gefüllt.
Maximum_Pointers - Die maximale Anzahl von NU_HISR- Zeigern, die im Array platziert werden können. Sie entspricht normalerweise der Größe des Arrays pointer_list .

Rückgabewert:
Dieser Dienstprogrammaufruf gibt die Anzahl der aktiven Interrupt-Handler auf hoher Ebene im System zurück.

Abrufen eines Zeigers auf den aktuellen Interrupt-Handler auf hoher Ebene
Dieser Dienstprogrammaufruf gibt einen Zeiger auf den aktuell ausgeführten Interrupt-Handler auf hoher Ebene zurück.

Prototyp eines Serviceabrufs:

 NU_HISR *NU_Current_HISR_Pointer(VOID); 

Parameter:
Sind abwesend.

Rückgabewert:
Dieser Serviceabruf gibt einen Zeiger auf die Steuereinheit des aktuell ausgeführten Interrupt-Handlers auf hoher Ebene zurück. Wenn ein Interrupt-Handler auf nicht hoher Ebene diese Funktion aufruft , wird NU_NULL zurückgegeben.

Abrufen von Informationen zu einem Interrupt-Handler auf hoher Ebene
Dieser Dienstprogrammaufruf gibt verschiedene Informationen zum angegebenen Interrupt-Handler auf hoher Ebene zurück.

Prototyp eines Serviceabrufs:

 STATUS NU_HISR_Information(NU_HISR *hisr, char *name, UNSIGNED *scheduled_count, DATA_ELEMENT *priority, VOID **stack_base, UNSIGNED *stack_size, UNSIGNED *minimum_stack); 

Parameter:

hisr - ein Zeiger auf einen Interrupt-Handler auf hoher Ebene;
name - Zeiger auf den 8-stelligen Bereich für den Namen des Interrupt-Handlers auf hoher Ebene, einschließlich der abschließenden Null;
Scheduled_Count - Zeiger auf eine Variable für die Gesamtzahl der geplanten Interrupt-Handler auf hoher Ebene.
Priorität - ein Zeiger auf eine Variable zum Speichern der Priorität eines Interrupt-Handlers auf hoher Ebene;
stack_base - Zeiger auf einen Zeiger zum Speichern des ursprünglichen Zeigers auf den Stapel; Dies ist derselbe Zeiger, der beim Erstellen des Interrupt-Handlers auf hoher Ebene übergeben wurde.
stack_size - ein Zeiger auf eine Variable zum Speichern der gesamten Stapelgröße eines Interrupt-Handlers auf hoher Ebene;
Minimum_stack - Ein Zeiger auf eine Variable zum Speichern der minimalen Menge an verfügbarem Stapelspeicher, die während der Ausführung eines Interrupt-Handlers auf hoher Ebene erkannt wurde.

Rückgabewert:

NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_INVALID_HISR - ungültiger Zeiger auf einen Interrupt-Handler auf hoher Ebene.

API-Aufrufe von Interrupt-Handlern


API-Aufruf von Interrupt-Handlern auf niedriger Ebene Ein Interrupt-Handler auf niedriger Ebene
kann nur die folgenden Nucleus RTOS-Funktionen verwenden:

 NU_Activate_HISR() NU_Local_Control_Interrupts() NU_Current_HISR_Pointer() NU_Current_Task_Pointer() NU_Retrieve_Clock() 

API-Serviceaufrufe von Interrupt-Handlern auf
hoher Ebene Interrupt-Handler auf hoher Ebene haben Zugriff auf die meisten Nucleus RTOS-Funktionen, mit Ausnahme von selbst pausierenden Funktionen. Da ein Interrupt-Handler auf hoher Ebene die Nucleus RTOS-Funktion nicht aussetzen kann, muss der Parameter immer NU_NO_SUSPEND sein .

Der nächste Artikel in dieser Reihe behandelt die Initialisierungs- und Startverfahren von Nucleus SE.

Über den Autor:Colin Walls ist seit über dreißig Jahren in der Elektronikindustrie tätig und verbringt die meiste Zeit mit Firmware. Heute ist er Firmware-Ingenieur bei Mentor Embedded (einer Abteilung von Mentor Graphics). Colin Walls spricht häufig auf Konferenzen und Seminaren, Autor zahlreicher technischer Artikel und zweier Bücher über Firmware. Lebt in Großbritannien. Colins professioneller Blog , E-Mail: colin_walls@mentor.com.

Source: https://habr.com/ru/post/de458770/


All Articles