Die ganze Wahrheit über RTOS. Artikel # 33. Verwenden des Echtzeit-Betriebssystems Nucleus SE

Bisher haben wir uns in dieser Artikelserie mit den Funktionen von Nucleus SE befasst. Jetzt ist es Zeit zu sehen, wie es in einer echten Firmware-Anwendung verwendet werden kann.



Frühere Artikel in der Reihe:
Artikel 32. Nucleus SE-Migration: Nicht realisierte Funktionen und Kompatibilität
Artikel Nr. 31. Diagnose und Fehlerprüfung RTOS
Artikel 30. Initialisierungs- und Startverfahren für Nucleus SE
Artikel 29. Unterbrechungen in Nucleus SE
Artikel 28. Software-Timer
Artikel 27. Systemzeit
Artikel Nr. 26. Kanäle: Nebendienstleistungen und Datenstrukturen
Artikel Nr. 25. Datenkanäle: Einführung und Basisdienste
Artikel 24. Warteschlangen: Nebendienstleistungen und Datenstrukturen
Artikel 23. Warteschlangen: Einführung und Grundversorgung
Artikel 22. Postfächer: Nebendienstleistungen und Datenstrukturen
Artikel 21. Postfächer: Einführung und Basisdienste
Artikel Nr. 20. Semaphoren: Nebendienstleistungen und Datenstrukturen
Artikel Nr. 19. Semaphoren: Einführung und Grundversorgung
Artikel Nr. 18. Ereignisflag-Gruppen: Hilfsdienste und Datenstrukturen
Artikel Nr. 17. Ereignisflag-Gruppen: Einführung und Basisdienste
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.

Was ist Nucleus SE?


Wir wissen, dass Nucleus SE der Kern eines Echtzeitbetriebssystems ist, aber Sie müssen verstehen, wie es in den Rest der Anwendung passt. Und es passt einfach, weil die Anwendung im Gegensatz zu einem Desktop-Betriebssystem (z. B. Windows) nicht auf Nucleus SE gestartet wird. Der Kernel ist einfach Teil eines Programms, das auf einem eingebetteten Gerät ausgeführt wird. Dies ist der häufigste Anwendungsfall für RTOS.

Aus einer übergeordneten Sicht ist eine eingebettete Anwendung eine Art Code, der beim Starten der CPU gestartet wird. In diesem Fall wird die Hardware- und Softwareumgebung initialisiert und anschließend die Funktion main () aufgerufen, mit der der Hauptanwendungscode gestartet wird.

Bei Verwendung von Nucleus SE (und vielen anderen ähnlichen Kerneln) besteht der Unterschied darin, dass die Funktion main () Teil des Kernel-Codes ist. Diese Funktion initialisiert einfach die Datenstrukturen des Kernels und ruft dann den Scheduler auf, was zum Start des Anwendungscodes (Tasks) führt. Der Benutzer kann der main () - Funktion einen beliebigen nativen Initialisierungscode hinzufügen.

Nucleus SE enthält auch eine Reihe von Funktionen - eine Anwendungsprogrammierschnittstelle (API), die eine Reihe von Funktionen wie Kommunikation und Task-Synchronisation, Arbeiten mit Timern, Speicherzuweisung usw. bereitstellt. Alle API-Funktionen wurden bereits in den Artikeln dieser Reihe beschrieben.

Die gesamte Nucleus SE-Software wird als Quellcode bereitgestellt (hauptsächlich in C). Um den Code gemäß den Anforderungen einer bestimmten Anwendung zu konfigurieren, wird eine bedingte Kompilierung verwendet. Dies wird in diesem Artikel im Abschnitt Konfiguration ausführlich beschrieben.

