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?
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.
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.
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.
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.
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?
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.
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.
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)
{
}
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!
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.