Die ganze Wahrheit über RTOS. Artikel Nr. 17. Ereignisflag-Gruppen: Einführung und Basisdienste



Gruppen von Ereignisflags wurden bereits in einem der vorherigen Artikel (Nr. 5) erwähnt. In Nucleus SE ähneln sie Signalen, sind jedoch flexibler. Sie bieten eine kostengünstige und flexible Möglichkeit, einfache Nachrichten zwischen Aufgaben zu übertragen.


Frühere Artikel in der Reihe:
Artikel 16. Signale
Artikel Nr. 15. Speicherpartitionen: Dienste und Datenstrukturen
Artikel # 14. Speicherbereiche: Einführung und Grundversorgung
Artikel Nr. 13. Aufgabendatenstrukturen und nicht unterstützte API-Aufrufe
Artikel 12. Dienstleistungen für die Arbeit mit Aufgaben
Artikel 11. Aufgaben: Konfiguration und Einführung in die API
Artikel 10. Scheduler: Erweiterte Funktionen und Kontexterhaltung
Artikel 9. Scheduler: Implementierung
Artikel 8. Nucleus SE: Internes Design und Bereitstellung
Artikel 7. Nucleus SE: Einführung
Artikel 6. Andere RTOS-Dienste
Artikel 5. Aufgabeninteraktion und Synchronisation
Artikel 4. Aufgaben, Kontextwechsel und Interrupts
Artikel 3. Aufgaben und Planung
Artikel 2. RTOS: Struktur und Echtzeitmodus
Artikel 1. RTOS: Einführung.


Ereignisflags verwenden


In Nucleus SE werden Ereignisflags während der Erstellungsphase definiert. Die maximale Anzahl von Ereignisflaggruppen in der Anwendung beträgt 16. Wenn keine Ereignisflaggruppen definiert sind, wird der Code, der sich auf die Datenstrukturen und Serviceaufrufe der Ereignisflaggruppen bezieht, nicht in die Anwendung aufgenommen.

Gruppe von Ereignisflags - Ein Satz von 8-Bit-Flags, auf die der Zugriff so geregelt ist, dass mehrere Aufgaben ein Flag sicher verwenden können. Eine Aufgabe kann eine beliebige Kombination von Ereignisflags setzen oder löschen. Eine andere Aufgabe besteht darin, jederzeit eine Gruppe von Flags zu lesen, und sie kann auch auf eine bestimmte Folge von Flags warten (durch Abfrage oder mit einer Pause).

Ereignisflaggruppen konfigurieren


Anzahl der Ereignisflaggruppen


Wie bei den meisten Nucleus SE-Objekten wird die Konfiguration von Ereignisflaggruppen durch die Direktiven #define in nuse_config.h festgelegt . Der Hauptparameter ist NUSE_EVENT_GROUP_NUMBER , der bestimmt, wie viele Gruppen von Ereignisflags in der Anwendung definiert werden. Standardmäßig ist dieser Parameter auf 0 gesetzt ( dh Gruppen von Ereignisflags werden nicht verwendet) und kann einen beliebigen Wert von bis zu 16 haben. Ein falscher Wert führt zu einem Kompilierungsfehler, der durch Einchecken von nuse_config_check.h generiert wird (aktiviert durch nuse_config.c) Dies bedeutet, dass es mit diesem Modul kompiliert wird. Infolgedessen funktioniert die Direktive #error . Die Auswahl eines Werts ungleich Null dient als Hauptaktivator für die Ereignisflaggruppen. Dieser Parameter wird beim Definieren von Datenstrukturen verwendet und ihre Größe hängt von ihrem Wert ab (mehr dazu in den folgenden Artikeln). Darüber hinaus aktiviert ein Wert ungleich Null die API-Einstellungen.

API-Aufrufe aktivieren


