Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 77 additions & 3 deletions examples/SineWaveCAN/SineWaveCAN.ino
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
// #define IS_MCP2515 // Any board with external MCP2515 based extension module. See below to configure the module.
// #define IS_STM32_BUILTIN // STM32 boards with built-in CAN interface (e.g. STM32F4 Discovery).
// #define IS_ESP32_TWAI // ESP32 boards with built-in TWAI (CAN) interface. Directly uses the ESP-IDF TWAI driver.

// #define IS_TCAN4X5X // RP2350 / Arduino boards with external TCAN4551 SPI controller.

/* Board-specific includes ---------------------------------------------------*/

#if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) + defined(IS_STM32_BUILTIN) + defined(IS_ESP32_TWAI) != 1
#if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) + defined(IS_STM32_BUILTIN) + defined(IS_ESP32_TWAI) + defined(IS_TCAN4X5X) != 1
#warning "Select exactly one hardware option at the top of this file."

#if CAN_HOWMANY > 0 || CANFD_HOWMANY > 0
Expand Down Expand Up @@ -72,7 +72,11 @@ struct ODriveStatus; // hack to prevent teensy compile error
#include "ODriveESP32TWAI.hpp"
#endif // IS_ESP32_TWAI


#ifdef IS_TCAN4X5X
#include <SPI.h>
#include <TCAN4x5x.h>
#include "ODriveTCAN4x5x.hpp"
#endif // IS_TCAN4X5X

/* Board-specific settings ---------------------------------------------------*/

