Themen:

AVR, avr-gcc, CAN, CPLD, Elektronik, Mikrocontroller, MSP430, PIC, Roboter, Schaltungen, Sensoren, Software, Testboards

Ansteuerung des MCP2515 (Tutorial)

Tags: AVR, Software, CAN, avr-gcc
Stand: 6. Juli 2007, 16:36
113 Kommentar(e)

Ein kleines Tutorial zum Einstieg in die Programmierung des MCP2515 unter avr-gcc

UPDATE: Wer den MCP2515 in eigenen Projekten verwenden will sollte sich auch meine canlib anschauen. Diese unterstützt neben dem MCP2515 auch den SJA1000 und die AT90CAN-Reihe. Der Quelltext davon baut auf den hier vorgestellten Routinen auf, ist aber besser dokumentiert und gut getestet und sollte daher dem Demo-Projektes hier vorgezogen werden.

Dies hier soll eine Schritt für Schritt Anleitung werden wie man mit Hilfe eines AVRs (in dem Fall ein ATMega8) und des MCP2515 Nachrichten über den CAN Bus verschickt. Es kann natürlich keine vollständige Beschreibung werden, da der MCP2515 doch eine ganze Menge an Funktionalität bietet. Ich hoffe aber das diese Anleitung trotzdem einen Einstieg in das Thema liefert.

Alle die noch nicht so richtig wissen was ein CAN Bus eigentlich ist bzw. was man damit machen kann sollten sich erstmal zum Beispiel bei Wikipedia oder im CAN-Wiki umschauen.

Doch zurück zum MCP2515. Das Tutorial ist in mehrere Bereiche gegliedert:

  1. Lesen bzw. schreiben der Register
  2. Initialisieren des MCP2515
  3. Senden von CAN Nachrichten
  4. Empfangen von CAN Nachrichten

Die Programmteile sind auf das CAN-Testboard ausgelegt, sollten sich aber auch ohne große Änderungen für andere Boards anpassen lassen.

Der MCP2515 wird per SPI angesteuert. Da der AVR schon von Haus aus ein Hardware SPI-Interface mitbringt erleichtert dies die Ansteuerung enorm. Um das SPI-Interface nutzen zu können muss man es als erstes initialisieren:

C:
void spi_init(void)
{
    // Aktivieren der Pins für das SPI Interface
    DDR_SPI  |= (1<<P_SCK)|(1<<P_MOSI);
    PORT_SPI &= ~((1<<P_SCK)|(1<<P_MOSI)|(1<<P_MISO));
   
    DDR_CS   |= (1<<P_CS);
    PORT_CS  |= (1<<P_CS);
   
    // Aktivieren des SPI Master Interfaces, fosc = fclk / 2
    SPCR = (1<<SPE)|(1<<MSTR);
    SPSR = (1<<SPI2X);
}

Das SPI Interface wird dadurch in den Master Modus geschaltet. Man kann für das SPI Interface ruhig die maximale Geschwindigkeit (fosc / 2 = 8 MHz Clk-Takt bei 16 MHz Quarz takt) wählen, da der MCP2515 bis zu 10 MHz SPI Takt verarbeiten kann. Bei schlechter Verlegung der SPI Leitungen kann es dabei allerdings zu Problemen kommen, in diesem Fall muss die Geschwindigkeit herunter gesetzt werden. Auf meinem CAN Testboard läuft der SPI Bus mit ca. 3,7 MHz (aufgrund der 7,3728 MHz Taktfrequenz des AVRs) einwandfrei.

Bei der Initialisierung muss man beachten, dass man auch die entsprechenden Pins als Ausgang (SCK, MOSI, \CS) bzw. Eingang (MISO) schaltet. Im Gegensatz zu vielen anderen Schnittstellen der AVRs (UART, TWI usw.) wird dies hier nicht alles automatisch von Chip selbst erledigt. Um den Code auch für andere AVRs anpassen zu können habe ich die entsprechenden Pins über #defines festgelegt. Somit müssen sie nur an einer Stelle geändert werden wenn man zum Beispiel einmal den \CS Pin umdefinieren will (die anderen drei Pins sind für den jeweiligen AVR spezifisch und können nicht geändert werden, es sei denn man programmiert das SPI Interface selber).

C:
#define DDR_CS      DDRB
#define PORT_CS     PORTB
#define P_CS        2

#define DDR_SPI     DDRB
#define PORT_SPI    PORTB
#define P_MISO      4
#define P_MOSI      3
#define P_SCK       5

Um für die SPI Kommandos bzw. die MCP2515 Registeradressen nicht immer im Datenblatt nachschauen zu müssen habe ich zudem eine Datei mit den wichtigsten #defines dazu angelegt. So kann man dann einfach die Registernamen bzw. Bit namen in Quellcode verwenden.

Die entsprechenden Datei kann hier heruntergeladen werden.

Ab jetzt kann man ganz einfach Bytes über den SPI Bus schieben:

C:
uint8_t spi_putc( uint8_t data )
{
    // Sendet ein Byte
    SPDR = data;
   
    // Wartet bis Byte gesendet wurde
    while( !( SPSR & (1<<SPIF) ) )
        ;
   
    return SPDR;
}

Da beim SPI-Bus das Senden um Empfangen auf zwei verschiedenen Leitungen läuft, kann gleichzeitig gesendet und empfangen werden. Um ein Byte zu empfangen muss man also ein Dummy-Byte beliebigen Inhalts auf den Bus legen und dabei die Daten auf der Empfangsleitung auswerten.

C:
// Daten per SPI senden
spi_putc(0xaa);

// Daten per SPI lesen
data = spi_putc(0xff);

Ab jetzt kann man auch einmal mit dem MCP2515 kommunizieren. Die Kommunikation läuft dabei immer nach dem gleichen Schema ab:

Die Funktionen zum Beschreiben bzw. Lesen von Registern sehen daher folgendermaßen aus:

Registerwerte schreiben

C:
void mcp2515_write_register( uint8_t adress, uint8_t data )
{
    // /CS des MCP2515 auf Low ziehen
    PORT_CS &= ~(1<<P_CS);
   
    spi_putc(SPI_WRITE);
    spi_putc(adress);
    spi_putc(data);
   
    // /CS Leitung wieder freigeben
    PORT_CS |= (1<<P_CS);
}

Registerwerte lesen

C:
uint8_t mcp2515_read_register(uint8_t adress)
{
    uint8_t data;
   
    // /CS des MCP2515 auf Low ziehen
    PORT_CS &= ~(1<<P_CS);
   
    spi_putc(SPI_READ);
    spi_putc(adress);
   
    data = spi_putc(0xff)
   
    // /CS Leitung wieder freigeben
    PORT_CS |= (1<<P_CS);
   
    return data;
}

Eine weiteres nützliche Feature des MCP2515 ist die Bit-Modify-Funktion. Dabei werden nur bestimmt Bits gesetzt bzw. gelöscht. Ein Bespiel dafür wird man unter anderem bei der Initialisierung sehen, wo damit die Operationsmodi umgeschaltet werden:

Einzelne Bits setzen/löschen

C:
void mcp2515_bit_modify(uint8_t adress, uint8_t mask, uint8_t data)
{
    // /CS des MCP2515 auf Low ziehen
    PORT_CS &= ~(1<<P_CS);
   
    spi_putc(SPI_BIT_MODIFY);
    spi_putc(adress);
    spi_putc(mask);
    spi_putc(data);
   
    // /CS Leitung wieder freigeben
    PORT_CS |= (1<<P_CS);
}

Allerdings muss man aufpassen da sich diese Funktion nur auf bestimmte Register anwenden lässt (siehe z.B. Datenblatt auf Seite 59).
Es wird dabei zuerst eine Bitmaske gesendet. Nur die Bits die dabei gesetzt sind werden später auch verändert. Danach werden die eigentlichen Daten gesendet. Ist ein Bit in der Maske gesetzt und bei den Daten gelöscht, so wird auch das entsprechende Bit im Register des MCP2515 gelöscht. Analog wenn das entsprechende Bit im Datenbyte gesetzt ist.

Jetzt kann man daran gehen den MCP2515 zum Senden bzw. Empfangen von CAN Nachrichten zu initialisieren:

Das einzig schwierige bei der Initialisierung ist das Einstellen des Bit-Timings bzw. der Bit Rate des CAN Buses.

Um die verschieden Konfigurationsregister des MCP2515 beschreiben zu können muss man ihn erstmal in den Konfigurationsmodus versetzen. Nur in diesem Modus sind die Register CNF1, CNF2, CNF3, TXRTSCTRL und die Register für die Filter bzw. Masken beschreibbar.

Es gibt noch einige andere Modi auf die ich an dieser Stelle nicht weiter eingehen werden. Das Datenblatt erklärt es aber eigentlich ganz gut ;-)

Einstellen des Bit Timings

Da das Thema doch recht komplex ist gibt es an dieser Stelle erstmal nur ein paar Links zu selbst weiter lesen bzw. fertige Registerwerte für verschiedene Bitraten.

Die Bitrate wird über die drei Register CNF1 bis CNF3 eingestellt:

C:
// CAN Bitrate 125 kbps
#define R_CNF1  (1<<BRP2)|(1<<BRP1)|(1<<BRP0)
#define R_CNF2  (1<<BTLMODE)|(1<<PHSEG11)
#define R_CNF3  (1<<PHSEG21)

// CAN Bitrate 250 kbps
#define R_CNF1  (1<<BRP1)|(1<<BRP0)
#define R_CNF2  (1<<BTLMODE)|(1<<PHSEG11)
#define R_CNF3  (1<<PHSEG21)

// CAN Bitrate 500 kbps
#define R_CNF1  (1<<BRP0)
#define R_CNF2  (1<<BTLMODE)|(1<<PHSEG11)   
#define R_CNF3  (1<<PHSEG21)

// CAN Bitrate 1 Mbps
#define R_CNF1  0
#define R_CNF2  (1<<BTLMODE)|(1<<PHSEG11)
#define R_CNF3  (1<<PHSEG21)

Allen die wissen wollen wie man auf diese Werte kommt kann ich das Datenblatt (ab Seite 37) bzw. folgende Links empfehlen:

Bit Timing Calculator
kvaser.com

Insbesondere der Microchip CAN Bit Timing Calculator (erster Link) ist sehr gut geeignet um die Berechnungen einmal grafisch darzustellen und verschiedene Einstellungen auszuprobieren. Interrupts

Es gibt acht verschiedene Interrupt-Quellen:

  1. Message Error Interrupt
  2. Wakeup Interrupt
  3. Error Interrupt Enable (multiple sources in EFLG register)
  4. Transmit Buffer 2 Empty Interrupt
  5. Transmit Buffer 1 Empty Interrupt
  6. Transmit Buffer 0 Empty Interrupt
  7. Receive Buffer 1 Full Interrupt
  8. Receive Buffer 0 Full Interrupt

Aktiviert man einen dieser Interrupts, so wird jedes mal die Interruptleitung auf Low gezogen wenn die Bedingung zutrifft. Die Leitunge bleibt dann so lange auf Low Pegel bis alle Interrupt-Bedingungen aufgelöst wurden.

Bei meiner Software habe ich nur die beiden Puffer-Voll-Interrupts genutzt, um anzeigen zu lassen das eine neue Nachricht eingetroffen ist. Natürlich gibt es aber noch viel mehr Möglichkeiten die Interrupts zu nutzen, im Datenblatt auf Seite 49 findet sich die genaue Beschreibung zum Weiterlesen.

Um die Interrupts zu aktivieren muss man nur das entsprechende Bit im CANINTE Register setzen:

C:
// Aktivieren der Rx Buffer Interrupts
mcp2515_write_register(CANINTE, (1<<RX1IE)|(1<<RX0IE));

Pin Funktionen

Der MCP2515 bietet auch noch fünf Pins die auf verschiedene Arten genutzt werden können. Die beiden Pins RX0BF und. RX1BF können entweder als Status Leitungen für den “Puffer voll” Interrupt fungieren oder aber von Anwender als Digitale Ausgänge genutzt werden. So könnte man zum Beispiel noch zwei zusätzliche Status-LEDs anschließen ohne dafür I/Os des AVRs zu benutzten.

Die Verwendung der Pins wird über das Register BFPCTRL gesteuert. Da auf diese Register die Bit-Modify-Funktion angewendet werden darf kann man darüber sehr leicht diese Pins steuern.

C:
// RX0BF als Digitalen Ausgang nutzen
mcp2515_bit_modify( BFPCTRL, (1<<B0BFE)|(1<<B0BFM), (1<<B0BFE));

// Pin setzen
mcp2515_bit_modify( BFPCTRL, (1<<B0BFE), (1<<B0BFE));

Die Ansteuerung erfolgt analog für RX1BF. Aber natürlich kann man die Pins auch als Interrupt-Ausgänge verwenden:

C:
// RX0BF und RX1BF als Interruptausgänge nutzen
mcp2515_write_register( BFPCTRL, (1<<B1BFE)|(1<<B0BFE)|(1<<B1BFM)|(1<<B0BFM));

Genauso kann man auch die TXnRTS Pins entweder als Digitale Eingänge verschalten oder damit das Senden der Nachricht in dem entsprechenden Puffers anstoßen. Dabei erfolgt das aktivieren der jeweiligen Funktion über das Register TXRTSCTRL (siehe Datenblatt Seite 19). Die Ansteuerung erfolgt im Prinzip genauso wie bei den RXnBF Pins.

CLKOUT Pin

Der MCP2515 bietet die Möglichkeit andere Bausteine mit einem Taktsignal zu versorgen. Über den CLKOUT Pin wird dann das Taktsignal des MCP2515 ausgegeben,um so zum Beispiel einen Mikrocontroller mit einem Takt zu versorgen. Es lässt sich außerdem noch ein interner Vorteiler aktivieren, der das Taktsignal noch einmal durch zwei, vier oder acht teilt und so nochmal verschiedene Taktfrequenzen ermöglicht. Bei 16 MHz Takt stehen einem so 16 MHz, 8 MHz, 4 MHz und 2 MHz Takt zu Verfügung.

Der CLKOUT Pin wird über die letzten drei Bits im CANCTRL Register gesteuert. Da standardmäßig beim an legen einer Spannung an den MCP2515 der CLKOUT Pin aktiviert ist kann man damit durchaus den AVR, der den MCP2515 steuert, betreiben.

C:
// Vorteiler des CLKOUT Pin auf null setzen,
// => Taktfrequenz des MCP2515 am CLKOUT Pin ausgeben
mcp2515_bit_modify( CANCTRL, 0x07, (1<<CLKEN));

Ein bisschen muss man allerdings aufpassen, dass man wenn man dies nutzten will, nicht die beiden Resetleitungen von AVR und MCP2515 verbindet, da man ansonsten ein Problem bei dem Programmieren des AVRs bekommt. Beim Programmieren würde nämlich die Resetleitung des AVRs und damit auch des MCP2515s auf Low gezogen werden, damit würde auch der MCP2515 in den Reset-Modus gezogen und dadurch den CLKOUT Pin deaktivieren. Allerdings ist es nicht möglich einen AVR zu programmieren wenn er kein Taktsignal hat. Das einzige was dann noch hilft ist die Resetleitung zu trennen und dem MCP2515 einen eigenen Pullup zur Versorgungsspannung zu spendieren. Allerdings sollte man dann immer in der Initialisierungsroutine den MCP2515 Reseten, damit er einen definierten Anfangszustand hat.

Akzeptanz Filter und Masken

Wie schon auf der Anfangsseite erwähnt ist eine der großen Stärken des CAN Buses (bzw. genauer gesagt der CAN Controller), dass man auf verschiedene Nachrichten Filtern kann. Der MCP2515 bietet zwei Filter für Buffer 0 und nochmal vier Stück für Buffer 1. So lassen sich Nachrichten die nicht für den Knoten bestimmt sind schon von vorne herein heraus filtern ohne das der AVR darauf reagieren muss.

Zu den Filtern findet man folgende Tabelle im Datenblatt:

Mask Bit Filter Bit Message Identifier bit  
0 x x Accept
1 0 0 Accept
1 0 1 Reject
1 1 0 Reject
1 1 1 Accept

Die einzelnen Bits des Identifiers werden also nur zur Filterung herangezogen, wenn man das entsprechende Bit in der Maske setzt. Will man also (z.B. zu Testzwecken) alle Nachrichten empfangen, so sollte man einfach alle Maskenbits auf Null setzten. Damit werden alle Nachrichten, unabhängig von ihrer ID in den Empfangspuffer weitergeleitet.

Man sollte allerdings beachten das die Filter bzw. Masken können nur im Konfigurationsmodus gesetzt werden.

Und jetzt auch einmal ein vollständiger Code zur Initialisierung:

C:
void mcp2515_init(void)
{
    // SPI Interface initialisieren
    spi_init();
   
    // MCP2515 per Software Reset zuruecksetzten,
    // danach ist der MCP2515 im Configuration Mode
    PORT_CS &= ~(1<<P_CS);
    spi_putc( SPI_RESET );
    _delay_ms(1);
    PORT_CS |= (1<<P_CS);
   
    // etwas warten bis sich der MCP2515 zurueckgesetzt hat
    _delay_ms(10);
   
    /*
     *  Einstellen des Bit Timings
     * 
     *  Fosc       = 16MHz
     *  BRP        = 7                (teilen durch 8)
     *  TQ = 2 * (BRP + 1) / Fosc  (=> 1 uS)
     * 
     *  Sync Seg   = 1TQ
     *  Prop Seg   = (PRSEG + 1) * TQ  = 1 TQ
     *  Phase Seg1 = (PHSEG1 + 1) * TQ = 3 TQ
     *  Phase Seg2 = (PHSEG2 + 1) * TQ = 3 TQ
     * 
     *  Bus speed  = 1 / (Total # of TQ) * TQ
     *             = 1 / 8 * TQ = 125 kHz
     */

   
    // BRP = 7
    mcp2515_write_register( CNF1, (1<<BRP0)|(1<<BRP1)|(1<<BRP2) );
   
    // Prop Seg und Phase Seg1 einstellen
    mcp2515_write_register( CNF2, (1<<BTLMODE)|(1<<PHSEG11) );
   
    // Wake-up Filter deaktivieren, Phase Seg2 einstellen
    mcp2515_write_register( CNF3, (1<<PHSEG21) );
   
    // Aktivieren der Rx Buffer Interrupts
    mcp2515_write_register( CANINTE, (1<<RX1IE)|(1<<RX0IE) );
   
    /*
     *  Einstellen der Filter
     */

   
    // Buffer 0 : Empfangen aller Nachrichten
    mcp2515_write_register( RXB0CTRL, (1<<RXM1)|(1<<RXM0) );
   
    // Buffer 1 : Empfangen aller Nachrichten
    mcp2515_write_register( RXB1CTRL, (1<<RXM1)|(1<<RXM0) );
   
    // Alle Bits der Empfangsmaske loeschen,
    // damit werden alle Nachrichten empfangen
    mcp2515_write_register( RXM0SIDH, 0 );
    mcp2515_write_register( RXM0SIDL, 0 );
    mcp2515_write_register( RXM0EID8, 0 );
    mcp2515_write_register( RXM0EID0, 0 );
   
    mcp2515_write_register( RXM1SIDH, 0 );
    mcp2515_write_register( RXM1SIDL, 0 );
    mcp2515_write_register( RXM1EID8, 0 );
    mcp2515_write_register( RXM1EID0, 0 );
   
    /*
     *  Einstellen der Pin Funktionen
     */

   
    // Deaktivieren der Pins RXnBF Pins (High Impedance State)
    mcp2515_write_register( BFPCTRL, 0 );
   
    // TXnRTS Bits als Inputs schalten
    mcp2515_write_register( TXRTSCTRL, 0 );
   
    // Device zurueck in den normalen Modus versetzten
    mcp2515_bit_modify( CANCTRL, 0xE0, 0);
}

Man kann das Einstellen der Register auch noch wesentlich eleganter lösen wenn man die Registerwerte im Flash des AVRs ablegt und von dort aus lädt (siehe canlib).

Nun fehlt nicht mehr viel bis wir die erste Nachricht über den CAN Bus senden können. Ich werde in diesem Kapitel allerdings nur auf das verschicken von Nachrichten mit Standard-IDs (also 11-Bit Länge) eingehen. Wer das möchte kann den Code aber sicherlich leicht auf Extended-IDs mit 29-Bit erweitern.

Der MCP2515 hat drei unabhängige Sendepuffer mit einstellbarer Priorität. Um eine Nachricht zu verschicken muss man nur noch die entsprechenden Register mit der ID und den zu senden Daten füllen und die Nachricht abzuschicken.

Für das losschicken der Nachricht gibt es drei verschiedene Möglichkeiten:

Wenn man am Mikrocontroller nur noch wenig freie Pins hat lohnt es sich natürlich an das Ganze per SPI zu machen. Man spart so eine bis drei Leitungen (je nachdem wieviele Puffer man nutzt), ohne das das verschicken wesentlich länger dauert.

Das verschicken einer Nachricht könnte also folgendermaßen aussehen:

C:
/* Verschickt eine Nachricht ueber Puffer 0
 * 2 Datenbytes (0x04, 0xf3)
 * Standard ID: 0x0123
 */

uint16_t id = 0x0123;

/* Nachrichten Puffer auf Hoechste Prioritaet einstellen
   (braucht man nicht wenn man nur mit einem Puffer sendet, siehe Text) */

mcp2515_bit_modify( TXB0CTRL, (1<<TXP1)|(1<<TXP0), (1<<TXP1)|(1<<TXP0) );

// ID einstellen
mcp2515_write_register(TXB0SIDH, (uint8_t) (id>>3));
mcp2515_write_register(TXB0SIDL, (uint8_t) (id<<5));

// Nachrichten Laenge + RTR einstellen
mcp2515_write_register(TXB0DLC, 2);

// Daten
mcp2515_write_register(TXB0D0, 0x04);
mcp2515_write_register(TXB0D1, 0xf3);

// CAN Nachricht verschicken
PORT_CS &= ~(1<<P_CS);
spi_putc(SPI_RTS | 0x01);
PORT_CS |= (1<<P_CS);

Senden mit einem Puffer

Allerdings ist es so noch nicht so sehr komfortabel jedesmal extra die Register ausfüllen zu müssen. Wir brauchen also eine entsprechende Funktion dafür die genau das tut. Um die Nachrichten besser zwischen den Funktionen austauschen zu können bietet es sich an diese in ein struct zu verpacken.

Dieses struct kann man jetzt sehr einfach an die Sendefunktion übergeben:

C:
typedef struct
{
    uint16_t  id;
    uint8_t   rtr;
    uint8_t   length;
    uint8_t   data[8];
} CANMessage;

// Neue Nachricht erzeugen
CANMessage message;

// Daten eintragen
message.id = 0x0123;
message.rtr = 0;
message.length = 2;
message.data[0] = 0x04;
message.data[1] = 0xf3;

// Nachricht verschicken
can_send_message(&message);

Und die Sendefunktion dazu:

C:
void can_send_message(CANMessage *p_message)
{
    uint8_t length = p_message->length;
   
    // ID einstellen
    mcp2515_write_register(TXB0SIDH, (uint8_t) (p_message->id>>3));
    mcp2515_write_register(TXB0SIDL, (uint8_t) (p_message->id<<5));
   
    // Ist die Nachricht ein "Remote Transmit Request"
    if (p_message->rtr)
    {
        /* Eine RTR Nachricht hat zwar eine Laenge,
           aber keine Daten */

       
        // Nachrichten Laenge + RTR einstellen
        mcp2515_write_register(TXB0DLC, (1<<RTR) | length);
    }
    else
    {
        // Nachrichten Laenge einstellen
        mcp2515_write_register(TXB0DLC, length);
       
        // Daten
        for (uint8_t i=0;i<length;i++) {
            mcp2515_write_register(TXB0D0 + i, p_message->data[i]);
        }
    }
   
    // CAN Nachricht verschicken
    PORT_CS &= ~(1<<P_CS);
    spi_putc(SPI_RTS | 0x01);
    PORT_CS |= (1<<P_CS);
}

Natürlich kann man diese Funktionen jetzt noch so erweitern, dass man immer den ersten freien Puffer zum senden verwendet in dem man das TXREQ Bit in den TXBnCTRL Register auswertet und dem entsprechend einen Puffer wählt. Aber es geht auch einfacher, da es dafür ein extra SPI Kommando (Read Status Instruction) gibt:

Senden mit allen drei Puffern

Im Gegensatz zum ersten Beispiel wollen wir diesmal allerdings nicht die vorgefertigen Funktionen zum setzen der Register verwenden, sondern die Übertragung etwas beschleunigen in dem man die Daten direkt auf den SPI Bus legt und ausnutzt, dass man bei MCP2515 auch einfach mehrere Bytes hintereinander senden kann. Diese werden dann in den darauffolgenden Registern abgelegt.

