Hier gibt es eine Vielzahl von Antworten, die sich hauptsächlich auf verschiedene Arten mit dem Problem befassen.
Ich schreibe seit über 25 Jahren eingebettete Low-Level-Software und -Firmware in einer Vielzahl von Sprachen - hauptsächlich in C (aber mit Umleitungen zu Ada, Occam2, PL / M und einer Vielzahl von Assemblern auf dem Weg).
Nach langen Überlegungen und Versuchen habe ich mich für eine Methode entschieden, die ziemlich schnell Ergebnisse liefert und es ziemlich einfach macht, Testverpackungen und -geschirre zu erstellen (wo sie WERT HINZUFÜGEN!).
Die Methode sieht ungefähr so aus:
Schreiben Sie einen Treiber oder eine Hardware-Abstraktionscode-Einheit für jedes wichtige Peripheriegerät, das Sie verwenden möchten. Schreiben Sie auch eine, um den Prozessor zu initialisieren und alles einzurichten (dies macht die freundliche Umgebung). In der Regel gibt es auf kleinen Embedded-Prozessoren - Ihr AVR ist ein Beispiel - 10 bis 20 solcher Einheiten, die alle klein sind. Dies können Einheiten für die Initialisierung, A / D-Konvertierung in nicht skalierte Speicherpuffer, bitweise Ausgabe, Tastereingang (keine Entprellung, nur abgetastet), Pulsweitenmodulationstreiber, UART / einfache serielle Treiber, die Interrupts verwenden, und kleine E / A-Puffer sein. Möglicherweise gibt es einige weitere - z. B. I2C- oder SPI-Treiber für EEPROM, EPROM oder andere I2C / SPI-Geräte.
Für jede der Hardware-Abstraktions- (HAL) / Treibereinheiten schreibe ich dann ein Testprogramm. Dies setzt eine serielle Schnittstelle (UART) und Prozessorinitialisierung voraus. Das erste Testprogramm verwendet also nur diese beiden Einheiten und führt lediglich einige grundlegende Ein- und Ausgaben durch. Auf diese Weise kann ich testen, ob ich den Prozessor starten kann und ob die grundlegende Debug-Unterstützung für serielle E / A funktioniert. Sobald dies funktioniert (und nur dann), entwickle ich die anderen HAL-Testprogramme und baue diese auf den bekanntermaßen guten UART- und INIT-Einheiten auf. Vielleicht habe ich Testprogramme zum Lesen der bitweisen Eingaben und zum Anzeigen dieser in einer netten Form (hex, dezimal, was auch immer) auf meinem seriellen Debug-Terminal. Ich kann mich dann größeren und komplexeren Dingen wie EEPROM- oder EPROM-Testprogrammen zuwenden. Die meisten dieser Menüs werden von mir gesteuert, damit ich einen Test zum Ausführen, Ausführen und Anzeigen des Ergebnisses auswählen kann. Ich kann es nicht SCHREIBEN, aber normalerweise nicht
Sobald ich alle meine HAL laufen habe, finde ich einen Weg, um einen regelmäßigen Timer-Tick zu bekommen. Dies liegt normalerweise zwischen 4 und 20 ms. Dies muss regelmäßig in einem Interrupt generiert werden. Der Rollover / Overflow von Zählern erfolgt normalerweise so. Der Interrupt-Handler ERHÖHT dann eine "Semaphor" -Byte-Größe. An dieser Stelle können Sie bei Bedarf auch mit der Energieverwaltung experimentieren. Die Idee des Semaphors ist, dass, wenn sein Wert> 0 ist, Sie die "Hauptschleife" ausführen müssen.
Das EXECUTIVE führt die Hauptschleife aus. Es wartet so ziemlich nur darauf, dass dieses Semaphor ungleich 0 wird (das Ich abstrahiere dieses Detail weg). An diesem Punkt können Sie mit Zählern spielen, um diese Ticks zu zählen (da Sie die Tick-Rate kennen) und so Markierungen setzen, die anzeigen, ob das aktuelle Executive-Tick für ein Intervall von 1 Sekunde, 1 Minute und andere von Ihnen gewohnte Intervalle gilt Vielleicht möchten Sie verwenden. Sobald die Führungskraft weiß, dass das Semaphor> 0 ist, führt sie einen einzelnen Durchlauf durch jede "Anwendungs" -Prozess "Aktualisierungs" -Funktion aus.
Die Bewerbungsprozesse sitzen effektiv nebeneinander und werden regelmäßig von einem "Update" -Tick ausgeführt. Dies ist nur eine Funktion, die von der Exekutive aufgerufen wird. Dies ist praktisch ein schlechtes Multitasking-System mit einem sehr einfachen RTOS-System für den Eigenbau, bei dem alle Anwendungen eingegeben, ein wenig bearbeitet und beendet werden müssen. Anwendungen müssen ihre eigenen Statusvariablen beibehalten und können keine Berechnungen mit langer Laufzeit durchführen, da es kein vorbeugendes Betriebssystem gibt, das Fairness erzwingt. Offensichtlich sollte die Laufzeit der Anwendungen (kumuliert) kleiner sein als die Haupt-Tick-Periode.
Der oben beschriebene Ansatz lässt sich leicht erweitern, sodass beispielsweise Kommunikationsstacks hinzugefügt werden können, die asynchron ausgeführt werden und Kommunikationsnachrichten an die Anwendungen übermittelt werden können. Dazu fügen Sie jeweils eine neue Funktion hinzu, nämlich den "rx_message_handler" heraus welcher Antrag zu versenden ist).
Dieser Ansatz funktioniert für so ziemlich jedes Kommunikationssystem, das Sie benennen möchten - er kann (und hat es getan) für viele proprietäre Systeme, offene Kommunikationssysteme und sogar für TCP / IP-Stacks.
Es hat auch den Vorteil, dass es modular mit genau definierten Schnittstellen aufgebaut ist. Sie können jederzeit Teile ein- und ausziehen und durch andere Teile ersetzen. An jedem Punkt auf dem Weg können Sie Testgeschirre oder -handler hinzufügen, die auf den bekannten guten Teilen der unteren Schicht aufbauen (das unten stehende Zeug). Ich habe festgestellt, dass ungefähr 30% bis 50% eines Designs davon profitieren können, speziell geschriebene Komponententests hinzuzufügen, die normalerweise relativ einfach hinzugefügt werden können.
Ich bin noch einen Schritt weiter gegangen (eine Idee, die ich von jemandem geklaut habe, der dies getan hat) und habe die HAL-Ebene durch eine Entsprechung für den PC ersetzt. So können Sie beispielsweise C / C ++ und Winforms oder ähnliches auf einem PC verwenden und durch sorgfältiges Schreiben des Codes jede Schnittstelle emulieren (z. B. EEPROM = eine in den PC-Speicher eingelesene Festplattendatei) und anschließend die gesamte eingebettete Anwendung auf einem PC ausführen. Die Verwendung einer benutzerfreundlichen Debug-Umgebung kann viel Zeit und Mühe sparen. Nur wirklich große Projekte können diesen Aufwand in der Regel rechtfertigen.
Die obige Beschreibung ist etwas, das sich nicht nur auf eingebettete Plattformen bezieht. Ich bin auf zahlreiche kommerzielle Organisationen gestoßen, die ähnliche Aufgaben ausführen. Die Art und Weise der Umsetzung ist normalerweise sehr unterschiedlich, aber die Prinzipien sind häufig sehr ähnlich.
Ich hoffe, das obige gibt ein bisschen Geschmack ... Dieser Ansatz funktioniert für kleine eingebettete Systeme, die in wenigen kB mit aggressivem Batteriemanagement laufen, bis hin zu Monstern mit 100K oder mehr Quellleitungen, die permanent mit Strom versorgt werden. Wenn Sie "eingebettet" auf einem großen Betriebssystem wie Windows CE oder so weiter ausführen, ist das alles völlig unerheblich. Aber das ist sowieso keine ECHTE Embedded-Programmierung.