Expand Down Expand Up @@ -213,6 +217,76 @@ bool setupCan() {

#endif // IS_ESP32_TWAI

/* TCAN4551 SPI Controller */

#ifdef IS_TCAN4X5X

const uint8_t MISO_PIN = 16;
const uint8_t CS_PIN = 17;
const uint8_t SCK_PIN = 18;
const uint8_t MOSI_PIN = 19;
const uint8_t INT1_PIN = 20;
const uint8_t INT2_PIN = 21;
const uint8_t RST_PIN = 0xFF;

TCAN4x5x can_intf(CS_PIN, MISO_PIN, MOSI_PIN, SCK_PIN, RST_PIN);

volatile bool tcanIrqAsserted = false;
void IRAM_ATTR onTcanPinFall() {
tcanIrqAsserted = true;
}

bool setupCan() {
uint16_t prescaler = (uint16_t)(1000000UL / CAN_BAUDRATE);
if (prescaler < 1) prescaler = 1;
can_intf.setNominalBitRate(prescaler, 32, 8);

TCAN4x5x_MRAM_Config customMRAM = {0};
customMRAM.SIDNumElements = 1;
customMRAM.XIDNumElements = 1;
customMRAM.Rx0NumElements = 16;
customMRAM.Rx0ElementSize = MRAM_8_Byte_Data;
customMRAM.TxBufferNumElements = 4;
customMRAM.TxBufferElementSize = MRAM_8_Byte_Data;
can_intf.setMRAMConfig(&customMRAM);

TCAN4x5x_MCAN_Global_Filter_Configuration gfc = {0};
gfc.RRFE = 1;
gfc.RRFS = 1;
gfc.ANFE = TCAN4x5x_GFC_ACCEPT_INTO_RXFIFO0;
gfc.ANFS = TCAN4x5x_GFC_ACCEPT_INTO_RXFIFO0;
can_intf.setGlobalFilterConfig(&gfc);

TCAN4x5x_DEV_CONFIG devCfg = {0};
devCfg.SWE_DIS = 1;
devCfg.CLK_REF = 1;
devCfg.GPIO1_CONFIG = TCAN4x5x_DEV_CONFIG_GPIO1_CONFIG_GPO;
devCfg.GPIO1_GPO_CONFIG = TCAN4x5x_DEV_CONFIG_GPO1_MCAN_INT1;
devCfg.GPO2_CONFIG = TCAN4x5x_DEV_CONFIG_GPO2_MCAN_INT0;
devCfg.WD_ACTION = TCAN4x5x_DEV_CONFIG_WDT_ACTION_nINT;
devCfg.WAKE_CONFIG = TCAN4x5x_DEV_CONFIG_WAKE_BOTH_EDGES;
can_intf.setDeviceConfig(&devCfg);

can_intf.attachInterruptPin(INT1_PIN, onTcanPinFall);
can_intf.attachInterruptPin(INT2_PIN, onTcanPinFall);

can_intf.begin(&SPI);

TCAN4x5x_MCAN_CCCR_Config cccr = {0};
can_intf.MCAN_EnableProtectedRegisters();
can_intf.MCAN_ReadCCCRRegister(&cccr);
cccr.FDOE = 0;
cccr.BRSE = 0;
can_intf.MCAN_ConfigureCCCRRegister(&cccr);
can_intf.MCAN_DisableProtectedRegisters();

can_intf.WDT_Disable();
can_intf.Device_SetMode(TCAN4x5x_DEVICE_MODE_NORMAL);

return (can_intf.Device_ReadDeviceVersion() != 0xFFFF && can_intf.Device_ReadMode() == TCAN4x5x_DEVICE_MODE_NORMAL);
}

#endif // IS_TCAN4X5X

/* Example sketch ------------------------------------------------------------*/

Expand Down
81 changes: 81 additions & 0 deletions src/ODriveTCAN4x5x.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once

#include <Arduino.h>
#include <string.h>
#include "ODriveCAN.h"
#include "TCAN4x5x.h"

struct CanMsg {
uint32_t id;
uint8_t len;
uint8_t buffer[8];
};

// Must be defined by the application
void onCanMessage(const CanMsg& msg);

static bool sendMsg(TCAN4x5x& can_intf, uint32_t id, uint8_t length, const uint8_t* data) {
if (length > 8) {
return false;
}
TCAN4x5x_MCAN_TX_Header txHdr = {0};
if (id & 0x80000000UL) {
txHdr.XTD = 1;
txHdr.ID = id & 0x1FFFFFFFUL;
} else {
txHdr.XTD = 0;
txHdr.ID = id & 0x000007FFUL;
}

txHdr.RTR = (data == nullptr) ? 1 : 0;
txHdr.DLC = length;
txHdr.FDF = 0;
txHdr.BRS = 0;
txHdr.ESI = 0;
txHdr.EFC = 0;
txHdr.MM = 0;

uint8_t payload[8] = {0};
if (data != nullptr && length > 0) {
memcpy(payload, data, length);
}
if (can_intf.MCAN_WriteTXBuffer(0, &txHdr, payload) == 0) {
return false;
}
return can_intf.MCAN_TransmitBufferContents(0);
}

static void onReceive(const CanMsg& msg, ODriveCAN& odrive) {
odrive.onReceive(msg.id, msg.len, msg.buffer);
}

static void pumpEvents(TCAN4x5x& can_intf) {
while (true) {
TCAN4x5x_MCAN_RX_Header rxHdr = {0};
uint8_t rxData[64] = {0};
rxHdr.DLC = 0x0F;
uint8_t numBytes = can_intf.MCAN_ReadNextFIFO(RXFIFO0, &rxHdr, rxData);

if (numBytes == 0 && rxHdr.DLC == 0x0F) {
break;
}

if (rxHdr.FDF || rxHdr.RTR || numBytes > 8) {
continue;
}

CanMsg msg = {0};
msg.id = rxHdr.ID;
if (rxHdr.XTD) {
msg.id |= 0x80000000UL;
}
msg.len = numBytes;
if (numBytes > 0) {
memcpy(msg.buffer, rxData, numBytes);
}

onCanMessage(msg);
}
}

CREATE_CAN_INTF_WRAPPER(TCAN4x5x)