Grund
Nun, der Grund ist einfach: Blockieren ist einfach und auf den ersten Blick scheint es zu funktionieren. Wehe dir, wenn du in der Zwischenzeit etwas anderes machen willst.
Da ich nicht auf viele Details eingehen muss, da ich den STM32 nicht kenne, können Sie dieses Problem je nach Ihren Anforderungen auf zwei Arten lösen.
I2C_GenerateSTART (HMC5883L_I2C, ENABLE);
/ * auf Bestätigung warten * /
while (! I2C_CheckEvent (HMC5883L_I2C, I2C_EVENT_MASTER_MODE_SELECT));
In nicht blockierende konvertieren
Entweder implementieren Sie eine Zeitüberschreitung für alle Ihre while
-Schleifen. Dies bedeutet:
I2C_GenerateSTART (HMC5883L_I2C, ENABLE);
/ * auf Bestätigung warten * /
statischer vorzeichenloser langer Start = now ();
while (! I2C_CheckEvent (HMC5883L_I2C, I2C_EVENT_MASTER_MODE_SELECT) && now () - < TIMEOUT starten);
if (now () - start > = TIMEOUT) {return ERROR_TIMEOUT; }}
(Dies ist natürlich ein Pseudocode, Sie haben die Idee. Sie können Ihre Codierungseinstellungen nach Bedarf optimieren oder anpassen.)
Sie müssen die Rückkehrcodes überprüfen, wenn Sie den Stapel hinauffahren, und die richtige Stelle auswählen, an der Sie Ihre Timeout-Behandlung durchführen. Beachten Sie, dass es hilfreich ist, auch eine globale Variable i2c_timeout_occured = 1
oder was auch immer festzulegen, damit Sie weitere I2C-Aufrufe schnell abbrechen können, ohne zu viele Argumente übergeben zu müssen.
Diese Änderung ist hoffentlich ziemlich schmerzlos.
Von innen nach außen
Wenn Sie stattdessen wirklich eine andere Verarbeitung durchführen müssen, während Sie auf dieses Ereignis warten, müssen Sie die innere while-Schleife vollständig entfernen. Sie machen das so:
void main_loop () {
do_i2c_stuff (); // darf niemals blockieren
do_other_stuff ();
...
}}
// Darf niemals blockieren. Angenommen, alle I2C _... Funktionen blockieren auch nicht.
void do_i2c_stuff () {
statischer int Zustand = ...;
if (state == 0) {
I2C_GenerateSTART (HMC5883L_I2C, ENABLE);
Zustand = 1;
} else if (state == 1) {
if (I2C_CheckEvent (HMC5883L_I2C, I2C_EVENT_MASTER_MODE_SELECT))
Zustand = 2;
} else ...
}}
Es ist nicht unbedingt schlecht kompliziert, abhängig von Ihrer anderen Logik. Sie können viel mit dem richtigen Einrücken / Kommentieren / Formatieren tun, damit Sie nicht den Überblick über das verlieren, was Sie programmieren.
Dies funktioniert durch Erstellen einer Zustandsmaschine . Wenn Sie sich Ihren Originalcode ansehen, sieht er folgendermaßen aus:
nicht blockierender Code
while (! nonblocking_function_call1 ());
nicht blockierender Code
while (! nonblocking_function_call2 ());
Um dies in eine Zustandsmaschine umzuwandeln, haben Sie jeweils einen Zustand:
state 0: nicht blockierender Code
Zustand 1: nonblocking_function_call1 ()
Zustand 2: Nicht blockierender Code
Zustand 3: nonblocking_function_call2 ()
Dann rufen Sie diesen Code, wie im obigen Beispiel gezeigt, in einer Endlosschleife (Ihrer Hauptschleife) auf und führen nur den Code aus, der Ihrem aktuellen Status entspricht (verfolgt in einer statischen Variablen state
). . Der nicht blockierende Code ist trivial und unverändert. Der Blockierungscode wird durch eine Variante ersetzt, die nicht blockiert, sondern den -Status
erst aktualisiert, wenn er abgeschlossen ist.
Beachten Sie, dass die einzelnen while
-Schleifen weg sind. Sie haben sie durch die Tatsache ersetzt, dass Sie sowieso Ihre Hauptschleife der obersten Ebene haben, die Ihre Zustandsmaschine wiederholt aufruft.
Diese Lösung kann schmerzhaft sein, wenn Sie viel Legacy-Code haben, da Sie die innerste Blockierungsfunktion nicht einfach anpassen können, wie in der ersten Lösung. Es leuchtet, wenn Sie anfangen, neuen Code zu schreiben und von Anfang an diesen Weg gehen. Kombinieren Sie es mit vielen anderen Dingen, die ein µC tun könnte (z. B. Warten auf Tastendruck usw.). Wenn Sie sich die ganze Zeit daran gewöhnen, erhalten Sie kostenlos beliebige Multitasking-Fähigkeiten.
Interrupts
Ehrlich gesagt, für so etwas (d. h. nur unendliches Blockieren loswerden) würde ich mich bemühen, Interrupts zu vermeiden, es sei denn, Sie haben extreme Timing-Anforderungen.Interrupts machen es kompliziert, schnell, Sie haben vielleicht sowieso nicht genug davon, und es läuft sowieso auf ziemlich ähnlichen Code hinaus, da Sie innerhalb des Interrupts nicht viel mehr tun möchten, außer einige Flags zu setzen.