I2C Befehl innerhalb einer ISR= Controller hängt sich auf

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • Guten Morgen,
      also zunächst einmal denke ich, dass das mit dem Anwalt nicht überbewertet werden solte, das war einfach eine Offtopic- Zwischenbemerkung, wie sie in einem doch sehr umfangreichen Thread vorkommt. Das macht hier ja keiner hauptberuflich und tschoeatsch hatte vielleicht lediglich eine kleine "Verbaleskalation" befürchtet. So hab ich das jedenfalls gelesen.

      Zum Thema: Deine Frage traue ich mir zu, zu beantworten: Ja, in der ISR darf ein I²C- Befehl stehen. Auch wenn I²C (nach meiner Meinung) ein einfach zu beherrschendes Bussystem ist, muss man doch darauf achten, das Ordnung auf dem Bus herrscht. Man muß also eine I²C- Aktion sicher zu Ende bringen, ehe man die nächste beginnt. Das hatte Zitronenfalter ja schon so beschrieben.

      Ich werte in meinen Schaltungen oft die Eingänge des PCF 8574 über die Interruptleitung aus. Die Befehle stehen dazu natürlich in der ISR. Allerdings beschränke ich mich in der Interruptroutine nur auf das wesentliche. Bus starten, einlesen und Bus sofort wieder verlassen. Die Auswertung der eingelesenen Werte erfolgt im Hauptprogramm. Mit dieser Vorgehensweise hatte ich bislang keine Probleme. Es wird nicht umsonst geraten, die Interruptroutine so kurz wie möglich zu halten. Das Setzen von Flagvariablen ist meiner Meinung nach dazu die beste Möglichkeit.

      Je umfangreicher das Program,m wird, umso mehr kommt es auf progammtechnisches Fingerspitzengefühl an und man muss genau darauf achten, dass zeitliche Abläufe auch passes. Schließlich sind die ATMEL- Controller nicht die größten. Ab einem gewissen Stadium kann man nicht mehr einfach drauflos schreiben, sondern muss die Programmstrucktur als Programmierer selber im Auge behalten. Wenn ich jetzt sehe, wie umfangreich Deine ISR in #9 ist, kommen mir dabei schon Bedenken. Das müßte m.E. überwiegend im Hauptprogramm stehen und nur relevante Dinge in der ISR.

      Gruß Christian
      Wenn das die Lösung ist, möchte ich mein Problem wieder haben.
    • So, hier jetzt meine Erkenntnisse.
      Ich arbeite aktuell an einem Projekt in welchem in einer Hauptschleife an einem 20x4-LCD-Display (welches mittels I2C-Adapter ausgerüstet ist) alle 100mS versucht wird dieses Display zu beschreiben (im wesentlichen werden so Statusänderungen im 100ms-Takt angezeigt und bei einer laufenden Uhr die Sekundenanzeige bei Bedarf verändert). Weiters wird eine I2C-RTC laufend abgefragt.

      Dies funktionierte solange klaglos, bis ich auf die Idee kam, ein einziges Byte an einem bestimmten Anlassfall einmalig in einem Timerinterrupt (zuerst war der auf 10ms, dann auf 20ms eingestellt) zu ändern (ursprünglich hatte ich dieses Flag im in einer RAM-Variable gespeichert welche in der Hauptschleife überwacht wurde und bei einer Veränderung eben im ERAM gespeichert wurde, was auch nicht schlimm wäre selbst bei einmal täglichen Gebrauch ;) , ich dachte mir aber in der RTC sind ja RAM-Zellen, die können das noch besser :D ).
      Wohlgemerkt wird dieser Schreibversuch bei einem einzigen Aufruf des IRQs verändert, beim nächsten Aufruf stimmt die Bedingung dafür für sehr lange Zeit nicht mehr.

      Und was soll ich sagen, nachdem der IRQ dieses Byte geschrieben hat, funktionierte die Hauptschleife nicht mehr.
      Ich hatte in der Folge sogar einen simplen Print-Befehl eingebaut, welcher nichts mehr an der seriellen Schnittstelle ausgab, was ein deutlicher Hinweis war, dass die Hauptschleife nicht mehr durchläuft.
      Es wurde weder das LCD weiter beschrieben, noch konnte eine 4x4-Tastatur welche an einem ADC-Port angeschlossen ist ausgewertet werden (der ADC wird wiederum im IRQ ausgewertet, in der Hauptschleife wiederum wird nur mehr eine entsprechende Variable verarbeitet).
      Man könnte meinen, dass sich der µC aufhing.
      Nur ganz aufgehangen hat sich der wohl nicht, weil alles was im Timer-IRQ abgehandelt wurde (da werden unter anderem mehrere LEDs angesteuert) hat weiter wie erwartet funktioniert.

      Dies lässt für mich den Schluss zu, dass sich eigentlich nur der I2C-Bus "aufgehangen" hat und daher die Hauptschleifen nicht mehr weiter funktionierte, der "höherwertige" IRQ aber klaglos weitermachte.

      Nachdem ich das schreiben des I2C-Bytes im IRQ durch ein Flag ersetzte welches dann in der Hauptschleife abgehandelt wurde, funktionierte das gesamte Programm wieder klaglos.

      Ich denke daher, dass man innerhalb eines IRQs solange am I2C arbeiten darf, solange dies der einzige Weg ist.
      Wird I2C aber bereits in der Hauptschleife verwendet, darf dieses innerhalb eines IRQ nicht verwendet werden.
      IRQs beeinflussen wird es auch nur dann, wenn die Ausführzeit für die I2C-Abhandlung (welche ja aus diversen Befehlen mit einer entsprechenden Ablaufzeit besteht) länger dauern würde als z.B. der Timer-IRQ Zeit hat. Dann wird es auch nicht klappen.

      Ich meine mich sogar zu erinnern, dass ich mal innerhalb eines IRQs das gesamte I2C-Handlich abhandelte und dafür sogar die Hauptschleife entsprechende Flags setzte bzw. Strings befüllte und der Timer-IRQ bei jedem Durchlauf I2C abhandelte und gleichzeitig einen PCF mit Tastatur abfragte. Das ist aber schon ein paar "Monate" a_38_b45e201d her und ich kann jetzt nicht sagen war das ein ATmega oder sogar noch eine C-Control.

      Als Fazit könnte man sagen I2C wird in einem Interrupt solange funktionieren, solange es dort alleine ist.
      Inwieweit BasCom Fehler behandelt vermag ich nicht zu sagen. Es gibt zwar die ERR-Variable die aber nichts nützen wird, wenn man diese nicht mehr abfragen kann.
      Auch würde sich ein Timeout statt dem aufhängen anbieten. Aber auch da ist es mir nicht bekannt, ob BasCom das überhaupt bei I2C/TWI unterstützt.
    • Mir fällt dazu gerade ein, könnte das ein Problem des I2C-Clock Stretching sein. Bascom berücksichtigt nämlich diese Funktion. Wenn während des I2C- Zugriffs auf die Peripherie die CLK-Leitung auf low gezogen wird, stoppt der Controller komplett und wartet bis die Taktleitung wieder freigegeben wird.
      Bleibt dieser Zustand aber bestehen ist der Controller nur durch Kaltstart wieder zu beleben. Sowas hatte ich selbst mal erlebt. Einfach mal die clk-Leitung testen ob die auf low-Pegel gezogen ist, wenn der Controller "stehen geblieben ist". Nur so ein Gedanke.
    • djmsc schrieb:

      Ich weiss nicht, ob du es schon gesehen hast aber es gibt eine Antwort im MCS-Forum.
      nee hatte ich noch nicht. Danke für den Hinweis!
      But here is the but : it is not a good idea to mix i2c commands from the main code and inside an ISR.
      ich verstehe das nicht so ganz. Bevor ich jetzt anfange, wild zu raten und wirre Theorien aufzustellen...
      Kannst Du mir das mal kurz erklären?
    • Also das Problem besteht darin, dass deine ISR genau dann dazschwischen funken kann wenn du etwas über I2C auf das Display schreiben willst. Bedeutet soviel, dass dein Bus gerade belegt ist. Deswegen sollte man lieber in der ISR ein Flag setzen und erst nach der Abarbeitung der I2C Befehle die in der Hauptschleife die anderen ISR relevanten Sachen in der Hauptschleife ausführen.

      BASCOM-Quellcode

      1. Dim Flag As Bit
      2. Do
      3. If Flag = 1 Then
      4. Reset Flag
      5. ...
      6. End If
      7. Loop
      8. End
      9. Isr:
      10. Set Flag
      11. Return
      Alles anzeigen
      Mark meint damit das I2C langsam arbeitet und durch einen Interrupt während der Abarbeitung gestört werden kann.
      Eine Lösung habe ich nicht, aber mir gefällt Ihr Problem.
    • Oder du machst dir dafür eine Sub und springst diese an wenn es gefordert ist. Aber soviel mehr Speicher brauchst du dann auch nicht.
      Ein Flag = Bit belegt ein Byte an Speicher, man kann aber auch mit nur einem Byte acht Flags setzen.
      In dem du

      BASCOM-Quellcode

      1. Dim Flags as Byte
      2. Do
      3. If Flags.0 = 1 Then
      4. ...
      5. Elseif Flag.1 = 1 Then
      6. ...
      7. Else
      8. ...
      9. End If
      10. Loop
      11. End
      Alles anzeigen
      das so abfragst. Das nur mal als Beispiel.
      Eine Lösung habe ich nicht, aber mir gefällt Ihr Problem.
    • so meinte ich das nicht mit "Speicherfresser".
      Ich habe z.B. ein Programm. Das besteht aus

      Hauptprogramm:
      do
      ...
      loop

      sub1:
      do
      ...
      loop

      sub2:
      do
      ...
      loop

      und noch 10 weitere subs mit do-loop Schleife

      Das Programm befindet sich nun gerade z.B. in der Sub 8.
      Jetzt wird in der ISR das besagte flag gesetzt und danach wieder zurückgesprungen in die Sub8.
      In der Sub 8 muß ich innerhalb der Do-Loop Schleife die Flag-Abfrage einbinden, so daß- sollte sich das flag ändern- auch
      SOFORT die Reaktion kommt (z.B. eine LED leuchtet) und nicht erst dann, wenn das Programm mal irgendwann wieder im Hauptprogramm 'zu Besuch' ist.
      Mit Speicherfresser meine ich, daß ich ja die Flagabfrage in JEDER Sub + Hauptprogramm- also eigentlich in jeden noch so versteckten Winkel des Programms plazieren müßte,
      denn ich könnte ja genauso gut auch gerade in der Sub 7 oder 6 oder sonstwo sein.
      Zumindestens müßte ich eine kleine Sub mit der Flagabfrage erstellen, aber immerhin 100x im Programm "gosub Flagabfrage, sub" schreiben.
      Oder geht das eleganter?
    • Das ist aber der falsche Ansatz. Das Hauptprogramm (Hauptschleife) hiesst nicht ohne Grund Hauptschleife, weil dort alles abläuft und sich das Programm am meisten dort aufhält.
      Eine Sub kann zwar Do-Loop Schleifen enthalten aber dann nur mit einer Abbruchbedingung ansonsten sind es auch nur Hauptschleifen.
      Ich weiss du sagst jetzt bestimmt wieder "aber es funktioniert ja", was aber nichts an der falschen Stucktur deines Programms ändert.
      Eine Lösung habe ich nicht, aber mir gefällt Ihr Problem.
    • @ichbinsmoin
      Vielleicht noch ein paar Sachen zur Verdeutlichung des Sachverhaltes:

      - Unterprogramme erfüllen immer nur eine genau definierte Aufgabe
      - in der main_loop befinden sich die Sprünge (gosub eventuell auch goto) auf die meisten Unterprogramme (UP) hintereinander angeordnet und diese werden in Abhängigkeit von Variablen (Flags sind auch nichts anderes) in if-Then-else oder select-case - Anweisungen ausgewählt
      - außerhalb der main_loop befinden sich die Deklarationen, Dim- Anweisungen, die ISR, die Funktionen und sub-routinen (d. sind die SUB - Endsub- Konstrukte bei denen auch Parameter übergeben werden können)

      Diese Programmstruktur sorgt für einen zügigen und vorallem übersichtlichen Programmablauf, außerdem kann man damit recht einfach in der Main_loop die Programmhülle eingeben und dann die entsprechenden UP nach und nach mit den Aufgaben füllen. Die Main_loop ist sowas wie der allgemeine Programmablauf wie er auch in einem PAP dargestellt wird.

      Gruß

      Harald
      Wem ich nicht ausdrücklich widerspreche, der darf das bis auf Widerruf als "Gefällt mir" verstehen.
    • So wie ich das Programm von ichbinsmoin verstanden hab', gibt es verschiedene Spiele. Da kann man eine sub, die ein Spiel enthält, schon als 'main für dieses Spiel' sehen. Ändert aber nix an den bislang angesprochenen, grundsätzlichen Aussagen. Die klassische main sollte die Auswahl der Spiele übernehmen, das hat sich hier leider nicht so entwickelt.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • djmsc schrieb:

      Ich weiss du sagst jetzt bestimmt wieder "aber es funktioniert ja", was aber nichts an der falschen Stucktur deines Programms ändert.
      nein nein...passt schon. Habe 2011 Bascon entdeckt und mir falsches Programmieren angewöhnt. Das fiel auch lange nicht auf, weil meine Programme sehr schmal waren und alles sofort funktionierte.
      Meine Projekte werden aber immer komplexer und nun fällt es mir auf dem Kopf. Ist halt schwierig, die alten (falschen) Gewohnheiten loszuwerden. Natürlich gefällt mir das nicht, wo ich ja nun
      gerade etwas Programmiersicherheit gewonnen habe, aber was solls. Da muss ich jetzt durch.