Ich habe versucht mit einem Atmega328 und zwei MCP2515 (je mit TJA1050 Transceiver) ein bidirektionales "Filter-Device" für einen 125kbit CAN-Bus zu bauen. Es sollen bestimmte Daten bestimmter IDs geändert werden. Aber selbst im "Durchzugmodus" ohne jegliche Datenverarbeitung verliert diese Lösung zuviele Pakete. Es sieht sieht so aus als wäre der MCP nicht für sowas zu gebrauchen... Frage: Gibt es eine günstige Alternative dazu? Sicher wäre es besser einen uC mit integriertem CAN-Controller zu haben, wie den AT90CAN oder den STM32. Die haben aber alle "nur" einen CAN drauf und wenn bräuchte ich wohl zwei? Wer hat schonmal solch einen Filter/Gateway gebaut und kann mir einen Tipp geben?
Olli Z. schrieb: > ohne jegliche Datenverarbeitung > verliert diese Lösung zuviele Pakete. Es sieht sieht so aus als wäre der > MCP nicht für sowas zu gebrauchen... Dann hast du was grundlegend falsch gemacht. Genauso eine Lösung läuft bei mir mit 2x500kBit und ziemlich viel traffic (Motor-CAN), dazu werden auch noch Daten verwurstet. SPI-Frequenz 8MHz, ATMega32 mit 16MHz.
STM32 hat 2 CAN onboard. 125kbit sollten eigendlich machbar sein, wenn ich mich recht erinnere, dann lief ein MCP2515 bei mir bis 250kbit ohne Datenverlust. Das kommt natürlich etwas auf die Firmware des ATmega drauf an. Allerdings würde ich mir heute keinen MCP2515 mehr antun. Viele Grüße, Stefan
Würde ich heute auch nicht mehr, aber es geht problemlos. CAN-Botschaften auf Bit-Ebene sind länger als die Daten, die auf der SPI hin- und hergeschoben werden, also zusätzlich Zeit gewonnen. Aber man kann das auch grob 1:1 rechnen. Bei 8Mbit SPI und 2x125kBit CAN hat man doch Zeit ohne Ende. Interessant wird allenfalls, wenn der MCP bei fast vollem Bus seine Sendedaten aufgrund niedriger Priorisierung nicht los wird und man sich ständig darum kümmern muss. Aber dann ist eh am Grundkonzept was faul.
Olli Z. schrieb: > bidirektionales "Filter-Device" für einen 125kbit > CAN-Bus zu bauen. Kommt halt auf die Buslast an. Wenn beide Busse schon über ~40% Buslast haben und dann noch die Botschaften des anderen Busses mit drauf sollen wird es arg voll. Das ist aber dann ein Transportproblem und keines des MCP2515. Alternativ zu Atmega haben die AtXMega zwei SPI-Schnittstellen. Aber wie H.Joachim schon schrieb sollte es daran nicht sccheitern. Nach meiner Erfahrung ist das Problem eher die Software die Herausforderung um darin schnelle Puffer in beide Richtungen zu implementieren.
H.Joachim S. schrieb: > Dann hast du was grundlegend falsch gemacht. > Genauso eine Lösung läuft bei mir mit 2x500kBit und ziemlich viel > traffic (Motor-CAN), dazu werden auch noch Daten verwurstet. > SPI-Frequenz 8MHz, ATMega32 mit 16MHz. Ok, das habe ich ja fast befürchtet :-/ Also ich habe zwei handelsübliche CAN-Shields mit MCP2515 und TJA1050 drauf genommen und von beiden Modulen MISO => D12, MOSI => D11, SCK => D13 verbunden. Die CS- und INT-Leitungen auf separate Eingänge des Arduino. Dann habe ich, nach langer Sucherei die MCP2515-Lib von Corey Fowler verwendet (https://github.com/coryjfowler/MCP_CAN_lib) weil meine CAN-Shields einen 8 MHz Quarz drauf haben und viele andere Libs einen 16 MHz voraussetzten. Ich initialisiere das ganze so:
1 | void setup() |
2 | {
|
3 | ...
|
4 | pinMode(CAR_CAN_INT, INPUT); |
5 | if (CAR_CAN.begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ) != CAN_OK) { |
6 | Serial.println("!! ERROR initializing CAR-CAN interface !!"); |
7 | }
|
8 | CAR_CAN.setMode(MCP_NORMAL); |
9 | CAR_CAN.enOneShotTX(); |
10 | |
11 | pinMode(NAV_CAN_INT, INPUT); |
12 | if (NAV_CAN.begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ) != CAN_OK) { |
13 | Serial.println("!! ERROR initializing NAV-CAN interface !!"); |
14 | halt(); |
15 | }
|
16 | NAV_CAN.setMode(MCP_NORMAL); |
17 | NAV_CAN.enOneShotTX(); |
18 | |
19 | SPI.setClockDivider(SPI_CLOCK_DIV2); // Set SPI to run at 8MHz (16MHz / 2 = 8 MHz) |
20 | ...
|
Das mit dem "enOneShotTX()" war nur ein Versuch, weil ich befürchtete das der MCP bestimmte Nachrichten nicht weg bekommt und diese deswegen im Buffer liegen bleiben und resendet werden. Währenddessen schiebe ich von "hinten" aber neue Nachrichten nach und dann verliert er welche. Inzwischen ist mir aber klar geworden das der OSM nur dazu dient, Nachrichten auf den bus zu senden die auch wenn niemand sie acknowledged keinen Sendefehler im Controller erzeugen. Zudem brachte das nichts für die Stabilität. Die Verarbeitungsroutine sieht dann so aus:
1 | void loop() |
2 | {
|
3 | // Forward message from CAR to NAV
|
4 | if (!digitalRead(CAR_CAN_INT)) { |
5 | CAR_CAN.readMsgBuf(&rxId, &len, rxBuf); |
6 | NAV_CAN.sendMsgBuf(rxId, 0, len, rxBuf); |
7 | }
|
8 | |
9 | // Forward message from NAV to CAR
|
10 | if (!digitalRead(NAV_CAN_INT)) { |
11 | NAV_CAN.readMsgBuf(&rxId, &len, rxBuf); |
12 | CAR_CAN.sendMsgBuf(rxId, 0, len, rxBuf); |
13 | }
|
14 | }
|
Also recht trivial, dachte ich. Das Teil steckt zwischen Radio und Fahrzeug auf dem Multimedia-CAN. Dieser hat 125kbaud und arbeitet mit Standard-IDs (11 Bit). Was mache ich denn hier falsch? Und wie hast Du es denn realisiert?
Stefan K. schrieb: > STM32 hat 2 CAN onboard. Ich sehe da im Blockschaltbild und der Doku nur einen CAN. Wie werden denn zwei angeschlossen? > 125kbit sollten eigendlich machbar sein, wenn ich mich recht erinnere, > dann lief ein MCP2515 bei mir bis 250kbit ohne Datenverlust. Das kommt Zumindest laut Datenblatt kann der bis 1 MBaud. Soviel will ich ja garnicht. Gebe mich mit lausigen 125kbit zufrieden :-) Der SPI dazwischen kann bis 8 MHz, also das sollte ja eigentlich reichen, dachte ich. > natürlich etwas auf die Firmware des ATmega drauf an. Allerdings würde > ich mir heute keinen MCP2515 mehr antun. Naja, das Ding ist ja billig und weit verbreitet. Und ich wollte mit meinen bestehenden Mitteln was zusammenbauen. Der STM32 und sicher auch viele andere Chips sind da mit sicherheit besser geeignet, da CAN-Controller integriert und eine Serielle Übertragung entfällt. Aber irgendwie dachte ich das die Anfoderung derart trivial sei, das sogar ein Arduino mit zwei MCP fast zuviel wäre...
:
Bearbeitet durch User
H.Joachim S. schrieb: > Würde ich heute auch nicht mehr, aber es geht problemlos. Ja, ich weiss wohl das ich da Altertümchen einsetze... ;-) > CAN-Botschaften auf Bit-Ebene sind länger als die Daten, die auf der SPI > hin- und hergeschoben werden, also zusätzlich Zeit gewonnen. Aber man > kann das auch grob 1:1 rechnen. > Bei 8Mbit SPI und 2x125kBit CAN hat man doch Zeit ohne Ende. So habe ich auch kalkuliert. Der MCP mach SPI bis 10 MHz in MODE0 oder MODE3. SPI selbst hat ja keinen Protokolloverhead, aber das MCP-Protokoll ein wenig. Um Daten zu lesen muss man natürlich ein Kommando senden. Der MCP empfängt erstmal alles in einen seiner beiden internen Puffer, dann senkt er INT. Ab hier muss dann die CPU einen Read-Befehl an den Controller senden und dieser sendet ID und Daten. Danach kann das ganze dann wieder vom anderen MCP gesendet werden. Also hat man wenigstens die Kommandobytes als Overhead. Aber wie Du schon sagst müsste das aufgrund der deutlich höheren Taktrate beim SPI kaum ins Gewicht fallen. > Interessant wird allenfalls, wenn der MCP bei fast vollem Bus seine > Sendedaten aufgrund niedriger Priorisierung nicht los wird und man sich > ständig darum kümmern muss. Aber dann ist eh am Grundkonzept was faul. In meinem Setup hat der MCP keine eigene Adresse. Er soll auf alle IDs reagieren und alle senden. Ich denke auch das das lesen grundsätzlich nicht das Problem ist.
Thomas F. schrieb: > Kommt halt auf die Buslast an. Wenn beide Busse schon über ~40% Buslast > haben und dann noch die Botschaften des anderen Busses mit drauf sollen > wird es arg voll. Das ist aber dann ein Transportproblem und keines des > MCP2515. Da war mein Eingangsthread vielleicht mißverständlich: Das Device kommt in Reihe zu einem bestehenden Bus. Da gibt es keine neuen Nachrichten, auch werden keine Busse zusammengelegt.
Du hast Recht, nicht alle STM32 haben 2 * CAN. Du kannst auf der ST-Seite nachschauen, welches Derivat wieviele Schnittstellen hat. STM32F1xx: ein paar ... STM32F2xx: alle ... STM32F4xx: fast alle ... haben 2 CAN-Schnittstellen. Ich hatte mich mit dem MCP2515 beim Debuggen ziemlich rumgeärgert, weil die Statusflags nur über SPI lesbar sind. Das ist ein ziemlicher Rückfall gewesen gegenüber dem Debuggen der anderen internen Schnittstellen über jtag. Wenn Du aber eine fertige Lib verwendest, dann ist das natürlich kein Problem für Dich. Viele Grüße, Stefan
Hm, ich kann mit deiner Software nicht viel anfangen... Wie regelst du den Empfang? Sollte per Interrupt geschehen. Leg dir 4 message-Variablen (2x Receive, 2xTransmit) an. typedef struct { unsigned int id; unsigned char rtr; unsigned char length; unsigned char data[8]; } CANMessage; In der jeweiligen ISR liest du den MCP2515 aus: // External Interrupt 0 service routine interrupt [EXT_INT0] void ext_int0_isr(void) {CAN_sw=0; //CS0 verwenden can_get_message(&CAN0Receive); CAN0MessageReceived=1; //zeigt an, dass auf CAN0 empfangen wurde } // External Interrupt 1 service routine interrupt [EXT_INT1] void ext_int1_isr(void) {CAN_sw=1; //CS1 verwenden can_get_message(&CAN1Receive); CAN1MessageReceived=1; } In der main fragst du die beiden flags (CAN0MessageReceived, CAN1MessageReceived) ab und kannst dann entsprechend reagieren. Im einfachsten Fall erst mal auf dem jeweiligen anderen Kanal senden. Da die Interrupts ohne Rücksicht auf Verluste auf du SPI zugreifen, musst du diese beim Senden sperren. if (CAN0MessageReceived) {CAN1Transmit=CAN0Received; CAN0MessageReceived=0; #asm ("cli") CAN_sw=1; can_send_message (&CAN1Transmit); #asm ("sei") } entsprechend für CAN1MessageReceived. Viel mehr brauchst du erstmal gar nicht.
H.Joachim S. schrieb: > Da die Interrupts ohne Rücksicht auf Verluste auf du SPI zugreifen, > musst du diese beim Senden sperren. Das trifft doch nur dann zu wenn der byteweise SPI-Transfer auch SPI-interruptgesteuert die Bytes rausschiebt? Oder denke ich gerade falsch?
H.Joachim S. schrieb: > SPI selbst läuft ohne Interrupt. Wenn man die komplette CAN-Botschaft "in einem Rutsch" per SPI abholt (RX) dann ist das Programm ja allein damit beschäftigt. Dann kann es nicht zeitgleich einen SPI-Senden-Task starten der dann das Empfangen stört. Somit braucht die Senderoutine nicht extra gesperrt werden, so mein Gedanke. Außer man startet den SPI-TX-Task im CAN-Interrupt. Aber das ist dann doch eher schlechter Stil denn so eine "Langzeitroutine" würde man doch eher aus der Main heraus starten?
-senden zum MCP läuft gerade -externer Interrupt kommt -und dann knallt dir die Leseroutine komplett dazwischen
Ich sehe noch nicht den funktionalen Unterschied zwischen meine Sende/Leseroutine, getriggert durch lesen der INT vom Shield und dem triggern einer ISR. Letztlich muss eine Botschaft gelesen und wieder rausgeschrieben werden. Ich gehe davon aus, das die Lib nach dem auslesen der Botschaft, den INT wieder hochzieht. Laut Datenblatt soll das MCP wphl selbst machen, wenn eine Interruptbedingung intern wieder geklärt wurde. Man kann das aber wohl auch per Statusregister machen. Die Routine wird doch nicht schneller oder stabiler durch den ISR im Atmega? Aber Joachim, vielleicht sendest Du mir mal Deinen Code und ich probiere es aus. Dann wird man ja sehen. Vielleicht irre ich mich ja.
Olli Z. schrieb: > vielleicht sendest Du mir mal Deinen Code Kann ich nicht machen, ist aus einem noch laufenden Produkt mmit ein paar relativ geheimen CAN-Infos. Könnte ja mal versuchen, alle interna rauszuschmeissen und das für einen Mega328 zu compilieren.
H.Joachim S. schrieb: > -senden zum MCP läuft gerade > -externer Interrupt kommt > -und dann knallt dir die Leseroutine komplett dazwischen Ach so. Ich setze bei mir im Ext.Interrupt vom MCP2515 nur mal schnell ein Flag-Bit und nicht mehr, wie man es halt so hier gelernt hat. Die eigentliche Leseroutine rufe ich dann erst in der Main aufgrund des Flags auf. So habe ich das Problem nicht.
Kann man so machen - aber der MCP2515 hat nur 2 rec-buffer. Wenn du in der main sicherstellen kannst, dass nichts verloren geht? Für mich hat der Empfang Priorität, damit nichts verlorengeht. Und das tütet man dann bei Bedarf in einen MC-internen Buffer ein und kann es (zur Not mit etwas Zeitversatz) immer noch bearbeiten. Läuft der MCP-Buffer über, sind Botschaften definitiv verloren.
Und genau diesen Überlauf müsste man doch erkennen können? Wie ist das mit dem REC oder TEC Fehlerzähler?
Olli Z. schrieb: > Und genau diesen Überlauf müsste man doch erkennen können? Wie ist das > mit dem REC oder TEC Fehlerzähler? Ja, aber nicht mit TEC und REC, sondern mit RX0OVR und RX1OVR. TEC und REC zählen Übertragungsfehler auf dem Bus.
Aber futsch ist futsch, auch wenn man es nachträglich erkennen kann. Thomas F. schrieb: > Ich setze bei mir im Ext.Interrupt vom MCP2515 nur mal schnell ein > Flag-Bit und nicht mehr, Halte ich in diesem Fall für falsch, da kannste dir den Interrupt gleich ganz sparen (statt dein flag in der main zu pollen, könntest du gleich zyklisch den Pegel am Int-Pin pollen). Vorteilhaft wäre in diesem Fall sogar, dass man jeden beliebigen Pin nehmen kann. Also: alle Ereignisse vom MCP so schnell wie möglich holen (umso mehr gilt das, wenn man mehr als einen dran hat), quittieren und wegschaufeln. Was damit dann passiert hat Zeit und kann in der main erledigt werden.
H.Joachim S. schrieb: > Thomas F. schrieb: >> Ich setze bei mir im Ext.Interrupt vom MCP2515 nur mal schnell ein >> Flag-Bit und nicht mehr, > > Halte ich in diesem Fall für falsch, da kannste dir den Interrupt gleich > ganz sparen (statt dein flag in der main zu pollen, könntest du gleich > zyklisch den Pegel am Int-Pin pollen). Vorteilhaft wäre in diesem Fall > sogar, dass man jeden beliebigen Pin nehmen kann. Stimmt. Das Platinenlayout mit Ext.Int war halt zuerst da. Und wenn eine Firmware mal läuft dann kopiert man die gerne immer wieder ins nächste Projekt mit rein;-)
Thomas F. schrieb: > Das Platinenlayout mit Ext.Int war halt zuerst da. So ist es ja auch erstmal richtig. Dann muss man es nur noch richtig benutzen :-). Je nach Buslast und Busgeschwindigkeit trennen sich dann irgendwann die optimalen von den suboptimalen Lösungen. Aber ich verfolge das Thema MCP2515 auch nicht mehr aktiv. War damals ne gute Lösung, als Controller mit internem CAN selten und teuer waren. Heute ist es Gefrickel und teurer als gleich eine CPU mit CAN zu verwenden.
Also, positive Rückmeldung von mir. Nachdem ich mich intensiv mit SPI und dem MCP beschäftigt hatte, etliche Libs und Sketches durchforscht, Datenblätter gelesen und viel viel mit dem LA debugged habe darf ich nun endlich Erfolg melden. Ich habe mein Interface störungsfrei am laufen. Arduino Nano mit zwei MCP2515-Shields. Aktuell verwende ich 8 MHz SPI-Takt, aber bereits bei 1 bzw. 2 MHz traten keine Störungen mehr auf. Es war aber nicht der SPI-Takt entscheident, sondern die Abfrage- und Sendelogik. Hier musste ich die MCP-Lib etwas anpassen und in meiner main einen Ringpuffer verwenden. Ich mache das ganze nicht interruptgesteuert, sondern einfach durch IO-Abfrage des INT-Signals. Wenn ich im weitern Verlauf des Projektes mehr Zeit in anderen Routinen verdaddeln muss, wäre eine Interrupt-Variante hilfreich damit wirklich keine Pakete verloren gehen. Da das ganze aber mehr auch ein Proof-Of-Concept war werde ich für komplexere Datenbehandlungen wohl eher auf einen CAN-Controller mit mehr Puffern zurückgreifen (SJA1000T) oder gleich einen STM32F3 oder F4 mit 2xCAN on Board. Habe aber viel gelernt dabei und danke Euch allen für die Unterstützung!
:
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.