Hallo, Sorry für den langen Text erstmal. Gleich vorab ich habe keinen Einfluss auf das Protokoll und/oder das Bustiming, das ist vorgegeben. Ich binde mich an einen bestehenden Master-Slave 485 Bus an, was auch prima klappt im Prinzip. Allerdings fülle ich im Moment beim Empfang nur einen Empfangspuffer mit den Daten vom Puffer. Sobald ich Zeit habe prüfe ich in MAIN einem riesigen Switch Case welches Kommando vorliegt und ob alle Bytes vorliegen. Ist das Kommando vollständig schicke ich das ACK auf den BUS und rufe eine Funktion in meiner "Applikation" auf, die die Daten auswertet. In der Zeit kommen natürlich neue Daten vom BUS. Die Eigentliche Buskommunikation ist in einer API versteckt. Sobald ich ein Kommando erkenne rufe ich eine Funktion in meiner Applikation auf, die alles weitere tun muss. Die API Headerdatei mit Beschreibung hab ich mal anbei, falls dies weiterhilft. Und hier kommt mein Problem. Brauche ich für die Auswertung der letzten Daten zu lange in meiner Applikation, verursache ich "Timingprobleme" auf dem BUS. Nun dachte ich, ich könnte die lange Funktion auf meinem Interrupt der RS485 aufrufen. Dann würde aber auch das parsing der Empfangenen Daten im Interrupt ablaufen... und wenn ich zu lange brauche für das vorherige Kommando rufe ich die gleiche API Funktion eventuell 2 mal zur gleichen Zeit auf :-( Beispiel: wie es sein sollte von Handling Master schickt Kommando "Mach hell". Slave schickt "ACK" und wertet das Kommando aus (Dauert jetzt zu lange) Master schickt Kommando "Mach was anderes" Slave schickt "ACK" und wertet das Kommando aus ("Mach Hell" wird noch bearbeitet) Nun, bis ich die Kommandos parse und entsprechend reagiere bin ich zu langsam für 2 direkt folgende Kommandos wie oben. In der ganzen Applikation wird nichts in Schleifen gelößt, sondern im Zyklischen Durchlauf von MAIN. Hat jemand eine gute Idee wie ich das Problem lösen könnte ? Achja, der Switch case enthält so ca 30 Case Anweisungen und enthält ca 600 Zeilen Code. Leider darf ich den nicht Posten da das Protokoll "closed ist". Daher der Gedanke mit der API.
Die uebliche Vorgehensweise. 1)das ACK aus dem interrupt senden. 2)In Interrupt eine Boolean setzten : RxReady 3)im Main main auf die Boolean Rxready testen 4)in Main nachher die Case abarbeiten und besfehl ausfuehren.
Hängt auch davon ab, was das ACK im Protokoll für eine Bedeutung hat. Wenn ACK bedeuten soll, daß das Kommando empfangen, akzeptiert und verarbeitet wurde, dann kann ich nicht in der IRQ Routine pauschal das ACK schicken.
Und wie wäre es, das ACK erst nach der Abarbeitung zu schicken? MW
ACK heisst ueblicherweise, das kommando wurde empfangen. Sonst wird es nochmals geschickt.
Wenn du wirklich jedes Kommando abarbeiten musst, und das Protokoll auch keine Möglichkeit bietet, eingehende Befehle mit "ich bin noch nicht fertig" zurückzuweisen, dann ist dein Mikro für die Aufgabe schlicht und einfach zu langsam. Worauf läuft das ganze denn? Oliver
Lösen kann man das auch mit einem RTOS. Ein höher prorisierte Task macht den Empfang der Kommandos und kann vielleicht 1-2 Kommandos in einer Warteschlange halten. Eine andere, nieder priorisierte Task macht das Auswerten. Wenn die Abarbeitung nicht mitkommt, und die Kommando Warteschlange voll ist, dann akzeptiert die Kommando-Erkennungs-Task das kommando nicht und schickt NACK zurück (das ist wohl auch der Sinn von ACK und NACK) Klaus
Hallo Jürgen, wie wäre es denn damit: Nach erkanntem Telegrammende kopierst Du die Daten in einen extra Puffer und arbeitest damit weiter. Das Kopieren (verschieben ohne zu löschen :-)) geht ja fix. Die API Empfangsroutine kann in der Zwischenzeit weitere Daten reinholen...
Das ACK bedeutet ich habe die Daten Empfangen. Bei Aufgaben die sehr lange dauern dürfen, wie Firmware laden, setzt man nach Abschluss eine Art zusätzliches ACK ab. Dann bekommt man erst das nächste Packet Das ganze läuft auf einem ATMega16 bzw ATMega32 der mit dem Internen Oszillator auf 8MHz läuft. Schneller Takten wäre technisch kein Problem würde aber zusätzliche Externe Komponenten benötigen. Da ich bisher alles auf Experimentier Platinen Aufbaue für meine Einzelstücke, versuche ich Externe Komponenten noch zu vermeiden. Die Daten in einen Eigenen Puffer zu kopieren habe ich mir auch schon überlegt, aber gerade die Kommandos die Lange zum bearbeiten brauchen sind bis zu 70 Byte lang (bis zu 64 Byte ASCII). Die gilt es zu parsen und die Entsprechende Reaktion auszulösen. Die kurzen Kommandos sind kein Problem, das ist meist nur ein Flag löschen oder setzen. Da ich für die Applikation auch noch RAM brauche, würde ich mir das Kopieren und vorhalten in einem extra RAM gerne sparen. Die 2k Ram des Mega32 sind nicht so viel. Das RTOS ist mir dann doch ne Nummer zu groß für die Applikation :-) Wenn man bedenkt, das das Original auf einem 68HC11 mit 4 MHz läuft. Da werde ich wohl doch noch Optimieren versuchen und zur Not den Takt hochdrehen. Ein Externer Quarz wäre für den UART sowieso nicht schlecht, wobei ich bisher keine Probleme mit dem internen UART habe... Ich hatte nur die Hoffnung das einer die Zündende Idee hat. Das ganze lässt sich bestimmt noch Optimieren, da muss ich eben ansetzen. Danke Jürgen
Nun, wie schon öfter diskutiert, muss ein RTOS nichts großes sein. AvrX ist sogar ausgesprochen klein, das setze ich auf einen ATMega32 mit Erfolg ein. Dafür macht es Ordung in deinem Programm und schafft definierte Reaktionszeiten. Solche Timing Probleme mit einem höheren Takt zu erschlagen rächt sich langfristig, wenn dann nochmals kompliziertere Berechnungen dazukommen. Das Kopieren des Buffers kann man sich sicher sparen, Du müßtest nur den Eingangsbuffer der IRQ groß genug machen, sodass das 2. Kommando auch Platz hat und mit einer Art Ringbuffer arbeiten. Klaus
Wie wäre es denn, mit einer Statemaschine die emfangenen Daten sofort zu parsen und nicht erst, wenn alle geladen sind?
ich würde auch nach jedem zeichen eine "kurze" auswertung machen - bei einsatz des RS485 und HC11 mit 4MHz wird wohl eine gemächliche Netzgeschwindigkeit vorliegen :-) also einfach ne Statemachine oder so implementieren. Vielleicht auch eine sortierte Tabelle mit allen möglichen Befehlen implementieren --> stelle ich mir jetzt erst mal sehr einfach vor.
Also, ich sollte das vielleicht kurz erklären, ich habe das vermutlich nicht richtig erklärt: Alle Daten die vom BUS kommen landen in einem Eingangspuffer, das erledigt bisher ein Interrupt. In "Mainline" passiert dann bisher folgendes: Als erstes prüfe ich auf das erste Busprotokollbyte, dann weis ich wieviele Zeichen folgen. Sobald diese da sind, gibt meine API dies Aufbereitet an das "Anwendungsprogramm" weiter. Es gibt einmal das Parsing des Busprotokolls. Über das Busprotokoll kommen Befehle wie "Schalte Ausgang 1 an" und führen zum Aufruf der API Funktion "void processChannel(uint8_t channel, bool val);". oder auch Text Kommandos wie "TEXT10-Hier ist der Text für Zeile 10". Diese Applikationsabhängig zu Interpretieren ! Das führt zum Aufruf der API-Funktion "void processCommand(char *data, uint8_t len);" Jetzt muss mein Anwendungsprogramm das weiter zerlegen und reagieren. Den Teil darf ich wieder posten, der ist ja Protokollfrei :-) Da es nur ein Teil ist lässt es sich auch nicht kompilieren. Wie gesagt ist der Interessante Teil processCommand(). Das ist der Teil wo ich wohl zu lange brauche. API wertet Kommando aus->processCommand() wertet Applikations Kommando aus->Löst Aktion aus. Die Kette ist zu lange. Ich denke die reine API Auswertung ist schnell genug für einen Interrupt. Nur der Aufruf von "processCommand()" habe ich nicht in der Hand...... Also könnte ich das Kommando Zwischenspeichern und in "Mainline" ausführen, wie oben beschrieben. Da ich die Ausführungszeit nicht in der Hand habe, könnte ja noch ein Kommando eintreffen, also einen Stack aufbauen. uff. Bin ich nur Paranoid ?! Ich hoffe es wurde etwas deutlicher :-)
Ohne mich jetzt durch Dein Programm durchgewühlt zu haben, denke ich gibt es 2 Möglichkeiten: - Die Interrupt Routine (API) ist schnell genug, um das Ende eines Kommandos zu erkennen. In diesem Fall sendet die IRQ das ACK Signal, kopiert das Kommando auf eine Ringbuffer, der von der MainProgramm abgearbeitet wird. Wenn im Ringbuffer kein Platz mehr ist, weil die MainRoutine nicht mitkommt, dann muß NACK zurückgeschickt werden. - Man verwendet ein RTOS und teilt das ganze in Interrupt Routine, Task zur Kommando-Erkennung und Task zur Kommando-Abarbeitung auf. Auch dabei braucht es eine Art Ringbuffer, mit dem die erkannten Kommandos an die Abarbeitungs-Task weitergereicht werden und der eventuell voll werden kann. Alles andere ist ein Pfusch !
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.