Ich versuche mit einem Arduino einen CAN-Filter für den Multimedia-CAN-Bus in meinem Auto zu bauen. Dieser soll zunächst bidirektional alles durchlassen und bei Bedarf den Inhalt bestimmter IDs ändern.
Der MM MS-CAN in meinem Fahrzeug arbeitet mit Standard-Frames (11-Bit ID) und 125 kbps. Für einen ersten Test habe ich einen Arduino Nano und zwei CAN-Modulen mit MCP2515 (8 MHz) und TJA1050 genommen. Die Module habe ich in Reihe zum CAN-Bus Eingang des Radios geschaltet, d.h. Fahrzeugseitiger CAN in ein Modul und Radioseitiger in das andere. Auf CAN_H und CAN_L habe ich geachtet. Da im Radio kein CAN-Bus Terminator drin ist, habe ich das Radioseitige Modul per Jumper terminiert und das Fahrzeugseitige natürlich nicht.
Als Lib habe ich die "CAN-BUS Lib" von Cory J. Fowler verwendet, da mir diese recht weit fortgeschritten, aktuell gepflegt erschien und vor allem weil sie den MCP2515 mit 8 MHz von hause aus unterstützt.
Darin enthalten ist sogar schon ein Beispiel für einen solchen Repeater. Leider bleiben aufgrund der nicht vorhandenen Dokumentation sehr viele Fragen offen, es könnte also gut sein das ich was falsch gemacht hab. Jedenfalls habe ich den Sketch leicht an meinen Aufbau angepasst in Betrieb genommen. Anfangs sah es sogar so aus als würde es funktionieren. Leider hat das Audiogerät immer wieder Aussetzer gezeigt.
Nach etwas Recherche bin ich auf den One-Shot-Mode (OSM) gekommen, denn die IDs werden sowohl vom Radio als auch vom Fahrzeug her ständig wiederholt, da braucht es kein Resend vom Controller aus. Ich vermute das hat mehr Kollisionen und Bufferüberläufe verursacht als Pakete erfolgreich gesendet. Der OneShot-Mode sendet einmal und gut, ohne Fehlerzähler und Resend. Aber auch in diesem Modus blieb es bei den Aussetzern.
Ich kann es mir nicht erklären und suche daher hier um Rat. Vielleicht muss man der Lib ja noch weitere Parameter mitgeben (Filter/Mask, oder sowas) damit wirklich alles transparent von A nach B und B nach A kommt. Oder ich mache einen grundsätzlichen Denkfehler und das geht allein der Performance wegen nicht?
Anbei der Code und hier noch meine Fragen, generell dazu:
1.) Muss die Zeile mit "SPI.setClockDivider(...)" nicht VOR der ersten Kommunikation mit den Controllern stehen? Wie kann der sonst kommunizieren weil zu schnell? Und ist das "SPI_CLOCK_DIV2" auch für meinen 8 MHz getatkteten MCP richtig? Weil das Beispiel von einem 16 MHz getakteten ausgeht...
2.) Offenbar wird in der "begin()" Methode der CAN-Lib der Interrupt-Mode aktiviert (zumindest entnehme ich das den Quellen). Wenn das Modul ein Paket empfangen hat, zieht er das Signal auf LOW und der Arduino erkennt das. Wie lange hält der Controller das LOW? Bis zum lesen der Nachricht? Oder muss ich den wieder mit einem Befehl auf HIGH setzen? Verliere ich ggf. Nachrichten dadurch?
3.) 125 kbaud max. pro Bus ist ja eigentlich nicht viel. Selbst mal zwei sind nur 250. Das sollte der MCP und der Arduino mit seinen 16MHz doch locker weckstecken?
#include "MCP_CAN_lib/mcp_can.h"
#include <SPI.h>
unsigned long rxId;
byte len;
byte rxBuf[8];
#define CAR_CAN_CS 10 // CS of interface where CAN-Bus of car is connected to
#define CAR_CAN_INT 2 // INT of car-interface is connect to this Arduino port
#define NAV_CAN_CS 9 // CS of interface where CAN-Bus of radio is connected to
#define NAV_CAN_INT 3 // INT of radio-interface is connect to this Arduino port
MCP_CAN CAN0(CAR_CAN_CS);
MCP_CAN CAN1(NAV_CAN_CS);
void setup()
{
Serial.begin(115200);
// init CAN0 bus, baudrate: 125k@8MHz
if(CAN0.begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ) == CAN_OK){
Serial.print("CAN0 (CAR): Init OK!\r\n");
CAN0.setMode(MCP_NORMAL);
} else Serial.print("CAN0 (CAR): Init Fail!!!\r\n");
CAN0.enOneShotTX();
// init CAN1 bus, baudrate: 125k@8MHz
if(CAN1.begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ) == CAN_OK){
Serial.print("CAN1 (NAV): Init OK!\r\n");
CAN1.setMode(MCP_NORMAL);
} else Serial.print("CAN1 (NAV): Init Fail!!!\r\n");
CAN1.enOneShotTX();
SPI.setClockDivider(SPI_CLOCK_DIV2); // Set SPI to run at 8MHz (16MHz / 2 = 8 MHz)
}
void loop(){
if(!digitalRead(CAR_CAN_INT)){ // If pin 2 is low, read CAN0 receive buffer
CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
CAN1.sendMsgBuf(rxId, 0, len, rxBuf); // Immediately send message out CAN1 interface
}
if(!digitalRead(NAV_CAN_INT)){ // If pin 3 is low, read CAN1 receive buffer
CAN1.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
CAN0.sendMsgBuf(rxId, 0, len, rxBuf); // Immediately send message out CAN0 interface
}
}
Dual_CAN_TEST.zip (36.9 KB)



