Sie haben eine Single-Core-Single-Threaded-CPU. Dies bedeutet, dass zu jedem Zeitpunkt genau eines getan wird. Wenn Ihre Anwendung mehrere Aufgaben erfordert, muss der Code so gestaltet sein, dass zwischen allen gewechselt werden kann. Dies kann so trivial oder so komplex sein, wie Sie möchten.
Normalerweise puttert die CPU um die Hauptschleife und macht alles, was sich dort befindet, und das ist alles gut und schön, bis ein Interrupt auftritt. Die Interrupt-Hardware erzwingt grundsätzlich einen Funktionsaufruf an das entsprechende ISR, obwohl es in der Hauptschleife keine Aufrufanweisung dafür gibt. Aufgrund dieser Unvorhersehbarkeit stammen die meisten Regeln für das Schreiben von ISRs.
Unabhängig davon, wie viel Zeit Sie in einem ISR verbringen, wird die Hauptschleife angehalten und auf die Rückkehr des ISR gewartet. Wenn die Hauptschleife die einzige ist, die einen aktiven Watchdog-Timer zurücksetzt (sehr gute Praxis), wird der Watchdog während dieser Zeit nicht zurückgesetzt. Wenn der Watchdog eine Zeitüberschreitung aufweist, wird ein Hard-Reset durchgeführt. Genau wie beim externen Zurücksetzen, jedoch mit verschiedenen Flags, die Sie beim Start überprüfen können. Dies ist wahrscheinlich der "Absturz", von dem Sie gehört haben.
Es ist sehr empfehlenswert, den Watchdog zu verwenden und ihn bei jeder Fahrt um die Hauptschleife nur einmal zurückzusetzen. Dies zwingt Sie dazu, Code zu schreiben, der reagiert. Wenn Sie auf etwas warten müssen, können Sie ein Ereignis einrichten (Timer abgelaufen, nächster Charakter empfangen usw.) und fortfahren. Suchen Sie regelmäßig nach diesem Ereignis oder setzen Sie einen anderen Interrupt auf dessen Abschluss und kehren Sie dann zu diesem zurück. In der Zwischenzeit fahren Sie mit dem fort, was Sie sonst noch getan haben.
Meine Hauptstruktur sieht normalerweise so aus:
#include "module1.h"
#include "module2.h"
void main (void)
{
//insgesamt
//Chip
//Konfiguration
mod1_init ();
mod2_init ();
// Interrupt-Flags löschen
// globale Interrupt-Aktivierung
während (1)
{
// Wachhund löschen
mod1_run ();
mod2_run ();
}}
}}
Und meine Module sind wie folgt:
void modX_init (void)
{
// Hardware und Variable init nur für dieses Modul
// Verwenden Sie keine Interrupts, wenn die Abfrage gut genug ist
}}
void modX_run (void)
{
if (POLLED_INTERRUPT_FLAG)
{
POLLED_INTERRUPT_FLAG = 0;
// nicht blockierender "ISR" -Code
}}
}}
void ISR modX_ISR (void)
{
// Okay, dies erfordert eine * sofortige * Antwort
// verbringe die absolute Mindestzeit hier und steige aus
}}
Die Funktionssignaturen müssen nicht void
sein, die meisten jedoch. Manchmal habe ich ein breites Timing in einem Modul, das auch von einem anderen verwendet wird, und es ist praktisch, den Rückgabewert eines modX_run ()
und die Argumente eines anderen (oder einer grundlegenden Logik) zu verwenden Stellen Sie diese Verbindung her. Zum Beispiel:
if (DMX_run ()) // enthält ein eigenes Timing und gibt zu Beginn jedes 30-Hz-Intervalls true zurück, andernfalls false
{
I2C_start (); // I2C-Frames werden mit DMX synchronisiert
}}
I2C_run (); // Einmal gestartet, läuft ein I2C-Frame frei, bis er fertig ist
Wenn Sie das Datenblatt studieren, stellen Sie möglicherweise auch fest, dass die Hardware-Peripheriegeräte massiert werden können, um das zu tun, was Sie wollen, ohne dass die CPU eingreift.
Die Erzeugung von Ausgangsimpulsen ist beispielsweise weit verbreitet. Schalten Sie es ein, stellen Sie das Peripheriegerät so ein, dass es einige Zeit später ausgeschaltet wird, und vergessen Sie es. Es befindet sich normalerweise im selben allgemeinen Bereich wie PWM.