Nachdem der Code kompiliert wurde, werden die resultierenden Nucleus SE-Objektmodule den Anwendungscodemodulen zugeordnet, was zu einem einzelnen Binärbild führt, das normalerweise im Flash-Speicher des eingebetteten Geräts abgelegt wird. Das Ergebnis dieser statischen Bindung ist, dass alle symbolischen Informationen sowohl aus dem Anwendungscode als auch aus dem Kernelcode verfügbar bleiben. Dies ist nützlich für das Debuggen. Es ist jedoch Vorsicht geboten, um einen Missbrauch der Nucleus SE-Daten zu vermeiden.

CPU- und Tool-Unterstützung


Da Nucleus SE als Quellcode geliefert wird, muss es portabel sein. Code, der auf einer so niedrigen Ebene ausgeführt wird (bei Verwendung von Schedulern, bei denen eine Kontextumschaltung erforderlich ist, dh alles andere als Run to Completion), kann jedoch nicht vollständig unabhängig von der Assemblersprache sein. Ich habe diese Abhängigkeit minimiert und für die Portierung auf eine neue CPU ist eine Low-Level-Programmierung fast nicht erforderlich. Die Verwendung neuer Entwicklungstools (Compiler, Assembler, Linker usw.) kann ebenfalls zu Portierungsproblemen führen.

Einrichten der Nucleus SE-App


Der Schlüssel zur effizienten Nutzung von Nucleus SE ist die richtige Einrichtung. Es mag kompliziert aussehen, aber tatsächlich ist alles ziemlich logisch und erfordert nur einen systematischen Ansatz. Fast die gesamte Konfiguration erfolgt durch Bearbeiten von zwei Dateien: nuse_config.h und nuse_config.c .

Einstellung Nuse_config.h


Diese Datei ist nur ein Zeichensatz der Direktive #define , dem die entsprechenden Werte zugewiesen wurden, um die erforderliche Kernelkonfiguration zu erhalten. In der Datei nuse_config.h sind standardmäßig alle Zeichen vorhanden, ihnen werden jedoch die Mindesteinstellungen zugewiesen.

Objektzähler
Die Anzahl der Kernelobjekte jedes Typs wird durch Symbolwerte der Form NUSE_SEMAPHORE_NUMBER festgelegt . Für die meisten Objekte kann dieser Wert zwischen 0 und 15 variieren. Aufgaben sind eine Ausnahme, es muss mindestens eine vorhanden sein. Signale sind in der Tat keine unabhängigen Objekte, da sie Aufgaben zugeordnet sind und durch Setzen von NUSE_SIGNAL_SUPPOR T auf TRUE aktiviert werden .

API-Funktionsaktivatoren
Jede Nucleus SE-API-Funktion kann separat aktiviert werden, indem TRUE ein Symbol zugewiesen wird, dessen Name mit dem Funktionsnamen (z. B. NUSE_PIPE_JAM ) übereinstimmt . Dies führt zur Aufnahme des Funktionscodes in die Anwendung.

Scheduler Auswahl und Einstellungen
Nucleus SE unterstützt vier Arten von Schedulern, wie in einem vorherigen Artikel beschrieben. Der verwendete Scheduler wird festgelegt, indem NUSE_SCHEDULER_TYPE einem der folgenden Werte zugewiesen wird: NUSE_RUN_TO_COMPLETION_SCHEDULER , NUSE_TIME_SLICE_SCHEDULER , NUSE_ROUND_ROBIN_SCHEDULER oder NUSE_PRIORITY_SCHEDULER .

Sie können andere Scheduler-Parameter konfigurieren:
NUSE_TIME_SLICE_TICKS gibt die Anzahl der Ticks pro Slot für den Time Slice Scheduler an. Wenn ein anderer Scheduler verwendet wird, sollte dieser Parameter auf 0 gesetzt werden.
NUSE_SCHEDULE_COUNT_SUPPORT kann auf TRUE oder FALSE gesetzt werden , um den Scheduler- Zählermechanismus zu aktivieren / deaktivieren.
NUSE_SUSPEND_ENABLE aktiviert das Sperren ( Anhalten) von Aufgaben für viele API-Funktionen. Dies bedeutet, dass ein Aufruf einer solchen Funktion zu einer Unterbrechung der aufrufenden Aufgabe führen kann, bis die Ressource freigegeben wird. Um diese Option auszuwählen, muss NUSE_SUSPEND_ENABLE ebenfalls auf TRUE gesetzt sein .

