Hallo, ich habe eine Frage, die mich schon seit längerem beschäftigt, ich aber keine professionelle Hilfe finden konnte. Ich arbeite mit einem STM32F4 und habe dort ein I2C Sensor angeschlossen. Manchmal habe ich das Problem dass ich z.B. in dem Bereich stecken beleibe wo auf das erfolgreiche Senden der I2C-Adresse gewartet wird. Hinzu kommt, bevor die while(1) in meiner Main ausgeführt wird und den Sensor durchgehend ließt werden viele Parameter vom Sensor gelesen (vor while loop - im main Anfang), die nachfolgend für Berechnungen benötigt werden. Wie kann ich wenn ein Fehler auftritt hier von vorne anfangen? Ist es sinnvoll ein Reset auszuführen wenn die Kommunikation fehlschlägt (z.B. eine for() in der while mit 35 durchgängen und dann einen System Reset? Watchdog will ich gerade noch nicht verwenden, da ich noch bei der Entwicklung des Codes bin. Ich würde mich sehr über eure Lösungen freuen, Danke
Retry Zähler und so programmieren, dass man nicht in einer while-Schleife ohne Abbruchbedingung hängen bleibt? Sich überlegen, wie man in Fehlersituationen weiter machen will?
Der wichtigste Punkt ist, das "Steckenbleiben" selbst zu verhindern. Grenze die Frage bitte in Bezug auf Ursache und genaue Natur des "Steckenbleibens" ein. Wenn das "Steckenbleiben" aus irgendeinem Grund nicht zu verhindern ist, sollte klar sein, warum das so ist. --- Aber nehmen wir mal an, es gibt einen Grund für das Steckenbleiben, der sich nicht korrigieren oder umgehen lässt, aber eine Wiederholung des Versuchs wäre dennoch erfolgversprechend. In dem Wort Wiederholung liegt genau die Lösung: Nimm eine while-Schleife oder irgendeine äquivalente Formulierung einer Wiederholung. Warum Reset des Controllers? Das wirft noch ganz andere und kompliziertere Problem auf. Das praktische Problem bei der Fehlerbehandlung besteht meist darin, dass komplexe Bedingungen für die Wiederholung formuliert werden müssen. Auch darin, dass Teilbedingungen kausal erst unter anderen vorher ermittelten Bedingungen auftreten. --- Für eine Beschreibung und Lösung der Fragestellung wäre ein konkretes Problem (ein Code, oder wenigstens ein Pseudocode) nützlich. Aber wie zuerst gesagt: Oberstes Regel ist: Fehler beseitigen; nicht umgehen!
Neben direkt in diverse Warteschleifen eingebaute Zeitbedingungen gibt es auch die Möglichkeit, ereignisgesteuert zu arbeiten. Also die I2C Aktionen nicht in der Mainloop abzuwickeln, sondern in Interrupts. Dann klemmt die Mainloop nicht. Eine Zeitbedingung benötigt man natürlich immer noch, aber dann reicht u.U. etwas wie eine "Sensorwert zu alt" Information in der Mainloop, verknüpft mit einem Statuswert, der anzeigt, an welcher Stelle im Zustandsdiagramm das I2C grad steckt - da reicht evtl ein I2C Statusregister. Probleme kann man dann ggf durch Reset vom I2C wieder beheben, ohne den ganzen Controller resetten zu müssen.
:
Bearbeitet durch User
Zum Debugging: Da Probleme auch in der Hardware liegen können, kann es hilfreich sein, ein DSO zu bemühen. Einerseits um die Signalqualität generell bewerten zu können. Andererseits kann man einen hinreichend schnell erkannten Timeout an einen Testausgang legen, der als DSO Trigger dient, um ein schlecht reproduzierbares Ereignis einzufangen.
Moin, der alte i2c Klassiker taucht doch immer wieder auf.. Die mE sauberste Variante, die aber bisschen Rahmenwerk erfordert: - Queue schreiben, in die aus der mainloop/user space die Transaktionen reintrudeln (A: Ich möchte was schreiben, B: was gelesen haben) - In Interrupt-Handler oder ähnlich gearteter Co-Routine (richtiger Thread oder irgendwo in der mainloop-Statemachine) die Queue abarbeiten. Fürs Lesen brauchts natürlich eine zweite Queue. So kriegt man die maximale Performance und Sicherheit (sofern man die Queue mit atomar zählenden Pointern designt). Von Watchdog-Gemurkse kann ich nur abraten. Das schreit nach einer Menge Probleme, manche "Spezialisten" spicken den Code an zig Stellen mit Wakeup-Anweisungen und brocken sich eine Menge schwer debugbarer 'race-conditions' ein. Noch ein anderer Klassiker: Wenn es um Anwendungen geht, wo fremde User nicht drin rumzulesen haben oder das System nicht in einen non-armierten Zustand bringen sollen können, sind solche while(1) {} -> Watchdog-Auslöser das Standard-Einbruchstörchen. Also: verboten :-)
Hallo, Danke erstmal für die Zahlreichen Antworten. Ich verwende ein STM32F429I-DISC1 Board. Warum ich in den I2C Bedingungen hängen bleibe weiß ich nicht! Ich suche schon über ein halbes Jahr... Aus meiner Sicht wahrscheinlich, der I2C-Slave ist ein Prototyp und ich denke wenn die I2C Kommunikation abgebrochen wird ist der Sensor beleidigt (400kHz SCL Signal). - Läuft der Code einmal, ist dieser stabil! => Wenn ich in so einer I2C-Blockade hänge sind beide Busleitungen (SDA und SCL) high. - Meistens komme ich aus dieser I2C-Blockade raus wenn ich das STM Disco Board resete (schwarzer Taster). Manchmal aber auch nicht, dann hilft neu flashen/ sensor verbindung ganz lösen/ logik analyser anschließen (ja es scheint wenn ein logik analyser angeschlossen ist, ist die Kommunikation stabiler) oder andere hilflose versuche. Mein Code bleibt meist hier hängen:
1 | void I2C_address(unsigned char address) |
2 | {
|
3 | char addess_flag; |
4 | I2C1->DR = address; |
5 | while (!(I2C1->SR1 & I2C_SR1_ADDR)); |
6 | address_flag = (I2C1->SR2); |
7 | address_flag = (I2C1->SR1); |
8 | }
|
@strubi: könntest du zu der "sauberste Variante" ein Beispiel oder genauere Beschreibungen posten? Ich bin mir nicht sicher ob ich das 100% verstehe.../ würde das gerne umsetzen.
Wenn du auf Kommunikationsstörungen sinnvoll reagieren kannst (z.B. eine Fehlermeldung anzeigen, den Bus zurücksetzen und wiederholen, auf Ersatz-System umschalten), dann mache das. Der Watchdog ist nur die letzte Option, wenn einem nicht besseres einfällt. Aber dann bedenke die Folgen. Nicht dass durch den unerwarteten Reset jemand zu Schaden kommt.
uks2 schrieb:
1 | void I2C_address(unsigned char address) |
2 | {
|
3 | char addess_flag; |
4 | I2C1->DR = address; |
5 | while (!(I2C1->SR1 & I2C_SR1_ADDR)); |
6 | address_flag = (I2C1->SR2); |
7 | address_flag = (I2C1->SR1); |
8 | }
|
Wenn du in der While-Schleife noch eine Variable herunterzählst, und diese in auch der Abfrage auf 0 testest, hast du ein Timeout. Den nbachfolgenden Code brauchst du dann nur ausführen, wenn Timeout > 0 ist.
1 | void I2C_address(unsigned char address) |
2 | {
|
3 | char addess_flag; |
4 | I2C1->DR = address; |
5 | uint32_t TimeOut = 50; |
6 | while(!(I2C1->SR1 & I2C_SR1_ADDR) && (Timeout > 0)){ |
7 | TimeOut--; |
8 | }
|
9 | address_flag = (I2C1->SR2); |
10 | address_flag = (I2C1->SR1); |
11 | }
|
> Mein Code bleibt meist hier hängen: > while (!(I2C1->SR1 & I2C_SR1_ADDR)); Anstatt eine leere Schleife zu bauen die schlimmstenfalls endlos hängt könntest du die verstriche Zeit kontrollieren und nach z.B. einer Sekunde abbrechen.
1 | unsigned long warteSeit=millis(); |
2 | while (!(I2C1->SR1 & I2C_SR1_ADDR)) |
3 | {
|
4 | if (millis()-warteSeit > 1000) |
5 | {
|
6 | return -1; // Fehler melden |
7 | }
|
8 | }
|
Die wichtige Frage wäre dann: Was kann man nach dem Abbruch sinnvolles tun?
uks2 schrieb: > Mein Code bleibt meist hier hängen:... Wenn du es nicht mit dem eingebauten I2C-Core hinbekommst, dann mach das Ganze doch schlichtweg in Software. Also: - Stardcondition - Adresse senden - wenn kein ACK --> Ende mit Fehlermeldung oder so - Daten senden, wenn auch hier kein ACK, dann ebenfalls Ende mit Schrecken - oder Daten empfangen, selber entscheiden, ab wann man NAK gibt - Stopconditon fertig. Ohne Warten auf irgendwas, einfach nur geradeaus. Und wenn da ein Slave nicht mitspielt, weiß man aus der Fehlermeldung auch, wer und wann. W.S.
uks2 schrieb: > Manchmal aber auch nicht, dann hilft > neu flashen/ Das deutet aber nicht auf eine Störung der Verbindung hin. Diese wird sich durch Reset oder zumindest spannungsfrei Schalten beheben lassen.
> > @strubi: könntest du zu der "sauberste Variante" ein Beispiel oder > genauere Beschreibungen posten? Ich bin mir nicht sicher ob ich das 100% > verstehe.../ würde das gerne umsetzen. Ich versuch's mal :-) Die Queue ist a priori ein Software-FIFO, wo du eine Transaktion (z.B. WRITE <addr> <sequence>) reinsteckst, und ein anderer Prozess (oder später aufgerufene Routine oder IRQ-Handler) holt es dort wieder raus und handelt mit der Hardware die Zustände aus ohne auf irgend etwas zu warten. Kann man jetzt schreien: Viel zu kompliziert. Aber angenommen, du darfst nirgendwo hängen bleiben, oder zu lange warten, gerade wenn z.B. ein Watchdog auslösen könnte. Oder die Anwendung erfordert, dass du in der Zwischenzeit die CPU für etwas anderes als nur Warten benutzt. Dann musst du garantieren, dass die Hauptschleife in sinnvoller Zeit in allen möglichen Zuständen abgearbeitet ist. Das mit gut dimensionierten Timeouts hinzukriegen ist so ein kniffliges Ding, insbesondere den 'worst case' im Rahmen zu halten. Da ich jetzt mal annehme, dass du "bare metal" (ohne Thread-fähiges Multitasking-Framework) arbeitest, ist deine Hauptschleife dein "Scheduler", der die Arbeiten an die Einzelroutinen verteilen muss. Das läuft bei der Anforderung auf asynchrone Transaktionen raus, also: du schickst eine Anfrage raus, und fragst die Rückantwort im nächsten Durchlauf ab. Typischerweise setzt du da in der Hauptschleife eine Zustandsmaschine ein, die nicht hängenbleiben kann. Das ganze ist einfach insofern unschön, dass du nicht unmittelbar nach dem Aufruf eines Kommandos gleich die Antwort (oder Fehlercode) bekommst. Im Ergebnis hast du aber damit sehr robusten Code, der (abgesehen von IRQ-Ereignissen) vorhersehbar abläuft und recht gut verifizierbar wird. Der andere Ansatz ist ein multitaskingfähiges Kernel, also Fähigkeit zu Tasks, die je nach Ereignis per IRQ zeitnah angesprungen werden. Da musst du aber allenfalls in die Tiefe gehen (Assembler, CPU-Architektur), falls du das selbermachst. Dann gibt's noch die "fancy" protothread-Ansätze, die aber nur Zustandsmaschine in 'schön geschrieben' abstrahieren und unter Umständen den konkreten Ablauf verundeutlichen, ergo sich da auch immer wieder ins Knie geschossen wird, weil man da eben nicht darf, was man bei echten Thread-Kernels darf. Ich würde mal mit der Implementierung einer Queue anfangen und die Zustandsmaschine implementieren. Dann kannst du die Performance/Timing später noch mit IRQ-Unterstützung optimieren, der DMA-Teil des STM32 kann da ja einiges.
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.