diff --git a/firmware/can.cpp b/firmware/can.cpp new file mode 100644 index 0000000..9f16b0a --- /dev/null +++ b/firmware/can.cpp @@ -0,0 +1,32 @@ +#include "can.h" +#include "hal.h" + +#include "can_helper.h" + +static const CANConfig canConfig500 = +{ + CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP, + CAN_BTR_SJW(0) | CAN_BTR_BRP(5) | CAN_BTR_TS1(12) | CAN_BTR_TS2(1), +}; + +void InitCan() +{ + canStart(&CAND1, &canConfig500); + + CanTxMessage::setDevice(CAND1); +} + +struct StandardDataFrame +{ + uint16_t lambda; + uint16_t measuredResistance; + uint8_t pad[4]; +}; + +void SendCanData(float lambda, uint16_t measuredResistance) +{ + CanTxTyped frame(0x130); + + frame.get().lambda = lambda / 1000.0f; + frame.get().measuredResistance = measuredResistance; +} diff --git a/firmware/can.h b/firmware/can.h new file mode 100644 index 0000000..091d7f2 --- /dev/null +++ b/firmware/can.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void InitCan(); +void SendCanData(float lambda, uint16_t measuredResistance); diff --git a/firmware/can_helper.cpp b/firmware/can_helper.cpp new file mode 100644 index 0000000..71509d2 --- /dev/null +++ b/firmware/can_helper.cpp @@ -0,0 +1,28 @@ +#include "can_helper.h" + +#include + +/*static*/ CANDriver* CanTxMessage::s_device = nullptr; + +/*static*/ void CanTxMessage::setDevice(CANDriver& device) { + s_device = &device; +} + +CanTxMessage::CanTxMessage(uint32_t eid, uint8_t dlc) { + m_frame.IDE = CAN_IDE_STD; + m_frame.EID = eid; + m_frame.RTR = CAN_RTR_DATA; + m_frame.DLC = dlc; + memset(m_frame.data8, 0, sizeof(m_frame.data8)); +} + +CanTxMessage::~CanTxMessage() { + auto device = s_device; + + // 100 ms timeout + canTransmit(device, CAN_ANY_MAILBOX, &m_frame, TIME_MS2I(100)); +} + +uint8_t& CanTxMessage::operator[](size_t index) { + return m_frame.data8[index]; +} \ No newline at end of file diff --git a/firmware/can_helper.h b/firmware/can_helper.h new file mode 100644 index 0000000..274742e --- /dev/null +++ b/firmware/can_helper.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include "hal.h" + +/** + * Represent a message to be transmitted over CAN. + * + * Usage: + * * Create an instance of CanTxMessage + * * Set any data you'd like to transmit either using the subscript operator to directly access bytes, or any of the helper functions. + * * Upon destruction, the message is transmitted. + */ +class CanTxMessage +{ +public: + /** + * Create a new CAN message, with the specified extended ID. + */ + explicit CanTxMessage(uint32_t eid, uint8_t dlc = 8); + + /** + * Destruction of an instance of CanTxMessage will transmit the message over the wire. + */ + ~CanTxMessage(); + + /** + * Configures the device for all messages to transmit from. + */ + static void setDevice(CANDriver& device); + + /** + * @brief Read & write the raw underlying 8-byte buffer. + */ + uint8_t& operator[](size_t); + +protected: + CANTxFrame m_frame; + +private: + static CANDriver* s_device; +}; + +/** + * A CAN message based on a type, removing the need for manually flipping bits/bytes. + */ +template +class CanTxTyped final : public CanTxMessage +{ + static_assert(sizeof(TData) == sizeof(CANTxFrame::data8)); + +public: + explicit CanTxTyped(uint32_t eid) : CanTxMessage(eid) { } + + /** + * Access members of the templated type. + * + * So you can do: + * CanTxTyped d; + * d->memberOfMyType = 23; + */ + TData* operator->() + { + return reinterpret_cast(&m_frame.data8); + } + + TData& get() + { + return *reinterpret_cast(&m_frame.data8); + } +}; + +template +void transmitStruct(uint32_t eid) +{ + CanTxTyped frame(eid); + // Destruction of an instance of CanTxMessage will transmit the message over the wire. + // see CanTxMessage::~CanTxMessage() + populateFrame(frame.get()); +}