T4.1 Initializing Ethernet and CAN at the same time does not work.

itmm

New member
Hey all,
I have a Teensy 4.1 wired up to Ethernet using its PHY and a SN65HVD230 CAN transceiver on CAN1. I am happy to go into more hardware details if it is relevant, but this seems like a software issue to me.

When initializing either CAN or Ethernet, they both work perfectly. I can send and receive packets on Ethernet, or control our downstream CAN devices when its solely enabled. However, once both are enabled, CAN stops sending messages. I can "send" a few with tryToSend, but inspecting the TX buffer length, they are just getting stuck in the queue, which eventually fills up and causes an error. It doesn't seem to matter if Ethernet is initialized before or after CAN, and there might be a very small window where the CAN peripheral can send messages with Ethernet on, but it doesn't last. The Ethernet peripheral seems to continue working throughout this.

I am using the QNEthernet library and the ACAN_T4 library, but the same issue seems to happen with the NativeEthernet and FLEXCAN_T4 library, see this github issue (not mine).

I am on Windows 10 using the platformio plugin for VSCode (Core version: 6.1.13).

Here is the minimum code to reproduce the error.
Code:
#include <QNEthernet.h>
#include <ACAN_T4.h>
using namespace qindesign::network;
constexpr uint32_t kDHCPTimeout = 15'000;  // 15 seconds
constexpr uint16_t kPort = 5190;
EthernetUDP udp;

void setup() {
    Serial.begin(115200);
    while (!Serial && millis() < 4000) {
    // Wait for Serial
    }
    printf("Starting...\r\n");
    uint8_t mac[6];
    Ethernet.macAddress(mac);  // This is informative; it retrieves, not sets
    printf("MAC = %02x:%02x:%02x:%02x:%02x:%02x\r\n",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    Ethernet.onLinkState([](bool state) {
    printf("[Ethernet] Link %s\r\n", state ? "ON" : "OFF");
    });
    printf("Starting Ethernet with DHCP...\r\n");
    if (!Ethernet.begin()) {
    printf("Failed to start Ethernet\r\n");
    return;
    }
    if (!Ethernet.waitForLocalIP(kDHCPTimeout)) {
    printf("Failed to get IP address from DHCP\r\n");
    return;
    }
    IPAddress ip = Ethernet.localIP();
    printf("    Local IP     = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
    ip = Ethernet.subnetMask();
    printf("    Subnet mask  = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
    ip = Ethernet.broadcastIP();
    printf("    Broadcast IP = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
    ip = Ethernet.gatewayIP();
    printf("    Gateway      = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
    ip = Ethernet.dnsServerIP();
    printf("    DNS          = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
    // Start UDP listening on the port
    udp.begin(kPort);
    ACAN_T4_Settings settings (125 * 1000); // 125 kbit/s
    const uint32_t errorCode = ACAN_T4::can1.begin (settings);
}
const uint8_t bytes[] = {0xDE, 0xAD, 0xBE, 0xEF};
uint32_t ethSendAt = 0;
static uint32_t gSendDate = 0;
static uint32_t gSentCount = 0;
static uint32_t gReceivedCount = 0;

void loop() {
    if(millis() > ethSendAt) {
        if (!udp.send(Ethernet.broadcastIP(), kPort, bytes, 4)) {
            Serial.print("Error sending ethernet message\n");
        }
        ethSendAt = millis() + 1000;
    }
    CANMessage message;
    if (gSendDate <= millis ()) {
        message.id = 0x542;
        const bool ok = ACAN_T4::can1.tryToSend (message);
        if (ok) {
            gSendDate += 2000;
            gSentCount += 1;
            Serial.print("Sent: ");
            Serial.print(gSentCount);
            Serial.println(" can messages");
        } else {
            Serial.println("Failed to send can Message");
        }
    }
    if (ACAN_T4::can1.receive (message)) {
        gReceivedCount += 1;
        Serial.print("Received: ");
        Serial.print(gReceivedCount);
        Serial.println(" can messages");
    }
}
 
Last edited:
I’m curious, does initialization order matter? For example, if you initialize CAN first instead of Ethernet.

Also, you need to account for millis() wraparound. Either compare with subtraction to a timeout value or use elapsedMillis instead.

Last, is your logic for gSendDate correct?
 
I didn't see any difference changing the initialization order.
The millis wraparound is an issue with the example program, but it works for testing purposes to send packets at that interval.
The gSendDate is copied example code, and works to try to send CAN messages, as is seen in the Serial monitor when ran.
 
Last edited:
You can try with initializing CAN on other Teensy's CAN interface. CAN2, for example.

Why not to use FlexCAN_T4 library?
 
Back
Top