Forum: Mikrocontroller und Digitale Elektronik Protokoll einbinden AVR


von Phillip H. (philharmony)


Lesenswert?

Moin, ich möchte verschiedene Boards bauen mit denen ich per UART mit 
dem PC kommuniziere.
Dazu haben wir uns ein einfache Protokoll ausgedacht:

Adresse
Flag für "jetzt kommen Daten" oder "Jetzt kommt ein Command"
Startbyte
Entweder Data 1 oder Command
Entweder Data 2 oder Value
...
Endbyte

Funktioniert auch wunderbar, allerdings habe ich das ganze auf dem Atmel 
so implementiert, daß der Empfang Interruptgesteuert ist, und dann per 
Switch-Abfrage abhängig von einem Empfangszähler entschieden wird was 
ich mache (siehe code)
1
//USART-Empfangs Interrupt Handler
2
ISR(USART_RX_vect)
3
{
4
unsigned char puffer=UDR;    //als erstes das USART Data Reg leeren
5
  switch (RX_count)      //was gemacht wird hängt davon ab, was im wievielten byte steht
6
  {
7
    case 0 : if (puffer == adress) //wenn byte die richtige adresse zeigt
8
      {RX_count++;         //ist das ganze gültig und der zähler wird um 1 erhöht
9
      break;}             //break damit die anderen cases nicht ausgeführt werden
10
       else break;           //wenn nicht die adresse, dann wird verworfen
11
    case 1 : if (puffer == flag_control) //wir nehmen in diesem board nur controls an
12
      {RX_count++;        //dann gehts weiter
13
      break;}
14
      else            //sonst wird verworfen
15
      {RX_count = 0;
16
      break;}
17
    case 2 : if (puffer == flag_begin_of_data) //dann MUSS BOD kommen
18
      {RX_count++;
19
      break;}
20
      else
21
      {RX_count = 0;          //alles andere wird verworfen
22
      break;}
23
    case 3 : if (puffer == flag_command_reset)  //wenn jetzt ein reset verlangt wird
24
      {RX_command = flag_command_reset;    //wird das vorgemerkt
25
      RX_count++;                //und weiter gehts
26
      break;}
27
      else if (puffer == flag_command_send_again) //oder es wird ein erneutes senden gewollt
28
      {RX_command = flag_command_send_again;    //auch das wird gemerkt
29
      RX_count ++;
30
      break;}
31
      else
32
      {RX_count = 0;                //alles andere wollen wir nicht
33
      break;}
34
    case 4 : if (puffer == flag_end_of_data)    //nur wenn jetzt noch das EOD kommt
35
      {if (RX_command == flag_command_send_again) send_data(); //wird neu gesendet
36
      else if (RX_command == flag_command_reset) reset();    //oder resettet
37
      break;
38
      }
39
      else
40
      {RX_count = 0;    //sonst wird wieder verworfen
41
      RX_command = 0;    //und das vorgemerkte command gelöscht
42
      break;}
43
    default: 
44
      {RX_count = 0;   //wird ein anderer zählerstand erreicht stimmt was nicht
45
      break;}      //dann wird er auf null gesetzt. Sollte es nötig sein könnte man
46
  }            //hier auch noch das board resetten...
47
}              //außerdem könnte man ein timeout zwischen zwei RX-Vorgängen starten
48
              //der jedesmal wieder gestartet wird und bei ablauf alle daten verwirft
49
              //und eine meldung abschickt

Dieses Board kann jetzt aber auch nur die beiden Commands reset und send 
again, andere dagegen sollen deutlich mehr Commands verarbeiten und 
zusätzlich noch Daten empfangen.
Daher meine Frage, ob das ganze auch irgendwie eleganter geht, mit 
weniger Text.

Btw, kann man eine Switch-Abfrage auch in Bereiche einteilen?
Also
1
Switch (irgendwas)
2
{
3
case 1: blabla;
4
case 3 bis 27 blablabla;
5
case 28: blubb;
6
default : nochwasanderes;
7
}
?

Grüße
Phil

von rene (Gast)


Lesenswert?

Nein, das macht man so. Als PC Beispiel :
http://www.ibrtses.com/embedded/shortmsgprotocol.html

Man kann immer weniger schreiben. Es gibt Leute die schreiben alles auf 
eine Zeile...

von rene (Gast)


Lesenswert?


von Phillip H. (philharmony)


Lesenswert?

