[funktioniert] UNO - Mega - Teensy mittels CAN-Bus verbinden

UNO - Mega

Hardware: UNO, Mega2560, zwei Stück MCP2515
Bibliothek: CAN_BUS_Shield mit mcp_can.h
Software: UNO und Mega mit den Bibliotheksbeispielen send_Blink und receive_Blink:

// UNO
// demo: CAN-BUS Shield, send data
#include <mcp_can.h>
#include <SPI.h>

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;
const int ledHIGH    = 1;
const int ledLOW     = 0;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

void setup()
{
    Serial.begin(115200);

    while (CAN_OK != CAN.begin(CAN_125KBPS))              // init can bus : baudrate
    {
        Serial.println("CAN BUS Shield init fail");
        Serial.println(" Init CAN BUS Shield again");
        delay(100);
    }
    Serial.println("CAN BUS Shield init ok!");
}

byte stmp[] = {0, 0, ledHIGH, 1, 2, 3, ledLOW, 5};
byte len = sizeof(stmp);

void loop()
{   Serial.println("In loop");
    // send data:  id = 0x70, standard frame, data len = 8, stmp: data buf
    CAN.sendMsgBuf(0x70, 0, len, stmp);
    delay(1000);                       // send data once per second
}

/*********************************************************************************************************
  END FILE
*********************************************************************************************************/
// Mega2560
// SCK - 52 
// SI  - 51
// SO  - 50
// CS  - 53

// demo: CAN-BUS Shield, receive data with check mode
// send data coming to fast, such as less than 10ms, you can use this way
// loovee, 2014-6-13
#include <mcp_can.h>
#include <SPI.h>

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 53;
const int LED        = 8;
boolean ledON        = 1;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

void setup()
{
    Serial.begin(115200);
    pinMode(LED,OUTPUT);

    while (CAN_OK != CAN.begin(CAN_125KBPS))              // init can bus : baudrate
    {
        Serial.println("CAN BUS Shield init fail");
        Serial.println("Init CAN BUS Shield again");
        delay(100);
    }
    Serial.println("CAN BUS Shield init ok!");
}

void loop()
{
    byte len = 0;
    byte buf[16];

    if(CAN_MSGAVAIL == CAN.checkReceive())            // check if data coming
    {
        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

        unsigned long canId = CAN.getCanId();

        Serial.println("-----------------------------");
        Serial.print("get data from ID: 0x");
        Serial.println(canId, HEX);

        for(int i = 0; i<len; i++)    // print the data
        {
            Serial.print(buf[i]);
            Serial.print("\t");
            if(ledON && i==2)
            {
                digitalWrite(LED, buf[i]);
                ledON = 0;
                delay(500);
            }
            else if((!(ledON)) && i==6)
            {
                digitalWrite(LED, buf[i]);
                ledON = 1;
            }
        }
        Serial.println();
    }
}

//END FILE

Ausgabe serieller Monitor Mega:

-----------------------------
get data from ID: 0x70
0 0 1 1 2 3 0 5

So weit, so gut :slight_smile:

UNO - Mega - Teensy

zusätzliche Hardware: Teensy 3.2, TJA1050

FlexCAN_pins.png
Bildquelle: liegt der Bibliothek bei

Teensy Pin 3 (CAN TX) - TJA1050 CAN TX
Teensy Pin 4 (CAN RX) - TJA1050 CAN RX

Bibliothek: FlexCAN (Die mit teensyduino gelieferte Bibliothek enthält nur Beispiele für Teensy 3.6, daher fliegt die leider raus.)
Software: Bibliotheksbeispiel CANtest

// -------------------------------------------------------------
// CANtest for Teensy 3.1
// by teachop
//
// This test is talking to a single other echo-node on the bus.
// 6 frames are transmitted and rx frames are counted.
// Tx and rx are done in a way to force some driver buffering.
// Serial is used to print the ongoing status.
//

#include <Metro.h>
#include <FlexCAN.h>

Metro sysTimer = Metro(1);// milliseconds

int led = 13;
FlexCAN CANbus(125000);
static CAN_message_t msg, rxmsg;
static uint8_t hex[17] = "0123456789abcdef";

int txCount, rxCount;
unsigned int txTimer, rxTimer;


// -------------------------------------------------------------
static void hexDump(uint8_t dumpLen, uint8_t *bytePtr)
{
  uint8_t working;
  while ( dumpLen-- ) {
    working = *bytePtr++;
    Serial.write( hex[ working >> 4 ] );
    Serial.write( hex[ working & 15 ] );
  }
  Serial.write('\r');
  Serial.write('\n');
}


