Guten Tag allerseitz,
ich programmiere derzeit einen PIC16F873 in MPLAB und dem xc8-compiler
und nutze seine UART-Schnittstelle um diesen selbst zu parametrisieren,
bzw. zu steuern.
Nun habe ich aber eine grundlegende Frage:
Die Verarbeitung der Bytes, welche über die UART-Schnittstelle
eintreffen geschieht derzeit bei mir in der Interrupt-Routine und zum
einfachen Debuggen schicke ich mir die empfangenen Bytes einfach zurück,
was auch im Einzelfall klappt.
Nun möchte ich allerdings dauerhaft Nachrichten bzw. mehrere Bytes
hintereinander empfangen (siehe Protokoll unten) und das bereitet mir
Probleme. Es werden Bytes verschluckt und falsch zurückgesendet. Ich
denke Schuld daran ist die Verarbeitung in der ISR.
Ich habe mir ein Protokoll definiert, welches folgendermaßen aussieht:
Byte 1 - Adresse bzw. Kommando
Byte 2 - Länge der Nutzdaten (max 32.Byte)
Byte 3-35 - Nutzdaten
Letztes Byte - einfache Checksumme der Nutzbytes
Ich möchte das erste Byte prüfen, ob es mit dem Kommando des PICs
übereinstimmt (das ist von mir selbst gewählt bsp.: 0xAA). Wenn ja, soll
der Rest gelesen und in einem Array gespeichert werden. Anschließend,
wenn alles in Ordnung ist (Anzahl des Nutzbytes ok, Checksum ok), soll
die Nachricht weiter verarbeitet werden und verschiedene Dinge tun.
Nur aber zu meiner Frage: Wo passiert denn die Verarbeitung der
empfangenen Daten bzw. die Prüfung des ersten Bytes?
Wie fange ich den Fall ab, dass der Sender meinen PIC zu schnell mit
neuen Bytes versorgt - klassisches Handshake-Verfahren?
Wie löst man so etwas generell elegant? Ich brauche keinen Beispielcode
oder ähnliches, sondern suche einen Ansatz für das Problem.
Vielen Dank und freundliche Grüße
musicmiles
Ich teil das immer auf:
Emfpangen / Senden - ISR ("automatisch"):
Die ISR stopft nur die Buffer. Ich hab immer zwei Hilfsbuffer, RX und
TX. Da steht je ein ganzes Paket drin.
Mit jedem Interrupt werden in der ISR die empfangenen Bytes in den
RX-Buffer geschreieben und aus dem TX-Buffer in die UART geschrieben.
Ist ein Paket vollständig, wird ein Jobflag gesetzt.
Auswerten / Antworten - Applikation:
Irgendwo in deiner Applikation prüfst du das Jobflag, wenn es gesetzt
wird, wertest du den RX-Buffer aus, stopfst die Antwort in den TX-Buffer
und startest fallweise die Übertragung - im Endeffekt Interrupt
einschalten, 1 Byte schreiben. Senden / Empfangen rattert automatisch
durch.
Auf die Art macht die ISR nur wenig - Buffer stopfen, Jobflag setzen -
und blockiert dir nicht andere Sachen, die Applikaion muss sich nicht um
den Low-Level-Mist kümmern. Du wirst auch nie Bytes vermissen, weil
alles zeitkritische in der ISR passiert.
musicmiles schrieb:> Ich habe mir ein Protokoll definiert, welches folgendermaßen aussieht:> Byte 1 - Adresse bzw. Kommando> Byte 2 - Länge der Nutzdaten (max 32.Byte)> Byte 3-35 - Nutzdaten> Letztes Byte - einfache Checksumme der Nutzbytes
Ich glaube, ich mache es ähnlich wie WehOhWeh. Zusätzlich zu deinen
Protokollüberlegungen benutze ich noch ein START- und STOPBYTE
(gegebenenfalls noch eine ESCBYTE wenn keine Länge der Nutzdaten)
Nach Empfang des STARTBYTE (z.B. 0xAA) wird alles bis zum Empfang des
STOPBYTE (z.B. 0xAB) in einen Puffer geschrieben und dann ausgewertet.
Ist die Checksumme korrekt geht das Paket zur Weiterverarbeitung.
Vielen Dank für die ausführlichen Antworten.
WehOhWeh schrieb:> Mit jedem Interrupt werden in der ISR die empfangenen Bytes in den> RX-Buffer geschreieben und aus dem TX-Buffer in die UART geschrieben.> Ist ein Paket vollständig, wird ein Jobflag gesetzt.
Ich habe es eben nochmal ausprobiert, die ISR wird von jedem empfangenen
Byte erneut ausgelöst, d.h. ich schreibe bei jedem UART-Interrupt ein
Byte ins RX-Buffer und zähle einen counter hoch. Um aber ein
vollständiges Paket zu identifizieren muss ich doch wissen wie lang es
ist. Dazu muss ich das zweite Byte prüfen. Wo findet diese Prüfung
statt, in der ISR? Wenn ich nicht weiß wie lang meine Nachricht ist,
kann ich ja nicht zu gegebenen Anlass das Job-Flag setzen. Oder ich
arbeite mit statischen Nachrichtenlängen, was mir eine wenig die
zukünftige Flexibilität nehmen würde.
Volker SchK schrieb:> Nach Empfang des STARTBYTE (z.B. 0xAA) wird alles bis zum Empfang des> STOPBYTE (z.B. 0xAB) in einen Puffer geschrieben und dann ausgewertet.> Ist die Checksumme korrekt geht das Paket zur Weiterverarbeitung.
Wo genau findet die Prüfung, ob ein Startbyte (z.B. 0xAA) empfangen
wurde statt? In der ISR oder außerhalb?
Vielen Dank und freundliche Grüße
musicmiles
musicmiles schrieb:> Dazu muss ich das zweite Byte prüfen. Wo findet diese Prüfung> statt, in der ISR? Wenn ich nicht weiß wie lang meine Nachricht ist,> kann ich ja nicht zu gegebenen Anlass das Job-Flag setzen. Oder ich> arbeite mit statischen Nachrichtenlängen, was mir eine wenig die> zukünftige Flexibilität nehmen würde.
Das muss, wie Du richtig erkannt hast, natürlich in der ISR passieren.
Aber das ist kein Problem, da das nicht viel Programm ist und auf nichts
gewartet wird.
> Volker SchK schrieb:>> Nach Empfang des STARTBYTE (z.B. 0xAA) wird alles bis zum Empfang des>> STOPBYTE (z.B. 0xAB) in einen Puffer geschrieben und dann ausgewertet.>> Ist die Checksumme korrekt geht das Paket zur Weiterverarbeitung.>> Wo genau findet die Prüfung, ob ein Startbyte (z.B. 0xAA) empfangen> wurde statt? In der ISR oder außerhalb?
Auch das muss in der ISR passieren. Solange das Startbyte nicht
empfangen wurde, wird das empfangene Byte verworfen und der Zeiger des
Buffers bleibt am Anfang.
Was auch noch fehlt, ist ein Time-Out - falls die Übertragung
unterbrochen wurde oder falsch gestartet ist.
Am Besten lässt sich das Ganze mit einer Zustandsmachine steuern - in
der ISR! Siehe http://www.mikrocontroller.net/articles/Statemachine
Gruß Dietrich
Dietrich L. schrieb:>> Wo genau findet die Prüfung, ob ein Startbyte (z.B. 0xAA) empfangen>> wurde statt? In der ISR oder außerhalb?>> Auch das muss in der ISR passieren. Solange das Startbyte nicht> empfangen wurde, wird das empfangene Byte verworfen und der Zeiger des> Buffers bleibt am Anfang.
Ich habe es anders gemacht. Bei mir wird im Interrupt nur ein Puffer
gefüllt, der dann in der Hauptschleife wieder abgearbeitet wird. Dann
puffert man natürlich gegebenenfalls unnötige Bytes, aber dafür muss der
Interrupt vom Protokoll nichts wissen.
Ich habe sozusagen zwei Puffer. Einen für den Empfänger (Erweiterung des
HW FIFOs) und einen für das "Empfangspaket"
-> Es gibt bestimmt mehrere Lösungen die nicht schlecht sind ;-)
Dietrich L. schrieb:> Was auch noch fehlt, ist ein Time-Out - falls die Übertragung> unterbrochen wurde
Ist sicher auch noch interessant. Habe ich aber bisher noch nie
implementiert.