Forum: Mikrocontroller und Digitale Elektronik Eingehende Nachrichten am MCP2515 CAN Controller handeln


von Olli Z. (z80freak)


Lesenswert?

Ich habe eine konzeptionelle Frage. Und zwar habe ich zwei MCP2515 
basierende Shields und einen Arduino um damit ein unidirektionales 
Gateway aufzubauen. Es solleb CAN Botschaften auf einem Bus gelesen 
(Entstehen auf dem Bus im Intervall von 10 ms), umgerechnet und auf dem 
anderen geschrieben werden (200 ms Sendeperiode).

Das ganze hatte ich mir so vorgestellt das für eingehende Nachrichten 
ein Hardware-Interrupt am Arduino ausgelöst wird, sobald am MCP eine 
vollständige Botschaft empfangen wurde. Weil es da noch mehr auf dem Bus 
gibt, möchte ich einen Filter auf die ID setzen, welche mich 
interessiert. Es könnten später aber mal auch noch zwei, drei weitere 
IDs dazukommen.

Nachdem der MCP den Interrupt ausgelöst hat, soll der Arduino in einer 
ISR die Botschaft via SPI auslesen und in einen Buffer im RAM schreiben. 
Dann wäre die Routine auch schon fertig. Ich habe also zur Laufzeit 
immer eine Kopie der letzten gültigen Botschaft in einem Array.

Nun soll die Senderoutine über einen Timer ausgelöst werden, weil ich 
delays() vermeiden möchte. Der Timer ruft dann alle 200 ms eine andere 
ISR auf, welche die Daten im Buffer umrechnet und via SPI ans andere 
Interface sendet.

Jetzt weiss ich nicht wie das ist, wenn während dem lesen der Daten vom 
SPI dort wieder neue Daten vom Bus reinkommen. Ich habe irgendwie Sorge 
das sich was überschneidet oder überschreibt bei den parallelen 
Zugriffen. Wie verhindere ich das am besten?

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Olli Z. schrieb:

Dein Konzept klingt gut.
>

> Jetzt weiss ich nicht wie das ist, wenn während dem lesen der Daten vom
> SPI dort wieder neue Daten vom Bus reinkommen.

Da passiert rein gar nichts, der MCP2515 speichert die und fertig. Das 
Interrupt-Pin bleibt gesetzt und löst bei der Rückkehr vom Interrupt 
sofort einen neuen aus.

von Olli Z. (z80freak)


Lesenswert?

Also trifft das hier zu?

Eingehende CAN Botschaften werden immer im "MAB" (Message Assembly 
Buffer) empfangen. Dieser wird unter bestimmten Bedingungen in einen der 
beiden Empfangsbuffer RXB0 oder RXB1 übertragen und das RX0IF bzw. RX1IF 
Flag gesetzt.

Und dazu steht im Datenblatt: "This bit must be cleared by the MCU in 
order to allow a new message to be received into the buffer. This bit 
provides a positive lockout to ensure that the MCU has finished with the 
message before the MCP2515 attempts to load a new message into the 
receive buffer."

Also würde ich dieses Bit nach dem lesen der Nachricht zurücksetzen und 
wäre somit sicher das während meines Lesevorganges eingehende CAN 
Nachrichten den Inhalt des RX-Buffers überschrieben. Stattdessen 
überschreiben sich die eingehenden Nachrichten im MAB.

Soweit richtig?!

Ich müsste jetzt nur mal in die Lin schauen ob dieses Flag bereits 
automatisch nach dem Lesen zurückgestellt wird oder ich mich darum 
selbst kümmern muss.

---

Zu dem INT Pin sagt das datenblatt: "If the RXnIE bit (CANINTE) is set, 
an interrupt will be generated on the INT pin to indicate that a valid 
message has been received."

Diesen /INT Pun würde ich zum HW INT Pin 2 vom Arduino legen um einen 
external interrupt auszulösen.

Weiterhin würde ich mal mit dem DSO schauen wie lang so ein SPI 
Lesezyklus ist. Wenn nämlich blöd läuft dann ist bereits nach dem lesen 
einer Botschaft schon der nächste interrupt da und somit läuft nur 
dieser?

Ich brauchte ja nich alle Botschaften, mir würde jede zehnte, fünfzehnte 
reichen. Aber würde ich das machen das ich einige INTs einfach auslasse? 
Ich hätte jetzt gesagt über eine volatile Zählervariable. Die wirkt dann 
am Anfang der ISR wie ein Frequenzteiler.

: Bearbeitet durch User
von H.Joachim S. (crazyhorse)


Lesenswert?