// -------------------------------------------------------------
void setup(void)
{
  Serial.begin(115200);
  CANbus.begin();
  pinMode(led, OUTPUT);
  digitalWrite(led, 1);

  delay(2000);
  Serial.println(F("Hello Teensy 3.1 CAN Test."));

  sysTimer.reset();
}


// -------------------------------------------------------------
void loop(void)
{
  // service software timers based on Metro tick
  if ( sysTimer.check() ) {
    if ( txTimer ) {
      --txTimer;
    }
    if ( rxTimer ) {
      --rxTimer;
    }
  }

  // if not time-delayed, read CAN messages and print 1st byte
  if ( !rxTimer ) {
    while ( CANbus.read(rxmsg) ) {
      hexDump( sizeof(rxmsg), (uint8_t *)&rxmsg );
      Serial.write(rxmsg.buf[0]);
      rxCount++;
    }
  }
/*
  // insert a time delay between transmissions
  if ( !txTimer ) {
    // if frames were received, print the count
    if ( rxCount ) {
      Serial.write('=');
      Serial.print(rxCount);
      rxCount = 0;
    }
    txTimer = 100;//milliseconds
    msg.len = 8;
    msg.id = 0x222;
    for ( int idx = 0; idx < 8; ++idx ) {
      msg.buf[idx] = '0' + idx;
    }
    // send 6 at a time to force tx buffering
    txCount = 6;
    digitalWrite(led, 1);
    Serial.println(".");
    while ( txCount-- ) {
      CANbus.write(msg);
      msg.buf[0]++;
    }
    digitalWrite(led, 0);
    // time delay to force some rx data queue use
    rxTimer = 3;//milliseconds
  }
*/
}

Den Teil zum Senden habe ich auskommentiert, da ich zunächst nur Empfangen möchte.

Serieller Monitor des Teensys:

Hello Teensy 3.1 CAN Test.

Es wird also nichts empfangen.

Mir ist aufgefallen, daß die Struktur beim Teensy von der des UNOs abweicht, da beim UNO timeout fehlt:

typedef struct CAN_message_t {
uint32_t id; // can identifier
uint8_t ext; // identifier is extended
uint8_t len; // length of data
uint16_t timeout; // milliseconds, zero will disable waiting
uint8_t buf[8];
} CAN_message_t;

Aber empfangen werden müßte doch irgendwas, hatte ich gehofft.

Was mache ich falsch, wie bekomme ich Daten empfangen?

FlexCAN_pins.png

Hallo,

agmue:
Bibliothek: FlexCAN (Die mit teensyduino gelieferte Bibliothek enthält nur Beispiele für Teensy 3.6, daher fliegt die leider raus.)

also zu dieser alten Version der Library kann ich nicht viel beitragen, die unterscheidet sich von den aktuellen ziemlich stark in der Schnittstelle.

Hier habe ich ein Beispiel mit der Version, die in Teensyduino mitgeliefert wird, gepostet
https://forum.arduino.cc/index.php?topic=572590.msg3905975#msg3905975
Läuft bei mir mit zwei Teensy 3.2 und zwei Transceivern (SN65HVD230 / bzw. 232, MCP2551, MCP2562 oder TJA1051T/3).

Alternative als funktionierend bekannte Libs sind, die aktuelle Version der in Teensyduino mitgelieferten "Collin80" Variante

sowie diese Variante aus der Arduino NMEA2000 Library

und die ganz neue aus dem Teensy Forum

Wenn es unbedingt die alte Lib sein muss, musst du mal schauen wo es hakt.

ArduFE:
Wenn es unbedingt die alte Lib sein muss, musst du mal schauen wo es hakt.

Nö, die mit teensyduino mitgelieferte wäre mir lieber, nur haben mich die ausschließlichen Beispiele für Teensy 3.6 genervt.

Ich werde die Bibliothek zurückkopieren und Deinen Sketch als Ausgangspunkt nehmen.

Alternativ könnte noch der Transceiver das Problem sein. Den 1050 kenne ich nicht so genau. Manche haben einen Enable-Pin, Sleep-Pin oder ähnliche Spässe. Wenn sich gar nichts tut, mal zwischen Teensy und Transceiver messen, eventuell mit den anderen Boards vergleichen ...