Jede API-Funktion (Dienstprogrammaufruf) in Nucleus SE wird durch die Direktive #define in nuse_config.h aktiviert. Für Gruppen von Ereignisflags gehören dazu:
NUSE_EVENT_GROUP_SET
NUSE_EVENT_GROUP_RETRIEVE
NUSE_EVENT_GROUP_INFORMATION
NUSE_EVENT_GROUP_COUNT

Standardmäßig sind sie auf FALSE gesetzt , wodurch jeder Serviceabruf deaktiviert und die Aufnahme von Code blockiert wird, der sie implementiert. Um Gruppen von Ereignisflags zu konfigurieren, müssen Sie die erforderlichen API-Aufrufe auswählen und die entsprechenden Anweisungen auf TRUE setzen .

Das Folgende ist ein Auszug aus der Standarddatei nuse_config.h.

 #define NUSE_EVENT_GROUP_NUMBER 0 /* Number of event groups in the system - 0-16 */ #define NUSE_EVENT_GROUP_SET FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_RETRIEVE FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_INFORMATION FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_COUNT FALSE /* Service call enabler */ 

Eine aktivierte API-Funktion, wenn keine Ereignisflaggruppen in der Anwendung vorhanden sind, führt zu einem Kompilierungsfehler (mit Ausnahme von NUSE_Event_Group_Count () , das immer aktiviert ist). Wenn Ihr Code einen API-Aufruf verwendet, der nicht aktiviert wurde, tritt ein Layoutfehler auf, da der Implementierungscode nicht in der Anwendung enthalten war.

Ereignisaufruf-Dienstprogrammaufrufe


Nucleus RTOS unterstützt sieben Dienstprogrammaufrufe mit den folgenden Funktionen:

  • Setzen Sie Ereignisflags. Nucleus SE ist in der Funktion NUSE_Event_Group_Set () implementiert.
  • Ereignisflags lesen. In Nucleus SE, implementiert in NUSE_Event_Group_Retrieve () .
  • Bereitstellung von Informationen zu einer bestimmten Gruppe von Ereignisflags. In Nucleus SE, implementiert in NUSE_Event_Group_Information () .
  • Gibt die Anzahl der aktuell konfigurierten Ereignisflaggruppen in der Anwendung zurück. In Nucleus SE, implementiert in NUSE_Event_Group_Count () .
  • Hinzufügen einer neuen Gruppe von Ereignisflags zur Anwendung. Nucleus SE ist nicht implementiert.
  • Entfernen einer Gruppe von Ereignisflags aus der Anwendung. Nucleus SE ist nicht implementiert.
  • Rückgabe von Zeigern auf alle Gruppen von Ereignisflags in der Anwendung. Nucleus SE ist nicht implementiert.

Die Implementierung jedes dieser Overhead-Aufrufe wird nachstehend ausführlich erläutert.

Es ist erwähnenswert, dass weder in Nucleus RTOS noch in Nucleus SE eine Rücksetzfunktion vorhanden ist. Dies geschieht absichtlich. Die Rücksetzfunktion impliziert die Prävalenz des Sonderzustands der Flags. Für Gruppen von Ereignisflags besteht der einzige "spezielle" Status darin, alle Flags zurückzusetzen, was mit NUSE_Event_Group_Set () erfolgen kann .

Serviceaufrufe zum Setzen und Lesen von Ereignisflaggruppen


Die grundlegenden Operationen, die für eine Gruppe von Ereignisflags ausgeführt werden können, sind das Setzen des Werts eines oder mehrerer Flags sowie das Lesen der aktuellen Flagwerte. Nucleus RTOS und Nucleus SE bieten vier grundlegende API-Aufrufe für diese Vorgänge.

Da Ereignisflags Bits sind, werden sie am besten als Binärzahlen dargestellt. Da Standard C die Darstellung von Binärkonstanten (nur oktal und hexadezimal) in der Vergangenheit nicht unterstützt hat, verfügt Nucleus SE über eine nützliche Header-Datei nuse_binary.h , die # define- Zeichen wie b01010101 für alle 256 8-Bit-Werte enthält.

Ereignisflags setzen