C:
uint8_t can_send_message(CANMessage *p_message)
{
    uint8_t status, address;
   
    // Status des MCP2515 auslesen
    PORT_CS &= ~(1<<P_CS);
    spi_putc(SPI_READ_STATUS);
    status = spi_putc(0xff);
    spi_putc(0xff);
    PORT_CS |= (1<<P_CS);
   
    /* Statusbyte:
     *
     * Bit  Funktion
     *  2   TXB0CNTRL.TXREQ
     *  4   TXB1CNTRL.TXREQ
     *  6   TXB2CNTRL.TXREQ
     */

   
    if (bit_is_clear(status, 2)) {
        address = 0x00;
    }
    else if (bit_is_clear(status, 4)) {
        address = 0x02;
    }
    else if (bit_is_clear(status, 6)) {
        address = 0x04;
    }
    else {
        /* Alle Puffer sind belegt,
           Nachricht kann nicht verschickt werden */

        return 0;
    }
   
    PORT_CS &= ~(1<<P_CS);    // CS Low
    spi_putc(SPI_WRITE_TX | address);
   
    // Standard ID einstellen
    spi_putc((uint8_t) (p_message->id>>3));
    spi_putc((uint8_t) (p_message->id<<5));
   
    // Extended ID
    spi_putc(0x00);
    spi_putc(0x00);
   
    uint8_t length = p_message->length;
   
    if (length > 8) {
        length = 8;
    }
   
    // Ist die Nachricht ein "Remote Transmit Request" ?
    if (p_message->rtr)
    {
        /* Ein RTR hat zwar eine Laenge,
           aber enthaelt keine Daten */

       
        // Nachrichten Laenge + RTR einstellen
        spi_putc((1<<RTR) | length);
    }
    else
    {
        // Nachrichten Laenge einstellen
        spi_putc(length);
       
        // Daten
        for (uint8_t i=0;i<length;i++) {
            spi_putc(p_message->data[i]);
        }
    }
    PORT_CS |= (1<<P_CS);      // CS auf High
   
    asm volatile ("nop");
   
    /* CAN Nachricht verschicken
       die letzten drei Bit im RTS Kommando geben an welcher
       Puffer gesendet werden soll */

    PORT_CS &= ~(1<<P_CS);    // CS wieder Low
    if (address == 0x00) {
        spi_putc(SPI_RTS | 0x01);
    } else {
        spi_putc(SPI_RTS | address);
    }
    PORT_CS |= (1<<P_CS);      // CS auf High
   
    return 1;
}

Theoretisch könnte es damit allerdings vorkommen, dass wenn man viele Nachrichten hintereinander sendet eine Nachricht in einem der hinteren Puffer liegen bleibt, da der erste Puffer bei gleicher eingesteller Priorität immer zuerst gesendet wird. Um das zu verhindern müsste man immer den Puffer mit dem man die aktuelle Nachricht senden will auf die niedrigste Priörität setzen und die Priorität der anderen beiden Puffer um eins erhöhen. So würde dann die Nachrichten auf jeden Fall auch in der Reihenfolge gesendet in der sie auch eingetragen wurden.

Um überprüfen zu können ob auch was passiert muss man seine Nachrichten natürlich auch wieder empfangen können.

Nachrichten empfangen

Um festzustellen ob eine neue Nachricht empfangen wurde gibt es je nach Konfiguration des MCP2515 mehrere Möglichkeiten:

Natürlich lassen sich diese Möglichkeiten auch noch untereinander kombinieren. Ein ganz sinnvolle Möglichkeit ist zum Beispiel nur die Interruptleitung zum AVR zu führen und sich darüber anzeigen lassen ob eine neue Nachricht vorliegt und nur dann per SPI Kommando abzufragen in welchem Puffer die Nachricht liegt. Da es so auch auf dem CAN Testboard realisiert wurde will ich auf diese Möglichkeit noch einmal genauer eingehen:

Um den Interruptpin nutzten zu können um sich neue Nachrichten anzeigen zu lassen muss man ihn natürlich erstmal entsprechend einstellen. Da wir dies aber schon bei der Initialisierung getan haben (siehe Unterpunkt Interrupts) können wir jetzt einfach drauf warten bis der Interruptpin auf low-Pegel geht und uns anzeigt das wir eine Nachricht auslesen können.

Um festzustellen in welchem Puffer die Nachricht liegt bzw. was es für ein Typ ist (Standard ID, Extended ID, Remote Transfer Request Frame usw.) stellt der MCP2515 ein extra SPI Kommando bereit (Alternativ kann man auch das CANINTF Register auswerten):

C:
uint8_t mcp2515_read_rx_status(void)
{
    uint8_t data;
   
    // /CS des MCP2515 auf Low ziehen
    PORT_CS &= ~(1<<P_CS);
   
    spi_putc(SPI_RX_STATUS);
    data = spi_putc(0xff);
   
    // Die Daten werden noch einmal wiederholt gesendet,
    // man braucht also nur eins der beiden Bytes auswerten.
    spi_putc(0xff);
   
    // /CS Leitung wieder freigeben
    PORT_CS |= (1<<P_CS);
   
    return data;
}

Wir überprüfen also zuerst ob der Interruptpin auf Low Pegel liegt. Ist dies der Fall, so wurde eine neue Nachricht empfangen.

C:
uint8_t can_get_message(CANMessage *p_message)
{
    // Status auslesen
    uint8_t status = mcp2515_read_rx_status();

    if (bit_is_set(status,6))
    {
        // Nachricht in Puffer 0
       
        PORT_CS &= ~(1<<P_CS);    // CS Low
        spi_putc(SPI_READ_RX);
    }
    else if (bit_is_set(status,7))
    {
        // Nachricht in Puffer 1
       
        PORT_CS &= ~(1<<P_CS);    // CS Low
        spi_putc(SPI_READ_RX | 0x04);
    }
    else {
        /* Fehler: Keine neue Nachricht vorhanden */
        return 0xff;
    }
   
    // Standard ID auslesen
    p_message->id =  (uint16_t) spi_putc(0xff) << 3;
    p_message->id |= (uint16_t) spi_putc(0xff) >> 5;
   
    spi_putc(0xff);
    spi_putc(0xff);
   
    // Laenge auslesen
    uint8_t length = spi_putc(0xff) & 0x0f;
    p_message->length = length;
   
    // Daten auslesen
    for (uint8_t i=0;i<length;i++) {
        p_message->data[i] = spi_putc(0xff);
    }
   
    PORT_CS |= (1<<P_CS);
   
    if (bit_is_set(status,3)) {
        p_message->rtr = 1;
    } else {
        p_message->rtr = 0;
    }
   
    // Interrupt Flag loeschen
    if (bit_is_set(status,6)) {
        mcp2515_bit_modify(CANINTF, (1<<RX0IF), 0);
    } else {
        mcp2515_bit_modify(CANINTF, (1<<RX1IF), 0);
    }
   
    return (status & 0x07);
}

Die Empfangsroutine holt sich dann über das oben beschrieben SPI Kommando die Informationen über den Typ der Nachricht und den Puffer in den die Nachricht liegt. Danach müssen die Daten nur noch aus dem entsprechenden Registern gelesen werden. Auch hier bleiben Frames mit Extended ID erstmal außen vor.

Die Funktion gibt die Filternummer des Filters der die Nachricht durchgelassen hat zurück. Wenn keine neue Nachricht vorliegt gibt die Funktion 0xff als Fehlermeldung zurück.

Damit können wir jetzt Nachrichten senden und empfangen und können an für sich anfangen die Software für das eigentliche Problem zu schreiben :-)

UPDATE: Unter Downloads gibt es ein kleines Demoprogram für das CAN-Testboard. Wenn man einen anderen Quarz verwendet muss man dies im Makefile anpassen, andere Pins für die /CS und INT Leitung kann man in defaults.h einstellen. Die Werte für P_MOSI, P_MISO und P_SCK müssen allerdings mit den Pins für das Hardware SPI übereinstimmen.
Das Programm sollte sich ohne Problem auch für andere AVRs compilieren lassen!

Es initialisiert den MCP2515, schaltet ihn in den Loopback Modus, sendet eine Nachricht (nur intern), empfängt die gesendete Nachricht, schaltet zurück in den normalen Modus, versendet die Nachricht nochmal per CAN und wartet dann auf ankommende Nachrichten.

Bibliothek

Es gibt mittlerweile eine CAN-Bibliothek die auf den hier vorgestellten Code-Fragmenten aufbaut.

Downloads:

mcp2515_demo.zip [17.49 kB]

Zum Anfang

Kommentare

# Manfred Feitzinger meinte am 15. November 2006, 11:46 dazu:

Hallo Schön und gut. Ich kenn mich mit GCC nicht aus. Hast Du das eventuell auch in Bascom, oder kannst Du mir verraten wie Ich die Register beschreiben kann.

Danke Manfred

# Fabian Greif meinte am 15. November 2006, 20:52 dazu:

Im Prinzip sollte es nicht so schwer sein den Code für Bascom umzuschreiben. Man müsste spi_init() und spi_putc() durch die entsprechenden Libraryfunktion ersetzen, das setzen des CS Pins entsprechend abändern und könnte ansonsten den C-Code relativ direkt übernehmen.
Da ich Bascom nicht verwende kann ich das leider auch nicht ohne weiteres ausprobieren.

Hier hat wohl jemand schon ein bißchen Code dafür geschrieben.

# Frank meinte am 2. Dezember 2006, 13:42 dazu:

Hallo Gibt es zu dem Tutorial ein komplettes Beispiel Programm zu ansteuern des MCP2515? Oder habe ich den Download Link übersehen? Übrigens gute Arbeit! Gruss Frank

# Ralf Handrich meinte am 11. Dezember 2006, 15:37 dazu:

Hallo,

auch von mir erstmal großes Lob. Die Schaltung und die Software sind echt Spitze.

Ich hab 2 Module nachgebaut. Der CAN-Bus funktioniert einwandfrei. Allerdings hab ich ein kleines Problem beim Reset der Module. Jedesmal wenn ich einen Hardware-Reset ausführe (bzw. beim Einschalten der Spannungsversorgung der Module) werden 2 bzw. 3 Nachrichten auf den Bus gelegt, ohne das ich explizit etwas sende. Ich habe dabei festgestellt, das die Datenbytes identisch sind mit der letzten Nachricht, die vor dem Reset versendet wurde. Die ID?s scheinen willkürlich zu sein. Wo liegt der Fehler? Was kann ich dagegen machen?

Gruß Ralf

# Maxx meinte am 30. Dezember 2006, 00:21 dazu:

Hallo zusammen! Erstmal danke für das geniale Tutorial!

Bin beim Compilieren über ein paar Sachen drübergefallen. Wo ist z.B. SPI_RESET oder SPI_CS definiert? Möchte mein Projekt mit dem avr-gcc kompilieren. Hat zufällig jemand ein lauffähiges Beispielprogramm mit Makefile, welches er zur Verfügung stellt?

Danke!

Gruß Maxx

# Volker Bosch meinte am 4. Januar 2007, 12:06 dazu:

Hallo Fabian,

ich möchte dir herzlich für das Tutorial zur Ansteuerung des MCP2515 danken — es hat mir den Einstieg erheblich erleichtert.

Drei Anmerkungen habe ich zu Deinem Text:

  1. Du schreibst, der MCP2515 würde das Status-Byte grundsätzlich zwei Mal senden. So wie ich das Datenblatt interpretiere, sendet er das Statusbyte beliebig oft, d.h., Du könntest m.E. auf das zweite Lesen Statusbytes verzichten.

  2. Das letzte Code-Beispiel verstehe ich nicht, das ist doch die bereits vorgestellte Routine zum Senden einer Nachricht über den ersten freien der drei Sendepuffer?

  3. Lt. Datenblatt reagiert der AVR Mega 16 sehr sensibel auf Veränderungen der Taktfrequenz. Atmel empfiehlt während des Umschaltens den Controller im Reset zu halten. Ich konnte zwar keine Probleme beobachten, löse aber nun einen Watchdog-Reset beim Umschalten des Taktteilers aus.

Gruß, Volker.

# Björn meinte am 5. Januar 2007, 19:52 dazu:

Hi,

dein link zu den #defines funktioniert auf der neuen seite nicht mehr!

MFG

Björn

# Fabian Greif meinte am 8. Januar 2007, 23:08 dazu:

Ist korrigiert.

# Manfred Feitzinger meinte am 23. Februar 2007, 08:57 dazu:

Hallo Fabian Ich habe versucht Dein Programm in Bascom zu erstellen. Ich bin der Meinung, dass die Komunikatin zum MCP2515 steht. Aber wenn ich zum beispiel die Register nach der Initalisierung auslese bekomme ich andere Werte als ich reingeschrieben habe. zB ins Register DCF1 schreibe ich 02 (HEX) und bekomme 06 zurück. An was kann das liegen ?

# Fabian Greif meinte am 23. Februar 2007, 16:50 dazu:

Zum dem Problem das der Wert falsch ist würde mir spontan ein zu schneller SPI Bus einfallen, dann kann es schonmal vorkommen, dass Zeichen falsch übertragen werden.

Wie sieht den deine SPI Übertragung aus? Nicht das der Fehler dort irgendwo liegt.

Btw. welches Register ist eigentlich mit DCF1 gemeint?

# Manfred Feitzinger meinte am 26. Februar 2007, 07:53 dazu:

Hallo Fabian Ich habe mich verschrieben Ich meinte das Register CNF1 Ich habe dabei versucht eine Übertragungsrate von 50kByte/sek einzustellen. Der MCP2515 hat einen 16MHz Quarz, der ATMega16 Lauft mit dem Internen Oszilator 1MHz

# Stephan meinte am 6. März 2007, 13:05 dazu:

Hallo,

mit interesse habe ich mich durch deine Seite geklickt. Ich möchte mittels des AVR Butterfly den MCP2515 ansteuern. Da ich leider von der Mikrocontroller Programmierung noch nicht viel Ahnung habe meine Frage: Lässt sich dein Code auch für den Schmetterling verwenden?

Gruß Stephan

# Fabian Greif meinte am 6. März 2007, 13:24 dazu:

Ich kenne mich mit dem AVR Butterfly zwar nicht aus, aber solange er einen normalen AVR Kern (idealerweise mit Hardware SPI) und mindestens 1 bis 2 kByte Flashspeicher hat sollte es überhaupt kein Problem sein den Code dafür anzupassen.

# Frank Lorenzen meinte am 24. Mai 2007, 21:34 dazu:

Hallo, ich hätte da mal eine Frage zur Funktion mcp2515_init. Am Ende setzt du die Werte von CANCTRL zurück: mcp2515_bit_modify( CANCTRL, 0xE0, 0);

Die Werte wollen mir so gar keinen Sinn machen. Laut Datenblatt ist der Def.Wert von CANCTRL 11100000. Sollte man da nicht eher “0xE0, 0xE0” setzen oder meinetwegen auch “0xE0, 0xFF”, ich denke du weißt auf was ich hinauswill. Könntest du das bitte mal erläutern?

Gruß frank

# Fabian Greif meinte am 25. Mai 2007, 10:42 dazu:

Ich will den MCP2515 in den “Normal Operation Mode” zurücksetzen. Dazu muss man alle Bits REQOPn löschen. Und genau das erreiche ich mit dem Kommando ;-)

Es stimmt schon, dass REQOP2..0 = 111 der Startwert ist, allerdings sagt das Datenblatt dazu das dieser Zustand ungültig ist und nicht vom Anwender verwendet werden sollte.
(Siehe Datenblatt Seite 56)

# Frank Lorenzen meinte am 25. Mai 2007, 19:54 dazu:

Ah, jetzt ja. Dankeschön.

Ich hatte das Auge eher bei ?Register Map?.

Gruß frank

# Maximilian meinte am 2. Juli 2007, 12:09 dazu:

Hallo Fabian!

Erstmal schöne Beschreibung. Ich denke, dass ich das alles soweit verstanden habe. Da ich bisher nicht viel mehr als über LED ein/aus gekommen bin dachte ich mir ich suche mir mal ein schönes Projekt. Dies wollte ich jetzt mit Can untereinander vernetzen.

Kannst du vielleicht eine kleine Beschreibung geben, Wie man jetzt sagen wir mal 3 Knoten zusammen schließt? Und wie man diese dann Filtern kann? Und wie man dann testen kann, ob eine Nachricht ortnungsgemäß vom AVR verarbeitet worden ist. So LED an/aus ist bestimmt nicht so schwer. Ich brauch nur so eine grobe vorgehensweise! :) Wäre super COOOL! :)

# Nils meinte am 3. Juli 2007, 13:19 dazu:

Hallo,

die Frage nach einem kompletten Beispielprogramm gab es schon mal weiter oben, aber diese Frage hat sich mir auch gestellt.

Kann jemand diesen Beispielcode als komplettes Projekt für WinAVR bereitstellen?

Ich habe noch nie mit WinAVR gearbeitet und möchte gern mit dem Beispielcode den Aufbau des CAN-Testboards testen und einen Einstieg in WinAVR und die CAN Programmierung finden.

Das würde mir sehr weiter helfen! Danke!

# Maximilian meinte am 4. Juli 2007, 15:45 dazu:

Stellt denn das CanTestboard einen Knoten dar? Sprich man halt eine Leitung H und L und schließt dann soviele Knoten an, wie man will? (Ende jeweils mit einem 120 Ohm wiederstand beendet)

# Fabian Greif meinte am 6. Juli 2007, 16:39 dazu:

@Maximilian
Ja, jedes Board würde einen Knoten darstellen. CANH, CANL und GND werden dann einfach bei allen Knoten verbunden und an den beiden Enden mit 120Ohm abgeschloßen.

@Nils
Ein kleines Beispielprogramm findet sich jetzt hier.

# Maximilian meinte am 10. Juli 2007, 11:34 dazu:

AH COOL! :D Hast du ein Spenden Konto. So einen Service müsste man mal unterstützen…

Wie könnte man den folgendes lösen. Denn soweit ich weiß kann man ja nur entweder senden oder empfangen. Folgendes Problem:

Ich habe einen AVR wo ein LCD angeschloßen ist. Der soll über CAN eine Schnittstelle ansprechen, die zum Beispiel “schnelle” Spannungswechsel misst. (Spannungsschwankungen an einem Verstärker im Auto) Wie man vielleicht einen Wert ließt würde ich vielleicht noch hinbekommen, aber wie man es jetzt anstellt das die Schnittstelle so lange die Messwerte sendet, wie es der AVR vom LCD will, kA! :(

# Fabian Greif meinte am 10. Juli 2007, 14:41 dazu:

AH COOL! :D Hast du ein Spenden Konto. So einen Service müsste man mal unterstützen?

Nein bisher noch nicht, aber du kannst du ja mal per Mail mit mir in Verbindung setzen ;-)

Denn soweit ich weiß kann man ja nur entweder senden oder empfangen.

Nein, man kann natürlich sowohl senden als auch empfangen.

Wie man vielleicht einen Wert ließt würde ich vielleicht noch hinbekommen, aber wie man es jetzt anstellt das die Schnittstelle so lange die Messwerte sendet, wie es der AVR vom LCD will, kA! :(

Das ist dann aber eher die Frage des Programms das auf der Schnittstelle läuft. CAN stellt ja nur die Möglichkeiten zum Senden und Empfangen bereit. Was man mit den Pakten dann im Endeffekt macht bleibt einem selbst überlassen.
Dort setzen dann die Schicht-7-Protokolle wie DeviceNet, CANOpen und dergleichen an.

# Maximilian meinte am 10. Juli 2007, 15:43 dazu:

Also ich könnte sagen wir mal umgangssprachlich folgendes machen. Der LCD sendet eine Nachricht an die Schnittstelle (die Temp etc. liesst), wo dann eine spezielle Variable gesetzt wird. Dann fängt die Schnittstelle an so lange zu senden, bis der LCD eine Nachricht an die Schnittstelle sagt, die das ständige Senden unterbindet. Könnte man das so machen?

Diesen Datastream bereich kann ich doch so einteilen wie ich will oder? Sprich wenn ich folgende Daten von der Schnittstelle senden, wie könnte ich das optimal verpacken.

Alles Beispielswerte:

  • Temperatur: 24.5
  • Remoteanschluß: 1 (1 = an; 0= aus)
  • Spannung: 12.5 (springt im ms bereich herum. Spannungsschwankungen eben! :))

Boar das Thema ist voll spannend aber irgendwie kompliziert… :(

# Fabian Greif meinte am 10. Juli 2007, 15:55 dazu:

Ich hab dir eine E-Mail geschrieben.

# Hossam meinte am 31. Juli 2007, 11:34 dazu:

Hallo Fabian;

das Thema interessiert mich sehr, und die Antwort auf die gestellte Frage von Maximilian ist für mich sehr relevant, wäre sehr nett von dir wenn du mir auch die Antwort zu meiner Email schickst.

Danke

# Medalist meinte am 11. August 2007, 01:43 dazu:

Hi,

deine Seite hier ist echt cool, ich habe soweit 2 platinen gebaut, ein controller sendet der andere empfaengt, und das ganze funktioniert hervorragend (125kbit/s).

Nun zu meinem problem:

Ich moechte den Controller auf 100kbit/s bringen um den komfortbus vom VW/Audi/Skoda/Seat auszulesen. Allerdings bekomme ich irgendwie Probleme beim setzen der Timing Register, koenntest du mir vielleicht die richtigen Werte fuer die Register mal posten?

# Fabian Greif meinte am 11. August 2007, 16:49 dazu:

Der Online Calculator von www.kvaser.com meint für 100 kBit/s bei einem Samplepunkt bei 75% folgendes:

CNF1 = 0x09
CNF2 = 0x91
CNF3 = 0x01

Sag mal Bescheid ob das damit dann funktioniert.

# Medalist meinte am 11. August 2007, 19:31 dazu:

Hey super, hat auf Anhieb funktioniert, anscheinend hab ich das mit dem Time Calculator nicht richtig begriffen.

Jedenfalls bessten Dank! ;)

# J. Harms meinte am 11. November 2007, 18:32 dazu:

Der Artikel hat mir grossartig über die Anfangshürden mit CAN geholfen und mich mein Vorurteil: ?CAN verwenden ist zu aufwändig? vergessen lassen - grossen Dank dafür!

Beim Ausprobieren bin ich auf einen ernsten Fehler gestossen: in der Prozedur MCP2515SendMessage wird am Anfang ein Statusregister gelesen um zu sehen, welche ?Transmit Buffer? verfügbar sind:

x_status = MCP2515ReadStatus ( SPI_RX_STATUS ) ;

Das ist falsch, es sollte sein:

x_status = MCP2515ReadStatus ( SPI_READ_STATUS ) ;

(SPI_RX_STATUS, Adresse 0xB0, gibt den Stand der ?Receive Buffer?, nicht wie gewünscht den der ?Transmit Buffer? - dafür ist SPI_READ_STATUS, Adresse 0xA0, zu verwenden).

Bei dem einfachen einmaligen ?Ping-pong? der Demoanwendung stört dieser Fehler nicht, wenn man aber eine Schleife baut und eine Reihe von Nachrichten sendet (so, dass manchmal auch die Buffer #1 und #2 verwendet werden müssen), bleibt die Anwendung hängen solange dieser Fehler nicht behoben ist. Wirklich lästig wurde das im Moment wo ich anfing mit Interrupts in Echtzeit zu arbeiten.

Frage: hat jemand Erfahrung mit Fehlerbehandlung? CAN tut da ja schon selber eine Menge, z.B. Korrektur mit Prüfsummen. Ich habe daher bisher nichts weiters dazugetan - einzig dringend nötige Aktion war das Erkennen und Wegwerfen von Nachrichten, die kein Empfänger erkannt hat - sonst ?verstopfen? sich die Sende Buffer. Die Dokumentation zu diesem Thema ist leider sehr unbefriedigend.

# Martin meinte am 21. November 2007, 19:34 dazu:

Kleiner Fehler?

Mir ist aufgefallen, dass in der mcp2515_defs.h bei den Bitdefinitionen vom EFLG Register TXB0 steht meiner Meinung nach sollte da TXBO stehen.
Ansonsten ein großes Lob super Seite super Artikel!

# Marco meinte am 26. November 2007, 21:34 dazu:

Hallo.
Ich habe das Testbord mal nachgebaut und habe dein Demo zum MCP darauf gespielt. Hat super geklappt. Das war bis dahin mit dem 2006er Compiler. Nun habe ich das ganze mit einem 2007er Compiler (WINAVR-20070525) wiederholt und bekomme über die RS232 die Meldung ?Kann MCP2515 nicht ansprechen?. Wie gesagt, orginal hex-file läuft, und nur einmal mit 2007er drüber und nix geht mehr. Was ist da anders? Danke schonmal.

# Marco meinte am 27. November 2007, 10:09 dazu:

Ich habe einen Unterschied zwischen den Compilerausgaben des 2006er und des 2007er feststellen können

2006er: …

Creating load file for EEPROM: main.eep avr-objcopy -j .eeprom —set-section-flags=.eeprom=”alloc,load” \ —change-section-lma .eeprom=0 -O ihex main.elf main.eep

Process Exit Code: 0 …

Das war alles super und hat auf anhieb funktioniert.

Als ich das selbe Projekt… wirklich nichts verändert … mit dem Winavr-20070525 compiliert habe, stand da folgendes:

Creating load file for EEPROM: main.eep avr-objcopy -j .eeprom —set-section-flags=.eeprom=”alloc,load” \ —change-section-lma .eeprom=0 -O ihex main.elf main.eep c:\WinAVR-20070525\bin\avr-objcopy.exe: there are no sections to be copied! c:\WinAVR-20070525\bin\avr-objcopy.exe: —change-section-lma .eeprom=0x00000000 never used make.exe: [main.eep] Error 1 (ignored)

Process Exit Code: 0 …

Und nun will der MCP2515 nicht mer mit sich reden lassen. Zumindest die Init gibt den Fehler zurück, was ja darauf schließen lässt, dass die CNF-Register nicht korrekt gesezt wurden. Sagt euch diese Fehlermeldung etwas?

Bis dann.

# Marco meinte am 27. November 2007, 12:55 dazu:

Ich habs :-)