Hardware: UNO, Mega2560, zwei Stück MCP2515

Die Module haben 8MHz Quarze

Du musst verschärft darauf achten, dass die CAN Lib das richtig abhandelt.
Denn Default ist 16MHz

Nachtrag:
CAN.begin(CAN_125KBPS)
CAN.begin(CAN_125KBPS,MCP_8MHz)

combie:
CAN.begin(CAN_125KBPS,MCP_8MHz)

Ich sehe Daten :smiley:

Den Inhalt verstehe ich noch nicht, dazu melde ich mich später.

typedef struct CAN_message_t {
uint32_t id; // can identifier
uint8_t ext; // identifier is extended
uint8_t len; // length of data
uint16_t timeout; // milliseconds, zero will disable waiting
uint8_t buf[8];
} CAN_message_t;

Der Teensy ist ein 32 Bit System, von daher wird der Struktur noch ein attribute((packed)) fehlen, wegen dem "alignment"

Auf dem Mega und dem Teensy lasse ich eine vom UNO ferngesteuerte LED blinken. So funktioniert es:

// UNO
// demo: CAN-BUS Shield, send data
#include <mcp_can.h>
#include <SPI.h>

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;
const int led    = 1;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

void setup()
{
  Serial.begin(115200);

  while (CAN_OK != CAN.begin(CAN_125KBPS, MCP_8MHz))  // init can bus : baudrate, Quarz vom MCP2551
  {
    Serial.println("CAN BUS Shield init fail");
    Serial.println(" Init CAN BUS Shield again");
    delay(100);
  }
  Serial.println("CAN BUS Shield init ok!");
}

byte stmp[] = {0, 1, 2, 3, 4, 5, 6, 7};
byte len = sizeof(stmp);

void loop()
{ Serial.println("In loop");
  // send data:  id = 0x70, standard frame, data len = 8, stmp: data buf
  CAN.sendMsgBuf(0x70, 0, len, stmp);
  stmp[2]++;
  delay(1000);                       // send data once per second
}

/*********************************************************************************************************
  END FILE
*********************************************************************************************************/
// Mega2560
// SCK - 52
// SI  - 51
// SO  - 50
// CS  - 53

// demo: CAN-BUS Shield, receive data with check mode
// send data coming to fast, such as less than 10ms, you can use this way
// loovee, 2014-6-13
#include <mcp_can.h>
#include <SPI.h>

const int SPI_CS_PIN = 53;
const int LED        = 8;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

void setup()
{
  Serial.begin(115200);
  pinMode(LED, OUTPUT);

  while (CAN_OK != CAN.begin(CAN_125KBPS, MCP_8MHz))  // init can bus : baudrate, Quarz vom MCP2551
  {
    Serial.println("CAN BUS Shield init fail");
    Serial.println("Init CAN BUS Shield again");
    delay(100);
  }
  Serial.println("CAN BUS Shield init ok!");
}

void loop()
{
  byte len = 0;
  byte buf[16];

  if (CAN_MSGAVAIL == CAN.checkReceive())           // check if data coming
  {
    CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

    unsigned long canId = CAN.getCanId();

    Serial.println("-----------------------------");
    Serial.print("get data from ID: 0x");
    Serial.println(canId, HEX);

    for (int i = 0; i < len; i++) // print the data
    {
      Serial.print(buf[i]);
      Serial.print("\t");
      if (i == 2)
      {
        digitalWrite(LED, buf[i]%2);
      }
    }
    Serial.println();
  }
}

//END FILE
#include <FlexCAN.h>

const int led = 13;

void setup() {
  Serial.begin(115200);
  pinMode(led, OUTPUT);
  Can0.begin(125000);
  delay(3000);
  Serial.println("Teensy 3.2");
}

void loop() {
  if (Can0.available()) {
    CAN_message_t rxmsg;

    if (Can0.read(rxmsg)) {
      Serial.print("Daten empfangen  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19\nrxmsg: \t\t");
      hexDump( sizeof(rxmsg), (uint8_t *)&rxmsg );
      Serial.print("rxmsg.buf: \t");
      hexDump( sizeof(rxmsg.buf), (uint8_t *)&rxmsg.buf );
      if (rxmsg.id == 0x70/* && rxmsg.len == 1*/) {
        digitalWrite(led, rxmsg.buf[2] % 2);
      }
    }
  }
}
/*
  typedef struct CAN_message_t {
  uint32_t id; // can identifier
  uint8_t ext; // identifier is extended
  uint8_t len; // length of data
  uint16_t timeout; // milliseconds, zero will disable waiting
  uint8_t buf[8];
  } CAN_message_t;
*/
static void hexDump(uint8_t dumpLen, uint8_t *bytePtr)
{
  uint8_t working;
  while ( dumpLen-- ) {
    working = *bytePtr++;
    if (working < 16) Serial.print('0');
    Serial.print( working, HEX );
    Serial.print(' ');
  }
  Serial.write('\r');
  Serial.write('\n');
}