Andere Optionen
Es können auch mehreren anderen Parametern TRUE- oder FALSE- Werte zugewiesen werden, um andere Kernelfunktionen zu aktivieren / deaktivieren:
NUSE_API_PARAMETER_CHECKING fügt einen Überprüfungscode für API-Funktionsaufrufparameter hinzu. Wird häufig zum Debuggen verwendet.
NUSE_INITIAL_TASK_STATE_SUPPORT legt den Anfangszustand aller Aufgaben als NUSE_READY oder NUSE_PURE_SUSPEND fest . Wenn dieser Parameter deaktiviert ist, haben alle Tasks den Ausgangszustand NUSE_READY .
NUSE_SYSTEM_TIME_SUPPORT - Unterstützung für die Systemzeit.
NUSE_INCLUDE_EVERYTHING - Ein Parameter, der der Konfiguration von Nucleus SE die maximale Anzahl von Funktionen hinzufügt. Dies führt zur Aktivierung aller optionalen Funktionen und jeder API-Funktion der konfigurierten Objekte. Wird verwendet, um schnell eine Nucleus SE-Konfiguration zu erstellen und eine neue Portierung des Kernel-Codes zu überprüfen.

Festlegen von nuse_config.c


Nach Angabe der Kernelkonfiguration in nuse_config.h müssen die verschiedenen im ROM gespeicherten Datenstrukturen initialisiert werden. Dies erfolgt in der Datei nuse_config.c . Die Definition von Datenstrukturen wird durch bedingte Kompilierung gesteuert, sodass alle Strukturen in einer Kopie der Standarddatei nuse_config.c enthalten sind.

Aufgabendaten
Das Array NUSE_Task_Start_Address [] muss mit dem Wert der Startadressen jeder Aufgabe initialisiert werden. Dies ist normalerweise nur eine Liste von Funktionsnamen ohne Klammern. Prototypen von Aufgabeneingabefunktionen sollten ebenfalls sichtbar sein. In der Standarddatei ist die Aufgabe mit dem Namen NUSE_Idle_Task () konfiguriert. Dies kann in die Anwendungsaufgabe geändert werden.

Wenn Sie einen Scheduler außer Run to Completion verwenden, benötigt jede Aufgabe einen eigenen Stapel. Für jeden Taskstack müssen Sie ein Array im RAM erstellen. Diese Arrays müssen vom Typ ADDR sein und die Adresse jedes Arrays muss in NUSE_Task_Stack_Base [] gespeichert sein. Es ist schwierig, die Größe des Arrays vorherzusagen, daher ist es besser, Messungen zu verwenden (siehe Abschnitt "Debuggen" weiter unten in diesem Artikel). Die Größe jedes Arrays ( dh die Anzahl der Wörter auf dem Stapel) sollte in NUSE_Task_Stack_Size [] gespeichert werden.

Wenn eine Funktion aktiviert wurde, um den Anfangsstatus der Aufgabe anzuzeigen (mithilfe des Parameters NUSE_INITIAL_TASK_STATE_SUPPORT ), muss das Array NUSE_Task_Initial_State [] mit dem Status NUSE_READY oder NUSE_PURE_SUSPEND initialisiert werden.

Partitionspooldaten
Wenn mindestens ein Partitionspool konfiguriert ist, muss für jeden im ROM ein Array (vom Typ U8 ) erstellt werden. Die Größe dieser Arrays wird wie folgt berechnet: (Anzahl der Partitionen * (Partitionsgröße + 1)). Die Adressen dieser Abschnitte ( dh ihre Namen) müssen den entsprechenden NUSE_Partition_Pool_Data_Address [] -Elementen zugewiesen werden. Für jeden Pool sollten die Anzahl der Partitionen und ihre Größe in NUSE_Partition_Pool_Partition_Number [] bzw. NUSE_Partition_Message_Size [] platziert werden.