Naja, vom reinen Protokoll her ist das ja im Prinzip das, was wir auch 
machen, außer daß wir keine CRC bilden, weil diese nach bisherigen 
Versuchen nicht notwendig war (hatten noch nicht einen einzigen 
Übertragungsfehler).
Allerdings setzt Dein Beispiel vorraus, daß der Slave nicht von sich aus 
sendet sondern nur auf Abfrage reagiert, was in unserer Anwendung aber 
nicht sinnvoll ist.
Meine Frage bezog sich eher darauf, wie man am geschicktesten im Source 
des Controllers die Empfangs-interrupt.routine so kurz wie möglich hält 
und darin die Kontrolle der ankommenden Bytes ermöglicht.
Wenn ich jetzt Daten mit 256 Bytes empfange, dann möchte ich ja nicht 
eine 256-Stellige Case-Abfrage schreiben die 253mal den selben Inhalt 
hat.
Das Board um das es Aktuell geht hat zwei extensions, einmal eine mit 32 
8bit Output-Treiber-Schieberegistern um LEDs oder andere Lasten 
anzusteuern, das andere Board hat 32 8bit input schieberegister wo 
diverse Schalter und ein Paar andere Logiken angeschlossen sind.
Das Board soll nun kontinuierlich die Schalter-Extension auslesen und 
bei veränderung die seit dem letzten senden veränderten Bits im Format 
[Bitnummer] [new value] sofort und selbständig an den Rechner senden.
Da sich durch die Logiken auch mehrere Bits verändern können, ist dieser 
String verschieden lang.
Gleichzeitig soll das Bitweise empfangene im Controller zum 
ensprechenden String zusammengebaut werden, bzw in Sequenz sinnvoll 
ausgewertet werden und die AUsgangsextension bei veränderung 
entpsrechend neu angeseteuert werden um eben die LEDs leuchten zu lassen 
oä.
Das Ankommend eist eben auch im Format [adresse] [announce daten] 
[startbyte] [bitnummer] [new value]...[endbyte]...

von 6635 (Gast)


Lesenswert?

Eine Statusmaschine im Interrupt dauert nicht laenger, nur weil's viel 
Text ist. Das wird ein Register gelsen, eine Wert in eine Sprungtabelle 
eingegeben und dann noch ein paar Instruktionen. Man muss den jeweile 
laengsten Pfad im Auge haben, sowie dessen Haeufigkeit. Der laengste 
Pfad limitiert die Interrupt rate.

von Phillip H. (philharmony)


Lesenswert?

Ja, das macht sinn.
Das heißt mit der Case Abfrage bin ich auf dem Richtigen Kurs?
Kann ich denn darin nun Bedingungen zusammenfassen?
Also "case (2 bis 17) : ... " ?

von 6635 (Gast)


Lesenswert?

Ueblicherweise nicht, dh so wie ich C kenne nicht. Das ist eine Frage 
des Compilers. Wenn eine Sprungtabelle verwendet wird, was am 
schnellsten ist, dann sollten nicht zuviele Eintrage nichts enthalten. 
Was gehen sollte, ist eine Aufzahleung anstelle eines Bereiches. Dh

Switch (irgendwas)
{
case 1: blabla; |
case 3,
case 4,
case 5,
..
case 27 :blablabla; |
case 28 : blubb; |
default : nochwasanderes; |
}

Die andere Moeglichkeit ist den Switch als repetitiven If else zu 
implementieren, und dann ist vieles mehr moeglich, auch nicht-ordinale 
Vergleiche.

von PP (Gast)


Lesenswert?

Nimm einfach eine extra Variable für die Statemachine (em besten per 
enum :).
Da hast dann die verscheidenen Zustände (zB Adresse, Datenlänge, Daten 
Empfangen, Ende, Warten auf Daten) und für jede  ein case.

von 6635 (Gast)


Lesenswert?

So ganz nebenbei. Falls ein Protokoll nicht Master-Slave ist, kann es 
Kollisionen geben. Die muss man detektieren koennen. Dann ist ein CRC 
schon Pflicht, da eine Kollision sich als CRC Fehler bemerkbar machen 
koennte. Falls nun von gleichwertige Teilnehmern eine Kollision 
detektiert wurde muss es weitergehen. Da braucht es Konzepte. zB eine 
zufaellige verzoegerte Wiederholung.

von Phillip H. (philharmony)


Lesenswert?