Der Dank geht an die Helfer!

combie:
Der Teensy ist ein 32 Bit System, von daher wird der Struktur noch ein attribute((packed)) fehlen, wegen dem "alignment"

Solche Angaben fehlen in der Bibliothek.

Wenn ich Dich richtig verstehe, werden mir zur Geschwindigkeitserhöhung leere Füllbytes angezeigt. Sowas kann man ja glücklicherweise gefahrlos probieren.

#include <FlexCAN.h>

const int led = 13;

void setup() {
  Serial.begin(115200);
  pinMode(led, OUTPUT);
  Can0.begin(125000);
  delay(3000);
  Serial.println("Teensy 3.2");
}

void loop() {
  if (Can0.available()) {
    CAN_message_t rxmsg;

    if (Can0.read(rxmsg)) {
      Serial.print("\nDaten empfangen  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19\nrxmsg: \t\t");
      hexDump( sizeof(rxmsg), (uint8_t *)&rxmsg );
      Serial.print("rxmsg.id: \t");
      if(rxmsg.id < 0x01000000) Serial.print("00 ");
      if(rxmsg.id < 0x010000) Serial.print("00 ");
      if(rxmsg.id < 0x0100) Serial.print("00 ");
      Serial.println(rxmsg.id, HEX);
      Serial.print("rxmsg.ext: \t");
      Serial.println(rxmsg.ext, HEX);
      Serial.print("rxmsg.len: \t");
      Serial.println(rxmsg.len, HEX);
      Serial.print("rxmsg.timeout: \t");
      Serial.println(rxmsg.timeout, HEX);
      
      Serial.print("rxmsg.buf: \t");
      hexDump( sizeof(rxmsg.buf), (uint8_t *)&rxmsg.buf );
      if (rxmsg.id == 0x70/* && rxmsg.len == 1*/) {
        digitalWrite(led, rxmsg.buf[2] % 2);
      }
    }
  }
}
/*
  typedef struct CAN_message_t {
  uint32_t id; // can identifier
  uint8_t ext; // identifier is extended
  uint8_t len; // length of data
  uint16_t timeout; // milliseconds, zero will disable waiting
  uint8_t buf[8];
  } CAN_message_t;
*/
static void hexDump(uint8_t dumpLen, uint8_t *bytePtr)
{
  uint8_t working;
  while ( dumpLen-- ) {
    working = *bytePtr++;
    if (working < 16) Serial.print('0');
    Serial.print( working, HEX );
    Serial.print(' ');
  }
  Serial.write('\r');
  Serial.write('\n');
}

Originale Bibliothek:

Daten empfangen  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
rxmsg:          70 00 00 00 00 00 08 00 00 40 00 01 EE 03 04 05 06 07 03 40 
rxmsg.id:       00 00 00 70
rxmsg.ext:       0
rxmsg.len:       8
rxmsg.timeout:  4000
rxmsg.buf:      00 01 EE 03 04 05 06 07

Mit typedef struct attribute((packed)) CAN_message_t in FlexCAN.h:

Daten empfangen  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
rxmsg:          70 00 00 00 00 00 08 00 57 00 01 04 03 04 05 06 07 
rxmsg.id:       00 00 00 70
rxmsg.ext:       0
rxmsg.len:       8
rxmsg.timeout:  5700
rxmsg.buf:      00 01 04 03 04 05 06 07

Drei Füllbytes wären damit verschwunden. Gut zu wissen, da kommt der Anfänger nicht drauf :slight_smile:

Jetzt bleibt mir eigentlich nur noch Byte 5 unklar, das kann ich keinem anderen Inhalt zuordnen.

Hi

Schicke Mal eine Extended-ID

CAN.sendMsgBuf(0x70, 0, len, stmp);

