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: 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ählerDie 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-FunktionsaktivatorenJede 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 EinstellungenNucleus 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 OptionenEs 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.
AufgabendatenDas 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.
PartitionspooldatenWenn 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.
WarteschlangendatenWenn 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.
DatenverbindungsdatenWenn 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.
SemaphordatenWenn mindestens ein Semaphor konfiguriert ist, muss das Array
NUSE_Semaphore_Initial_Value [] mit den Anfangswerten des Countdowns initialisiert werden.
Anwendungs-Timer-DatenWenn 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.
- 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.
- Der Benutzer verfügt über umfangreiche Erfahrung mit der API eines anderen Betriebssystems. Diese Erfahrung ist auch sehr ratsam.
- 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.- Nucleus SE. , Nucleus SE , Nucleus SE .
- CPU/. .
- . , , .
- . . . , . 16 .
- . - main() ?
- . 4 , .
- , .
- .
- . , .
- . , .
- . , . . — 16 .
- . , .
- . , (, ).
- 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.