Der Aufruf des Nucleus RTOS API-Dienstprogramms zum Kennzeichnen ist sehr flexibel und ermöglicht das Festlegen und Löschen von Kennzeichnungswerten mithilfe von UND- und ODER- Operationen. Nucleus SE bietet ähnliche Funktionen, die Unterbrechung der Aufgabe ist jedoch optional.

Aufruf zum Setzen von Flags in Nucleus RTOS
Prototyp eines Serviceabrufs:

STATUS NU_Set_Events (Gruppe NU_EVENT_GROUP *, UNSIGNED event_flags, OPTION-Operation);

Parameter:

group - ein Zeiger auf einen vom Benutzer bereitgestellten Steuerblock für eine Gruppe von Ereignisflags;
event_flags - Wert der Bitmaske der Flaggruppe ;
Operation - Die auszuführende Operation , NU_OR (zum Setzen von Flags) oder NU_AND (zum Löschen von Flags).

Rückgabewert:

NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_INVALID_GROUP - ungültiger Zeiger auf eine Gruppe von Ereignisflags;
NU_INVALID_OPERATION - Die angegebene Operation unterscheidet sich von NU_OR und NU_AND .

Aufruf zum Setzen von Flags in Nucleus SE
Dieser API-Aufruf unterstützt die Kernfunktionalität der Nucleus RTOS-API.

Prototyp eines Serviceabrufs:

STATUS NUSE_Event_Group_Set (Gruppe NUSE_EVENT_GROUP, U8 event_flags, OPTION-Operation);

Parameter:

Gruppe - der Index (ID) der Ereignisgruppe, deren Flags gesetzt / gelöscht sind;
event_flags - Wert des Bitmaxi einer Gruppe von Flags;
Operation - Die auszuführende Operation , NUSE_OR (zum Setzen von Flags) oder NUSE_AND (zum Löschen von Flags).

Rückgabewert:

NUSE_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NUSE_INVALID_GROUP - ungültiger Index einer Gruppe von Ereignisflags;
NUSE_INVALID_OPERATION - Die angegebene Operation unterscheidet sich von NUSE_OR und NUSE_AND .

Implementierung der Installation von Ereignisflags in Nucleus SE
Der Anfangscode der API-Funktion NUSE_Event_Group_Set () ist allgemein (nach Überprüfung der Parameter), unabhängig davon, ob die API das Blockieren von Aufrufen unterstützt (Task-Suspendierung) oder nicht. Die Logik ist ziemlich einfach:

 NUSE_CS_Enter(); if (operation == NUSE_OR) { NUSE_Event_Group_Data[group] |= event_flags; } else /* NUSE_AND */ { NUSE_Event_Group_Data[group] &= event_flags; } 

Die Bitmaske event_flags wird (unter Verwendung der UND- oder ODER- Verknüpfung) dem Wert der ausgewählten Gruppe von Ereignisflags überlagert.

Der verbleibende Code wird nur aktiviert, wenn die Task-Sperre aktiviert ist:

 #if NUSE_BLOCKING_ENABLE while (NUSE_Event_Group_Blocking_Count[group] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this event group */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_EVENT_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == group)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Event_Group_Blocking_Count[group]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif #endif NUSE_CS_Exit(); return NUSE_SUCCESS; 

Wenn Aufgaben (zum Lesen) von dieser Gruppe von Flags angehalten werden, werden sie fortgesetzt. Wenn sie die Möglichkeit erhalten, die Ausführung fortzusetzen (dies hängt vom Scheduler ab), können sie feststellen, ob die Bedingungen für ihre Wiederaufnahme erfüllt sind oder nicht (siehe Lesen von Ereignisflags).

Ereignisflags lesen