Die 0 (2.te Argument =1) gibt an, ob die ID als 29Bit übertragen werden soll, oder Standard als 11Bit (=0).
0x70 ist die übertragene ID (muß einzigartig sein und darf nur von diesem einen Chip versendet werden)
len = die Länge der Nachricht (0 ... 8, CAN kann max 8 Byte am Stück übertragen)
stmp = der Platz, wo die Nachricht steht, hier ein Array.

Der Aufbau sollte sich in Wikipedia finden lassen - kann mir nicht vorstellen, daß len als 32bit übertragen wird (das Byte 5 Deiner Übertragung sollte nicht zur Länge gehören - bei maximal 8 Byte Nutzlast kann ich mir nicht vorstellen, daß man dafür ein Word sendet)

MfG

PS: Sehe gerade, EXT hast Du schon benannt - hmm ...
Du kannst übrigens die kleinen IDs auch als Extended versenden - musst also beim Empfang drauf achten, ob Deine gesuchte ID wirklich die Gesuchte ist!

postmaster-ino:
Du kannst übrigens die kleinen IDs auch als Extended versenden - musst also beim Empfang drauf achten, ob Deine gesuchte ID wirklich die Gesuchte ist!

Leichter gesagt, als getan, wollte mir gerade nicht gelingen. Senden geht, aber die Empfänger empfangen dann nichts mehr.

Der Teufel steckt im Detail :sleeping:

agmue:
Der Teufel steckt im Detail :sleeping:

Was den Empfang auf dem Teensy angeht, könnten die Beispiele helfen. Im Beispiel ObjectOrientedCANExtendedIIDs taucht das auf

  CAN_filter_t allPassFilter;
  allPassFilter.id=0;
  allPassFilter.ext=1;
  allPassFilter.rtr=0;

  //leave the first 4 mailboxes to use the default filter. Just change the higher ones
  for (uint8_t filterNum = 4; filterNum < 16;filterNum++){
    Can0.setFilter(allPassFilter,filterNum);

Eventuell werden Extended IDs standardmässig nicht reingelassen. Aber aufpassen, wenn man auch senden will, dass man die dafür verwendeten Mailboxen (normalerweise 14 und 15) nicht kaputkonfiguriert.

ArduFE:
Eventuell werden Extended IDs standardmässig nicht reingelassen.

Dieser Eindruck drängt sich mir auf. Das Beispiel hatte ich auch gesehen, aber gehofft, ohne Filter auskommen zu können, da ich sie zunächst nicht benötige, denn ich möchte alle Nachrichten sehen. Da ich keinen Weg ohne Filter finden konnte, habe ich es mit versucht und empfange tatsächlich Daten.

Allerdings habe ich vorher noch die Bibliothek für UNO und Mega ausgetauscht, da ein Parameter MCP_ANY alle Daten auch mit erweiterten IDs reinläßt.

Mein derzeitiger funktionierender Stand:

// UNO
// http://henrysbench.capnfatz.com/henrys-bench/arduino-projects-tips-and-more/arduino-can-bus-module-1st-network-tutorial/
// CAN Send Example der Bibliothek https://github.com/coryjfowler/MCP_CAN_lib
#include <mcp_can.h>
#include <SPI.h>

MCP_CAN CAN0(10);     // Set CS to pin 10

void setup()
{
  Serial.begin(115200);

  while (CAN_OK != CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ))  // init can bus : masks and filters disabled, baudrate, Quarz vom MCP2551
  {
    Serial.println("CAN BUS Shield init fail");
    Serial.println(" Init CAN BUS Shield again");
    delay(100);
  }
  Serial.println("CAN BUS Shield init ok!");
  CAN0.setMode(MCP_NORMAL);   // Change to normal mode to allow messages to be transmitted
}

byte data[] = {0, 1, 2, 3, 4, 5, 6, 7};
byte len = sizeof(data);

void loop()
{
  // byte sndStat = CAN0.sendMsgBuf(0x70, 0, len, data);  // send data:  id = 0x70, standard frame, data len = 8, data buf
  byte sndStat = CAN0.sendMsgBuf(0x12345670, 1, len, data);  // send data:  id = 0x12345670, extended frame, data len = 8, data buf
  if (sndStat == CAN_OK) {
    Serial.println("Message Sent Successfully!");
  } else {
    Serial.println("Error Sending Message...");
  }
  data[2]++;
  delay(1000);                       // send data once per second
}
// Mega2560
// SCK - 52
// SI  - 51
// SO  - 50
// CS  - 53
// http://henrysbench.capnfatz.com/henrys-bench/arduino-projects-tips-and-more/arduino-can-bus-module-1st-network-tutorial/