Warteschlangendaten
Wenn mindestens eine Warteschlange konfiguriert ist, muss für jedes Array im RAM ein Array (vom Typ ADDR ) erstellt werden. Die Größe dieser Arrays entspricht der Anzahl der Elemente in jeder Warteschlange. Die Adressen dieser Arrays ( dh ihre Namen) müssen den entsprechenden NUSE_Queue_Data [] -Elementen zugewiesen werden. Die Größe jeder Warteschlange muss dem entsprechenden Element NUSE_Queue_Size [] zugewiesen werden.

Datenverbindungsdaten
Wenn mindestens ein Datenkanal konfiguriert ist, muss dafür (oder für jeden von ihnen) ein Array (vom Typ U8 ) im RAM erstellt werden. Die Größe dieser Arrays wird wie folgt berechnet: (Kanalgröße * Nachrichtengröße im Kanal). Die Adressen dieser Arrays ( dh ihre Namen) müssen den entsprechenden NUSE_Pipe_Data [] -Elementen zugewiesen werden. Für jeden Kanal müssen seine Nachrichtengröße und -größe den entsprechenden Elementen NUSE_Pipe_Size [] bzw. NUSE_Pipe_Message_Size [] zugewiesen werden.

Semaphordaten
Wenn mindestens ein Semaphor konfiguriert ist, muss das Array NUSE_Semaphore_Initial_Value [] mit den Anfangswerten des Countdowns initialisiert werden.

Anwendungs-Timer-Daten
Wenn mindestens ein Timer konfiguriert ist, muss das Array NUSE_Timer_Initial_Time [] mit den Anfangswerten der Zähler initialisiert werden. Außerdem müssen NUSE_Timer_Reschedule_Time [] Neustartwerte zugewiesen werden. Diese Timerwerte werden nach dem Ende des ersten Timerzyklus verwendet. Wenn die Neustartwerte auf 0 gesetzt sind, stoppt der Zähler nach einem Zyklus.

Wenn die Unterstützung für Kontenabschlussmechanismen konfiguriert ist (indem der Parameter NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT auf TRUE gesetzt wird ), müssen zwei weitere Arrays erstellt werden. Adressen von Vervollständigungsmechanismen (nur eine Liste von Funktionsnamen ohne Klammern) müssen in NUSE_Timer_Expiration_Routine_Address [] platziert werden . Das Array NUSE_Timer_Expiration_Routine_Parameter [] muss mit den Werten der Abschlussparameter initialisiert werden.

Welche API?


Alle Betriebssysteme in der einen oder anderen Form verfügen über eine API (Application Programming Interface). Nucleus SE ist keine Ausnahme, und die Funktionen, aus denen sich die API zusammensetzt, wurden in dieser Artikelserie ausführlich beschrieben.

Es mag offensichtlich erscheinen, dass Sie beim Schreiben einer Anwendung mit Nucleus SE die API verwenden müssen, wie in den vorherigen Artikeln beschrieben. Dies ist jedoch nicht immer der Fall.

Für die meisten Benutzer ist die Nucleus SE-API etwas Neues, möglicherweise sogar ihre erste Erfahrung mit der Betriebssystem-API. Und da es ganz einfach ist, kann es als gute Einführung in das Thema dienen. In diesem Fall ist das Verfahren klar.

Für einige Benutzer kann eine alternative API eine attraktivere Option sein. Es gibt drei offensichtliche Situationen, in denen dies möglich ist.
  1. Nucleus SE ist nur ein Teil eines Systems, das andere Betriebssysteme für andere Komponenten verwendet. Daher sind die Portabilität des Codes und vor allem die Erfahrung mit der Verwendung verschiedener Betriebssysteme sehr verlockend.
  2. Der Benutzer verfügt über umfangreiche Erfahrung mit der API eines anderen Betriebssystems. Diese Erfahrung ist auch sehr ratsam.
  3. Der Benutzer möchte Code, der für die API eines anderen Betriebssystems geschrieben wurde, wiederverwenden. Das Ändern von API-Aufrufen ist möglich, aber zeitaufwändig.