Die Aufrufe des Nucleus RTOS API-Dienstprogramms zum Lesen sind sehr flexibel und ermöglichen es Ihnen, Aufgaben auf unbestimmte Zeit oder mit einem bestimmten Zeitlimit anzuhalten, wenn der Vorgang nicht sofort abgeschlossen werden kann (z. B. wenn Sie versuchen, eine bestimmte Folge von Ereignisflags zu lesen, die nicht den aktuellen Status darstellen). Nucleus SE bietet dieselben Funktionen, nur die Unterbrechung der Aufgabe ist optional und das Zeitlimit ist nicht implementiert.

Flags Challenge in Nucleus RTOS
Prototyp eines Serviceabrufs:

STATUS NU_Retrieve_Events (Gruppe NU_EVENT_GROUP *, UNSIGNED Requested_events, OPTION-Operation, UNSIGNED * Retrieved_events, UNSIGNED Suspend);

Parameter:

group - ein Zeiger auf einen vom Benutzer bereitgestellten Steuerblock für eine Gruppe von Ereignisflags;
request_events - eine Bitmaske, die die zu lesenden Flags definiert;
Operation - Es stehen vier Operationen zur Verfügung: NU_AND , NU_AND_CONSUME , NU_OR und NU_OR_CONSUME . Die Operationen NU_AND und NU_AND_CONSUME geben an, dass alle angeforderten Flags erforderlich sind. Die Operationen NU_OR und NU_OR_CONSUME zeigen an, dass eines oder mehrere der angeforderten Flags ausreichend sind. Der Parameter CONSUME löscht nach einer erfolgreichen Anforderung automatisch vorhandene Flags.
retrieved_events - Speicherzeiger für die Werte der Leseereignisflags;
suspend - Spezifikation für das Anhalten von Aufgaben; kann die Werte NU_NO_SUSPEND oder NU_SUSPEND oder den Timeout-Wert in Takt- Ticks (von 1 bis 4.294.967.293) annehmen .

Rückgabewert:

NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_NOT_PRESENT - Die angegebene Operation hat keine Ereignisse zurückgegeben (kein einziges Ereignis im Fall von NU_OR und nicht alle Ereignisse im Fall von NU_AND).
NU_INVALID_GROUP - ungültiger Zeiger auf eine Gruppe von Ereignisflags;
NU_INVALID_OPERATION - Die angegebene Operation war falsch.
NU_INVALID_POINTER - Nullzeiger auf den Speicher der Ereignisflags (NULL);
NU_INVALID_SUSPEND - Versuch, von einem nicht aufgabenbezogenen Thread aus anzuhalten ;
NU_TIMEOUT - Die erforderliche Kombination von Ereignisflags wurde auch nach dem angegebenen Zeitlimit nicht gesetzt.
NU_GROUP_DELETED - Die Gruppe von Ereignisflags wurde gelöscht, während die Aufgabe angehalten wurde.

Flags Challenge in Nucleus SE
Dieser API-Aufruf unterstützt die Kernfunktionalität der Nucleus RTOS-API.

Prototyp eines Serviceabrufs:

STATUS NUSE_Event_Group_Retrieve (Gruppe NUSE_EVENT_GROUP, U8 angeforderte_Ereignisse, OPTION-Operation, U8 * abgerufene_Ereignisse, U8-Suspend);

Parameter:

Gruppe - Index (ID) der gelesenen Gruppe von Ereignisflags;
request_events - eine Bitmaske, die die zu lesenden Flags definiert;
operation - eine Spezifikation, die die Anzahl der benötigten Flags angibt : NUSE OR (einige Flags) oder NUSE AND (alle Flags);
retrieved_events - ein Zeiger auf den Speicher für die tatsächlichen Werte der Leseereignisflags (bei der Operation NUSE_AND entspricht dies der Übergabe im Parameter request_events );
suspend - Spezifikation zum Anhalten einer Aufgabe, die die Werte NUSE_NO_SUSPEND oder NUSE_SUSPEND annehmen kann .

Rückgabewert:

NUSE_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NUSE_NOT_PRESENT - Die angegebene Operation hat keine Ereignisse zurückgegeben (kein einziges Ereignis im Fall von NUSE_OR und nicht alle Ereignisse im Fall von NUSE_AND ).
NUSE_INVALID_GROUP - ungültiger Index einer Gruppe von Ereignisflags;
NUSE_INVALID_OPERATION - Die angegebene Operation unterscheidet sich von NUSE_OR oder NUSE_AND .
NUSE_INVALID_POINTER - ein Nullzeiger auf den Speicher der Leseereignisflags ( NULL );
NUSE_INVALID_SUSPEND - Ein Versuch, eine Pause von einem Nicht-Task-Flow einzulegen oder wenn die Unterstützung für das Blockieren von API-Aufrufen deaktiviert ist.

Implementieren des Lesens von Ereignisflags in Nucleus SE
Die Version des API-Funktionscodes NUSE_Event_Group_Retrieve () (nach Überprüfung der Parameter) wird während der bedingten Kompilierung ausgewählt, je nachdem, ob die Unterstützung für die API-Aufrufe zum Blockieren (Anhalten) von Aufgaben aktiviert ist oder nicht. Betrachten wir diese beiden Optionen getrennt.

Wenn die Sperre deaktiviert ist, sieht der vollständige Code für diesen API-Aufruf folgendermaßen aus:

 temp_events = NUSE_Event_Group_Data[group] & requested_events; if (operation == NUSE_OR) { if (temp_events != 0) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } else /* operation == NUSE_AND */ { if (temp_events == requested_events) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } 

Die erforderlichen Ereignisflags werden aus der angegebenen Ereignisflaggruppe ausgewählt. Der Wert wird unter Berücksichtigung der UND / ODER- Operation sowie des zurückgegebenen Ergebnisses und der unmittelbaren Werte der angeforderten Flags mit den erforderlichen Ereignissen verglichen.

Wenn die Task-Sperre aktiviert ist, wird der Code komplexer:

 do { temp_events = NUSE_Event_Group_Data[group] & requested_events; if (operation == NUSE_OR) { if (temp_events != 0) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } else /* operation == NUSE_AND */ { if (temp_events == requested_events) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } if (return_value == NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } else { if (suspend == NUSE_SUSPEND) /* block task */ { NUSE_Event_Group_Blocking_Count[group]++; NUSE_Suspend_Task(NUSE_Task_Active, (group << 4) | NUSE_EVENT_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND); 

Der Code wird in eine do ... while-Schleife eingefügt , die funktioniert, während der suspend- Parameter NUSE_SUSPEND lautet .

Die angeforderten Ereignisflags werden so gelesen, als würden sie ohne Blockierung aufgerufen. Wenn der Lesevorgang nicht erfolgreich ist und der Suspend- Parameter NUSE_NO_SUSPEND lautet , wird der API-Aufruf auf NUSE_NOT_PRESENT gesetzt . Wenn der Suspend- Parameter auf NUSE_SUSPEND gesetzt wurde , wird die Task angehalten . Wenn bei der Rückkehr (wenn die Aufgabe fortgesetzt wird) der Rückgabewert NUSE_SUCCESS lautet und angibt , dass die Aufgabe fortgesetzt wurde, weil die Ereignisflags in dieser Gruppe gesetzt oder gelöscht wurden, beginnt der Zyklus von vorne , die Flags werden gelesen und überprüft. Da es keine API-Funktion zum Zurücksetzen von Ereignisflaggruppen gibt, ist dies der einzige Grund für die Wiederaufnahme der Aufgabe. Der Überprüfungsprozess NUSE_Task_Blocking_Return [] wurde jedoch auf dem System belassen, um die Kompatibilität der Sperrsteuerung mit anderen Objekttypen zu gewährleisten.

Der folgende Artikel beschreibt zusätzliche API-Aufrufe, die Ereignisflaggruppen zugeordnet sind, sowie deren Datenstrukturen.

Über den Autor: Colin Walls ist seit über dreißig Jahren in der Elektronikindustrie tätig und widmet sich die meiste Zeit der 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/de428131/


All Articles