// CAN Receive Example der Bibliothek https://github.com/coryjfowler/MCP_CAN_lib
#include <mcp_can.h>
#include <SPI.h>

MCP_CAN CAN0(53);               // Set CS pin

char msgString[128];            // Serial Output String Buffer
const int LEDpin = 8;

void setup()
{
  Serial.begin(115200);
  pinMode(LEDpin, OUTPUT);

  while (CAN_OK != CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ))  // init can bus : masks and filters disabled, baudrate, Quarz vom MCP2551
  {
    Serial.println("CAN BUS Shield init fail");
    Serial.println(" Init CAN BUS Shield again");
    delay(100);
  }
  Serial.println("CAN BUS Shield init ok!");
  CAN0.setMode(MCP_NORMAL);   // Change to normal mode to allow messages to be transmitted
}

void loop()
{
  unsigned long rxId = 0;
  byte len = 0;
  byte rxBuf[8];

  if (CAN_MSGAVAIL == CAN0.checkReceive())           // check if data coming
  {
    CAN0.readMsgBuf(&rxId, &len, rxBuf);      // Read data: len = data length, buf = data byte(s)
    Serial.println("-----------------------------");
    if ((rxId & 0x80000000) == 0x80000000)            // Determine if ID is standard (11 bits) or extended (29 bits)
      sprintf(msgString, "Extended ID: 0x%.8lX  DLC: %1d  Data:", (rxId & 0x1FFFFFFF), len);
    else
      sprintf(msgString, "Standard ID: 0x%.3lX       DLC: %1d  Data:", rxId, len);

    Serial.print(msgString);

    if ((rxId & 0x40000000) == 0x40000000) {          // Determine if message is a remote request frame.
      sprintf(msgString, " REMOTE REQUEST FRAME");
      Serial.print(msgString);
    } else {
      for (byte i = 0; i < len; i++) {
        sprintf(msgString, " 0x%.2X", rxBuf[i]);
        Serial.print(msgString);
        if (i == 2)
        {
          digitalWrite(LEDpin, rxBuf[i] % 2);
        }
      }
    }
    Serial.println();
  }
}
#include <FlexCAN.h>

elapsedMillis RXtimer;
uint32_t RXCount = 0;
const uint8_t redLEDpin = 13;

class CANClass : public CANListener
{
  public:
    void printFrame(CAN_message_t &frame, int mailbox);
    void gotFrame(CAN_message_t &frame, int mailbox); //overrides the parent version so we can actually do something
};

void CANClass::printFrame(CAN_message_t &frame, int mailbox)
{
  Serial.print("Mailbox: ");
  Serial.print(mailbox);
  Serial.print(" ID: ");
  Serial.print(frame.id, HEX);
  Serial.print(" Data: ");
  for (int c = 0; c < frame.len; c++)
  {
    if (0x10 > frame.buf[c]) Serial.write(' ');
    Serial.print(frame.buf[c], HEX);
    Serial.write(' ');
    if (c == 2) digitalWrite(redLEDpin, frame.buf[c] % 2);
  }
  Serial.println();
  RXCount++;
}

void CANClass::gotFrame(CAN_message_t &frame, int mailbox)
{
  printFrame(frame, mailbox);
}

CANClass CANClass0;

// -------------------------------------------------------------
void setup(void)
{
  delay(3000);
  Serial.println(F("Teensy 3.2 CAN Test With Objects."));
  pinMode(redLEDpin, OUTPUT);

  Can0.begin(500000);
  Can0.attachObj(&CANClass0);

  CAN_filter_t allPassFilter;
  allPassFilter.id = 0;
  allPassFilter.ext = 1;
  allPassFilter.rtr = 0;

  //leave the first 4 mailboxes to use the default filter. Just change the higher ones
  for (uint8_t filterNum = 4; filterNum < 16; filterNum++) {
    Can0.setFilter(allPassFilter, filterNum);
  }
  for (uint8_t filterNum = 0; filterNum < 16; filterNum++) {
    CANClass0.attachMBHandler(filterNum);
  }
  //CANClass0.attachGeneralHandler();
  //CANClass1.attachGeneralHandler();
}

// -------------------------------------------------------------
void loop(void)
{
  if (RXtimer > 10000) {
    Serial.print("Total Received Messages in 10 Sec: ");
    Serial.println(RXCount);
    RXtimer = 0;
    RXCount = 0;
  }
}

