Serie inDetail - komplexe Themen verständlich dargestellt

Empfangen eines CAN Frames

Der MCP2515 überwacht ständig den CAN Bus. Empfängt er eine Nachricht wird diese in einem von zwei Buffer mit je 13 Registerbytes verschoben (RXB0 u. RXB1). Ob die Nachricht in den Buffer geladen wird, kann durch vorgeschaltete Filter eingestellt werden (siehe Einstellungen).

Wurde in RXB0 oder RXB1 eine Nachricht geladen wird das RX_IF bit im Register CANINTF auf '1' gesetzt und es kann keine weitere neue Nachricht gespeichert werden. Erst wenn die Nachricht vom Mikrocontroller abgeholt wurde und das RX_IF bit wieder gelöscht ist, kann eine neue Nachricht empfangen werden.

Zum Empfangen eines CAN Frames sind auch wieder 3 Schritte notwendig:

  1. Überprüfen ob das RX_IF bit gesetzt ist und damit anzeigt, dass eine neue Nachricht zum abholen vorhanden ist.
  2. Mit 'loadRXbuffer()' wird die neue Nachricht in den gleichnamigen Buffer des Arduinos geladen.
  3. Über get-Methoden kann der Nachrichtentyp angezeigt und die ID oder die Nutzdaten (=Payload) ausgegeben werden.

Um die Nutzdaten mit einer Methode als Rückgabewert auszugeben wird ein eigener Variablentyp 'Payload' definiert. Dieser Typ enthält bis zu 8 Nutzdatenbytes.

Wichtig ist, dass die Empfangsfilter richtig eingestellt oder gelöscht wurden. Zum Empfangen einer EID Nachricht muss das EXIDE bit in mindestens einem Filter auf '1' gesetzt sein (siehe Einstellungen).

