diff --git a/hardware_integration/include/isobus/hardware_integration/ntcan_fifo_plugin.hpp b/hardware_integration/include/isobus/hardware_integration/ntcan_fifo_plugin.hpp new file mode 100644 index 00000000..3022270f --- /dev/null +++ b/hardware_integration/include/isobus/hardware_integration/ntcan_fifo_plugin.hpp @@ -0,0 +1,64 @@ +//================================================================================================ +/// @file ntcan_fifo_plugin.hpp +/// +/// @brief An interface for using a ESD NTCAN FIFO driver. +/// @attention Use of the NTCAN driver is governed in part by their license, and requires you +/// to install their driver first, which in-turn requires you to agree to their terms and conditions. +/// @author Alex "Y_Less" Cole +/// +/// @copyright 2023 Alex "Y_Less" Cole +//================================================================================================ +#ifndef NTCAN_FIFO_PLUGIN_HPP +#define NTCAN_FIFO_PLUGIN_HPP + +#include + +#include +#include "isobus/hardware_integration/can_hardware_plugin.hpp" +#include "isobus/isobus/can_frame.hpp" +#include "isobus/isobus/can_hardware_abstraction.hpp" + +//================================================================================================ +/// @class NTCANPlugin +/// +/// @brief A CAN Driver for ESD NTCAN FIFO Devices +//================================================================================================ +class NTCANFIFOPlugin : public CANHardwarePlugin +{ +public: + /// @brief Constructor for the ESD NTCAN FIFO CAN driver + /// @param[in] channel The logical net number assigned to the physical CAN port to use. + explicit NTCANFIFOPlugin(int channel); + + /// @brief The destructor for NTCANFIFOPlugin + virtual ~NTCANFIFOPlugin() = default; + + /// @brief Returns if the connection with the hardware is valid + /// @returns `true` if connected, `false` if not connected + bool get_is_valid() const override; + + /// @brief Closes the connection to the hardware + void close() override; + + /// @brief Connects to the hardware you specified in the constructor's channel argument + void open() override; + + /// @brief Returns a frame from the hardware (synchronous), or `false` if no frame can be read. + /// @param[in, out] canFrame The CAN frame that was read + /// @returns `true` if a CAN frame was read, otherwise `false` + bool read_frame(isobus::HardwareInterfaceCANFrame &canFrame) override; + + /// @brief Writes a frame to the bus (synchronous) + /// @param[in] canFrame The frame to write to the bus + /// @returns `true` if the frame was written, otherwise `false` + bool write_frame(const isobus::HardwareInterfaceCANFrame &canFrame) override; + +private: + int net; + std::uint64_t timestampFreq; + std::uint64_t timestampOff; + NTCAN_HANDLE handle; ///< The handle as defined in the NTCAN FIFO driver API + NTCAN_RESULT openResult; ///< Stores the result of the call to begin CAN communication. Used for is_valid check later. +}; + +#endif // NTCAN_FIFO_PLUGIN_HPP diff --git a/hardware_integration/lib/README.md b/hardware_integration/lib/README.md index 10bd89b1..69a31d6a 100644 --- a/hardware_integration/lib/README.md +++ b/hardware_integration/lib/README.md @@ -6,3 +6,11 @@ Please read the End User License Agreement of the company PEAK-System Technik Gm www.peak-system.com/quick/eula Use of the PEAK driver libraries is governed by their EULA. + +### ESD NTCAN FIFO Driver + +Please read the End User License Agreement of the company esd electronics GmbH at: +https://esd.eu/en/products/can-tools + +You must install their driver and SDK to use this backend. + diff --git a/hardware_integration/src/ntcan_fifo_plugin.cpp b/hardware_integration/src/ntcan_fifo_plugin.cpp new file mode 100644 index 00000000..16efc0da --- /dev/null +++ b/hardware_integration/src/ntcan_fifo_plugin.cpp @@ -0,0 +1,153 @@ +//================================================================================================ +/// @file ntcan_fifo_plugin.cpp +/// +/// @brief An interface for using a ESD NTCAN FIFO driver. +/// @attention Use of the NTCAN driver is governed in part by their license, and requires you +/// to install their driver first, which in-turn requires you to agree to their terms and conditions. +/// @author Alex "Y_Less" Cole +/// +/// @copyright 2023 Alex "Y_Less" Cole +//================================================================================================ + +#include "isobus/hardware_integration/ntcan_fifo_plugin.hpp" +#include "isobus/isobus/can_stack_logger.hpp" + +#include +#include + +NTCANFIFOPlugin::NTCANFIFOPlugin(int channel) : + net(channel), + timestampFreq(1), + timestampOff(0), + handle(0), + openResult(NTCAN_SUCCESS) +{ +} + +bool NTCANFIFOPlugin::get_is_valid() const +{ + return (NTCAN_SUCCESS == openResult); +} + +void NTCANFIFOPlugin::close() +{ + canClose(handle); +} + +void NTCANFIFOPlugin::open() +{ + { + std::uint32_t mode = 0; + std::int32_t txQueueSize = 8; + std::int32_t rxQueueSize = 8; + std::int32_t txTimeOut = 100; + std::int32_t rxTimeOut = 1000; + + openResult = canOpen(net, mode, txQueueSize, rxQueueSize, txTimeOut, rxTimeOut, &handle); + } + + if (NTCAN_SUCCESS == openResult) + { + CAN_IF_STATUS status {0}; + + openResult = canSetBaudrate(handle, NTCAN_BAUD_250); + + if (NTCAN_SUCCESS == openResult) + { + openResult = canStatus(handle, &status); + } + + if (NTCAN_FEATURE_TIMESTAMP == (status.features & NTCAN_FEATURE_TIMESTAMP)) + { + std::uint64_t timestamp = 0; + if (NTCAN_SUCCESS == openResult) + { + openResult = canIoctl(handle, NTCAN_IOCTL_GET_TIMESTAMP_FREQ, ×tampFreq); + } + + if (NTCAN_SUCCESS == openResult) + { + openResult = canIoctl(handle, NTCAN_IOCTL_GET_TIMESTAMP, ×tamp); + } + + if (NTCAN_SUCCESS == openResult) + { + auto now = std::chrono::system_clock::now(); + auto unix = now.time_since_epoch(); + long long millis = std::chrono::duration_cast(unix).count(); + timestampOff = millis - timestamp; + } + } + + if (NTCAN_SUCCESS == openResult) + { + std::int32_t ids = (1 << 11); + openResult = canIdRegionAdd(handle, 0, &ids); + if (NTCAN_SUCCESS == openResult && ids != (1 << 11)) + { + openResult = NTCAN_INSUFFICIENT_RESOURCES; + } + } + + if (NTCAN_SUCCESS == openResult) + { + std::int32_t ids = (1 << 29); + // Address 0, with the wide address flag. + openResult = canIdRegionAdd(handle, 0 | NTCAN_20B_BASE, &ids); + if (NTCAN_SUCCESS == openResult && ids != (1 << 29)) + { + openResult = NTCAN_INSUFFICIENT_RESOURCES; + } + } + + if (NTCAN_SUCCESS != openResult) + { + isobus::CANStackLogger::CAN_stack_log(isobus::CANStackLogger::LoggingLevel::Critical, "[NTCAN]: Error trying to connect to NTCAN driver"); + canClose(handle); + } + } + else + { + isobus::CANStackLogger::CAN_stack_log(isobus::CANStackLogger::LoggingLevel::Critical, "[NTCAN]: Error trying to connect to NTCAN driver"); + } +} + +bool NTCANFIFOPlugin::read_frame(isobus::HardwareInterfaceCANFrame &canFrame) +{ + NTCAN_RESULT result; + CMSG_T msgCanMessage {0}; + bool retVal = false; + std::int32_t count = 1; + + result = canReadT(handle, &msgCanMessage, &count, nullptr); + + if (NTCAN_SUCCESS == result && 1 == count) + { + canFrame.dataLength = msgCanMessage.len; + memcpy(canFrame.data, msgCanMessage.data, msgCanMessage.len); + canFrame.identifier = (msgCanMessage.id & ((1 << 29) - 1)); + canFrame.isExtendedFrame = (NTCAN_20B_BASE == (msgCanMessage.id & NTCAN_20B_BASE)); + canFrame.timestamp_us = msgCanMessage.timestamp * 1000000 / timestampFreq + timestampOff; + retVal = true; + } + else + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + return retVal; +} + +bool NTCANFIFOPlugin::write_frame(const isobus::HardwareInterfaceCANFrame &canFrame) +{ + NTCAN_RESULT result; + CMSG_T msgCanMessage {0}; + std::int32_t count = 1; + + msgCanMessage.id = canFrame.isExtendedFrame ? (canFrame.identifier | NTCAN_20B_BASE) : canFrame.identifier; + msgCanMessage.len = canFrame.dataLength; + memcpy(msgCanMessage.data, canFrame.data, canFrame.dataLength); + + result = canWriteT(handle, &msgCanMessage, &count, nullptr); + + return (NTCAN_SUCCESS == result && 1 == count); +}