ArduFE:
Aber aufpassen, wenn man auch senden will, dass man die dafür verwendeten Mailboxen (normalerweise 14 und 15) nicht kaputkonfiguriert.

Was sind denn in diesem Zusammenhang Mailboxen? Bei normaler ID bekomme ich die Mailboxen 0 und 1, bei erweiteter ID die Mailboxen 4 und 5.

</sup> <sup>Mailbox: 5 ID: 12345670 Data:  0  1 FA  3  4  5  6  7 Mailbox: 4 ID: 12345670 Data:  0  1 FB  3  4  5  6  7 Mailbox: 5 ID: 12345670 Data:  0  1 FC  3  4  5  6  7 Mailbox: 4 ID: 12345670 Data:  0  1 FD  3  4  5  6  7 Mailbox: 5 ID: 12345670 Data:  0  1 FE  3  4  5  6  7 Mailbox: 4 ID: 12345670 Data:  0  1 FF  3  4  5  6  7 Mailbox: 5 ID: 12345670 Data:  0  1  0  3  4  5  6  7 Mailbox: 4 ID: 12345670 Data:  0  1  1  3  4  5  6  7 Mailbox: 5 ID: 12345670 Data:  0  1  2  3  4  5  6  7 Total Received Messages in 10 Sec: 9 Mailbox: 0 ID: 70 Data:  0  1  2  3  4  5  6  7 Mailbox: 1 ID: 70 Data:  0  1  3  3  4  5  6  7 Mailbox: 0 ID: 70 Data:  0  1  4  3  4  5  6  7 Mailbox: 1 ID: 70 Data:  0  1  5  3  4  5  6  7 Mailbox: 0 ID: 70 Data:  0  1  6  3  4  5  6  7 Mailbox: 1 ID: 70 Data:  0  1  7  3  4  5  6  7 Mailbox: 0 ID: 70 Data:  0  1  8  3  4  5  6  7 Mailbox: 1 ID: 70 Data:  0  1  9  3  4  5  6  7 Total Received Messages in 10 Sec: 8</sup> <sup>

Mailboxen sind quasi Register im Controller, in denen die empfangenen CAN-Messages landen, bzw. die ausgehenen auf einen freien Bus warten.

Der Teensy hat 16 Mailboxen, die entweder zum Senden oder empfangen verwendet werden können. Normalerweise werden die empfangen Messages da irgendwann vom Interrupt der Library abgeholt und in einen Puffer im RAM des Controllers abgelegt.

Der MCP2515 hat zum Empfangen nur zwei, das ist seine Achillesferse. Liest der Arduino nicht schnell genug, werden die Inhalte der Register einfach mit neueren Daten überschrieben.