void loop() if(CAN.loadRXbuffer(0) == true) { Serial.println(CAN.getID(0)); Payload PL = CAN.getPayload(0); Serial.print(PL.anzahlBytes); // 0= Remote Frame (1-8 = Anzahl empfangene Datenbytes) Serial.print(" "); Serial.print(PL.D0, HEX); Serial.print(" "); Serial.print(PL.D1, HEX); Serial.print(" "); Serial.print(PL.D2, HEX); Serial.print(" "); Serial.print(PL.D3, HEX); Serial.print(" "); Serial.print(PL.D4, HEX); Serial.print(" "); Serial.print(PL.D5, HEX); Serial.print(" "); Serial.print(PL.D6, HEX); Serial.print(" "); Serial.print(PL.D7, HEX); Serial.println(" "); } }
Klasse "MCP25215" mit den Methoden zum Empfangen eines CAN Frames #include "SPI.h" /* Erstellen eines eigenen Datentyps 'Buffer' mit den gleichen Namen und der gleichen Reihenfolge wie die * entsprechenden Buffer im MCP2515 (Kopie der 5 Buffer 3x Tx und 2xRx) */ struct Buffer{ byte SIDH; byte SIDL; byte EID8; byte EID0; byte DLC; byte D0, D1, D2, D3, D4, D5, D6, D7; }; /* Erstellen eines eigenen Datentyps 'Payload' um die empfangenen Nutzdaten mit der Funktion 'get Payload' * zurückzugeben.*/ struct Payload { byte anzahlBytes; byte D0, D1, D2, D3, D4, D5, D6, D7; }; class MCP2515 { private: byte CS; // Speichern des von Konstruktor übertragenen 'Chip Select' Pins Buffer RXB0, RXB1, TXB0, TXB1, TXB2; // Byte-Container vom oben definierten Typ 'Buffer' void reset(); // Reset des MCP2515 (= wie VCC Unterbrechung) byte registerRead(byte); // Liest ein Registerinhalt (regAdr) void registerWrite(byte, byte); // Beschreibt ein Register (regAdr, daten) void modifyBits(byte, byte, byte); // Verändert einzelne Bits des Registers (regAdr, maske, daten) void sendRTS(byte); // 'Request to Send' eines TX Buffers (bufferNr) void setMode(byte); // 0=Normal Mode, 1=Sleep , 2=Loopback , 3= Listen , 4= Config void readRXbuffer(byte); // Auslesen und quittieren eines RX/Empfangs Buffer (bufferNr) void loadTXbuffer(byte); // Übertragen eines kompletten Buffers auf den MCP2515 (bufferNr) public: // Konstruktor (Initialisierungsfunktion) MCP2515(byte cs = 10) { SPI.begin(); SPI.setDataMode(SPI_MODE0); // oder SPI_MODE3 SPI.setClockDivider(SPI_CLOCK_DIV4); // max. 10 Mhz SPI.setBitOrder(MSBFIRST); CS = cs; // Ein andere CS Pin kann optional übergeben werden pinMode(CS, OUTPUT); // Chip Select Pin für die SPI Schnittstelle konfigurieren // Befehle zum Senden von Nachrichten void setSID(byte, unsigned int); // Schreibt die SID in den gewählten Buffer (bufferNr, SID) void setEID(byte, unsigned long); // schreibt die EID in den gewählten Buffer (bufferNr, EID) void setPayload(byte, byte, byte, byte, byte, byte, byte, byte, byte); // schreibt die Nutzdaten void setDLC(byte, byte, bool); // (bufferNr, anzahlPayload, RemoteFrame) void sendTXbuffer(byte); // sendet den gewählten Buffer auf den CAN Bus (bufferNr) // Befehle zum Empfangen von Nachrichten bool loadRXbuffer(byte); // Überträgt einen RX Buffer. Gibt true zurück bei neuer Nachricht byte getStatus(byte); // Analysiert die die geladene Nachricht und gibt den Status aus unsigned long getID(byte); // Gibt die ID (SID oder EID) der heruntergeladenen Nachricht zurück Payload getPayload(byte); // Gibt die kompl. Nutzdaten der heruntergeladenen Nachricht zurück }; //// PRIVATE METHODS ////////////////////////////////////////////////////////////////////////////////// /* Zur besseren Übersichtlichkeit werden die PRIVATE Methoden hier nicht mehr angezeigt */ // PUBLIC METHODEN /////////////////////////////////////////////////////////////////////////////////// /* Zur besseren Übersichtlichkeit werden die Methoden zum Senden hier nicht mehr angezeigt */ // Befehle zum Empfangen von Nachrichten bool MCP2515::loadRXbuffer(byte bufferNr) { /* Liest zunächst das Register CANINTF aus. Ist dort das entsprechende Empfangs-Flag gesetzt * (B0 für RX-Buffer 0 und B1 für Rx-Buffer 1) wird der Buffer auf den Mikrocontroller geladen und * 'true' zurückgegeben. * Ist kein Empfangs-Flag gesetzt wrd der Funktion 'false' zurückgegeben. */ byte mask; if (bufferNr == 0) mask = 0b00000001; else mask= 0b00000010; byte CANINTF = registerRead(0x2C); // Register CANINTF auslesen if ((CANINTF & mask) == true) { readRXbuffer(bufferNr); return true; } else { return false; } } //---------------------------------------------------------------------------------------------------- byte MCP2515:: getStatus(byte bufferNr) { /* Die Funktion liest verschieden Bits aus und ermittelt anhand dieser die Art der Nachricht. * SID oder EID, RTR oder Standard Nachricht. Die Bits sind im Datenblatt beschrieben. */ byte status; switch(bufferNr) { case 0: if ((RXB0.SIDL & 0b00001000) == 0b00001000) Serial.println("Extended Identifier Frame"); else Serial.println("Standard Identifier Frame"); if ((RXB0.DLC & 0b01000000) == 0b01000000) Serial.println(" als Remote Frame (RTR)"); else { Serial.println(" als Normal Frame (kein RTR)"); Serial.println(" mit " + String(RXB0.DLC & 0b00001111) + " Byte Payload"); } Serial.println(); break; default: break; }; } //---------------------------------------------------------------------------------------------------- unsigned long MCP2515::getID(byte bufferNr) { /* Die Funktion gibt die ID der CAN Botschaft zurück (egal ob SID oder EID). Mit der if-Abfrage wird * zunächst der Buffer ausgewählt. Danach wird überprüft of es sich um eine SID oder EID ID handelt. * Abhängig davon wird die ID aus den entsprechenden heruntergeladenen Bytes zusammengebaut. */ if (bufferNr == 0) { // weiter bei Buffer RXB0 if ((RXB0.SIDL & B00001000) == 0) { // EXIDE=0 ist eine SID uint32_t SID = (uint32_t)RXB0.SIDH << 3 | ((uint32_t)RXB0.SIDL & B11100000) >> 5; return SID; } else { // EXIDE=1 ist eine EID uint32_t EID = (uint32_t)RXB0.SIDH << 21 | ((uint32_t)RXB0.SIDL & B11100000) << 13 | ((uint32_t)RXB0.SIDL & B00000011) << 16 | (uint32_t)RXB0.EID8 << 8 | (uint32_t)RXB0.EID0; return EID; } } else { // weiter bei Buffer RXB1 (o. Falscheingabe) if ((RXB1.SIDL & B00001000) == 0) { // EXIDE=0 ist eine SID uint32_t SID = (uint32_t)RXB1.SIDH << 3 | ((uint32_t)RXB1.SIDL & B11100000) >> 5; return SID; } else { // EXIDE=1 ist eine EID uint32_t EID = (uint32_t)RXB1.SIDH << 21 | ((uint32_t)RXB1.SIDL & B11100000) << 13 | ((uint32_t)RXB1.SIDL & B00000011) << 16 | (uint32_t)RXB1.EID8 << 8 | (uint32_t)RXB1.EID0; return EID; } } } //---------------------------------------------------------------------------------------------------- Payload MCP2515::getPayload(byte bufferNr) { /* Die Funktion getPayload() gibt die heruntergeladenen Nutzdatenbytes zurück. Dazu wird ein eigenen * Dateityp 'Payload' verwendet. Es werden alle 8 Bytes zurückgegeben. Unter 'anzahlBytes' wird die Zahl * zurückgegeben wie viele Nutzdaten davon gültig sind. Bei einem Remote Frame werden die Nutzdaten * auf 0 gesetzt. Unabhängig ob im DLC Register ein andere Wert steht. */ Payload pl; // Variable des Typs Payload erstellen Buffer gewaehlterBuffer; // Variable des Typs Buffer erstellen if (bufferNr == 0) gewaehlterBuffer = RXB0; else gewaehlterBuffer = RXB1; pl.D0 = gewaehlterBuffer.D0; pl.D1 = gewaehlterBuffer.D1; pl.D2 = gewaehlterBuffer.D2; pl.D3 = gewaehlterBuffer.D3; pl.D4 = gewaehlterBuffer.D4; pl.D5 = gewaehlterBuffer.D5; pl.D6 = gewaehlterBuffer.D6; pl.D7 = gewaehlterBuffer.D7; if ((gewaehlterBuffer.DLC & B01000000) == B01000000) { pl.anzahlBytes = 0; } else { pl.anzahlBytes = gewaehlterBuffer.DLC & B00001111; } return pl; // Variable 'pl' der Funktion zurückgeben }