Ach ja, logisch, ich kann ja alle ereignise von x bis y einfach ohne 
break aufzählen, dann fallen sie einfach bis y durch und dann wird y 
ausgeführt klick ;)

Aber warum es zu kollisionen kommen sollte verstehe ich noch nicht so 
ganz.
Selbst wenn der Controller etwas vom PC empfängt während er etwas 
sendet, der USART ist ja eh Hardware, arbeitet also auch bei einem 
Interrupt seine Aufgabe ab.
Der Empfang dauert jeweils nur ein byte, bei 4mhz Controllertakt und 
115200 Baud ist das so kurz, daß in der Zwischenzeit wieder gut ein 
Stück weitergearbeitet wird bevor evtl wieder ein neues Byte ein 
Interrupt auslöst.
Der Timeout vom PC UART sollte doch lang genug sein, und wenn nicht: den 
Fall fange ich ab.
Oder mache ich hier einen Denkfehler?

von rene (Gast)


Lesenswert?

Master-Slave bedeutet der Master, meist der PC, bestimmt die 
Kommunikation, Saemtliche Kommunikation wird vom Master initiiert, der 
Slave antwortet nur. Dies ist relativ einfach zu implementieren, ist 
aber nicht in allen Faellen geeignet. Bei gleichwertigen 
Kommunikationspartnern gibt es zwei Moeglichkeiten, Das 
Kollosionsdetektion/Retry oder das Token Verfahren. Beim ersteren blaest 
jeder mal seine Nachricht raus und versucht eine Kollision zu 
detektieren. Bei einer Kollision versuchen es beide an der Kollision 
beteiligten Partner eine zufaellige Zeit spaeter nochmals. Beim Token 
verfahren bestimmt ein Faehnchen (Token) wer senden darf, und das 
Faehnchen wird im Kreis rum gegeben. Das Tokenverfahren hat die bessere 
Auslastung der Bandbreite ist aber aufwendiger zu implementieren. zB 
muss das Token regeneriert werden falls es verloren geht.

von Phillip H. (philharmony)


Lesenswert?

Ja, das ist mir soweit klar, aber der UART is doch duplex-fähig,
warum sollte es da zu kollissionen kommen?

von rene (Gast)


Lesenswert?

Na, wenn zwei Stationen gleichzeitig senden gibt es eine Kollision. 
RS232 ist uebrigens nur punkt-zu-punkt. Das geht hier nicht, Ein 
Standardansatz is RS485 und das ist nur halb duplex. Ein RS422 waere 
fullduplex und busfaehig. Es wird fuer Master-Slave auch verwendet, bei 
multimaster hat man aber die Schwierigkeit welche Richtung nun verwendet 
werden soll.

von STK500-Besitzer (Gast)


Lesenswert?

>Ein Standardansatz is RS485 und das ist nur halb duplex.
Richtig.

>Ein RS422 waere fullduplex und busfaehig.
Nö, RS422 ist RS232 symmetrisch/differentiell.
Fullduplex schon, aber auch nur Punkt zu Punkt.


Es gibt noch RS485-4-wire. Das ist ein fullduplex-Bus.

von 6635 (Gast)


Lesenswert?

RS422 is Busfaehig. Ein Paar geht in die eine Richtung, das andere Paar 
geht in die andere Richtung. Eben fuer Master-Slave. Der nicht sendende 
Slave schaltet seinen Transmitter Tristate.

von JojoS (Gast)


Lesenswert?

eine Prüfsumme macht auch bei einer einfachen RS232 Sinn weil die 
Verbindung damit robuster wird. Wenn Müll übertragen wird weil z.B. die 
Bitraten nicht richtig stehen können zufällig falsche Kommandos 
ausgelöst werden. Und auch wenn die Stecker abgezogen oder aufgesteckt 
werden können Zeichen generiert werden die als Fehler erkannt werden 
müssen. Noch sicherer wird es wenn man ein Timeout für eine Message 
implementiert um zu verhindern das unvollständige Telegramme die 
Schnittstelle blockieren.

von Phillip H. (philharmony)


Lesenswert?

Ok, dann füg ich das mal meinem Wissen hinzu ;)
Wie gesagt, bei bisherigen Anwendungen war es nicht nötig weil bei 
vielen Tausend versendeten Paketen nicht ein einziger Fehler aufgetreten 
ist.
Sollten sich im Betrieb aber welche zeigen, ist unser System flexibel 
genug um nachträglich diverse Prüf- und Sicherungsverfahren einzubauen.
Danke erstmal für die vielen Tips und Hinweise.
Grüße
Phil