Mailboxen = Postfächer zum Empfang von Daten (sorry, da hätte ich auch selbst drauf kommen können :-[ )

Ich habe nun eine Variante zum Empfangen und Senden von Daten:

#include <FlexCAN.h>

elapsedMillis RXtimer;
uint32_t RXCount = 0;
const uint8_t redLEDpin = 13;
static CAN_message_t msg0;

class CANClass : public CANListener
{
  public:
    void printFrame(CAN_message_t &frame, int mailbox);
    void gotFrame(CAN_message_t &frame, int mailbox); //overrides the parent version so we can actually do something
};

void CANClass::printFrame(CAN_message_t &frame, int mailbox)
{
  Serial.print("Mailbox: ");
  Serial.print(mailbox);
  Serial.print(" ID: ");
  Serial.print(frame.id, HEX);
  Serial.print(" Data: ");
  for (int c = 0; c < frame.len; c++)
  {
    if (0x10 > frame.buf[c]) Serial.write(' ');
    Serial.print(frame.buf[c], HEX);
    Serial.write(' ');
    if (c == 2) digitalWrite(redLEDpin, frame.buf[c] % 2);
  }
  Serial.println();
  RXCount++;
}

void CANClass::gotFrame(CAN_message_t &frame, int mailbox)
{
  printFrame(frame, mailbox);
}

CANClass CANClass0;

// -------------------------------------------------------------
void setup(void)
{
  delay(3000);
  Serial.println(F("Teensy 3.2 CAN Test With Objects."));
  pinMode(redLEDpin, OUTPUT);

  Can0.begin(500000);
  Can0.attachObj(&CANClass0);

  msg0.id = 0x10000;
  msg0.ext = 1;
  msg0.rtr = 0;
  msg0.len = 2;
  msg0.buf[0] = 0x26;
  msg0.buf[1] = 0x48;

  CAN_filter_t allPassFilter;
  allPassFilter.id = 0;
  allPassFilter.ext = 1;
  allPassFilter.rtr = 0;

  //leave the first 4 mailboxes to use the default filter. Just change the higher ones
  for (uint8_t filterNum = 4; filterNum < 16; filterNum++) {
    Can0.setFilter(allPassFilter, filterNum);
  }
  for (uint8_t filterNum = 0; filterNum < 16; filterNum++) {
    CANClass0.attachMBHandler(filterNum);
  }
  //CANClass0.attachGeneralHandler();
  //CANClass1.attachGeneralHandler();
}

// -------------------------------------------------------------
void loop(void)
{
  if (RXtimer > 10000) {
    Serial.print("Total Received Messages in 10 Sec: ");
    Serial.println(RXCount);
    RXtimer = 0;
    RXCount = 0;
    Can0.write(msg0); //Write message
  }
}

Der serielle Monitor vom Mega zeigt:

```
Extended ID: 0x12345670  DLC: 8  Data: 0x00 0x01 0x9F 0x03 0x04 0x05 0x06 0x07

Extended ID: 0x00010000  DLC: 2  Data: 0x26 0x48

Extended ID: 0x12345670  DLC: 8  Data: 0x00 0x01 0xA0 0x03 0x04 0x05 0x06 0x07
-----------------------------
```

ArduFE:
Der Teensy hat 16 Mailboxen, die entweder zum Senden oder empfangen verwendet werden können. Normalerweise werden die empfangen Messages da irgendwann vom Interrupt der Library abgeholt und in einen Puffer im RAM des Controllers abgelegt.

Mache ist das richtig? In welche Mailbox schreibe ich mit Can0.write(), oder muß ich mich nicht darum kümmern? Mich beschleicht der Verdacht, es geht nur zufällig ::slight_smile:

agmue:
Mache ist das richtig? In welche Mailbox schreibe ich mit Can0.write(), oder muß ich mich nicht darum kümmern? Mich beschleicht der Verdacht, es geht nur zufällig ::slight_smile:

Also ich habe auch noch nicht alles durchdrungen, aber der Code der Library zusammen mit der Lektüre des Controllerdatenblattes ist doch recht hilfreich.

Beim write() ist schon der Kommentar darüber hilfreich

 * \note Will do one of two things - 1. Send the given frame out of the first available mailbox
 * or 2. queue the frame for sending later via interrupt. Automatically turns on TX interrupt
 * if necessary.
 * 
 * Returns whether sending/queueing succeeded. Will not smash the queue if it gets full.
 */    
int FlexCAN::write(const CAN_message_t &msg)
{

Standardmässig sind zwei Mailboxen zum Senden reserviert. Das sind die 14 und 15, weil immer die höchsten Nummern zum Senden verwendet werden, die unteren zum Empfangen.

Dies ist bibliotheksspezifisches Design für 14 und 15, jedoch können Sie eine TX-Mailbox in den Steckplätzen 0 und 1 und Ruhe empfangen, oder sogar 8 Standard-ID-Mailboxen empfangen, 2 Sende-Mailboxen und dann 2 erweiterte rx-Mailboxen, dann wieder Sie können 1 FIFO 6 tiefe und 8 Sendepostfächer sowie 6 tiefe Fifo RX mit 2 interruptgesteuerten Mailboxen für RX und 4 TX Mailboxen haben. Nur IFCT gibt Ihnen vollen Zugriff auf die Controller-Hardware, solange Sie wissen, wie Sie Ihre Postfächer einrichten, wird die Bibliothek jede Aufgabe behandeln, die Sie damit werfen :slight_smile:

Tony

Danke für einen deutschsprachigen Beitrag :slight_smile:

Die notwendige Hardware habe ich erst seit ein paar Tagen und ich befinde mich daher ganz am Anfang, die Möglichkeiten vom Can-Bus zu verstehen. Da freue ich mich schon, wenn ich eine LED ferngesteuert zum Blinken bringe.

Es bleibt spannend!

Ursprünglich ging es darum, eine Kommunikation aufzubauen. Das funktioniert sogar mit blinkender LED. Dann wollte ich noch eine erweiterte ID übertragen, das geht nun auch. Inzwischen kann der Teensy sogar antworten.

Aber am Wochenende fehlt mir die Ruhe zum Lesen und Probieren.