Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NTCAN hardware layer #198

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
@@ -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 <string>

#include <NTCAN.h>
#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
8 changes: 8 additions & 0 deletions hardware_integration/lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

153 changes: 153 additions & 0 deletions hardware_integration/src/ntcan_fifo_plugin.cpp
Original file line number Diff line number Diff line change
@@ -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 <thread>
#include <chrono>

NTCANFIFOPlugin::NTCANFIFOPlugin(int channel) :
net(channel),
timestampFreq(1),
timestampOff(0),
handle(0),
openResult(NTCAN_SUCCESS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going forward the best practice is to put initializers in the class header instead of here, so like:
std::uint64_t timestampOff = 0; in the header file instead of in the class initializer list.

{
}

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, &timestampFreq);
}

if (NTCAN_SUCCESS == openResult)
{
openResult = canIoctl(handle, NTCAN_IOCTL_GET_TIMESTAMP, &timestamp);
}

if (NTCAN_SUCCESS == openResult)
{
auto now = std::chrono::system_clock::now();
auto unix = now.time_since_epoch();
long long millis = std::chrono::duration_cast<std::chrono::microseconds>(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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 | something is always something. Do you need the 0?

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);
}