Porting¶
Connect the driver to a CAN controller: the one method you implement, its contract, and a working adapter for
HardFOC's BaseCan.
The interface¶
| Method | Called | Must |
|---|---|---|
bool send(const VortexCanFrame& frame) noexcept |
Once per NMT or SDO call, from the caller's thread | Queue or send the frame and return true, or return false; never throw |
That is the whole interface. The session calls it synchronously and passes its result back to your code.
Rules for send()¶
- Copy the frame. It lives on the session's stack; do not keep the reference.
- Classic CAN.
dlcis 0 to 8,extendedandrtrare false for every frame the session builds. Send it as a classic (non-FD) frame. - Decide on blocking. Return at once with false when the TX queue is full (the adapter below does), or block with a short timeout; either way, do not block forever.
- Report bus errors as false. Bus-off, controller not started, no acknowledgement: the caller retries or reports.
- Be thread-safe if you share it. Two sessions on one transport in two tasks call
send()concurrently.
Over BaseCan¶
hf-internal-interface-wrap's BaseCan is the CAN
interface the HardFOC HALs expose (EspCan on the ESP32-C6 TWAI today). Its hf_can_message_t holds FD-sized
payloads; a classic frame uses the first 8 bytes.
#include <cstring>
#include "BaseCan.h"
#include "vortex/VortexCanTransport.hpp"
class BaseCanVortexTransport final : public hf::vortex::VortexCanTransport {
public:
explicit BaseCanVortexTransport(BaseCan& can) noexcept : can_(can) {}
bool send(const hf::vortex::VortexCanFrame& frame) noexcept override {
if (frame.dlc > 8) {
return false; // classic CAN only
}
hf_can_message_t msg{}; // classic frame: is_canfd = false
msg.id = frame.id;
msg.is_extended = frame.extended;
msg.is_rtr = frame.rtr;
msg.dlc = frame.dlc;
std::memcpy(msg.data, frame.data, frame.dlc);
return can_.TrySendMessage(msg) == hf_can_err_t::CAN_SUCCESS; // never blocks
}
private:
BaseCan& can_;
};
TrySendMessage() is SendMessage(message, 0): it queues the frame or fails at once with CAN_ERR_QUEUE_FULL or
CAN_ERR_BUS_OFF. On Vortex V1, CommChannelsManager::GetCanBus() returns the BaseCan* (null when the bus did not
initialise).
For tests¶
A transport that records frames lets host tests check the exact bytes, as this repository's own check does:
class LoopbackTransport final : public hf::vortex::VortexCanTransport {
public:
bool send(const hf::vortex::VortexCanFrame& frame) noexcept override {
if (count_ == kMaxFrames) {
return false; // full: report a TX failure
}
frames_[count_++] = frame;
return true;
}
std::size_t count() const noexcept { return count_; }
const hf::vortex::VortexCanFrame& frame(std::size_t i) const noexcept { return frames_[i]; }
private:
static constexpr std::size_t kMaxFrames = 16;
hf::vortex::VortexCanFrame frames_[kMaxFrames]{};
std::size_t count_ = 0;
};
How to check a port¶
- Run
examples/genericon the host and note the two frames it checks. - Send the same calls through your transport and watch the bus with an analyser: IDs
0x000and0x601, the bytes in Hardware. - Disconnect the bus (no acknowledgement) and check that
send()eventually returns false rather than hanging.
Next: Examples.