Bei mir heissen die Dinger MOBs :-).
Das ist alles Zeitlupe bei dir, brauchst keine Sorge zu haben.
Nimm aber die Hardware-SPI für beide MCPs, CS per Software erzeugen. 
SPI-Takt möglichst hoch.

von Olli Z. (z80freak)


Lesenswert?

Ich muss da nochmal drüber, aber eine erste Messung ergab einen 
Lesezyklus von ca. 0,6 ms. Das wäre in der Tat schnell genug. Diese 
einfachen MCP Shields haben alle einen 8 Mhz Quarz drauf, was den Takt 
begrenzt. Durch umlöten auf 16 MHz wäre da wohl potentiell noch eine 
Halbierung drin...

Ich denke so wie ich derzeit das Setup habe empfange ich nur im RX0B, 
dafür würde ich auch den INT auslösen (RX0IE) lassen. Ich hoffe man kann 
den MCP so konfigurieren, das ein INT nur nach dem Empfang einer 
Botschaft (Mask und Filter auf die zu empfangende ID gesetzt) auslöst. 
Zumindest habe ich das Register CANINTF so verstanden. Ansonsten müsste 
ich vorher ja immer schauen ob ich auch wirklich etwas empfangen habe, 
oder es einen anderen Grund für den INT gab.

Nun kommt dann für mich die Frage nach dem Sleepmode ins Spiel. Ich 
würde gern meinen Arduino schlafen legen, wenn über CAN eine gewisse 
Zeit nichts mehr empfangen wird. Macht es Sinn den bis zum nächsten 
Empfang/Sende-Interrupt in den Schlaf zu versetzten? Oder nach einigen 
Sekunden ohne Nachrichteneingang?

Der MCP hat ja auch sleepmodes. Damit würde ich die Stromaufnahme noch 
weiter reduzieren können. Eigentlich könnte der auch den Spannungsregler 
für den Arduino einschalten, wenn dieser sich vorher darüber selbst 
ausgeschaltet hat.

von Falk B. (falk)


Lesenswert?

Olli Z. schrieb:

> Also würde ich dieses Bit nach dem lesen der Nachricht zurücksetzen und
> wäre somit sicher das während meines Lesevorganges eingehende CAN
> Nachrichten den Inhalt des RX-Buffers überschrieben.

NICHT überschreiben!

> Stattdessen
> überschreiben sich die eingehenden Nachrichten im MAB.

???

> Soweit richtig?!

 Nö.

Es gibt 2 Empfangspuffer. Die sind im Normalfall beide leer. Jetzt kommt 
eine CAN-Bachricht, die landet im Puffer 0. Dann wird das RXB0 Bit 
gesetzt und der IC löst einen Interrupt über seinen Ausgang aus. Die CPU 
liest die Nachricht. enn jetzt während des Auslesens schon die nächtste 
Nachricht reinflattert, landet die im noch leeren Puffer 1. Den liest 
die CPU praktisch unmittelbar im ANschluß zu Puffer 0. Damit ist immer 
ein Puffer leer, welcher eine ankommende Nachricht puffern kann, wenn 
die CPU gerade den anderen Puffer liest. Das ist ein einfaches FIFO. 
Kann man aber auch als Doppelpuffer betrachten.

> Zu dem INT Pin sagt das datenblatt: "If the RXnIE bit (CANINTE) is set,
> an interrupt will be generated on the INT pin to indicate that a valid
> message has been received."
>
> Diesen /INT Pun würde ich zum HW INT Pin 2 vom Arduino legen um einen
> external interrupt auszulösen.

Ja.

> Weiterhin würde ich mal mit dem DSO schauen wie lang so ein SPI
> Lesezyklus ist. Wenn nämlich blöd läuft dann ist bereits nach dem lesen
> einer Botschaft schon der nächste interrupt da

Naja, nicht wirklich. Denn selbst bei 1 Mbit/s braucht ein CAN-Frame mit 
8 Bytes um die 100 Bit, macht ~100us. Das Lesen einer Nachricht mit 8 
Mbit/s über SPI dauer vielleicht 20-30us.

> und somit läuft nur
> dieser?

Nur, wenn die CAN-Datenrate sehr hoch ist, dauerhft Messeages an deine 
Adresse gehen und das Auslesen lahmarschig ist.

> Ich brauchte ja nich alle Botschaften, mir würde jede zehnte, fünfzehnte
> reichen.

Warum? Du willst ALLE Botschaften haben, die an deine Adresse gehen. Das 
kann man ja bei den Filtern einstellen.

> Aber würde ich das machen das ich einige INTs einfach auslasse?

Was soll der Unsinn?

von Falk B. (falk)


Lesenswert?

Olli Z. schrieb:
> Ich muss da nochmal drüber, aber eine erste Messung ergab einen
> Lesezyklus von ca. 0,6 ms. Das wäre in der Tat schnell genug.

