Forum: Mikrocontroller und Digitale Elektronik PIC USART Interrupt - suche elegante Lösung


von musicmiles (Gast)


Lesenswert?

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

von WehOhWeh (Gast)


Lesenswert?

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.

von Volker S. (vloki)


Lesenswert?

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.

von musicmiles (Gast)


Lesenswert?

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

von Dietrich L. (dietrichl)


Lesenswert?

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

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

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.

: 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
Noch kein Account? Hier anmelden.