Serie inDetail - komplexe Themen verständlich dargestellt

SPI Kommunikation zwischen dem MCP2515 und dem Mikrocontroller

Buffer Methoden

Sämtliche Kommunikation mit dem MCP2515 läuft über das Auslesen bzw. Beschreiben von Registern. Der Zugriff auf die Register erfolgt nach folgendem Muster:

Wichtig für das Verständnis sind noch die Variablen vom Typ "Buffer". Eine CAN Botschaft besteht aus vielen einzelnen Bytes die zusammengefasst als Buffer bezeichnet werden. Auf dem Arduino werden diese Buffer des MCP2515 gespiegelt. Sendebotschaften werden zunächst auf dem Arduino mit den "set-Methoden" zusammengestellt und dann gemeinsam mit "loadTxBuffer()" auf den CAN Controller übertragen.
Empfangenen Botschaften dagegen werden gemeinsam mit "readRxBuffer()" auf den Arduino geladen und mit get-Methoden anschließend zusammengefasst und bereitgestellt. .

In der vorbereiteten Klasse werden nun die wichtigsten der im Datenblatt angegebenen Befehle zur SPI Kommunikation als PRIVATE Methoden programmiert.

Klasse "MCP25215" mit PRIVATE Methoden #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; }; 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 }; //// PRIVATE METHODS ////////////////////////////////////////////////////////////////////////////////// void MCP2515::reset() { /* Funktion bildet den im Datenblatt angegebenen Befehl RESET als komplette SPI Sequenz ab. * Alle Control-Register werden mit den Werten der "Werkseinstellung" beschrieben. * Die Datenregisterinhalte werden nicht geändert. */ digitalWrite(CS, LOW); // CS auf LOW ziehen um den Slave auszuwählen SPI.transfer(B11000000); // RESET Befehl zum Auslesen eines Registers gemäß Datenblatt digitalWrite(CS, HIGH); // CS wieder freigeben delay(10); // Warten bis Daten vollständig gespeichert sind } //---------------------------------------------------------------------------------------------------- byte MCP2515::registerRead(byte regAdr) { /* Funktion bildet den im Datenblatt angegebenen Befehl READ als komplette SPI Sequenz ab. * Der ausgelesene Registerinhalt wird der Funktion zurückgegeben. */ digitalWrite(CS, LOW); // CS auf LOW ziehen um den Slave auszuwählen SPI.transfer(B00000011); // READ Befehl zum Auslesen eines Registers gemäß Datenblatt SPI.transfer(regAdr); // Übernahme der Adresse des auszulesenden Registers byte empfangen = SPI.transfer(0xff); // Einlesen von einem Byte über MISO digitalWrite(CS, HIGH); // CS wieder freigeben return empfangen; } //---------------------------------------------------------------------------------------------------- void MCP2515::registerWrite(byte regAdr, byte daten) { /* Funktion bildet den im Datenblatt angegebenen Befehl WRITE als komplette SPI Sequenz ab. */ digitalWrite(CS, LOW); // CS auf LOW ziehen um den Slave auszuwählen SPI.transfer(B00000010); // WRITE Befehl zum Auslesen eines Registers gemäß Datenblatt SPI.transfer(regAdr); // Übernahme der Adresse des zu beschreibenden Registers SPI.transfer(daten); // Daten die in das Register geschriebene werden sollen digitalWrite(CS, HIGH); // CS wieder freigeben delay(10); // Warten bis Daten vollständig gespeichert sind } //---------------------------------------------------------------------------------------------------- void MCP2515::modifyBits(byte regAdr, byte mask, byte daten) { /* Funktion bildet den im Datenblatt angegebenen Befehl BIT MODIFY als komplette SPI Sequenz ab. * Der Befehl dient zum ändern von einzelnen Bit innerhalb eines Registerbytes. Welche Bits * überschrieben werden wird durch eine MASKE festgelegt. Durch die MASKE B11100000 werden vom * anschließenden Byte nur die Stellen [7:5] übernommen. Bsp. Der Registerinhalt B10001100 wird durch * die Maske B11100000 und das Datenbyte B10100001 zu dem neuen Registerinhalt B10101100. * Es lassen sich nur die im Datenblatt grau hinterlegten Register modifizieren (meist Control-Register). */ digitalWrite(CS, LOW); // CS auf LOW ziehen um den Slave auszuwählen SPI.transfer(B00000101); // BIT MODIFY Befehl senden gemäß Datenblatt SPI.transfer(regAdr); // Register Adresse eines im Datenblatt grau hinterlegten Registers SPI.transfer(mask); // MASKE welche Bits geändert werden sollen (=1) SPI.transfer(daten); // Databyte digitalWrite(CS, HIGH); // CS wieder freigeben delay(10); } //---------------------------------------------------------------------------------------------------- void MCP2515::sendRTS(byte bufferNr) { /* Funktion bildet den im Datenblatt angegebenen Befehl RTS als komplette SPI Sequenz ab. * Der Variablen 'bufferNr' wird mitgegeben welcher Buffer gesendet werden soll (s. Datenblatt) * 0=kein Buffer, 1=TXB0, 2=TXB1, 3=TXB1+TXB0, 4=TXB2, 5=TXB2+TXB0, 6=TXB2+TXB1, 7=TXB2+TXB1+TXB0 * Durch eine if-Abfrage am Anfang wird verhindert, dass Werte größer 7 bearbeitet werden. */ if (bufferNr > 7) bufferNr = 7; // nur die Bits [2:0] werden betrachte digitalWrite(CS, LOW); // CS auf LOW ziehen um den Slave auszuwählen SPI.transfer(B10000000|bufferNr); // RTS (Request To Send) Befehl senden gemäß Datenblatt digitalWrite(CS, HIGH); // CS wieder freigeben } //---------------------------------------------------------------------------------------------------- void MCP2515::setMode(byte modus) { /* Mit der Funktion kann der Betriebsmodus des MCP2515 eingestellt werden. Mit der 'modifyBits()' * Funktion werden dazu die dafür vorgesehenen Bit im CANCTRL Register eingestellt. * 0=Normal Mode (Senden und Empfangen), 1=Sleep Mode (Ruhezustand), 2=Loopback Mode (Testmodus zum * Übertragen von TX auf RX Buffer ohne Bus), 3= Listen Mode, 4= Config Mode (zum Einstellen einiger * Register notwendig). */ if (modus > 4) modus = 4; byte neueBits = modus << 5; modifyBits(B00001111, B11100000, neueBits); // Register Adresse 'CANCTRL', Maske [7:5], neue Datenbits) } //---------------------------------------------------------------------------------------------------- void MCP2515::loadTXbuffer(byte bufferNr) { /* Funktion bildet den im Datenblatt angegebenen Befehl LOAD TX BUFFER als komplette SPI Sequenz ab. * Im Datenblatt sind drei unterschiedliche Befehle angegeben mit denen man jeweils Daten in die Buffer * TX0B, TX1B oder TX2B auf dem MCP2515 übertragen kann. Die zu Beginn initialisierten Variablen * 'loadBuffer' und 'befehl' dienen zum temporären speichern des Befehls und des Buffer-Inhalts die im * unteren Teil per SPI übertragen werden. Mit der if-Anweisung werden die Variablen mit den jeweils * richtigen Daten gefüllt. */ Buffer loadBuffer; // Variable des selbst definierten Typs Buffer byte befehl; // Variable die den Befehl abhängig vom der gewählten 'bufferNr' aufnimmt // If Anweisung wählt den entsprechenden Befehl und den Inhalt des ausgewählten Buffers if (bufferNr == 0) { befehl = B01000000; // Start Auslesen bei Register TXB0SIDH loadBuffer = TXB0; } else if (bufferNr == 1) { // Start Auslesen bei Register TXB1SIDH befehl = B01000010; loadBuffer = TXB1; } else { // Start Auslesen bei Register TXB2SIDH befehl = B01000100; loadBuffer = TXB2; } // Übertragen des Buffers digitalWrite(CS, LOW); // CS auf LOW ziehen um den Slave auszuwählen SPI.transfer(befehl); // LOAD TX BUFFER Befehl senden gemäß Datenblatt SPI.transfer(loadBuffer.SIDH); // Aufgrund Autoincrement werden nachfolgende Register... SPI.transfer(loadBuffer.SIDL); // ... hintereinander eingelesen. SPI.transfer(loadBuffer.EID8); SPI.transfer(loadBuffer.EID0); SPI.transfer(loadBuffer.DLC); SPI.transfer(loadBuffer.D0); SPI.transfer(loadBuffer.D1); SPI.transfer(loadBuffer.D2); SPI.transfer(loadBuffer.D3); SPI.transfer(loadBuffer.D4); SPI.transfer(loadBuffer.D5); SPI.transfer(loadBuffer.D6); SPI.transfer(loadBuffer.D7); digitalWrite(CS, HIGH); // CS wieder freigeben } //---------------------------------------------------------------------------------------------------- void MCP2515::readRXbuffer(byte bufferNr) { /* Funktion bildet den im Datenblatt angegebenen Befehl READ RX BUFFER als komplette SPI Sequenz ab. * Im Datenblatt sind zwei Befehle angegeben mit denen man jeweils Daten entweder aus RXb0 oder RXB1 * auslesen kann. Die beiden zu Beginn initialisierten Variablen dienen zur Zwischenspeicherung des über * die if-Abfrage festgelegten Befehls und der per SPI eingelesenen Daten. Mit der if-Abfrage am Ende * wird der Inhalt an den entsprechenden Buffer RXB0 oder RXB1 an den MCU übertragen. */ Buffer receiveBuffer; // Variable des selbst definierten Typs Buffer byte befehl; // Variable die den Befehl abhängig vom der gewählten 'bufferNr' aufnimmt // If Anweisung wählt den entsprechenden Befehl für den gewählten Buffer aus if (bufferNr == 0) { befehl = B10010000; // Start Auslesen bei Register RXB0SIDH } else { befehl = B10010100; // Start Auslesen bei Register RXB1SIDH } // Übertragen des Buffers digitalWrite(CS, LOW); // CS auf LOW ziehen um den Slave auszuwählen SPI.transfer(befehl); // READ RX BUFFER Befehl senden gemäß Datenblatt receiveBuffer.SIDH = SPI.transfer(0xff); // Aufgrund Autoincrement werden nachfolgende... receiveBuffer.SIDL = SPI.transfer(0xff); // ...Register hintereinander ausgelesen. receiveBuffer.EID8 = SPI.transfer(0xff); receiveBuffer.EID0 = SPI.transfer(0xff); receiveBuffer.DLC = SPI.transfer(0xff); receiveBuffer.D0 = SPI.transfer(0xff); receiveBuffer.D1 = SPI.transfer(0xff); receiveBuffer.D2 = SPI.transfer(0xff); receiveBuffer.D3 = SPI.transfer(0xff); receiveBuffer.D4 = SPI.transfer(0xff); receiveBuffer.D5 = SPI.transfer(0xff); receiveBuffer.D6 = SPI.transfer(0xff); receiveBuffer.D7 = SPI.transfer(0xff); digitalWrite(CS, HIGH); // CS wieder freigeben // if-Abfrage zum Kopieren des receiveBuffers in den entsprechenden RX-Buffer if (bufferNr == 0) { RXB0 = receiveBuffer; // receiveBuffer kopieren in RXB0 } else { RXB1 = receiveBuffer; // receiveBuffer kopieren in RXB1 } }