von STK500-Besitzer (Gast)


Lesenswert?

>RS422 is Busfaehig. Ein Paar geht in die eine Richtung, das andere Paar
>geht in die andere Richtung. Eben fuer Master-Slave. Der nicht sendende
>Slave schaltet seinen Transmitter Tristate.

Nö...
http://de.wikipedia.org/wiki/RS422
Das was du beschreibst, ist der RS485-4-Wire-Bus.
RS422-Transceiver haben keine Richtungspins.

von Falk B. (falk)


Lesenswert?

@  6635 (Gast)

>RS422 is Busfaehig.

Nope. du verwechselst das Mit Multi-Drop. Ein Sender, mehrere Empänger. 
DMX wäre somit klassisches RS422, auch wenn es oft als RS485 bezeichnet 
und realisiert wird. Die Unterschiede sind eher gering.

http://www.maxim-ic.com/appnotes.cfm/appnote_number/723/

MfG
Falk

von 6635 (Gast)


Lesenswert?

Naja. wie auch immer. Ich empfehle den ADm489 Treiber fuer Master-Slave, 
aka multidrop. Kostet um die 1.10 @ 100. Fuer Multi Master geht der 
nicht.

von Phillip H. (philharmony)


Lesenswert?

Für meine Anwendung aber alles etwas sehr überdinemsioniert ;)
Es geht um ca 200 Schalter und 200 Lampen...

von Falk B. (falk)


Lesenswert?

@ Phillip Hommel (Firma hs-bremen) (philharmony)

>Für meine Anwendung aber alles etwas sehr überdinemsioniert ;)

Was ist daran überdimensioniert? Ein kleiner 8 Piner und gut ist.

>Es geht um ca 200 Schalter und 200 Lampen...

Ja eben! Und da die sicherlich räumlich etwas auseinander liegen, 
sollte man einen robusten Kommunikationsstandard wählen. RS485.

MFG
Falk

von 6635 (Gast)


Lesenswert?

Ja. Ohne RS485 geht's nicht. Da kann man den ADm483 fuer 1.14 @100 (auch 
dollar) nehmen. Ein Tiny26 oder so dazu und gut ist.

von Phillip H. (philharmony)


Lesenswert?

Nope, liegen alle auf einem Panel, werden von 2 Man bedient und in 
ersten Versuchen ging es mit RS232 Fehlerfrei.
Direkt auf der Plattine mit dem Atmega ist auch ein FTDI UM232R-Modul 
dran, von da an ists eh ne USB-Leitung.
Die eigentlich Leitung wo ich 5V UART betreibe ist ca 5cm Lang.
Meine Frage bezog sich wirklich nur darauf, wie ich die Abfrage des 
Protokolls eleganter Lösen kann.

von 6635 (Gast)


Lesenswert?

Mit RS232 kann man aber keinen Bus bedienen. Allenfalls kann man auf 
einer Leiterplatte die Signale in TTL-Level fuehren, und die nicht 
betroffenen Tristaten. 200 Stueck benoetigen aber satte Bustreiber. HC 
oder TTL alleine ist nicht genug.

von Phillip H. (philharmony)


Lesenswert?

???
Die RS 232 bedient auch nicht den BUS sondern schickt dem ATmega nur ein 
Paar Daten, welche der Schieberegister er wie setzen soll.
Wie gesagt:
ES FUNKTIONIERT GENAU SO!!!! Ich weiß das weil ichs aufgebaut habe und 
es tadellos sendet und empfängt.
Ich wollte nur wissen ob man das PROTOKOLL anders einbinden kann bzw man 
den switch-Befehl optimieren kann.
Alles andere ist doch schon lange gelöst...

von Falk B. (falk)


Lesenswert?

@  Phillip Hommel (Firma hs-bremen) (philharmony)

>Ich wollte nur wissen ob man das PROTOKOLL anders einbinden kann bzw man
>den switch-Befehl optimieren kann.

Kann man. Erstmal das Startbyte erkennen, dann immer die Daten in einen 
Puffer schreiben bis das Endzeichen erkannt wird. Dann erst den 
empangenen String/Datenblock dekodieren.

MFG
Falk

von Phillip H. (philharmony)


Lesenswert?

@ Falk,
Danke, macht Sinn!

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.