Das mit den Meldungen des Compilers war es nicht, wurde mir zumindest von Jörg Wunsch… jeder kennt ihn denke ich, gesagt. Es war die delay.h. Der Compiler hat die anscheinend wegotimiert und damit kahm der MCP2515 nicht anständig in den Reset bzw. Config. Mode. MfG Marco

# benjamin meinte am 7. Februar 2008, 11:19 dazu:

Hallo!
Ich hab eine Frage? und zwar verstehe ich die Registerconfiguration von CNF1-3 im Testprogram nicht ganz. Du beschreibst CNF3 und schiebst die restlichen Bits rüber. Kann man nicht einfach alle drei register wie CNF3 beschreiben?

Ich bräuchte eine Bustakt von 768 kBit/s und wenn ich die werte vom Online Rechner nehme funktioniert es nicht

# benjamin meinte am 7. Februar 2008, 17:48 dazu:

Ich mein 800kBit/s? war nur mit den gedanken woanderst

# Fabian Greif meinte am 8. Februar 2008, 15:53 dazu:

Hi,

natürlich kann man auch jeweils

mcp2515_write_register(CNF1, ...);
mcp2515_write_register(CNF2, ...);
...

schreiben, ich spare mir aber im Program das jeweilige neu setzen der angesprochenen Adresse und nutzte aus, dass nach dem schreiben eines Wertes der MCP2515 automatisch intern auf die nächste Adresse weiterschaltet. Sendet man also weitere Werte werden diese in den darauf folgenden Registern abgelegt. Und da CNF3..1 genau hintereinander liegen klappt das wunderbar.

Was für Werte hast du den versucht?
Wenn ich 800kbps bei 16 MHz eingebe komme ich auf folgendes:

CNF1 = 0x80
CNF2 = 0x92
CNF3 = 0x02

Grüße Fabian

# Benjamin meinte am 22. Februar 2008, 13:24 dazu:

Hallo!
Entschuldigung dass ich so lang gebraucht habe zum zurückschreiben? war auf Urlaub

Es geht jetzt zwer das er die Nachricht verschickt allerdings können die AVRs die NAchricht nciht empfangen (weder im loopback als auch normal verbunden)

mfg Benjamin

# Heiko meinte am 5. Juli 2008, 00:55 dazu:

Hallo Fabian,
auch von mir vielen Dank für diese Seite, ist wirklich sehr nett von Dir, den Code zu veröffentlichen.
Ich hab leider noch ein Problem damit, und zwar habe ich zwei CAN-Module gebaut und verkabelt und bekomme nun von beiden die folgende Nachricht über den UART zurück:


MCP2515 is aktiv!
Erzeuge Nachricht
  id:     0x123
  laenge: 2
  rtr:    0
  daten:  0xab 0xcd
Wechsle zum Loopback-Modus
Nachricht wurde in die Puffer geschrieben.
Fehler: keine Nachricht empfangen
Wechsle zurueck zum normalen Modus
Versuche die Nachricht per CAN zu verschicken
Nachricht wurde in die Puffer geschrieben
Warte auf den Empfang von Nachrichten?


Ich weiß leider gar nicht, wo ich mit der Fehlersuche beginnen soll, da die Kommunikation zwischen meinem ATmega8 und dem MCP2515 ja scheinbar funktioniert. Hast Du eine Idee?

Vielen Dank für Hilfe und ein schönes Wochenende, Heiko

# Fabian Greif meinte am 5. Juli 2008, 13:25 dazu:

Das einzige was mir spontan dazu einfallen würde wäre die INT-Leitung.
Wie du schon gesagt hast, die Kommunikation klappt, der AVR kann also die Register des MCP2515 beschreiben und wieder zurück lesen.

Ohne eine korrekt angeschlossene INT-Leitung wird er aber im Demo-Programm trotzdem keinerlei Nachrichten empfangen. Schau also am besten in ob der Wert in der ‘defaults.h’ mit dem real verwendeten Pin übereinstimmt und ob auch elektrisch eine Verbindung da ist.

Ohne die INT-Leitung funktioniert es prinzipiell auch, allerdings musst du dann die can-lib verwenden, die kann das.

# Heiko meinte am 5. Juli 2008, 17:04 dazu:

Hallo Fabian,
vielen Dank für die schnelle Hilfe! Der Fehler war aber ein anderer: Ich hatte die Reset-Leitung des MCP2515 zunächst nicht angeschlossen und sah dafür auch keine Notwendigkeit, da der Stein ja zu arbeiten schien. Wenn ich sie nun aber auf + lege, funktioniert alles, wie es soll.

Nochmals vielen vielen Dank!
Heiko

# Sven meinte am 14. August 2008, 18:16 dazu:

Hallo, ich möchte dein Programm auf einen Mega128 ausführen. Ich habe folgende Dinge in der defaults.h geändert:

P_MOSI  B,2;
P_MISO  B,3;
P_SCK   B,1;

MCP2515_CS B,5; MCP2515_INT E,5;

Leider kann ich den MCP2515 nicht initialisieren. Habe ich noch etwas übersehen ? Gruß Sven

# Fabian Greif meinte am 15. August 2008, 12:38 dazu:

Hallo Sven,

da sollte kein Semikolon am Ende der Zeile stehen, außerdem musst du aufpassen, dass du PB0 als Ausgang verwendest. Ansonsten sollte das so passen.

Spricht etwas dagegen die canlib zu verwenden?

Grüße Fabian

# Sven meinte am 15. August 2008, 13:26 dazu:

Hallo,
danke für deine Antwort das mit dem Semikolon ist klar,sollte nur der Übersicht in diesem Posting dienen.
Was hat den PB0 damit zu tun?
Was kann den die lib was dein Demo Programm nicht kann?

Gruß
Sven

# Fabian Greif meinte am 15. August 2008, 14:08 dazu:

Was hat den PB0 damit zu tun?

Das ist der SS Pin, er schaltet zwischen SPI-Master und Slave um. Und wenn er als Eingang gesetzt wird und auf Low gezogen interpretiert der AVR das als einen anderen Master der gerade senden will und schaltet in den Slave Modus. Damit kann man dann keine Daten mehr zu MCP2515 senden. Nicht unbedingt das was man möchte.

Was kann den die lib was dein Demo Programm nicht kann?

Zum Beispiel extended-Frames mit 29-Bit Identifier verschicken oder die Filter des MCP2515 setzten.

Grüße Fabian

# Simon meinte am 16. August 2008, 17:39 dazu:

Bei der Initialisierung muss man beachten, dass man auch die entsprechenden Pins als Ausgang (SCK, MOSI, \CS) bzw. Eingang (MISO) schaltet. Im Gegensatz zu vielen anderen Schnittstellen der AVRs (UART, TWI usw.) wird dies hier nicht alles automatisch von Chip selbst erledigt.

Laut Datenblatt wird bei SCK und MISO automatisch die DDR eingestellt.

# Fabian Greif meinte am 19. August 2008, 09:01 dazu:

Laut Datenblatt wird bei SCK und MISO automatisch die DDR eingestellt.

Das stimmt teilweise ;-) Bei den neueren Controllern wird MISO im Master Modus und SCK, MOSI und SS im Slave Modus überschrieben.

Die Warnung mit dem SS Pin im Master Modus gilt aber trotzdem.

Um mal das Datenblatt des ATMega644 zu zitieren

When the SPI is configured as a Master (MSTR in SPCR is set), the user can determine the direction of the SS pin.

If SS is configured as an output, the pin is a general output pin which does not affect the SPI system. Typically, the pin will be driving the SS pin of the SPI Slave.

If SS is configured as an input, it must be held high to ensure Master SPI operation. If the SS pin is driven low by peripheral circuitry when the SPI is configured as a Master with the SS pin defined as an input, the SPI system interprets this as another master selecting the SPI as a slave and starting to send data to it.

# Marc meinte am 13. November 2008, 08:31 dazu:

Hallo, das Tutorial ist wirklich super! Es hat mir sehr geholfen die Arbeitsweise des MCP2515 zu verstehen.

Mir ist allerdings eine Sache aufgefallen. Ist es richtig, dass in der Funktion “void mcp2515_init” anstelle von PORTB PORTCS und anstatt SPI_CS P_CS stehen müßte?

Viele Grüße Marc

# Fabian Greif meinte am 13. November 2008, 10:06 dazu:

Das stimmt natürlich, ist jetzt korrigiert.

Grüße Fabian

# adlerweb meinte am 21. November 2008, 11:17 dazu:

Der Link für den Bit-Timing-Rechner scheint kaputt zu sein. Ich komme nur über http://intrepidcs.com/support/mbtime.htm drauf. Außerdem gibts unter http://www.kvaser.com/can/protocol/timing_calc.htm auch etwas vergleichbares in Javascript.

# Marc meinte am 1. Dezember 2008, 15:09 dazu:

Ich bins nochmal. Habe 1 zu 1 den Code aus Deinem Tutorial benutzt. Dabei mußte ich noch zwei Dinge hinzufügen, damit der MCP2515 korrekt arbeitet. Und zwar ist es notwendig bei der Funktion void mcp2515_init(void) zwei Warteroutinen einzufügen. Ansonsten nimmt der MCP2515 den ersten Befehl für die Einstellung der Baudrate nicht an und sendet immer mit 1000 kBaud (Register CNF1 bleibt 0). Siehe auch: http://www.mikrocontroller.net/topic/69198.

Also nach spi_putc(SPI_RESET); warte ich 1ms und nachdem die CS Leitung wieder freigegeben wurde warte ich 10ms bevor ich das Register CNF1 beschreibe. Dann nimmt der MCP2515 auch die Baudraten an.

VG Marc

# Fabian Greif meinte am 2. Dezember 2008, 13:02 dazu:

Danke für den Hinweis, ich hab das mal geändert und auch in die canlib übernommen. Seltsamerweise ist das Problem bei mir allerdings noch nie aufgetreten. Schaden können die Warteschleifen aber auch nicht ;-)

# Heiko meinte am 24. Januar 2009, 01:00 dazu:

Hallo Fabian,
ich hab auch noch eine Frage: Wenn ich das im Datenblatt richtig verstehe, geht die erste Nachricht verloren, die den MCP2515 aus dem Sleep-Modus aufweckt. Mein Problem ist, dass in meinem geplanten Bus die Teilnehmer meistens schlafen werden; wenn jetzt ab und zu eine Nachricht verschickt wird, die von mehreren Teilnehmern empfangen werden soll, kann es ja passieren, dass einer gerade wach ist und den Empfang quittiert, sodass der Sender keinen zweiten Versuch unternimmt die Nachricht zu versenden, dass aber die restlichen (gerade aus dem Schlaf erwachenden) MCP2515 die Nachricht verpassen, was fatal wäre.

Kann das im Prinzip passieren und wie kann ich es verhindern?? Einmal mehr vielen Dank für Deine Hilfe!
Heiko

# Fabian Greif meinte am 24. Januar 2009, 11:30 dazu:

Hallo Heiko,

Kann das im Prinzip passieren und wie kann ich es verhindern?

Ja, laut Datenblatt wird genau das passieren. Das Einzige was mir einfiele wie du es umgehen könntest wäre vor der eigentlichen Nachricht eine Art Ankündigungs-Nachricht zu verschicken.
Alle Konten die wach sind ignorieren sie, aber alle schlafenden Konten wachen auf. Nach dieser Nachricht wartet man dann bis alle Knoten sicher wach sind und verschickt die eigentliche Nachricht.

Grüße Fabian

# Frank meinte am 28. Januar 2009, 14:35 dazu:

Hallo Ich möchte den intertainment Can Bus meines Opel Meriva auslesen. Der hat 95,2 kbps. Kann man das gar nicht beim MCP2515 einstellen wegen der “krummen” kbps ? Der Rechner auf http://www.kvaser.com/ gibt nur eine Fehlermeldung. Gruss Frank

# Hans Müller meinte am 15. März 2009, 10:45 dazu:

Hallo. Ich konnte deine Routinen sehr gut verwenden. Danke dafür. Ein kleines Problem habe ich aber noch. Ich verwende den Prozessor P89LPC938 (ebenfalls mit intergriertem SPI). Nach der Programmierung des Prozessors ist der MCP2515 zunächst nicht ansprechbar. Heißt, der MCP2515 nimmt keine Registerwerte an. Obwohl entsprechend beschrieben, erhalte ich beim Auslesen der Rx-Pufferregister den Wert 0 zurück. Wenn ich dann ein bisschen rumfummel und ein paarmal die Spannung wegnehm, klappts irgendwann. Und ab diesem Zeitpunkt immer, bis zur nächsten Programmänderung im Prozessor. Alle anderen Komponenten (LEDs, Tasten, 7-Seg, seriell-Schnittstelle, Ultraschall) die direkt an den Prozessor angeschlossen sind, funktionieren immer. Ich verwende den Reset-PIN des MCP2515 nicht, der ist offen ! Das Reset-Kommando mit den Wartezeiten habe ich weiter oben gelesen und genau so umgesetzt. Ich kann mir nicht erklären, woran das liegt. Hat jemand eine Idee ? Grüße Hans