Wie meinen? Das ist lahm^3. Ein CAN-Nachricht hat maximal 10 Bytes incl. 
Header. Dad sind bei 8 MHz SPI-Takt 10us. Du mißt 600us!


> Diese
> einfachen MCP Shields haben alle einen 8 Mhz Quarz drauf, was den Takt
> begrenzt. Durch umlöten auf 16 MHz wäre da wohl potentiell noch eine
> Halbierung drin...

Nö, denn den SPI-Takt macht der Master!

> Ich denke so wie ich derzeit das Setup habe empfange ich nur im RX0B,
> dafür würde ich auch den INT auslösen (RX0IE) lassen. Ich hoffe man kann
> den MCP so konfigurieren, das ein INT nur nach dem Empfang einer
> Botschaft (Mask und Filter auf die zu empfangende ID gesetzt) auslöst.

Klar kann man das.

> Zumindest habe ich das Register CANINTF so verstanden. Ansonsten müsste
> ich vorher ja immer schauen ob ich auch wirklich etwas empfangen habe,
> oder es einen anderen Grund für den INT gab.

Das muss man sowieso, wenn mehrere Interruptquellen aktiv sind.

> Nun kommt dann für mich die Frage nach dem Sleepmode ins Spiel. Ich
> würde gern meinen Arduino schlafen legen, wenn über CAN eine gewisse
> Zeit nichts mehr empfangen wird. Macht es Sinn den bis zum nächsten
> Empfang/Sende-Interrupt in den Schlaf zu versetzten?

Sicher.

> Oder nach einigen
> Sekunden ohne Nachrichteneingang?

Dann erst recht. Das kann man aber auch schon DEUTLICH kürzer. 
Mikrocontroller beherrschen das Power-Napping, da sind auch 10ms Schlaf 
gewinnbringend.

> Der MCP hat ja auch sleepmodes. Damit würde ich die Stromaufnahme noch
> weiter reduzieren können.

Kann man, aber ist das wirklich nötig? Das ist ein CMOS-IC. Wenn der 
nicht sendet, braucht der fast keinen Strom.

> Eigentlich könnte der auch den Spannungsregler
> für den Arduino einschalten, wenn dieser sich vorher darüber selbst
> ausgeschaltet hat.

Unsinn.

von H.Joachim S. (crazyhorse)


Lesenswert?

Olli Z. schrieb:
> Ich muss da nochmal drüber, aber eine erste Messung ergab einen
> Lesezyklus von ca. 0,6 ms.

??
SPI-Takt kannst du 4MHz benutzen, eine komplette Botschaft zu lesen 
waren so um die 20Byte, vielleicht auch 25.
Also 0,25µs*20Byte*8Bit=40µs.

von H.Joachim S. (crazyhorse)


Lesenswert?

Und du kannst es dir auch locker leisten, alle Botschaften in den MC zu 
holen und erst dort anhand der ID zu entscheiden, ob du damit was tust 
oder die Dingerchen in den Mülleimer beförderst.
Masken/Filter kann man später noch setzen, wirklich nötig wird das erst 
wenn der MC auch sonst viel zu tun hat. So kann man z.B. in der 
Entwicklungsphase via USB-CAN-Dongle Daten an den MC senden ohne 
jedesmal die Filtereinstellungen im CAN-Controller anzupassen.

Rec_Interrupt:
-message lesen
-flag setzen, dass was empfangen wurde

main:
-rec_flag gesetzt?
     switch (ID)
            {
            }

von Falk B. (falk)


Lesenswert?

H.Joachim S. schrieb:
> Und du kannst es dir auch locker leisten, alle Botschaften in den MC zu
> holen und erst dort anhand der ID zu entscheiden, ob du damit was tust
> oder die Dingerchen in den Mülleimer beförderst.

Wieso? Das kann der MCP2515 deutlich besser.

> Masken/Filter kann man später noch setzen, wirklich nötig wird das erst
> wenn der MC auch sonst viel zu tun hat. So kann man z.B. in der
> Entwicklungsphase via USB-CAN-Dongle Daten an den MC senden ohne
> jedesmal die Filtereinstellungen im CAN-Controller anzupassen.

Oh wie schrecklich! Da muss man doch TATSÄCHLICH ein paar Zahlen während 
der Entwicklung ändern!

von Thomas F. (igel)


Lesenswert?

Olli Z. schrieb:
> Der MCP hat ja auch sleepmodes. Damit würde ich die Stromaufnahme noch
> weiter reduzieren können.

Ich würde jetzt mal tippen der meiste Strom lässt sich sparen wenn man 
den Transceiver in den Sleep-Mode setzt.

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.