Da der vollständige Quellcode von Nucleus SE für alle verfügbar ist, hindert Sie nichts daran, jede API-Funktion so zu bearbeiten, dass sie wie die eines anderen Betriebssystems aussieht. Es wird jedoch viel Zeit in Anspruch nehmen und sehr unproduktiv sein. Ein korrekterer Ansatz wäre, einen „Wrapper“ zu schreiben. Es gibt verschiedene Möglichkeiten, dies zu tun. Am einfachsten ist es jedoch, eine Header-Datei ( #include ) zu erstellen, die eine Reihe von # Define- Makros enthält, mit denen API-Funktionen von Drittanbietern Nucleus SE-API-Funktionen zugeordnet werden.

Ein Wrapper, der die Funktionen der Nucleus RTOS-API (teilweise) auf Nucleus SE überträgt, wird mit Nucleus SE verteilt. Dies kann für Entwickler nützlich sein, die Erfahrung mit Nucleus RTOS haben oder in Zukunft auf dieses RTOS umsteigen können. Dieser Wrapper kann auch als Beispiel für die Entwicklung ähnlicher Dinge dienen.

Debuggen von Nucleus SE-Anwendungen


Das Schreiben einer eingebetteten Anwendung mit einem Multitasking-Kernel ist eine komplexe Aufgabe. Es kann eine entmutigende Aufgabe sein, sicherzustellen, dass der Code funktioniert und Fehler zu erkennen. Trotz der Tatsache, dass dies nur Code ist, der auf einem Prozessor ausgeführt wird, macht es die gleichzeitige Ausführung mehrerer Aufgaben ziemlich schwierig, sich auf einen bestimmten Ausführungsthread zu konzentrieren. Dies ist noch komplizierter, wenn mehrere Aufgaben einen gemeinsamen Code verwenden. Am schlimmsten ist es, wenn zwei Aufgaben genau den gleichen Code haben (aber mit unterschiedlichen Daten arbeiten). Erschwerend kommt auch das Auflösen von Datenstrukturen hinzu, mit denen Kernelobjekte implementiert werden, um aussagekräftige Informationen zu erhalten.

Zum Debuggen von Anwendungen, die mit Nucleus SE erstellt wurden, sind keine zusätzlichen Bibliotheken oder anderen Dienste erforderlich. Der gesamte Kernel-Code kann vom Debugger gelesen werden. Daher stehen alle symbolischen Informationen zum Studium zur Verfügung. Bei der Arbeit mit Nucleus SE-Anwendungen kann jedes moderne Debugging-Tool verwendet werden.

Verwenden eines Debuggers


Debugging-Tools, die speziell für eingebettete Systeme entwickelt wurden, sind in den 30 Jahren ihres Bestehens sehr leistungsfähig geworden. Das Hauptmerkmal einer eingebetteten Anwendung im Vergleich zu einem Desktop-Programm ist, dass alle eingebetteten Systeme unterschiedlich sind (und alle PCs einander sehr ähnlich sind). Ein guter eingebetteter Debugger sollte flexibel sein und über genügend Einstellungen verfügen, um den verschiedenen eingebetteten Systemen und Benutzeranforderungen zu entsprechen. Die Anpassbarkeit des Debuggers wird in verschiedenen Formen ausgedrückt, normalerweise besteht jedoch die Möglichkeit, Skripte zu erstellen. Mit dieser Funktion kann der Debugger gut mit einer Anwendung auf Kernelebene zusammenarbeiten. Im Folgenden werde ich einige Fälle der Verwendung des Debuggers erläutern.

Es ist erwähnenswert, dass ein Debugger normalerweise eine Familie von Werkzeugen ist, nicht nur ein Programm. Der Debugger kann über verschiedene Betriebsmodi verfügen, über die er bei der Entwicklung von Code auf einem virtuellen System oder auf realer Hardware hilfreich ist.

Aufgabensensitive Haltepunkte


Wenn das Programm einen Code hat, der mehreren Aufgaben gemeinsam ist, ist die Verwendung herkömmlicher Haltepunkte während des Debuggens kompliziert. Höchstwahrscheinlich muss der Code nur gestoppt werden, wenn ein Haltepunkt im Kontext einer bestimmten Aufgabe erreicht wird, die Sie gerade debuggen. Dazu benötigen Sie einen Haltepunkt, der die Aufgabe berücksichtigt.

Glücklicherweise machen die Möglichkeit, Skripte auf modernen Debuggern zu erstellen, und die Verfügbarkeit von Nucleus SE-Zeichendaten die Implementierung aufgabenspezifischer Haltepunkte zu einer ziemlich einfachen Sache. Sie müssen lediglich ein einfaches Skript schreiben, das einem Haltepunkt zugeordnet wird, den Sie zur Unterscheidung zwischen Aufgaben einlernen möchten. Dieses Skript übernimmt den Parameter: index (ID) der Aufgabe, an der Sie interessiert sind. Das Skript vergleicht diesen Wert einfach mit dem Index der aktuellen Aufgabe ( NUSE_Task_Active ). Wenn die Werte übereinstimmen, wird das Programm angehalten. Wenn sie unterschiedlich sind, wird die Ausführung fortgesetzt. Es ist anzumerken, dass die Ausführung dieses Skripts die Ausführung der Anwendung in Echtzeit beeinflusst ( Anmerkung des Übersetzers: Dies bedeutet, dass die Ausführung des Programms im Vergleich zum normalen Betrieb langsamer wird ). Befindet sich das Skript jedoch nicht in einer Schleife, die sehr oft ausgeführt wird, ist dieser Effekt minimal.

Kernel-Objektinformationen


Die offensichtliche Notwendigkeit für das Debuggen der Nucleus SE-Anwendung besteht in der Möglichkeit, Informationen über die Kernelobjekte abzurufen: Welche Eigenschaften haben sie und welchen aktuellen Status haben sie? Auf diese Weise erhalten Sie Antworten auf Fragen wie: "Wie groß ist diese Warteschlange und wie viele Nachrichten befinden sich jetzt darin?"

Dies kann verwendet werden, indem Sie Ihrer Anwendung zusätzlichen Debugging-Code hinzufügen, der die "informativen" API-Aufrufe (z. B. NUSE_Queue_Information ) verwendet. Dies bedeutet natürlich, dass Ihre Anwendung jetzt zusätzlichen Code enthält, der nach der Implementierung der Anwendung nicht mehr erforderlich ist. Die Verwendung von #define zum Ein- und Ausschalten dieses Codes mithilfe der bedingten Kompilierung wäre eine logische Entscheidung.

Einige Debugger können einen gezielten Funktionsaufruf ausführen, dh direkt eine API-Funktion aufrufen, um Informationen abzurufen.Dadurch ist kein zusätzlicher Code erforderlich, diese API-Funktion muss jedoch konfiguriert sein, damit der Debugger sie verwenden kann.

Ein alternativer, flexiblerer, aber weniger alternder Ansatz ist der direkte Zugriff auf die Datenstrukturen von Kernelobjekten. Am wahrscheinlichsten ist es, dies mit Debugger-Skripten zu tun. In unserem Beispiel kann die Warteschlangengröße von NUSE_Queue_Size [] und die aktuelle Verwendung von NUSE_Queue_Data [] abgerufen werden . Außerdem können Nachrichten in der Warteschlange unter Verwendung der Adresse des Warteschlangendatenbereichs (von NUSE_Queue_Data [] ) angezeigt werden .

Rückgabewerte für API-Aufrufe


Viele API-Funktionen geben einen Statuswert zurück, der angibt, wie erfolgreich der Aufruf abgeschlossen wurde. Es wäre nützlich, diese Werte zu verfolgen und Fälle zu markieren, in denen sie nicht gleich NUSE_SUCCESS sind ( dh sie haben den Wert Null). Da dieses Tracking nur zum Debuggen dient, ist die bedingte Kompilierung durchaus angemessen. Die Definition einer globalen Variablen (z. B. NUSE_API_Call_Status ) kann bedingt kompiliert werden (unter der Kontrolle des Direktiven-Symbols #define). Dann kann ein Teil der Definition von API-Aufrufen, nämlich NUSE_API_Call_Status = , auch bedingt kompiliert werden. Zum Debuggen beispielsweise ein Aufruf, der normalerweise folgendermaßen aussieht:

NUSE_Mailbox_Send (mbox, msg, NUSE_SUSPSEND);

wird die folgende Form annehmen:

NUSE_API_Call_Status = NUSE_Mailbox_Send (mbox, msg, NUSE_SUSPEND);

Wenn die Task-Sperre aktiviert ist, können viele API-Funktionsaufrufe nur Informationen über einen erfolgreichen Anrufabschluss oder das Zurücksetzen des Objekts zurückgeben. Wenn jedoch die API-Parameterprüfung aktiviert ist, können API-Aufrufe viele andere Werte zurückgeben.

Festlegen der Größe des Taskstapels und des Stapelüberlaufs


Das Thema Stapelüberlaufschutz wurde in einem früheren Artikel (Nr. 31) erörtert. Beim Debuggen gibt es mehrere andere Möglichkeiten.

Der Speicherbereich des Stapels kann mit einem charakteristischen Wert gefüllt werden: etwas anderes als alle Einsen oder alle Nullen. Danach kann der Debugger verwendet werden, um die Speicherbereiche zu überwachen und wie stark die Werte geändert werden, wodurch wir den Grad der Fülle des Stapels verstehen können. Wenn alle Speicherbereiche geändert wurden, bedeutet dies nicht, dass der Stapel voll war, aber es kann bedeuten, dass seine Größe kaum ausreicht, was gefährlich ist. Es sollte erhöht und die Tests fortgesetzt werden.

Wie in Artikel 31 beschriebenBei der Implementierung der Diagnose können sich zusätzliche Bereiche, „Schutzwörter“, an jeder Kante des Stapelspeicherbereichs befinden. Der Debugger kann verwendet werden, um den Zugriff auf diese Wörter zu verfolgen, da jeder Versuch, in sie zu schreiben, einen Überlauf oder eine Erschöpfung des Stapels bedeutet.

Checkliste für die Nucleus SE-Konfiguration


Da Nucleus SE als hochflexibles und anpassbares System zur Erfüllung der Anwendungsanforderungen konzipiert wurde, sind zahlreiche anpassbare Parameter erforderlich. Aus diesem Grund ist dieser gesamte Artikel der Konfiguration von Nucleus SE gewidmet. Um sicherzustellen, dass wir nichts verpassen, finden Sie im Folgenden eine Checkliste aller wichtigen Schritte, die Sie zum Erstellen der eingebetteten Nucleus SE-Anwendung ausführen müssen.
  1. Nucleus SE. , Nucleus SE , Nucleus SE .
  2. CPU/. .
  3. . , , .
  4. . . . , . 16 .
  5. . - main() ?
  6. . 4 , .
  7. , .
  8. .
  9. . , .
  10. . , .
  11. . , . . — 16 .
  12. . , .
  13. . , (, ).
  14. API. API, .


Der nächste Artikel (der letzte in dieser Reihe) fasst die gesamte Geschichte mit Nucleus SE zusammen und enthält Informationen, die Sie beim Erstellen und Verwenden von Nucleus SE-Implementierungen unterstützen.

Ü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/de467763/


All Articles