# Fabian Greif meinte am 15. März 2009, 13:20 dazu:

Ich verwende den Reset-PIN des MCP2515 nicht, der ist offen!

Das ist ganz schlecht, der Reset-Pin sollte auf jeden Fall einen Pull-up Widerstand haben.

Grüße Fabian

# Ankur meinte am 3. Mai 2009, 19:26 dazu:

I am trying to communicate between two nodes (AVR mega16 with CAN chips), however there is some problem which is not clear.

I also tried running your code with modifications in default.h file (for mega16), but nothing worked out. Please find my circuit schematic here. http: // ankur. nsit. googlepages. com / schematic. jpg>

I am debugging my code with the inbuilt USART of AVR, but still no positive results.

I will really appreciate any pointers.

Thanks in anticipation. Ankur

# Fabian Greif meinte am 3. Mai 2009, 21:47 dazu:

Hi Ankur,

there are a few things with your schematic which can’t work. For example the MCP2515 needs a clock source as a crystal oscillator or an external clock. But the worst part is that you inverted the power supply for the MCP2515s.

U2 and U1 don’t have any power supply, this won’t work as well.

S2 makes no sense too, the RESET pin should be connected to the power supply throu a pull-up resistor.

So there are a few things to be done before the ciruit will work as expected ;-)

For the software i suggest that you take a look at the canlib. This lib is well tested and provides more features.

greetings
Fabian

# emo meinte am 4. Mai 2009, 16:21 dazu:

Hallo zusammen,
ich wollte fragen,ob es ein Unterschied macht,wenn man den MCP2515 mit 20MHz betreibt anstatt 16MHz?

# Fabian Greif meinte am 4. Mai 2009, 20:46 dazu:

Ja, du musst die Timing-Informationen zum Einstellen der Bitrate anpassen. Ansonsten ändert sich aber nichts.

Grüße Fabian

# emo meinte am 17. Mai 2009, 03:06 dazu:

