diff --git a/include/sta/intf/can/controller.hpp b/include/sta/intf/can/controller.hpp new file mode 100644 index 0000000..f666a2b --- /dev/null +++ b/include/sta/intf/can/controller.hpp @@ -0,0 +1,100 @@ +/** + * @file + * @brief CAN controller driver interface. + */ +#ifndef STA_INTF_CAN_CONTROLLER_HPP +#define STA_INTF_CAN_CONTROLLER_HPP + +/** + * @defgroup can CAN + * @brief CAN Controller interface definition. + */ + +/** + * @defgroup canAPI API + * @ingroup can + * @brief Public library interface. + */ + +/** + * @defgroup canBuildConfig Build config + * @ingroup can + * @brief Build configuration options. + */ + +#include +#include + + +namespace sta +{ + /** + * @brief CAN controller driver interface. + * + * @ingroup canAPI + */ + class CanController + { + public: + // RX/TX + // + + /** + * @brief Send frame to CAN controller for transmission. + * + * @param header CAN frame TX header + * @param payload CAN frame payload + * @return True on success + */ + virtual bool sendFrame(const CanTxHeader & header, const uint8_t * payload) = 0; + + /** + * @brief Get received frame from the CAN controller. + * + * @param[in] fifo FIFO storing frame + * @param[out] header CAN frame RX header destination + * @param[out] payload CAN frame payload destination + * @return True on success + */ + virtual bool receiveFrame(uint8_t fifo, CanRxHeader * header, uint8_t * payload) = 0; + + /** + * @brief Get RX FIFO flags. + * + * @return FIFO flags + */ + virtual uint32_t getRxFifoFlags() = 0; + + + // RX filter + // + + /** + * @brief Change filter settings. + * + * @param idx Filter index + * @param filter Filter configuration + * @param active Enable filter after applying settings + */ + virtual void configureFilter(uint8_t idx, const CanFilter & filter, bool active = false) = 0; + /** + * @brief Enable filter. + * + * @param idx Filter index + */ + virtual void enableFilter(uint8_t idx) = 0; + /** + * @brief Disable filter. + * + * @param idx Filter index + */ + virtual void disableFilter(uint8_t idx) = 0; + /** + * @brief Disable and clear all filters. + */ + virtual void clearFilters() = 0; + }; +} // namespace sta + + +#endif // STA_INTF_CAN_CONTROLLER_HPP diff --git a/include/sta/intf/can/filter.hpp b/include/sta/intf/can/filter.hpp new file mode 100644 index 0000000..ef832f5 --- /dev/null +++ b/include/sta/intf/can/filter.hpp @@ -0,0 +1,49 @@ +/** + * @file + * @brief CAN message filter types. + */ +#ifndef STA_INFT_CAN_FILTER_HPP +#define STA_INFT_CAN_FILTER_HPP + +#include + +#include + + +namespace sta +{ + /** + * @defgroup canFilter Filters + * @ingroup canAPI + * @brief CAN message filter types. + */ + + + /** + * @brief ID format matched by CAN filter. + * + * @ingroup canFilter + */ + enum class CanFilterIdFormat + { + ANY, /**< Match both ID formats */ + STD, /**< Match standard format IDs */ + EXT /**< Match extended format IDs */ + }; + + /** + * @brief CAN filter settings. + * + * @ingroup canFilter + */ + struct CanFilter + { + CanId obj; /**< ID object */ + CanId mask; /**< ID mask */ + CanFilterIdFormat type; /**< ID format to match */ + uint8_t fifo; /**< FIFO to store matches */ + }; +} // namespace sta + + +#endif // STA_INFT_CAN_FILTER_HPP diff --git a/include/sta/intf/can/headers.hpp b/include/sta/intf/can/headers.hpp new file mode 100644 index 0000000..6ece67e --- /dev/null +++ b/include/sta/intf/can/headers.hpp @@ -0,0 +1,48 @@ +/** + * @file + * @brief CAN frame headers. + */ +#ifndef STA_INTF_CAN_HEADERS_HPP +#define STA_INTF_CAN_HEADERS_HPP + +#include + +#include + + +namespace sta +{ + /** + * @defgroup canHeader Frame headers + * @ingroup canAPI + * @brief CAN header types for transmitted / received frames. + */ + + + /** + * @brief CAN TX frame header. + * + * @ingroup canHeader + */ + struct CanTxHeader + { + CanFrameId id; /**< Frame ID */ + uint8_t payloadLength; /**< Size of data to send */ + }; + + /** + * @brief CAN RX frame header. + * + * @ingroup canHeader + */ + struct CanRxHeader + { + CanFrameId id; /**< Frame ID */ + uint8_t payloadLength; /**< Size of received data */ + uint32_t timestamp; /**< RX timestamp */ + uint8_t filter; /**< RX filter match */ + }; +} // namespace sta + + +#endif // STA_INTF_CAN_HEADERS_HPP diff --git a/include/sta/intf/can/id.hpp b/include/sta/intf/can/id.hpp new file mode 100644 index 0000000..7c1d653 --- /dev/null +++ b/include/sta/intf/can/id.hpp @@ -0,0 +1,82 @@ +/** + * @file + * @brief CAN frame ID types. + */ +#ifndef STA_INTF_CAN_ID_HPP +#define STA_INTF_CAN_ID_HPP + +#include + + +namespace sta +{ + /** + * @defgroup canID Frame IDs + * @ingroup canAPI + * @brief Types for working with CAN ID values and formats. + */ + + + /** + * @brief CAN frame ID format. + * + * @ingroup canID + */ + enum class CanIdFormat : uint8_t + { + STD, /**< Standard format */ + EXT /**< Extended format */ + }; + + + /** + * @brief CAN frame ID. + * + * @ingroup canID + */ + struct CanId + { + uint32_t sid; /**< Standard ID field (11 bits) */ + uint32_t eid; /**< Extended ID field (18 bits) */ + }; + + + /** + * @brief CAN frame ID and format. + * + * @ingroup canID + */ + struct CanFrameId + { + CanIdFormat format; /**< ID format */ + uint32_t sid; /**< Standard ID field (11 bits) */ + uint32_t eid; /**< Extended ID field (18 bits) */ + }; + + + // Comparison operators + // + + bool operator ==(const CanId & lhs, const CanId & rhs); + bool operator !=(const CanId & lhs, const CanId & rhs); + + bool operator ==(const CanFrameId & lhs, const CanFrameId & rhs); + bool operator !=(const CanFrameId & lhs, const CanFrameId & rhs); +} // namespace sta + + +/** + * @brief Maximum CAN standard ID value. + * + * @ingroup canID + */ +#define CAN_SID_MAX UINT32_C(0x7FF) +/** + * @brief Maximum CAN extended ID value. + * + * @ingroup canID + */ +#define CAN_EID_MAX UINT32_C(0x3FFFF) + + +#endif // STA_INTF_CAN_ID_HPP diff --git a/include/sta/intf/can/subscribable.hpp b/include/sta/intf/can/subscribable.hpp new file mode 100644 index 0000000..3e8583d --- /dev/null +++ b/include/sta/intf/can/subscribable.hpp @@ -0,0 +1,104 @@ +/** + * @file + * @brief Subscription interface for CAN controller drivers. + */ +#ifndef STA_INTF_CAN_SUBSCRIBABLE_HPP +#define STA_INTF_CAN_SUBSCRIBABLE_HPP + +#include +#include + + +namespace sta +{ + /** + * @defgroup canSub Subscription + * @ingroup canAPI + * @brief Subscription interface for CAN controller drivers. + */ + + + /** + * @brief Callback for handling received frames. + * + * @param header Frame header + * @param buffer Frame payload buffer + * + * @ingroup canSub + */ + using CanRxCallback = void (*) (const CanRxHeader & header, const uint8_t * buffer); + + + /** + * @brief Filter configuration and message handler. + * + * @ingroup canSub + */ + struct CanFilterConfig + { + CanFilter filter; /**< Filter handled by callback */ + CanRxCallback callback; /**< Callback for message handling */ + }; + + + /** + * @brief CAN controller with subscriptions. + * + * @tparam T Implementation of CanController interface + * + * @ingroup canSub + */ + template + class SubscribableCanController : public T + { + public: + using T::T; + + // Subscriptions + // + + /** + * @brief Subscribe to specific message types. + * + * @param subscriptions Array of message filters and handlers + * @param num Number of array entries + */ + bool subscribe(const CanFilterConfig * subscriptions, uint8_t num); + + /** + * @brief Subscribe to all messages. + * + * @param callback Called when message is received + * @param fifo FIFO used for received messages + */ + void subscribeAll(CanRxCallback callback, uint8_t fifo); + + /** + * @brief Unsubscribe from all messages. + * + * No more messages will be received. + */ + void unsubscribeAll(); + + + /** + * @brief Read message from RX FIFO and notify subscriber. + */ + void receiveAndNotify(uint8_t fifo); + + /** + * @brief Process pending frames from RX FIFOs. + */ + void processMessages(); + + + private: + CanRxCallback filterCallbacks_[T::MAX_FILTER_COUNT]; /**< Callbacks for RX filters */ + }; +} // namespace sta + + +#include + + +#endif // STA_INTF_CAN_SUBSCRIBABLE_HPP diff --git a/include/sta/intf/can/subscribable.tpp b/include/sta/intf/can/subscribable.tpp new file mode 100644 index 0000000..83cbe64 --- /dev/null +++ b/include/sta/intf/can/subscribable.tpp @@ -0,0 +1,120 @@ +/** + * @brief Implementation of template class CanController. + */ +#ifndef STA_INTF_CAN_SUBSCRIBABLE_TPP +#define STA_INTF_CAN_SUBSCRIBABLE_TPP + +#ifndef STA_INTF_CAN_SUBSCRIBABLE_HPP +#error "Direct use of internal header. Use instead" +#endif // !STA_INTF_CAN_SUBSCRIBABLE_HPP + +#ifndef STA_STDLIB_DISABLE +# include // fill_n +#endif // !STA_STDLIB_DISABLE + + +namespace sta +{ + template + bool SubscribableCanController::subscribe(const CanFilterConfig * subscriptions, uint8_t num) + { + // Check bounds + if (num > T::MAX_FILTER_COUNT) + return false; + + // Clear previous subscriptions + unsubscribeAll(); + + for (uint8_t i = 0; i < num; ++i) + { + // Save handler callback + filterCallbacks_[i] = subscriptions[i].callback; + + // Configure and enable filter + T::configureFilter(i, subscriptions[i].filter, true); + } + + return true; + } + + template + void SubscribableCanController::subscribeAll(CanRxCallback callback, uint8_t fifo) + { + uint8_t filterIdx = 0; + + // Clear previous subscriptions + unsubscribeAll(); + + // Setup default filter + CanFilter filter{}; + filter.type = CanFilterIdFormat::ANY; + filter.fifo = fifo; + + // Store callback + filterCallbacks_[filterIdx] = callback; + + // Configure and enable default filter + T::configureFilter(filterIdx, filter, true); + } + + template + void SubscribableCanController::unsubscribeAll() + { + // Disable all filters + T::clearFilters(); + + // Clear filter callbacks +#ifndef STA_STDLIB_DISABLE + std::fill_n(filterCallbacks_, T::MAX_FILTER_COUNT, nullptr); +#else // STA_STDLIB_DISABLE + for (uint8_t i = 0; i < T::MAX_FILTER_COUNT; ++i) + { + filterCallbacks_[i] = nullptr; + } +#endif // STA_STDLIB_DISABLE + } + + template + void SubscribableCanController::receiveAndNotify(uint8_t fifo) + { + CanRxHeader header; + uint8_t payload[T::MAX_PAYLOAD_SIZE]; + + if (T::receiveFrame(fifo, &header, payload)) + { + //displayFrameUART(frame); + + // Forward frame to filter callback + if (fifo <= T::MAX_FILTER_COUNT && filterCallbacks_[header.filter]) + { + filterCallbacks_[header.filter](header, payload); + } + } + } + + template + void SubscribableCanController::processMessages() + { + // Read RX interrupt flags + uint32_t RFIF = T::getRxFifoFlags(); + + if (RFIF != 0) + { + // Check all flags + for (uint8_t fifo = 0; fifo < T::MAX_FIFO_COUNT; ++fifo) + { + // Process messages if flag is set + if (RFIF & 0x1) + { + receiveAndNotify(fifo); + } + + // Shift next RX flag to LSB + RFIF >>= 1; + } + } + } +} // namespace sta + + +#endif // STA_INTF_CAN_SUBSCRIBABLE_TPP diff --git a/src/can/id.cpp b/src/can/id.cpp new file mode 100644 index 0000000..e9138bb --- /dev/null +++ b/src/can/id.cpp @@ -0,0 +1,26 @@ +#include + + +namespace sta +{ + bool operator ==(const CanId & lhs, const CanId & rhs) + { + return (lhs.sid == rhs.sid && lhs.eid == rhs.eid); + } + + bool operator !=(const CanId & lhs, const CanId & rhs) + { + return !(lhs == rhs); + } + + + bool operator ==(const CanFrameId & lhs, const CanFrameId & rhs) + { + return (lhs.format == rhs.format && lhs.sid == rhs.sid && lhs.eid == rhs.eid); + } + + bool operator !=(const CanFrameId & lhs, const CanFrameId & rhs) + { + return !(lhs == rhs); + } +} // namespace sta