diff --git a/examples/SineWaveCAN/SineWaveCAN.ino b/examples/SineWaveCAN/SineWaveCAN.ino index fb65417..0af6543 100644 --- a/examples/SineWaveCAN/SineWaveCAN.ino +++ b/examples/SineWaveCAN/SineWaveCAN.ino @@ -21,11 +21,12 @@ // #define IS_ARDUINO_BUILTIN // Arduino boards with built-in CAN interface (e.g. Arduino Uno R4 Minima) // #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. /* Board-specific includes ---------------------------------------------------*/ -#if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) + defined(IS_STM32_BUILTIN) != 1 +#if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) + defined(IS_STM32_BUILTIN) + defined(IS_ESP32_TWAI) != 1 #warning "Select exactly one hardware option at the top of this file." #if CAN_HOWMANY > 0 || CANFD_HOWMANY > 0 @@ -65,6 +66,12 @@ struct ODriveStatus; // hack to prevent teensy compile error #include "ODriveSTM32CAN.hpp" #endif // IS_STM32_BUILTIN +#ifdef IS_ESP32_TWAI +// See https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/twai.html +#include "driver/twai.h" +#include "ODriveESP32TWAI.hpp" +#endif // IS_ESP32_TWAI + /* Board-specific settings ---------------------------------------------------*/ @@ -160,6 +167,53 @@ bool setupCan() { #endif // IS_STM32_BUILTIN +/* ESP32 boards with built-in TWAI (CAN) */ + +#ifdef IS_ESP32_TWAI + +// Pins used to connect to CAN bus transceiver +#define ESP32_TWAI_TX_PIN 5 +#define ESP32_TWAI_RX_PIN 4 + +ESP32TWAIIntf can_intf; + +bool setupCan() { + twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT( + (gpio_num_t)ESP32_TWAI_TX_PIN, + (gpio_num_t)ESP32_TWAI_RX_PIN, + TWAI_MODE_NORMAL + ); + + twai_timing_config_t t_config; + switch (CAN_BAUDRATE) { + case 1000000: t_config = TWAI_TIMING_CONFIG_1MBITS(); break; + case 800000: t_config = TWAI_TIMING_CONFIG_800KBITS(); break; + case 500000: t_config = TWAI_TIMING_CONFIG_500KBITS(); break; + case 250000: t_config = TWAI_TIMING_CONFIG_250KBITS(); break; + case 125000: t_config = TWAI_TIMING_CONFIG_125KBITS(); break; + case 100000: t_config = TWAI_TIMING_CONFIG_100KBITS(); break; + case 50000: t_config = TWAI_TIMING_CONFIG_50KBITS(); break; + case 25000: t_config = TWAI_TIMING_CONFIG_25KBITS(); break; + default: t_config = TWAI_TIMING_CONFIG_250KBITS(); break; + } + + twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); + + if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) { + return false; + } + + if (twai_start() != ESP_OK) { + twai_driver_uninstall(); + return false; + } + + return true; +} + +#endif // IS_ESP32_TWAI + + /* Example sketch ------------------------------------------------------------*/ // Instantiate ODrive objects diff --git a/platformio.ini b/platformio.ini index 993d4af..c768ff0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -46,4 +46,10 @@ build_flags = ; See https://github.com/pazi88/STM32_CAN -DHAL_CAN_MODULE_ENABLED lib_deps = - https://github.com/pazi88/STM32_CAN.git#1.2.0 \ No newline at end of file + https://github.com/pazi88/STM32_CAN.git#1.2.0 + +[env:esp32] +platform = espressif32 +board = esp32dev +framework = arduino +build_flags = -DIS_ESP32_TWAI \ No newline at end of file diff --git a/src/ODriveESP32TWAI.hpp b/src/ODriveESP32TWAI.hpp new file mode 100644 index 0000000..a6a4f2d --- /dev/null +++ b/src/ODriveESP32TWAI.hpp @@ -0,0 +1,58 @@ +// CAN glue layer for ESP32 platforms using the native TWAI driver. +// See ODriveHardwareCAN.hpp for documentation. + +#pragma once + +#include "ODriveCAN.h" +#include "driver/twai.h" + +// Simple struct to hold TWAI interface state. Unlike other platforms, ESP32's +// TWAI driver uses global functions rather than a class instance. +struct ESP32TWAIIntf {}; + +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(ESP32TWAIIntf& intf, uint32_t id, uint8_t length, const uint8_t* data) { + (void)intf; + twai_message_t tx_msg = {}; + tx_msg.identifier = id; + tx_msg.data_length_code = length; + tx_msg.extd = (id & 0x80000000) ? 1 : 0; + tx_msg.rtr = (data == nullptr) ? 1 : 0; + + if (data) { + for (int i = 0; i < length; ++i) { + tx_msg.data[i] = data[i]; + } + } + + return twai_transmit(&tx_msg, 0) == ESP_OK; +} + +static void onReceive(const CanMsg& msg, ODriveCAN& odrive) { + odrive.onReceive(msg.id, msg.len, msg.buffer); +} + +static void pumpEvents(ESP32TWAIIntf& intf, int max_events = 100) { + (void)intf; + // max_events prevents an infinite loop if messages come at a high rate + twai_message_t rx_msg; + while (twai_receive(&rx_msg, 0) == ESP_OK && max_events--) { + CanMsg msg; + msg.id = rx_msg.identifier; + msg.len = rx_msg.data_length_code; + for (int i = 0; i < rx_msg.data_length_code && i < 8; ++i) { + msg.buffer[i] = rx_msg.data[i]; + } + onCanMessage(msg); + } +} + +CREATE_CAN_INTF_WRAPPER(ESP32TWAIIntf)