Hallo zusammen,
ich habe ein CAN- Bus Netzwerk mit 3 Teilnehmener (2x Arduino, 1x
Raspberry), welche miteinander Daten austauschen.
Als Controller für den Bus wird ein MCP2515 verwndet.
Wenn auf Teilnehmer A ein bestimmtes Event eintritt, so soll Teilnehmer
B davon in Kenntnis gesetzt werden (wobei garantiert werden muss, dass
Teilnehmer B auch das Event empfängt).
Es kommt auch vor, dass innerhalb 400ms 20 Events generiert werden.
Der MCP2515 kann aber nur 2 Receive Events gleichzeitig speichern. Nun
kommt es vor, dass Daten zu schnell geschickt werden und dabei Events
verschluckt werden.
Um das zu lösen, habe ich ein kleines Protokoll gebaut, wo Teilnehmer B
bestätigen muss, dass das Event von Teilnehmer A empfangen wurde. Erhält
Teilnehmer A keine Bestätigung, so schickt er das gleiche Event
nochmals.
Funktioniert seit einem Jahr problemlos, nur mir gefällt der ganze
Software Overhead nicht (Teilnehmer A muss sich alle Events solange
merken, bis die Empfangsbestätigung eingetroffen ist, Teilnehmer B muss
sich alle bereits empfangen Events merken, damit dieser keine doppelt
empfängt).
Es gibt bestimmt bessere und einfachere Lösungen.
Wie garantiert ihr, dass bestimmte Pakete auch tatsächlich beim
Empfänger ankommen.
Bin auf eure Tipps gespannt!
lG
Thomas schrieb:> Es gibt bestimmt bessere und einfachere Lösungen.
Glaube ich nicht. Wenn du deine Quelltexte zeigen würdest, finden wir
sicher Kleinigkeiten, die man dabei verbessern kann. Aber das Konzept an
sich ist so in Ordnung.
CAN enthält halt kein Handshake mit ACK in Hardware.
Thomas schrieb:> Teilnehmer B muss> sich alle bereits empfangen Events merken, damit dieser keine doppelt> empfängt
wenn Du nicht doppelt sendest, dann muss B sich auch nichts merken
Thomas schrieb:> Wenn auf Teilnehmer A ein bestimmtes Event eintritt, so soll Teilnehmer> B davon in Kenntnis gesetzt werden (wobei garantiert werden muss, dass> Teilnehmer B auch das Event empfängt).> Es kommt auch vor, dass innerhalb 400ms 20 Events generiert werden.
Muss B wirklich über jedes einzelne Event separat informiert werden?
Würde es ausreichen, wenn im Falle der vielen Events in kurzer Zeit nur
ein Zähler übermittelt wird der sagt wie viele Events in den letzten
z.B. 100ms aufgetreten sind?
Mit anderen Protokollen auf anderen Bussystemen.
CAN ist ausdrücklich nicht dazu da, sicherzustellen das "der eine"
Empfänger die Nachricht auch bekommt, sondern eher "fire and forget".
Ich würde eine Queue realisieren, immer eine Nachricht abschicken,
Quittung abwarten -> Antwort ok = nächste, Timeout = Repeat. 3x Repeat =
#Bus Error.
Dann müssen eben so viele Nachrichten gespeichert werden, wie in 3x
Timeout passen. Oder Timeout so kurz machen das das RAM passt.
Thomas schrieb:> Es kommt auch vor, dass innerhalb 400ms 20 Events generiert werden.
Das ist doch eher wenig. Da langweilt sich doch der Bus.
> Der MCP2515 kann aber nur 2 Receive Events gleichzeitig speichern. Nun> kommt es vor, dass Daten zu schnell geschickt werden und dabei Events> verschluckt werden.
Daten werden nicht zu schnell verschickt. Das regelt der MCP2515 schon
von selbst und sendet nur wenn der Bus frei ist.
Deine Software ist einfach zu langsam die empfangenen Botschaften aus
den Receive-Puffern auszulesen bevor eine neuen Botschaft eintrifft.
Also: Software optimieren.
Verschiedenes:
Dass B "garantiert" das Event von A empfängt bekommst du nur mit einer
expliziten Bestätigungsmeldung hin.
CAN an sich funktioniert zwar sehr zuverlässig. Das hilft aber nicht,
wenn B ausgeschaltet ist :-)
Jetzt zu den 20 Events in 400ms:
Wenn so viele Events auf einmal kommen liegt der Verdacht nahe, dass die
was miteinander zu tun haben. Lässt sich das vielleicht konsolidieren
und in weniger Nachrichten packen?
Dann die Empfangsseite: Ja, der MCP2515 hat nur 2 Puffer. Aber er kann
einen Hardwareinterrupt auslösen. Und wenn ich mich recht erinnere, ist
es sogar möglich, über die IO-Pins des MCP auszugeben, welcher der 2
Puffer belegt ist. Wenn man nicht rumtrödelt kann man die Nachricht
locker abholen während im 2. Puffer noch der Empfang läuft. Den SPI-Bus
kann man schnell takten. Wenn du die Nachrichten natürlich
superaufwändig auswerten willst musst du dir einen Empfangspuffer bauen.
Wenn dir das zu stressig ist kannst du sicher auch langsamer senden. Wie
schnell muss denn dein Bus sein? Du kannst sicher mit der Bitrate extrem
runtergehen. Dann soll A halt schauen, wie er seine Events loswird bzw.
muss eine Sende-Queue haben. (Die hast du dort ja offensichtlich eh
schon, wenn du es dir leisten kannst, auf das ACK-Event zu warten)
Gerd E. schrieb:> Thomas schrieb:>> Wenn auf Teilnehmer A ein bestimmtes Event eintritt, so soll Teilnehmer>> B davon in Kenntnis gesetzt werden (wobei garantiert werden muss, dass>> Teilnehmer B auch das Event empfängt).>> Es kommt auch vor, dass innerhalb 400ms 20 Events generiert werden.>> Muss B wirklich über jedes einzelne Event separat informiert werden?>> Würde es ausreichen, wenn im Falle der vielen Events in kurzer Zeit nur> ein Zähler übermittelt wird der sagt wie viele Events in den letzten> z.B. 100ms aufgetreten sind?
die Zähler dann am Besten noch zu einem großen "Sägezahn" aufsummieren,
dann tut auch eine Botschaft die nicht ankommt nicht weh wenn man die
Überlaufbehandlung vom Sägezahn sauber macht.
Hallo,
danke für eure Antworten:
Stefan E. schrieb:> Thomas schrieb:>> Teilnehmer B muss>> sich alle bereits empfangen Events merken, damit dieser keine doppelt>> empfängt>> wenn Du nicht doppelt sendest, dann muss B sich auch nichts merken
Empfänger muss sich merken, welches Paket schon empfangen wurde.
Beispiel: A sendet B ein Paket, B sendet ACK zu A, A empfängt es aber
nicht. Nun glaubt A, B hat Paket nicht empfangen und sendet nochmal. B
merkt nun, dass Paket bereits empfangen wurde und sendet erneut das ACK.
Tilo R. schrieb:> Dass B "garantiert" das Event von A empfängt bekommst du nur mit einer> expliziten Bestätigungsmeldung hin.> CAN an sich funktioniert zwar sehr zuverlässig. Das hilft aber nicht,> wenn B ausgeschaltet ist :-)
Wenn der Node ausgeschalten ist, hab ich sowies ein Problem. Daher würde
ich das Problem, wenn ein Node nicht mehr verfügbar ist, ignorieren.
Jeder Node sendet jede Sekunde ein "HELLO", damit jeder Node weiß, ob eh
alle verfügbar sind.
Tilo R. schrieb:> Jetzt zu den 20 Events in 400ms:> Wenn so viele Events auf einmal kommen liegt der Verdacht nahe, dass die> was miteinander zu tun haben. Lässt sich das vielleicht konsolidieren> und in weniger Nachrichten packen?
Ja, kann man bestimmt zusammenfassen., gute Idee
Tilo R. schrieb:> Dann die Empfangsseite: Ja, der MCP2515 hat nur 2 Puffer. Aber er kann> einen Hardwareinterrupt auslösen. Und wenn ich mich recht erinnere, ist> es sogar möglich, über die IO-Pins des MCP auszugeben, welcher der 2> Puffer belegt ist. Wenn man nicht rumtrödelt kann man die Nachricht> locker abholen während im 2. Puffer noch der Empfang läuft. Den SPI-Bus> kann man schnell takten. Wenn du die Nachrichten natürlich> superaufwändig auswerten willst musst du dir einen Empfangspuffer bauen.
Ja, den Hardware- Interrupt könnte ich auch noch implementieren. Aber
funktioniert das auch korrekt?
Wenn ich gerade eine Nachricht sende und der Hardware- Interrupt löst
aus, kann ich dann auch die Nachricht lesen, wobei ich gerade am Senden
bin, ohne das ich das SPI Protokoll zerstöre?
Tilo R. schrieb:> Wenn dir das zu stressig ist kannst du sicher auch langsamer senden. Wie> schnell muss denn dein Bus sein? Du kannst sicher mit der Bitrate extrem> runtergehen. Dann soll A halt schauen, wie er seine Events loswird bzw.> muss eine Sende-Queue haben. (Die hast du dort ja offensichtlich eh> schon, wenn du es dir leisten kannst, auf das ACK-Event zu warten)
Meine Aktuelle CAN Geschwindigkeit ist CAN_125KBPS, wobei ich diese auch
noch um ein vielfaches reduzieren kann.
Dnake für eure Tipps!
Thomas schrieb:> Empfänger muss sich merken, welches Paket schon empfangen wurde.> Beispiel: A sendet B ein Paket, B sendet ACK zu A, A empfängt es aber> nicht. Nun glaubt A, B hat Paket nicht empfangen und sendet nochmal. B> merkt nun, dass Paket bereits empfangen wurde und sendet erneut das ACK.
Das wäre mir viel zu umständlich. Wie oben schon beschrieben: Der Sender
sendet nur, die Empfänger müssen schon selber klar kommen und alles
richtig empfangen. Zur Nachverfolgung kann man in die einzelnen
Botschaften einen Botschaftszähler einbauen welcher von 0-255 hochläuft
und dann wieder bei 0 beginnt.
Thomas schrieb:> Ja, den Hardware- Interrupt könnte ich auch noch implementieren. Aber> funktioniert das auch korrekt?
Natürlich funktioniert der korrekt!
> Wenn ich gerade eine Nachricht sende und der Hardware- Interrupt löst> aus, kann ich dann auch die Nachricht lesen, wobei ich gerade am Senden> bin, ohne das ich das SPI Protokoll zerstöre?
Bei einem Interrupt wird das Übertragen (nicht Senden) der Botschaft zum
MCP2515 nicht unterbrochen. Die kurze und schlanke CAN-Interrupt-Routine
setzt beim Auslösen nur einen Merker (Flag), mehr nicht. Das
Hauptprogramm überprüft dann nach dem Übertragen anhand des Merkers ob
eine neuen Botschaft vorhanden ist und holt diese dann ab.
Thomas F. schrieb:>> Wenn ich gerade eine Nachricht sende und der Hardware- Interrupt löst>> aus, kann ich dann auch die Nachricht lesen, wobei ich gerade am Senden>> bin, ohne das ich das SPI Protokoll zerstöre?>> Bei einem Interrupt wird das Übertragen (nicht Senden) der Botschaft zum> MCP2515 nicht unterbrochen. Die kurze und schlanke CAN-Interrupt-Routine> setzt beim Auslösen nur einen Merker (Flag), mehr nicht. Das> Hauptprogramm überprüft dann nach dem Übertragen anhand des Merkers ob> eine neuen Botschaft vorhanden ist und holt diese dann ab.
Wenn man das so macht, lohnt sich der Interrupt eigentlich nicht.
Richtig ist: eine laufende SPI-Übertragung darf man natürlich nicht
unterbrechen. Der Interrupt-Ausgang des MCP sagt ja eigentlich nur "da
ist was, schau mal".
Der uC hat jetzt 2 Möglichkeiten, damit umzugehen:
1. Als IO-Pin. Als erster Befehl in der Mainloop schauen, ob man
Nachrichten holen muss. Das ist so eine einzelne IO-Abfrage und kostet
praktisch nichts. Ohne den angeschlossenen Interrupt-Ausgang müsste man
den MCP per SPI fragen, das dauert länger. Dann Nachrichten holen, bis
der MCP leer ist.
Nachteil: die restliche Main-Loop darf nicht zu lange dauern. Mit der
CAN-Bitrate und der Messagegröße, bzw. der maximalen Message-Rate kannst
du diese Maximaldzeit ausrechnen und den Rest des Programms darauf
abstimmen. Ggf. ist es notwendig, aufwendige Tasks in kleinere zu
zerteilen. Die Mainloop muss halt oft genug laufen, sonst können
Nachrichten verloren gehen.
2. Ein echter Hardware-Interrupt. In der angesprungenen
Interrupt-Routine muss dann die Nachricht abgeholt und gespeichert oder
verarbeitet werden. Dein restliches Programm kann dann beliebig langsam
sein und praktisch alles machen, außer den SPI benutzen. Zum Senden muss
man den Interrupt abschalten.
Es gibt auch die Möglichkeit beides zu kombinieren: d.h. es wird ein
echter Interrupt ausgelöst. In der ISR macht man aber nichts (oder setzt
nur ein Flag, wie von Thomas F vorgeschlagen). Das Abholen macht man
dann wie bei 1. in der Mainloop. Vorteil hier ist, dass der Interrupt
den uC auch aufweckt, wenn man in einem Stromsparmodus war.