Danke dir für deine schnelle Antwort :-) Eine Frage hab ich noch,wie kann ich verhindern, dass der mcp2515 ständig Nachricht verschickt. Dass heißt, wenn ich eine nachricht verschicke sendet er ohne eine Anweisung die selben Nachricht weiter :-(

Viele Grüße Emo

# Marco meinte am 20. Juni 2009, 19:17 dazu:

Hallo zusammen!

Ich hab hier ein Problemchen mit meinem MCP2515. So weit funktioniert alles wunderbar! Nur wenn ich versuche Filter&Masken zu benutzen werden überhaupt keine Nachrichten mehr empfangen, selbst wenn ich alle Filter&Masken auf Null setzt.

So sieht es z.Z. aus:

prog_char can_filter[] = {
    // Group 0
    MCP2515_FILTER(0),              // Filter 0
    MCP2515_FILTER(0),              // Filter 1

    // Group 1
    MCP2515_FILTER(0),              // Filter 2
    MCP2515_FILTER(0),              // Filter 3
    MCP2515_FILTER(0),              // Filter 4
    MCP2515_FILTER(0),              // Filter 5

    MCP2515_FILTER(0),              // Mask 0 (for group 0)
    MCP2515_FILTER(0),              // Mask 1 (for group 1)
};

// Filter und Masken-Tabelle laden
can_static_filter(can_filter);
Nun werden aber keinen Nachtrichten mehr empfangen. Woran könnte das liegen?

Grüße

# Fabian Greif meinte am 21. Juni 2009, 11:00 dazu:

Was für Nachrichten verschickst du denn? Mit dieser Einstellung sollten eigentlich alle Nachrichten mit Standard-Identifier empfangen werden.

Grüße Fabian

# Lynn meinte am 9. Juli 2009, 17:28 dazu:

Hallo,

Vielen Dank für das super Tutorial!

Ich habe den MCP2515 und MCP2551 gemäss des Testboard Schemas dieses Tutorials in mein Boarddesign integriert. Ich benutze ein Atmega8 mit 8MHz und der MCP2515 läuft mit 16MHz.

Wenn ich das MCP2515Demo Programm aus diesem Tutorial laufen lasse kommt: Fehler: kann den MCP2515 nicht ansprechen!

Im MCP2515 Demo Programm habe ich gesehen, ist die SCK Frequency auf fosc/16 gewählt. Sollte eigentlich gehen. Ich habe es mit allen SCK Frequenzen versucht. Bei allen kommt die selbe Meldung. Das einzige, was ich angepasst habe, ist der F_CPU Eintrag im Makefile: F_CPU = 8000000 anstelle von F_CPU = 7372800

Der Rest sollte ok sein. Hier mein Board Schematic http://sites.google.com/site/doritsite/schematic/Atmega8CAN.pdf

Woran könnte es liegen, dass ich den MCP2515 nicht ansprechen kann? Vielen Dank für Hinweise.

Danke und Gruss Lynn

# Fabian Greif meinte am 9. Juli 2009, 18:04 dazu:

Hi Lynn,

der Schaltplan sieht im Prinzip richtig aus, du solltest aber den ICs noch 100nF Kondensatoren möglichst nah am IC spendieren.

Hast du mal geschaut ob der Quarz vom MCP2515 korrekt anschwingt? Ansonsten kann ich keinen Fehler entdecken.

Grüße Fabian

# Lynn meinte am 9. Juli 2009, 19:13 dazu:

Hallo Fabian,

Vielen Dank für die superschnelle Antwort!

Ich habe jetzt bei allen 3 ICs zwischen VCC und GND einen 100nF Kondensator gesetzt. Den MCP2515 Quarz habe ich auch gemessen, er schwingt wie es sein sollte. Der Atmega8 läuft mit einem Ceramic Resonator. An XTAL2 messe ich 4.3MHz und and XTAL1 7.97MHz. Ist es normal, dass die Werte nicht gleich sind? Programmieren kann ich den Atmega8 einwandfrei.

Fehlerhafte ICs schliesse ich aus, da ich das selbe Board 2x habe und das Verhalten bei beiden gleich ist.

Danke und Gruss, Lynn

# Lynn meinte am 13. Juli 2009, 17:55 dazu:

Hallo,
Ich habe das selbe Board nochmals gebaut, dieses Mal mit den 100nF Kondensatoren eingeplant und ein 8MHz Quarz anstelle des Resonators. Siehe da, mit dem neuen Board klappt alles! Keine Ahnung was vorher falsch war..

Besten Dank für die Hilfe!
Lynn

# Dieter meinte am 24. Juli 2009, 20:56 dazu:

Hallo, die Beschreibung des MCP2515 ist wirklich sehr gut gemacht. Ich habe einen ATMega16 mit 16 Mhz und MCP2515 laufen, alles funktioniert soweit. Jedoch erhalte ich nur Daten im ersten Empfangspuffer. Wenn ich die Daten die im Puffer 0 bereits liegen nicht auslese müsste die nächste Nachricht doch dann im Puffer 1 ankommen. Die Filter und Masken sind so eingestellt dass alle Nachrichten empfangen werden können. Wenn ich den Puffer 0 abschalte erhalte ich überhaupt keine Daten mehr. Ich möchte die beiden Pins RX0BF und RX1BF verwenden, jedoch habe ich auch nur am RX0BF eine Reaktion. Wo ist mein Fehler?

# Fabian Greif meinte am 27. Juli 2009, 16:43 dazu:

Hallo Dieter,

schwer zu sagen wo der Fehler liegt, ohne Quelltext würde ich auf eine falsche Konfiguration tippen.

Am einfachsten wäre es wenn du die canlib verwendest, dort sollte das alles funktionieren.

Grüße Fabian

# Peter Mainau meinte am 18. September 2009, 20:43 dazu:

Hallo Fabian,
es ist genau was ich suchte um mein Projekt zu verwirklichen. Es ist einfach gebaut und die Kosten sind minimal. Jetzt kommt es? Ich möchte das Board im Fahrzeug verwenden um den Bremsdruck optisch darzustellen. Je nach Druck soll eine Anzahl von LED ( 10-12 bei Maximaldruck ) leuchten. Dazu muss MCP2515 in LISTEN MODE gesetzt werden ( ich möchte nicht das er im Fahrzeugbus quatscht ) und NUR die ID (124) des Bremsdruckmessages lesen, den Wert (8 Bit lang ) umrechnen und entsprechend die LED darzustellen. Freie Pin?s sind noch genug frei. Das Problem ist, ich kenne mich nur mit Bascom aus, da kann ich etwas zaubern, aber hier?.ist es so schwer. Köntest du viellecht das Demoprogram etwas ändern, in Listen Mode setzen und die Maske/Filter setzen, das Teil mit den LED bekomme ich irgendwie hin.

Grüße Peter

# Fabian Greif meinte am 20. September 2009, 21:12 dazu:

Hallo Peter,

in der canlib gibt es einen direkten Befehl um den Listen-Modus zu aktivieren. Ansonsten müsstest du nur die Masken auf 0x7FF und die Filter auf 124 stellen, dann hättest du genau das gewünschte Verhalten.

Grüße Fabian

# Peter Mainau meinte am 20. September 2009, 23:35 dazu:

Hallo Fabian, ich werde es versuchen. Noch ist die Hardware nicht fertig um es live zu probieren. Fortsetzung folgt ...
Grüße Peter

# Peter Mainau meinte am 23. September 2009, 15:51 dazu:

Hallo Fabian, hab mir die CANLIB angeschaut und hab etwas herumgespielt. Was meinst du?ist das korrekt ? 1. Filter und Maske setzen

prog_uint8_t can_filter[] = 
{
    // Group 0
    MCP2515_FILTER(0x124),              // Filter 0
    MCP2515_FILTER(0x124),              // Filter 1

    // Group 1
    MCP2515_FILTER(0x124),              // Filter 2
    MCP2515_FILTER(0x124),              // Filter 3
    MCP2515_FILTER(0x124),              // Filter 4
    MCP2515_FILTER(0x124),              // Filter 5

    MCP2515_FILTER(0x7FF),              // Mask 0 (for group 0)
    MCP2515_FILTER(0x7FF),              // Mask 1 (for group 1)
};
  1. Message empfangen und LED auswertung
int main(void) 
 {

	can_init(BITRATE_500_KBPS);
	
	can_static_filter(can_filter);
	
	can_set_mode(LISTEN_ONLY_MODE);
	
	// Check if a new messag was received
		
		if (can_check_message())
		{
			can_t msg;
			
			// Try to read the message
			if (can_get_message(&msg))
			{
				 //Hier kommt dann die LED Auswertung...oder ?				
                        }
		}

	
	return 0;
   }

Grüße Peter

# Fabian Greif meinte am 23. September 2009, 16:10 dazu:

Was meinst du?ist das korrekt ?

Ja, sieht gut aus.

Grüße Fabian

# Peter Mainau meinte am 28. September 2009, 16:28 dazu:

Hallo Fabian,
ich plage mich seit einer Woche mit dem Programm. Das Demoprogramm füe das CAN Testboard ist sehr hilfreich weil ich kann gleich am Hyperterminal sehen was dei Microcontroller macht. Nur wie die Maske und Filter gesetzt werden komme ich nicht dahinter?.egal was ich einstelle es wird alles genommen. Ich möchte aber NUR Nachricht 124? Ich wollte z.B. dar der Microkontroller keine Nachricht annimmt und hab alle Masken und alle Filter auf 1 gesetzt?trotzdem wird alles mitgelesen. Ich versteh?s nichtmehr !!!

mcp2515_write_register( RXM0SIDH, 0xf );
mcp2515_write_register( RXM0SIDL, 0xf );
mcp2515_write_register( RXM0EID8, 0xf );
mcp2515_write_register( RXM0EID0, 0xf );

mcp2515_write_register( RXM1SIDH, 0xf );
mcp2515_write_register( RXM1SIDL, 0xf );
mcp2515_write_register( RXM1EID8, 0xf );
mcp2515_write_register( RXM1EID0, 0xf );

mcp2515_write_register( RXF0SIDH, 0xf );
mcp2515_write_register( RXF0SIDL, 0xf );
mcp2515_write_register( RXF0EID8, 0xf );
mcp2515_write_register( RXF0EID0, 0xf );

mcp2515_write_register( RXF1SIDH, 0xf );
mcp2515_write_register( RXF1SIDL, 0xf );
mcp2515_write_register( RXF1EID8, 0xf );
mcp2515_write_register( RXF1EID0, 0xf );

mcp2515_write_register( RXF2SIDH, 0xf );
mcp2515_write_register( RXF2SIDL, 0xf );
mcp2515_write_register( RXF2EID8, 0xf );
mcp2515_write_register( RXF2EID0, 0xf );

mcp2515_write_register( RXF3SIDH, 0xf );
mcp2515_write_register( RXF3SIDL, 0xf );
mcp2515_write_register( RXF3EID8, 0xf );
mcp2515_write_register( RXF3EID0, 0xf );

mcp2515_write_register( RXF4SIDH, 0xf );
mcp2515_write_register( RXF4SIDL, 0xf );
mcp2515_write_register( RXF4EID8, 0xf );
mcp2515_write_register( RXF4EID0, 0xf );

mcp2515_write_register( RXF5SIDH, 0xf );
mcp2515_write_register( RXF5SIDL, 0xf );
mcp2515_write_register( RXF5EID8, 0xf );
mcp2515_write_register( RXF5EID0, 0xf );

# Peter Mainau meinte am 29. September 2009, 13:24 dazu:

Habe es endlich verstanden… die Kombination Maske-Filter gibt die ID die durchgelassen wird… Es funktioniert prima !!

# Walter meinte am 2. November 2009, 20:50 dazu:

Hi!
Bei mir funktioniert die Umschaltung in den loopback modus über das bit modify Kommando ?mcp2515_bit_modify(CANCTRL, 0x40, 0x40);? nicht. Hab schon delays eingefügt etc aber er bleibt einfach auf 0 = normal. Ich wäre für jeden Tip dankbar!

LG Walter

# Fabian Greif meinte am 3. November 2009, 08:27 dazu:

Besser wäre:

mcp2515_bit_modify(CANCTRL, 0xe0, 0x40);

dein Kommando sollte aber auch funktionieren. Wartest du danach lang genug bis der MCP2515 den Modus gewechselt hat? In der canlib sieht das folgendermaßen aus:

// set the new mode
mcp2515_bit_modify(CANCTRL, 0xe0, 0x40);
while ((mcp2515_read_register(CANSTAT) & 0xe0) != 0x40) {
    // wait for the new mode to become active
}

Grüße Fabian

# Walter meinte am 3. November 2009, 10:38 dazu:

hab ich auch schon versucht, dann bleibt er für immer in der Schleife :( Ich hab auch das kleine Demo nochmal geladen, wo der Loopback vorkommt - geht auch nicht. Er lässt sich nur im init fix aktivieren…

# Ferdinand meinte am 7. November 2009, 22:33 dazu:

hallo, ich habe 2 boards aufgebaut die mit dem democode auch wirklich super, von Anfang an liefen. Jetzt will ich, wie medalist oben, meinen Audi belauschen. Dazu habe ich in der mcp2515_init die drei Zeilen für die Register in die oben gennanten Werte geändert. Reihenfolge 3-2-1. Jetzt kann er aber nicht mehr zum MCP2515 Connecten. Mein Code wird hier leider immer gelöscht. Ich hoffe man kann auch so verstehen was ich (falsch) gemacht habe. Grüße Ferdinand

# Fabian Greif meinte am 12. November 2009, 11:51 dazu:

Hallo Ferdinand,

Dazu habe ich in der mcp2515_init die drei Zeilen für die Register in die oben gennanten Werte geändert (?) Jetzt kann er aber nicht mehr zum MCP2515 Connecten.

Das kann eigentlich nicht der Fehler sein, selbst wenn man bei den Werte Unsinn reinschreiben würde müsste die Kommunikation per SPI immer noch funktionieren.

Mein Code wird hier leider immer gelöscht.

HTML wird gelöscht, und damit die spitzen Klammern. Der Rest deines Codes bleibt unverändert wenn du ihn um 4 Leerzeichen oder ein Tab einrückst. Siehe auch unten den Link zur Textformatierung mit Markdown.

Grüße Fabian

# Walter meinte am 14. November 2009, 20:27 dazu:

Problem gelöst - man sollte eben nicht einen SN75176A als can treiber reinstecken (au weia?:)

# Max meinte am 3. Dezember 2009, 22:04 dazu:

Hallo,

ich versuche meine CAN Platinen in Betrieb zu nehmen. Der Loopbackmodus funktioniert bei beiden Boards einwandfrei! Aber leider empfange ich keine Nachrichten im Normalmodus!!! Ich bin irgendwie schon sehr verzweifelt! Kann mir da jemand helfen. Prinzipiell habe ich die Programmierung 1:1 übernommen, ich musste nur die PORTs für den ATMEGA32 anpassen.

Danke im Voraus

Grüße Max

Hier noch die Ausgabe meiner seriellen Schnittstelle:

Erzeuge Nachricht
id: 0x123
laenge: 2
rtr: 0
daten: 0xab 0xcd

wechsle zum Loopback-Modus

Nachricht wurde in die Puffer geschrieben
Nachricht empfangen!
id: 0x123
laenge: 2
rtr: 0
daten: 0xab 0xcd

zurueck zum normalen Modus

Versuche die Nachricht per CAN zu verschicken
Nachricht wurde in die Puffer geschrieben

Warte auf den Empfang von Nachrichten

# Stefan meinte am 23. Dezember 2009, 14:29 dazu:

Hallo Fabian, Ich hab leider noch ein Problem damit, ich habe ein CAN-Module gebaut und verkabelt und bekomme nun die folgende Nachricht über den UART zurück:

MCP2515 is aktiv!

Erzeuge Nachricht

id: 0x123

laenge: 2

rtr: 0

daten: 0xab 0xcd

Wechsle zum Loopback-Modus

Nachricht wurde in die Puffer geschrieben.

Fehler: keine Nachricht empfangen

Wechsle zurueck zum normalen Modus

Versuche die Nachricht per CAN zu verschicken

Nachricht wurde in die Puffer geschrieben

Warte auf den Empfang von Nachrichten?

Habe schon alle Leitungen überprüft weiß nicht wo ich noch suchen soll. Kannst du mir da helfen? Wünsch dir ein schönes Weihnachtsfest.

# Fabian Greif meinte am 2. Januar 2010, 21:28 dazu:

Ist die INT-Leitung richtig angeschlossen? Das wäre das einzige was mir so spontan einfällt was den Fehler verursachen könnte.

Grüße Fabian

# N19 meinte am 4. Februar 2010, 17:27 dazu:

Danke für die Anleitung, leider habe ich folgendes Problem:

Ich habe das Demo-Programm jeweils ein bisschen abgeändert und auf zwei ATmega8-µCs mit 16MHz gespielt.

Einer empfängt nur (Client) während der andere Nachrichten vom UART-weiterleitet (Master).

Leider kommen die vom Master gesendeten Daten nie beim Client an.

Ich weiß anhand der UART-Ausgabe, dass die zu sendende Nachricht korrekt in die Sendepuffer geschrieben wird, jedoch kann ich am TX-Pin des MCP2515 keinerlei Aktivität feststellen (überprüft mit Speicheroszi).

Außerdem kann ich maximal 3 Pakete senden, weil dann anscheinend die Puffer voll sind (weil ja nie wirklich was weggeschickt wird).

Ich vermute dass der MCP2515 nichts wegschickt, weil der Bus nicht verfügbar (available) ist, woran könnte das liegen?

Die Kommunikation zwischen MCP2515 und Atmega8 habe ich im Loopbackmodus erfolgreich überprüfen können.

Ich würde außerdem gerne die MCP2551 als Fehlerquelle ausschließen, ist es möglich die beiden MCP2515 direkt zu verbinden (also jeweils Tx an Rx)?

Danke für eure Bemühungen.

Liebe Grüße

# Fabian Greif meinte am 5. Februar 2010, 12:49 dazu:

Ich vermute dass der MCP2515 nichts wegschickt, weil der Bus nicht verfügbar (available) ist, woran könnte das liegen?

Hast du mal alle Verbindungen zum CAN Transeiver überprüft? Mindestens einen Abschlusswiderstand vorgesehen?

Ich würde außerdem gerne die MCP2551 als Fehlerquelle ausschließen, ist es möglich die beiden MCP2515 direkt zu verbinden (also jeweils Tx an Rx)?

Nein, das ist nicht möglich. Du brauchst immer einen CAN Transeiver.

Grüße Fabian

# N19 meinte am 5. Februar 2010, 22:13 dazu:

Danke für die rasche Antwort.

Ich würde außerdem gerne die MCP2551 als Fehlerquelle ausschließen, ist es möglich die beiden MCP2515 direkt zu verbinden (also jeweils Tx an Rx)?

Nein, das ist nicht möglich. Du brauchst immer einen CAN Transeiver.

Ich habe es gestern trotzdem mal ohne MCP2551 versucht und es hat geklappt!

Mit dem Wissen, das der Fehler eigentlich nur noch beim MCP2551 liegen kann, habe ich dann nochmal das Datenblatt studiert und anschließend den Rs-Pin auf GND gehängt, dann hat alles reibungslos funktioniert!

Jetzt ist es mir auch gelungen eure Universelle CAN-Bibliothek einzusetzen.

Danke für die tolle Arbeit!

# bronko meinte am 3. März 2010, 13:51 dazu:

Hi Fabian,

zunaechst danke fuer deine erklaerungen um die ganze can-bus ansteuerung! Wirklich sehr gelungen!

Eine Frage habe ich: du schreibst von einem Interrupt, mit dem du den Eingangsbuffer ueberpruefst - ich finde allerdings keine Hinweise im Quellcode (hier auf der Seite sowie bei der Demo-applikation), dass du wirklich einen Interrupt im Controler nutzt, sonder es sieht fuer mich so aus, dass du den Nachrichteneingang am Status des MCP pollst.

Ist das richtig beobachtet? und wenn ja, hat es einen besonderen Grund, warum du nicht mittels Interrupt auf den Nachrichteneingang reagierst?

besten Dank und viele Gruesse

# Louis meinte am 4. März 2010, 09:31 dazu:

Hallo, Fabian Zuerst danke für Deine Mühe und Gratulation zu Deiner gelungenen Seite!
Nachdem Du ein erwiesener MCP2515 Experte bist, erlaube ich mir, mit einem kleinen Problem zu Dir zu kommen, das ich bisher nicht erklären konnte. Vielleicht hast Du ja zufällig bereits etwas ähnliches erlebt:
Ich verwende zwar nicht Deine Software, arbeite allerdings sehr ähnlich, mit Kommunikation ausschließlich über SPI (AT Mega 8).
Alles funktioniert bestens, allerdings passiert es sehr selten (etwa alle 100.000 Nachrichten), dass beim Auslesen einer empfangenen Nachricht (Read RX Buffer Kommando) die ersten 5 gelesenen Bits, das sind also die 5 höchsten im RXB0SIDH Register, unkorrekt ausgelesen werden. Ich kann nicht sicher sagen, ob sie einfach 0 gesetzt werden oder invertiert.
Eine andere Node im selben System kann dieselbe Nachricht korrekt lesen, also ist der Fehler empfängerseitig. Derselbe Fehler tritt auf wenn ich das normale Read Kommando mit der Adresse 0x61 ausführe. Er tritt scheinbar NICHT auf, wenn ich ab Adresse 0x60 lese und das erste Byte ignoriere.
Es könnte sich also um ein Problem im SPI Modul handeln. Ich denke nicht, dass es ein SPI-Timing-Problem ist, da ich einen SPI-Takt von nur 250 kb/sek verwende.
Was ich anders mache als Du ist, dass ich beim Lesen von SPI keine 0xff Bytes sende sondern 0x00 Bytes, aber das sollte hoffentlich keinen Unterschied machen.
Bist Du zufällig in Deiner bisherigen Arbeit mal über ein ähnliches Problem gestolpert? Ich bin für jeden Hinweis dankbar.
liebe Grüße
Louis

# Konrad meinte am 27. März 2010, 23:44 dazu:

Hallo Fabian, erstmal großes Lob Echt gut das Tutorial. ich habe ein kleines Problem… ich habe den MCP2515 an einen ATMEGA2560 angeschloßen… das funktioniert auch wunderbar… ich kann nachrichten auf den CAN schicken und auch empfangen… nun habe ich den INT PIN mit dem Interrupt 1 vom Atmel verbunden und in den und folgendes gemacht :
SIGNAL (SIG_INTERRUPT1) { can_get_message; if (recMessage.id==0x70) { gang=recMessage.data[0]; } }

gang ist ein global int

dass geht auch wunderbar aber leider nur im Schrittbetrieb… sobald ich den Debugmodus verlasse und der Controller normal läuft geht nix mehr … also der Controller tut sein ding noch aber er springt nur nach dem Reset des kompletten Boards (inklusive MCP2515) zwei mal in den Interrupt und dann nicht mehr … also denke ich es liegt am MCP2515 … (der ist übrigens auch genauso eingestellt wie im Beispiel oben nur mit geänderte Baudrate)

ich bin echt schon einwenig ratlos …

noch kurz ein paar daten… f atmel 16MHz f SPI 8Mhz Baudrate CAN 1Mbt f MCP2515 16MHz

# true religion jeans sale meinte am 29. September 2010, 09:21 dazu:

dass geht auch wunderbar aber leider nur im Schrittbetrieb… sobald ich den Debugmodus verlasse und der Controller normal läuft geht nix mehr … also der Controller tut sein ding noch aber er springt nur nach dem Reset des kompletten Boards (inklusive MCP2515) zwei mal in den Interrupt und dann nicht mehr … also denke ich es liegt am MCP2515 … (der ist übrigens auch genauso eingestellt wie im Beispiel oben nur mit geänderte Baudrate)

# Matthias meinte am 27. Oktober 2010, 17:24 dazu:

Tolles Tutorial, leider habe ich ein kleines Problem mit dem MCP2515, dass umschalten der Operationsmodi funktioniert bei mir nur nach einem Reset des Bauteils. Ansonsten klappt die Kommunikation Register schreiben und lesen wunderbar. Ich bin jetzt ein wenig ratlos…

# Andreas meinte am 2. Januar 2011, 09:52 dazu:

Hallo

Ich habe den MCP 2515 erfolgreich im einstaz mit 6 Knoten und einer Anzeigeeinheit für mein boot. Habe aber eine Frage. Mir gehen Packete verloren. arbeite mit allen empfangspuffern. Halte mich dabei an die NMEA2000 wo genau die Datenlänge PGN und intervall zum senden dokumentiert ist.

z.b geschwindigkeit un kurs alle 100ms. Batteriecontroller alle 600ms Tankanzeige alle 300ms usw. Datum Uhrzeit alle 1s. ich habe mal eine LED zum anzeigen wenn der Datum Uhrzeit PGN kommt manchmal wirklich alle sek dann aber mal 4sec 6sec usw.

Kann mir das einer erklären????

Busgeschwindigkeit 250kbit. Alle MCP’s mit 16Mhz.

WOher weiß der MCP wann der Bus frei zum senden ist.

Ich vermute das die packete verschickt werden wenn der bus belegt ist

Danke

# Fabian Greif meinte am 2. Januar 2011, 10:44 dazu:

Hallo Andreas,

WOher weiß der MCP wann der Bus frei zum senden ist. Ich vermute das die packete verschickt werden wenn der bus belegt ist

Nein, das kann nicht vorkommen. Das CAN Protokoll schließt das aus. Stichwort wäre hier CSMA/CR (oder auch CSMA/CA).

Wichtig dafür ist allerdings das du sicherstellst das niemals zwei Knoten gleichzeitig Frames mit dem gleichen Identifier verschicken können!

Ist das sichergestellt bleibt nur noch ein Pufferüberlauf im MCP2515. Wenn du die Frames nicht schnell genug abholst und somit schon alle Empfangspuffer belegt sind wird eine neu ankommende Nachricht verworfen.

Für beide Möglichkeiten gibt es Fehlerregister im MCP2515, du müsstest also mal TEC, REC und EFLG Register auslesen. Die sollten dir sagen ob an diesen Stellen Fehler auftreten.

Grüße Fabian

# Andreas meinte am 2. Januar 2011, 13:03 dazu:

Hallo

Also die Identifier sind extended wie NMEA2000 mit den entsprechenden PGN diese sind bei mir eindeutig für jedes Packet auch multiframe.

rufe das register 0x1c und 0x1d auf.

da stehen bei transmit und receive immer 0. Welche register wären noch zu beachten???

Danke

# Fabian Greif meinte am 2. Januar 2011, 16:52 dazu:

Also die Identifier sind extended wie NMEA2000 mit den entsprechenden PGN diese sind bei mir eindeutig für jedes Packet auch multiframe.

Da ich von NMEA2000 keine Ahnung habe kann ich dazu wenig sagen.

Welche register wären noch zu beachten???

Wie gesagt, das EFLG oder Error Flag Register. Das liegt an Adresse 0x2D.

Hast du auch überprüft das die Nachrichten auch zu den erwarteten Zeiten gesendet werden? Nicht das das Problem auf der anderen Seite liegt.

Grüße Fabian

# Andreas meinte am 2. Januar 2011, 17:52 dazu:

Hallo

Also im Register 0x2d bekomme ich einen Receive Buffer Overflow

01000000 bit dezimal 64

So wie es aussieht schaufelt er alles in Buffer 0 obwohl alle 2 Buffer freigegeben und benutzt werden.

# Andreas meinte am 3. Januar 2011, 18:34 dazu:

Hallo

Habe noch den BUKT=1 gesetzt wenn Buffer 0 voll dann Buffer1 das macht er auch.

Mein Problem ist das die einmalige abfrage in der Hauptschleife bis zur nächsten zu lange dauert. habe jetzt 3 x die Abfrage in der Hauptschleife und es hat sich deutlcih verbessert 130Overflows /min. statt über 1500.

Wie kann man es noch weiter verbessern???? Oder ist es vieleicht normal das nicht immer alle nachrichten kommen.

Ich habe nach NMEA2000

Die Geschwindigkeit und Kurs 2Packete im 100ms Datum Uhrzeit 2 Packete 1000ms Füllstand 600ms 1Packet (8byte) Batterie 500ms 1Packet 8byte Schalter 1000ms 8byte GPS Signalstärke 300ms 12byte

Dann soll noch ein Ausßenbordmotor mit nMEA 2000 Bus an diesen dran. Der liefert mir auch Drehzahl usw im 100ms tackt

Wieviel Fehler darf es geben bei 250kbit???

Ich bekomme ja eigendlich keine Fehler sondern nur overflow das ja heist mein CPU 2561 16Mhz ist zu lahm.

Danke

# Fabian Greif meinte am 6. Januar 2011, 09:54 dazu:

Oder ist es vieleicht normal das nicht immer alle nachrichten kommen.

Nein. Du musst halt sicherstellen das sie schnell genug abgeholt werden.

Ich bekomme ja eigendlich keine Fehler sondern nur overflow das ja heist mein CPU 2561 16Mhz ist zu lahm.

Das das ist eher das Problem.

Eine Möglichkeit wäre den Interrupt-Pin des MCP2515 wirklich zum Auslösen eines Interrupts zu verwenden und dort die Nachrichten abzuholen. Dann sollten eigentlich keine Nachrichten mehr verloren gehen.

Aber das hängt natürlich auch davon ab was dein AVR sonst noch so alles zu tun.

Grüße Fabian

# Christian meinte am 7. Januar 2011, 13:24 dazu:

Hallo! Ich möchte gerne vom CAN-BUS (BMW) die aktuelle Motordrehzahl auslesen. Ist dieser “Befehl” bekannt? Ich bin auf der Suche nach einem Codebeispiel.

Danke Christian

# Fabian Greif meinte am 7. Januar 2011, 15:05 dazu:

Hallo Christian,

Ich möchte gerne vom CAN-BUS (BMW) die aktuelle Motordrehzahl auslesen. Ist dieser “Befehl” bekannt?

Mir nicht. Ich habe mich allerdings auch bisher nicht mit dem CAN Bus im Auto und den dort verwendeten Identifieren beschäftigt. Von daher kann ich da wenig weiterhelfen.

Grüße Fabian

# Tobi meinte am 13. Januar 2011, 16:14 dazu:

Hallo, worin unterscheiden sich der Filter und die Maske? Haben diese nicht die selbe Funktion? Danke Tobi

# Fabian Greif meinte am 13. Januar 2011, 16:51 dazu:

worin unterscheiden sich der Filter und die Maske? Haben diese nicht die selbe Funktion?

Nein. Die Maske gibt an welche Bits des Identifiers mit denen des Filters verglichen werden sollen.

Grüße Fabian

# Tobi meinte am 14. Januar 2011, 08:10 dazu:

Danke! So macht das natürlich Sinn.

# Hermann meinte am 13. März 2011, 08:07 dazu:

Hallo Fabian, erstmal herzlichen Dank für Ihr hervorragend geschriebenes und sehr gut nachvollziehbares MCP2515- Tutorial. Nachdem ich mittlerweile im Loopback Modus alle Nachrichten senden bzw empfangen kann, versuche ich nun bestimmte Nachrichten zu filtern. Im Konfigurationsmodus habe ich folgende Werte gesetzt:

Rxb0ctrl: 0010 0000 (11-Bit Identifier)

Rxm0sidh: 1111 1111 Rxm0sidl: 1110 0000

Rxf0sidh: 0000 0001 Rxf0sidl: 0000 0000 Rxf1sidh: 0000 0001 Rxf1sidl: 0000 0000

Mit dieser Einstellung sollten nur Nachrichten mit der ID :000 0000 1000 angenommen werden.

Anschließend habe ich im Loopback Modus eine Nachricht mit der ID : 000 0100 0011 (67) versandt. Diese Nachricht wurde nicht herausgefiltert d. h. die Daten dieser Nachricht wurden übernommen. Aus den entspechenden Registern habe ich anschließend folgende Werte herausgelesen: Rxb0sidh:150, Rxb0sidl:56 und Rxb0dlc:41. Ist das OK? Habe ich etwas vergessen bzw falsch konfiguriert?

Laut Datenblatt lassen sich Filter und Masken auch im Loopback Modus benutzen. Für eine Hilfe wäre ich sehr dankbar. mfg Hermann

# Fabian Greif meinte am 13. März 2011, 10:30 dazu:

Hallo Hermann,

deine Konfiguration sieht soweit richtig aus. Ich kann jedenfalls jetzt keinen Fehler entdecken.

Mit dem Loopback-Modus habe ich bisher noch nicht viel gemacht, daher kann dir dazu nicht viel sagen.

Rxb0sidh:150, Rxb0sidl:56 und Rxb0dlc:41

Das ergibt aber nicht deine gesendete Nachricht. Standardteil des Identifier wäre 10010110001, es ist aber ein Extended-RTR-Frame mit einem Datenbyte. Das RTR-Flag ist auch im DLC Register passend gesetzt.

Kannst du mal schauen ob das mit dem canlib (siehe Link im ersten Absatz auf dieser Seite) funktioniert?

Grüße Fabian

# Christian W meinte am 2. Mai 2011, 21:18 dazu:

Hallo! Eine sehr schöne Hilfe für den Einstieg in die CAN-Geschichten :-) Nur bei einem Punkt komme ich nicht voran, und zwar beim Auslesen von angekommenen Nachrichten von den Puffern des 2515… Ich greife mit meinem Code auf den Puffer zu und lese den Inhalt aus. Kann der Quellcode eigentlich so stimmen, überhaupt? Danke! Christian.

// Prüfen ob Nachricht in Buffer 0 liegt
uint8_t buffer_0;
buffer_0 = MCP2515_read_register(0x61);

// RXnIF Bit löschen, damit Puffer wieder neue Nachrichten aufnehmen kann
MCP2515_bit_modify(0x2C, 0, 0);

# Toni meinte am 28. Juli 2011, 13:53 dazu:

Hallo,

Mir sind die beiden Zeilen, in denen die ID ausgelesen wird nicht ganz klar. Die ID ist 16 bit lang und liegt beispielsweise für Puffer 0 in RXB0SIDH und RXB0SIDL, aber wie kommt man auf die Werte 3 und 5 für die Shiftoperationen? Warum wird der gelesene Wert verschoben und nicht einfach nacheinander in einen 16-bit-Wert geschrieben, sodass man einfach die ID hat?

Vielen Dank

# Fabian Greif meinte am 28. Juli 2011, 16:38 dazu:

Die ID ist 16 bit lang und liegt beispielsweise für Puffer 0 in RXB0SIDH und RXB0SIDL

Nein, die ID ist 11 oder 29-Bit lang. Und da die 11-Bit nicht genau in den unteren 16-Bit von zwei Registern liegen, sondern in den oberen, muss entsprechend geschoben werden um sie auszurichten.

Du könntest also auch zuerst den 16-Bit Wert auslesen und diesen dann um 5-Stellen nach rechts verschieben. Das käme auf das Selbe heraus.

Grüße Fabian

# Stefan meinte am 19. November 2011, 16:16 dazu:

Hi Super Anleitung für den Can Controller!! Ich hab ein Problem mit der Funktion “can_send_message” über alle drei Puffer. Als Rückgabewert bekomme ich immer eine 0, also das keine Puffer frei sind. Woran kann das liegen ? Wo ist denn die “bit_is_clear” Funktion deklariert? Die find ich nirgends, lässt sich aber dennoch fehlerfrei compilieren… Vielen Dank!!

# christian meinte am 29. Mai 2012, 19:57 dazu:

ahoi, ich muss in der fh auf eine platine aufbauen, die deiner sehr ähnlich. nur werden dort nachrichten über RXnBF empfangen. gibt es dafür irgendwo eine anleitung?

vielen dank

gruß

# Fabian Greif meinte am 4. September 2013, 11:04 dazu:

Kommentare wegen zu viel SPAM geschlossen! Weitere Fragen bitte per E-Mail an mich.