diff --git a/.vscode/settings.json b/.vscode/settings.json index ba530265aee5551604bf78df79042eaae66e1dba..68d8988bcafbddb302ab2276396aab2f81057fc8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -139,7 +139,9 @@ "Boardcorev", "boudrate", "Canbus", + "canprotocol", "Carlucci", + "compid", "Corigliano", "CORTEXM", "cpitch", @@ -205,7 +207,10 @@ "LISR", "logdecoder", "Luca", + "Mandelli", "Matteo", + "Mavlink", + "mavlinkdriver", "MEKF", "microcontrollers", "MINC", @@ -271,9 +276,12 @@ "TEIE", "Terraneo", "testsuite", + "THID", "TMEIE", + "tparam", "TSCPP", "TSVREFE", + "Tweakable", "txfp", "TXIRQ", "TXOK", @@ -295,4 +303,4 @@ ], "cSpell.language": "en", "cSpell.enabled": true -} \ No newline at end of file +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dec47d98c5ef5522710677cddbbb93df618f99d..1ff18054a313ad837a9647b44d283827b9cc7eac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,7 @@ add_executable(runcam-settings src/entrypoints/runcam-settings.cpp) sbs_target(runcam-settings stm32f407vg_stm32f4discovery) add_executable(sdcard-benchmark src/entrypoints/sdcard-benchmark.cpp) -sbs_target(sdcard-benchmark stm32f205rc_skyward_ciuti) +sbs_target(sdcard-benchmark stm32f429zi_skyward_death_stack_x) #-----------------------------------------------------------------------------# # Tests # @@ -159,14 +159,18 @@ sbs_target(test-ada stm32f429zi_skyward_death_stack_v3) #-----------------------------------------------------------------------------# # Tests - Drivers # #-----------------------------------------------------------------------------# -add_executable(test-can-protocol src/tests/drivers/canbus/test-can-protocol.cpp) -sbs_target(test-can-protocol stm32f429zi_skyward_pyxis_auxiliary) -add_executable(test-canbus-loopback src/tests/drivers/canbus/test-canbus-loopback.cpp) -sbs_target(test-canbus-loopback stm32f429zi_stm32f4discovery) +add_executable(test-can-2way src/tests/drivers/canbus/CanDriver/test-can-2way.cpp) +sbs_target(test-can-2way stm32f429zi_skyward_pyxis_auxiliary) -add_executable(test-canbus-2way src/tests/drivers/canbus/test-canbus-2way.cpp) -sbs_target(test-canbus-2way stm32f429zi_skyward_pyxis_auxiliary) +add_executable(test-can-filters src/tests/drivers/canbus/CanDriver/test-can-filters.cpp) +sbs_target(test-can-filters stm32f429zi_skyward_pyxis_auxiliary) + +add_executable(test-can-loopback src/tests/drivers/canbus/CanDriver/test-can-loopback.cpp) +sbs_target(test-can-loopback stm32f429zi_skyward_death_stack_x) + +add_executable(test-can-protocol src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp) +sbs_target(test-can-protocol stm32f429zi_skyward_death_stack_x) add_executable(test-dma-spi src/tests/drivers/dma/test-dma-spi.cpp) sbs_target(test-dma-spi stm32f429zi_stm32f4discovery) diff --git a/src/shared/drivers/canbus/CanDriver/CanData.h b/src/shared/drivers/canbus/CanDriver/CanData.h index 8123c8514b1e25e58ea88eb04d453bb742c7b509..97e583073d3f823b35b79ea7dde0df4c7e2c599d 100644 --- a/src/shared/drivers/canbus/CanDriver/CanData.h +++ b/src/shared/drivers/canbus/CanDriver/CanData.h @@ -54,7 +54,7 @@ struct CanPacket uint32_t timestamp = 0; uint32_t id; - bool ext = false; + bool ext = false; ///< Whether to use extended packet id bool rtr = false; diff --git a/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp b/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp index a1c8aade82ccd9ee2b246f69a9f21e7cdd668e18..5f6189f22817e0f9811ffaa1b4650db14eed2c61 100644 --- a/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp +++ b/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp @@ -22,169 +22,242 @@ #include "CanProtocol.h" +using namespace miosix; + namespace Boardcore { namespace Canbus { -// For each board contains the packet that we are re-assembling -CanData data[N_BOARDS]; -CanProtocol::CanProtocol(CanbusDriver* can, MsgHandler callback) +CanProtocol::CanProtocol(CanbusDriver* can, MsgHandler onReceive) + : can(can), onReceive(onReceive) { - this->can = can; - this->callback = callback; } -CanProtocol::~CanProtocol() { delete can; } +bool CanProtocol::start() +{ + stopFlag = false; -void CanProtocol::send(CanData toSend) { TXbuffer.put(toSend); } + // Start sender (joinable thread) + if (!sndStarted) + { + sndThread = miosix::Thread::create( + sndLauncher, skywardStack(4 * 1024), miosix::MAIN_PRIORITY, + reinterpret_cast<void*>(this), miosix::Thread::JOINABLE); -void CanProtocol::sendData() -{ + if (sndThread != nullptr) + sndStarted = true; + else + LOG_ERR(logger, "Could not start sender!"); + } - while (true) + // Start receiver + if (!rcvStarted) { - TXbuffer.waitUntilNotEmpty(); - if (!TXbuffer.IRQisEmpty()) - { - CanData dataToSend = TXbuffer.pop(); - CanPacket packet = {}; - uint8_t tempLen = dataToSend.length - 1; - uint32_t tempId = dataToSend.canId; - - // Send the first packet - packet.ext = true; - // create the id for the first packet - packet.id = - (tempId << shiftSequentialInfo) | (63 - (tempLen & leftToSend)); - packet.length = byteForInt(dataToSend.payload[0]); - // Splits payload[0] in the right number of uint8_t - for (int k = 0; k < packet.length; k++) - packet.data[k] = dataToSend.payload[0] >> (8 * k); - can->send(packet); - tempLen--; - - for (int i = 1; i < dataToSend.length; i++) - { - // create the id for the remaining packets - packet.id = (tempId << shiftSequentialInfo) | firstPacket | - (63 - (tempLen & leftToSend)); - packet.length = byteForInt(dataToSend.payload[i]); - // Splits payload[i] in the right number of uint8_t - for (int k = 0; k < packet.length; k++) - packet.data[k] = dataToSend.payload[i] >> (8 * k); - - can->send(packet); - tempLen--; - } - } + rcvThread = miosix::Thread::create(rcvLauncher, skywardStack(4 * 1024), + miosix::MAIN_PRIORITY, + reinterpret_cast<void*>(this)); + + if (rcvThread != nullptr) + rcvStarted = true; + else + LOG_ERR(logger, "Could not start receiver!"); } + + if (sndStarted && rcvStarted) + LOG_DEBUG(logger, "Sender and receiver started"); + + return sndStarted && rcvStarted; } -uint8_t CanProtocol::byteForInt(uint64_t number) +bool CanProtocol::isStarted() { return sndStarted && rcvStarted; } + +void CanProtocol::stop() { - uint8_t i; + stopFlag = true; - for (i = 1; i <= 8; i++) + // Wait for sender to stop + sndThread->join(); +} + +bool CanProtocol::enqueueMsg(const CanMessage& msg) +{ + // Append the message to the queue + outQueue.put(msg); + + // Update stats + // updateQueueStats(appended); + + // Return always true because the circular buffer overrides current packets + // and can't get full. + return true; +} + +void CanProtocol::sendMessage(const CanMessage& msg) +{ + CanPacket packet = {}; + uint32_t leftToSend = msg.length - 1; + + // Create the id for the first packet + packet.ext = true; // Use extended packet id + + // The number of left to send packets + packet.id = static_cast<uint32_t>(msg.id) | + ((static_cast<uint32_t>(0x3F) - leftToSend) & + static_cast<uint32_t>(CanPacketIdMask::LEFT_TO_SEND)); + packet.length = byteForUint64(msg.payload[0]); + + // Splits payload[0] in the right number of uint8_t + for (int i = 0; i < packet.length; i++) + packet.data[i] = msg.payload[0] >> (8 * i); + + // Send the first packet + can->send(packet); + leftToSend--; + + // Prepare the remaining packets + for (int i = 1; i < msg.length; i++) { - number >>= 8; - if (number == 0) - return i; - } + packet.id = static_cast<uint32_t>(msg.id) | + static_cast<uint32_t>(CanPacketIdMask::FIRST_PACKET_FLAG) | + ((static_cast<uint32_t>(0x3F) - leftToSend) & + static_cast<uint32_t>(CanPacketIdMask::LEFT_TO_SEND)); + packet.length = byteForUint64(msg.payload[i]); - return i; + // Splits payload[i] in the right number of uint8_t + for (int k = 0; k < packet.length; k++) + packet.data[k] = msg.payload[i] >> (8 * k); + + can->send(packet); + leftToSend--; + } } -void CanProtocol::run() +void CanProtocol::runReceiver() { - std::thread send(std::bind(&CanProtocol::sendData, this)); - while (!shouldStop()) + CanMessage msg; + uint8_t nReceived = 0; + + while (!stopFlag) { // Wait for the next packet can->getRXBuffer().waitUntilNotEmpty(); + // If the buffer is not empty retrieve the packet if (!can->getRXBuffer().isEmpty()) { - CanPacket packet = can->getRXBuffer().pop().packet; + CanPacket pkt = can->getRXBuffer().pop().packet; - // Discard the sequence number - uint32_t idNoSeq = packet.id >> shiftSequentialInfo; - // Extract the sourceID - uint8_t sourceId = (idNoSeq & source) >> shiftSource; + uint8_t leftToReceive = + static_cast<uint32_t>(0x3F) - + (pkt.id & static_cast<uint32_t>(CanPacketIdMask::LEFT_TO_SEND)); - // Check for maximum size - if (sourceId < N_BOARDS) + // Check if the packet is the first in the sequence, if this is the + // case then the previous message is overriden + if ((pkt.id & static_cast<uint32_t>( + CanPacketIdMask::FIRST_PACKET_FLAG)) == 0) { + // If it is we save the id (without the sequence number) and the + // message length + msg.id = pkt.id & static_cast<uint32_t>( + CanPacketIdMask::MESSAGE_INFORMATION); + msg.length = leftToReceive + 1; + + // Reset the number of received packets + nReceived = 0; + } - uint8_t left = 63 - (packet.id & leftToSend); + // Accept the packet only if it has the expected id + // clang-format off + if (msg.id != -1 && + (pkt.id & static_cast<uint32_t>(CanPacketIdMask::MESSAGE_INFORMATION)) == + (msg.id & static_cast<uint32_t>(CanPacketIdMask::MESSAGE_INFORMATION))) + // clang-format on + { + // Check if the packet is expected in the sequence. The received + // packet must have the expected left to send value - // Check if it is the first packet in the sequence - if (((packet.id & firstPacket) >> shiftFirstPacket) == 0) + if (msg.length - nReceived - 1 == leftToReceive) { - // if it is we save the id (without the sequence number) - // the number of packet (left to send + 1) - data[sourceId].length = left + 1; + uint64_t payload = 0; - // the number of packet = left to send + 1 since it is - // the first packet - data[sourceId].canId = idNoSeq; + // Assemble the packet data into a uint64_t + for (uint8_t i = 0; i < pkt.length; i++) + payload |= pkt.data[i] << (i * 8); - // And we reset nRec - data[sourceId].nRec = 0; + // Add the data to the message + msg.payload[pkt.length - leftToReceive - 1] = payload; + nReceived++; } + } - // we accept the packet only if we already received a first - // packet (it is already in data) - if (data[sourceId].canId == -1 || - ((data[sourceId].canId & source) >> shiftSource) == - sourceId) - { - // if the packet is expected, the length of data - the - // number of packet recorded +1 (+1 since we are not - // counting the last packet) equals the number of packet - // left to receive - if ((data[sourceId].length - (data[sourceId].nRec + 1)) == - left) - { - uint64_t tempPayload = 0; - - // we reassemble the payload - for (int f = 0; f < packet.length; f++) - { - uint64_t tempData = packet.data[f]; - tempPayload = tempPayload | (tempData << (f * 8)); - } - - if (data[sourceId].length - left - 1 >= 0 && - data[sourceId].length - left - 1 < - 32) // check for index to avoid out of bounds - // error - { - // and put it in data - data[sourceId] - .payload[data[sourceId].length - left - 1] = - tempPayload; - data[sourceId].nRec++; - } - } - // If we have received the right number of packet - if (data[sourceId].nRec == data[sourceId].length && - data[sourceId].nRec != 0) - { - // We put the element of data in buffer - callback(data[sourceId]); - - // Empties the struct - data[sourceId].canId = -1; - data[sourceId].length = 0; - } - } + // If we have received the right number of packet call onReceive and + // reset the message + if (nReceived == msg.length && nReceived != 0) + { + onReceive(msg); + + // Reset the packet + msg.id = -1; + msg.length = 0; } } } } +void CanProtocol::runSender() +{ + LOG_DEBUG(logger, "Sender is running"); + CanMessage msg; + + while (!stopFlag) + { + outQueue.waitUntilNotEmpty(); + + if (!outQueue.isEmpty()) + { + // Get the first packet in the queue, without removing it + msg = outQueue.pop(); + + LOG_DEBUG(logger, "Sending message, length: {}", msg.length); + + sendMessage(msg); + + // updateSenderStats(); + } + else + { + // Wait before sending something else + miosix::Thread::sleep(50); + } + } +} + +void CanProtocol::rcvLauncher(void* arg) +{ + reinterpret_cast<CanProtocol*>(arg)->runReceiver(); +} + +void CanProtocol::sndLauncher(void* arg) +{ + reinterpret_cast<CanProtocol*>(arg)->runSender(); +} + +uint8_t CanProtocol::byteForUint64(uint64_t number) +{ + uint8_t i; + + for (i = 1; i <= 8; i++) + { + number >>= 8; + if (number == 0) + return i; + } + + return i; +} + } // namespace Canbus } // namespace Boardcore diff --git a/src/shared/drivers/canbus/CanProtocol/CanProtocol.h b/src/shared/drivers/canbus/CanProtocol/CanProtocol.h index 88316ffad940f4884fa1a000f5a02e10dee9a732..1526ca461cbaca68edc80532b500001ae828219f 100644 --- a/src/shared/drivers/canbus/CanProtocol/CanProtocol.h +++ b/src/shared/drivers/canbus/CanProtocol/CanProtocol.h @@ -22,14 +22,12 @@ #pragma once -#include <ActiveObject.h> +#include <diagnostic/PrintLogger.h> #include <drivers/canbus/CanDriver/CanDriver.h> #include <utils/Debug.h> -#include <utils/collections/IRQCircularBuffer.h> +#include <utils/collections/SyncCircularBuffer.h> -#include <thread> - -#define N_BOARDS 3 ///< Number of boards on the bus. +#include "CanProtocolData.h" namespace Boardcore { @@ -38,85 +36,77 @@ namespace Canbus { /** - * The id of a can packet is composed of 29 bits and will be divided such as: - * - Priority 4 bit - priority \ - * - Type 6 bit - type | - * - Source 4 bit - source | 22 bits - * - Destination 4 bit - destination | - * - Type id 4 bit - idType / - * - First packet flag 1 bit - firstPacket \ 7 bits - * - Remaining packets 6 bit - leftToSend / + * The CanProtocol allows to transmit arbitrarily sized messages over the CanBus + * overcoming the 8 byte limitation of each single packet. + * + * Our CanProtocol uses the extended can packet, the 29 bits id is divided such + * as: + * - Priority 4 bit - priority \ + * - Primary type 6 bit - primaryType | + * - Source 4 bit - source | 22 bits - Message informations + * - Destination 4 bit - destination | + * - Secondary type 4 bit - secondaryType / + * - First packet flag 1 bit - firstPacket \ 7 bits - Sequential informations + * - Remaining packets 6 bit - leftToSend / * shiftNameOfField the number of shift needed to reach that field - */ - -/** - * @brief The mask of the ID without the sequential information. * - * CompleteID = (IDMask << shiftSequentialInfo) || SequentialInformation + * The id is split into 2 parts: + * - Message information: Common to every packet of a given message + * - Sequential information: Used to distinguish between packets + * + * The sender splits into multiple packets a message that is then recomposed on + * the receiver end. The message informations are encoded into the packets id, + * therefore they have an effect on packets priorities. */ /** - * @brief Enumeration that contains masks of the elements composing the can - * packet id without sequential information. + * @brief Masks of the elements composing can packets ids. */ -enum IDMask : uint32_t +enum class CanPacketIdMask : uint32_t { - priority = 0x3C0000, + PRIORITY = 0x1E000000, + PRIMARY_TYPE = 0x01F80000, + SOURCE = 0x00078000, + DESTINATION = 0x00003800, + SECONDARY_TYPE = 0x00000780, - type = 0x03F000, - source = 0x000F00, - destination = 0x0000F0, - idType = 0x00000F -}; + MESSAGE_INFORMATION = 0x1FFFFF80, -/** - * @brief Enumeration that contains masks of the elements composing the - * sequential information. - */ -enum SequentialInformation : uint8_t -{ - firstPacket = 0x40, - leftToSend = 0x3F -}; + FIRST_PACKET_FLAG = 0x00000040, + LEFT_TO_SEND = 0x0000003F, -enum ShiftInformation : uint8_t -{ - // Shift info of IDMask - shiftPriority = 18, - shiftType = 12, - shiftSource = 8, - shiftDestination = 4, - shiftIdType = 0, - // Shift info of SequentialInformation - shiftFirstPacket = 6, - shiftLeftToSend = 0, - shiftSequentialInfo = 7 + SEQUENTIAL_INFORMATION = 0x0000007F }; -/** - * @brief Generic struct that contains a logical can packet. - * - * i.e. 1 accelerometer packet 3 * 4byte (acc: x,y,z) +timestamp, will be 4 - * canPacket but a single canData. - */ -struct CanData +enum ShiftInformation : uint8_t { - int32_t canId = -1; ///< Id of the packet without the sequential info. - uint8_t length; - uint8_t nRec = 0; - uint64_t payload[32]; + // Shift values for message informations + PRIORITY = 25, + PRIMARY_TYPE = 19, + SOURCE = 15, + DESCRIPTION = 11, + SECONDARY_TYPE = 7, + + // Shift values for sequential informations + FIRST_PACKET_FLAG = 6, + LEFT_TO_SEND = 0, + + // Position of the message infos relative to the entire can packet id + SEQUENTIAL_INFORMATION = 7 }; /** * @brief Canbus protocol implementation. * - * Given a can interface this class takes care of sending CanData packets - * segmented into multiple CanPackets and receiving single CanPackets that are - * then reframed as CanData. + * Given a can interface this class takes care of sending messages segmented + * into multiple packets, and receiving single packets that are then reframed + * into messages. + * + * This driver has been implemented following the MavlinkDriver. */ -class CanProtocol : public ActiveObject +class CanProtocol { - using MsgHandler = std::function<void(CanData data)>; + using MsgHandler = std::function<void(CanMessage data)>; public: /** @@ -124,51 +114,97 @@ public: * * @param can Pointer to a CanbusDriver object. */ - explicit CanProtocol(CanbusDriver* can, MsgHandler callback); + CanProtocol(CanbusDriver* can, MsgHandler onReceive); + + /** + * @brief Start the receiving and sending threads. + * + * @return False if at least one could not start. + */ + bool start(); + + /** + * @brief Tells whether the driver was started. + */ + bool isStarted(); + + /** + * @brief Stops sender and receiver threads. + */ + void stop(); - ~CanProtocol(); + /** + * @brief Non-blocking send function, puts the messages in a queue. + * Message is discarded if the queue is full. + * + * @param msg Message to send (CanMessage struct). + * @return True if the message could be enqueued. + */ + bool enqueueMsg(const CanMessage& msg); +private: /** - * @brief Sends a CanData object on the bus. + * @brief Blocking send function, puts the CanMessage object on the bus. * - * Takes a CanData object, splits it into multiple CanPackets with the + * Takes a CanMessage object, splits it into multiple CanPackets with the * correct sequential id. - * @warning requires @param data to be not empty. * - * @param data Contains the id and the data of the packet to send. + * @param msg Contains the id and the data of the packet to send. */ - void send(CanData toSend); + void sendMessage(const CanMessage& msg); -private: /** - * @brief Count the number of bytes needed to encode a uint64_t number. + * @brief Receiver thread: waits for one packet at a time from the can + * driver and tries to parse a message. + * + * If the message is successfully parsed, the onReceive function is + * executed. + * + * For now if a packet is received in the wrong order or if a packet with a + * different id is received, the current (incomplete) message will be lost. + * Once we receive a new first packet, currently saved data are reset. */ - uint8_t byteForInt(uint64_t number); + void runReceiver(); - void sendData(); + /** + * @brief Sender Thread: Periodically flushes the message queue and sends + * all the enqueued messages. + */ + void runSender(); /** - * @brief Keeps listening on the canbus for packets. + * @brief Calls the run member function. * - * Once a packet is received, it checks if it is expected (that id is - * already present in data), if it is the case, it's added to the list. - * Once we receive the correct amount of packet we offer it in buffer. + * @param arg The object pointer cast to void*. + */ + static void rcvLauncher(void* arg); + + /** + * @brief Calls the run member function. * - * For now if a packet is missed/received in the wrong order the whole - * packet will be lost once we receive a new first packet without warning - * CanHandler. + * @param arg The object pointer cast to void*. */ - void run() override; + static void sndLauncher(void* arg); + + /** + * @brief Count the number of bytes needed to encode a uint64_t number. + */ + uint8_t byteForUint64(uint64_t number); + + CanbusDriver* can; ///< Device used to send and receive packets. + MsgHandler onReceive; ///< Function executed when a message is ready. - // The physical implementation of the CanBus - CanbusDriver* can; + // Threads + bool stopFlag = false; + bool sndStarted = false; + bool rcvStarted = false; - // The buffer used to store the CanData to send - IRQCircularBuffer<CanData, N_BOARDS> TXbuffer; + miosix::Thread* sndThread = nullptr; + miosix::Thread* rcvThread = nullptr; - miosix::FastMutex mutex; + SyncCircularBuffer<CanMessage, 10> outQueue; - MsgHandler callback; + PrintLogger logger = Logging::getLogger("canprotocol"); }; } // namespace Canbus diff --git a/src/shared/drivers/canbus/CanProtocol/CanProtocolData.h b/src/shared/drivers/canbus/CanProtocol/CanProtocolData.h new file mode 100644 index 0000000000000000000000000000000000000000..7eb3c8808a43a60d70f52a1f778a1bb89e7212f1 --- /dev/null +++ b/src/shared/drivers/canbus/CanProtocol/CanProtocolData.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Federico Mandelli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <stdint.h> + +namespace Boardcore +{ + +namespace Canbus +{ + +/** + * @brief Generic struct that contains a can protocol message. + * + * For example an accelerometer message could have: + * - 4 bytes for the timestamp + * - 3x4 bytes for float values + * This message would be divided into 2 can packets. + * + * Note that the maximum size for a message is 520 bytes since the remaining + * packet information is 6 bit wide. + */ +struct CanMessage +{ + int32_t id = -1; ///< Id of the message without sequential infos. + uint8_t length = 0; ///< Length of the message content. + uint64_t payload[65]; +}; + +inline bool operator==(const CanMessage& lhs, const CanMessage& rhs) +{ + if (lhs.id != rhs.id || lhs.length != rhs.length) + return false; + + for (int i = 0; i < lhs.length; i++) + if (lhs.payload[i] != rhs.payload[i]) + return false; + + return true; +} + +inline bool operator!=(const CanMessage& lhs, const CanMessage& rhs) +{ + return !(lhs == rhs); +} + +} // namespace Canbus + +} // namespace Boardcore diff --git a/src/shared/radio/MavlinkDriver/MavlinkDriver.h b/src/shared/radio/MavlinkDriver/MavlinkDriver.h index 1bf8c8047e51277aa25155f5221b9ac51625ffa6..81607862cfe14f10b7de94b1faf00bd2fcce23bb 100644 --- a/src/shared/radio/MavlinkDriver/MavlinkDriver.h +++ b/src/shared/radio/MavlinkDriver/MavlinkDriver.h @@ -22,15 +22,12 @@ #pragma once -#include <diagnostic/PrintLogger.h> - #include <vector> /** * This object includes only the protocol header (`protocol.h`). To use this * driver, you should include YOUR OWN implementation of the messages definition - * (`mavlink.h`) before including this header. To create your implementation you - * can use Skyward's *Mavlink Editor*. + * (`mavlink.h`) before including this header. */ #ifndef MAVLINK_H #error \ @@ -38,6 +35,7 @@ implementation before including MavlinkDriver.h" #endif +#include <diagnostic/PrintLogger.h> #include <diagnostic/SkywardStack.h> #include <diagnostic/StackLogger.h> #include <mavlink_lib/mavlink_types.h> @@ -84,7 +82,8 @@ public: /** * @brief Start the receiving and sending threads. - * @return false if at least one could not start. + * + * @return False if at least one could not start. */ bool start(); @@ -116,6 +115,17 @@ public: */ bool enqueueRaw(uint8_t* msg, size_t size); + /** + * @brief Synchronized status getter. + */ + MavlinkStatus getStatus(); + + /** + * @brief Setter for the sleep after send value. + */ + void setSleepAfterSend(uint16_t newSleepTime); + +private: /** * @brief Receiver thread: reads one char at a time from the transceiver and * tries to parse a mavlink message. @@ -134,21 +144,10 @@ public: */ void runSender(); - /** - * @brief Synchronized status getter. - */ - MavlinkStatus getStatus(); - - /** - * @brief Setter for the sleep after send value. - */ - void setSleepAfterSend(uint16_t newSleepTime); - -private: /** * @brief Calls the run member function. * - * @param arg the object pointer cast to void* + * @param arg The object pointer cast to void*. */ static void rcvLauncher(void* arg) { @@ -158,7 +157,7 @@ private: /** * @brief Calls the run member function. * - * @param arg the object pointer cast to void* + * @param arg The object pointer cast to void*. */ static void sndLauncher(void* arg) { @@ -169,8 +168,8 @@ private: void updateSenderStats(size_t msgCount, bool sent); - Transceiver* device; ///< transceiver used to send and receive - MavHandler onReceive; ///< function executed on message rcv + Transceiver* device; ///< Transceiver used to send and receive packets. + MavHandler onReceive; ///< Function executed when a message is ready. // Tweakable params uint16_t sleepAfterSend; @@ -273,7 +272,7 @@ bool MavlinkDriver<PktLength, OutQueueSize, MavMsgLength>::enqueueMsg( uint8_t msgTempBuf[MAVLINK_NUM_NON_PAYLOAD_BYTES + MavMsgLength]; int msgLen = mavlink_msg_to_send_buffer(msgTempBuf, &msg); - // Append message to the queue + // Append the message to the queue bool appended = outQueue.put(msgTempBuf, msgLen); // Update stats diff --git a/src/tests/drivers/canbus/SimpleCanManager.h b/src/tests/drivers/canbus/CanDriver/SimpleCanManager.h similarity index 100% rename from src/tests/drivers/canbus/SimpleCanManager.h rename to src/tests/drivers/canbus/CanDriver/SimpleCanManager.h diff --git a/src/tests/drivers/canbus/test-canbus-2way.cpp b/src/tests/drivers/canbus/CanDriver/test-can-2way.cpp similarity index 100% rename from src/tests/drivers/canbus/test-canbus-2way.cpp rename to src/tests/drivers/canbus/CanDriver/test-can-2way.cpp diff --git a/src/tests/drivers/canbus/test-canbus-filters.cpp b/src/tests/drivers/canbus/CanDriver/test-can-filters.cpp similarity index 97% rename from src/tests/drivers/canbus/test-canbus-filters.cpp rename to src/tests/drivers/canbus/CanDriver/test-can-filters.cpp index c56e948c238fa50528df5466e64290f296fa2914..b6c3f6b667ffcac3ff1021d99634f5430ec08b78 100644 --- a/src/tests/drivers/canbus/test-canbus-filters.cpp +++ b/src/tests/drivers/canbus/CanDriver/test-can-filters.cpp @@ -22,8 +22,8 @@ #include <ActiveObject.h> #include <diagnostic/PrintLogger.h> -#include <drivers/canbus/BusLoadEstimation.h> -#include <drivers/canbus/CanDriver.h> +#include <drivers/canbus/CanDriver/BusLoadEstimation.h> +#include <drivers/canbus/CanDriver/CanDriver.h> #include <string> diff --git a/src/tests/drivers/canbus/test-canbus-loopback.cpp b/src/tests/drivers/canbus/CanDriver/test-can-loopback.cpp similarity index 100% rename from src/tests/drivers/canbus/test-canbus-loopback.cpp rename to src/tests/drivers/canbus/CanDriver/test-can-loopback.cpp diff --git a/src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp b/src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de42a9c3d9b3cd911b44f0b628fd5eee14a66ef2 --- /dev/null +++ b/src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp @@ -0,0 +1,88 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Federico Mandelli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <drivers/canbus/CanDriver/BusLoadEstimation.h> +#include <drivers/canbus/CanDriver/CanDriver.h> +#include <drivers/canbus/CanProtocol/CanProtocol.h> +#include <scheduler/TaskScheduler.h> +#include <utils/collections/CircularBuffer.h> + +#include <functional> +#include <thread> + +using namespace std; +using namespace miosix; +using namespace Boardcore; +using namespace Canbus; + +void print(CanMessage data) { printf("Received packet %lu\n", data.id); } + +int main() +{ + // Prepare the cab driver + CanbusDriver::CanbusConfig config; + config.loopback = true; + CanbusDriver::AutoBitTiming bitTiming; + bitTiming.baudRate = 500 * 1000; + bitTiming.samplePoint = 87.5f / 100.0f; + CanbusDriver* driver = new CanbusDriver(CAN1, config, bitTiming); + + // Prepare the can driver + CanProtocol protocol(driver, print); + + // Add a filter to allow every message + Mask32FilterBank f2(0, 0, 1, 1, 0, 0, 0); + driver->addFilter(f2); + driver->init(); + + // Start the protocol + protocol.start(); + + CanMessage msg1, msg2; + + msg1.id = 0x200; + msg1.length = 2; + msg1.payload[0] = 0xffffffffffffffff; + msg1.payload[1] = 0x0123456789ABCDEF; + + msg2.id = 0x100; + msg2.length = 1; + msg2.payload[0] = 0; + + TaskScheduler scheduler; + + scheduler.addTask([&]() { protocol.enqueueMsg(msg1); }, 1000); + scheduler.addTask( + [&]() + { + msg2.payload[0]++; + protocol.enqueueMsg(msg2); + }, + 2000); + + scheduler.start(); + + printf("Started\n"); + + while (true) + Thread::sleep(1000); +} diff --git a/src/tests/drivers/canbus/test-can-protocol.cpp b/src/tests/drivers/canbus/test-can-protocol.cpp deleted file mode 100644 index 01b8a5f271fbaa49bc0834ca788c8a164f7edae1..0000000000000000000000000000000000000000 --- a/src/tests/drivers/canbus/test-can-protocol.cpp +++ /dev/null @@ -1,160 +0,0 @@ - - -/* Copyright (c) 2022 Skyward Experimental Rocketry - * Author: Federico Mandelli - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include <drivers/canbus/CanDriver/BusLoadEstimation.h> -#include <drivers/canbus/CanDriver/CanDriver.h> -#include <drivers/canbus/CanProtocol/CanProtocol.h> - -#include <functional> -#include <thread> - -#include "utils/collections/CircularBuffer.h" - -constexpr uint32_t BAUD_RATE = 500 * 1000; -constexpr float SAMPLE_POINT = 87.5f / 100.0f; - -using std::string; -using namespace Boardcore; -using namespace Boardcore::Canbus; -using namespace miosix; - -#ifdef _ARCH_CORTEXM3_STM32 -using CanRX = Gpio<GPIOA_BASE, 11>; -using CanTX = Gpio<GPIOA_BASE, 12>; -#else -using CanRX = Gpio<GPIOA_BASE, 11>; -using CanTX = Gpio<GPIOA_BASE, 12>; -#endif - -#define SLP 100 -miosix::FastMutex mutex; -CanData toSend1; -CanData toSend2; -bool equal(CanData* first, CanData* second) -{ - if ((*first).canId != (*second).canId || - (*first).length != (*second).length) - { - return false; - } - for (int i = 0; i < (*first).length; i++) - { - if ((*first).payload[i] != (*second).payload[i]) - return false; - } - return true; -} - -void print(CanData data) -{ - if ((!equal(&data, &toSend1) && !equal(&data, &toSend2))) - { - TRACE("Error\n"); - TRACE("Received id %lu\n", data.canId); - TRACE("length %d\n", data.length); - for (int i = 0; i < data.length; i++) - { - TRACE("Received payload %d: %llu,\n", i, data.payload[i]); - } - } - else - { - TRACE("OK :) id %lu\n", data.canId); - } -} -void sendData(CanProtocol* protocol, CanData* toSend) -{ - while (true) - { - - // TRACE("send id %d\n", toSend->canId); - { - miosix::Lock<miosix::FastMutex> l(mutex); - (*protocol).send(*toSend); - } - Thread::sleep(SLP); - } -} -int main() -{ - { - miosix::FastInterruptDisableLock dLock; - -#ifdef _ARCH_CORTEXM3_STM32 - CanRX::mode(Mode::ALTERNATE); - CanTX::mode(Mode::ALTERNATE); -#else - CanRX::mode(Mode::ALTERNATE); - CanTX::mode(Mode::ALTERNATE); - - CanRX::alternateFunction(9); - CanTX::alternateFunction(9); -#endif - } - CanbusDriver::CanbusConfig cfg{}; - CanbusDriver::AutoBitTiming bt; - bt.baudRate = BAUD_RATE; - bt.samplePoint = SAMPLE_POINT; - CanbusDriver* c = new CanbusDriver(CAN1, cfg, bt); - CanProtocol protocol(c, - &print); // std::bind(&print, std::placeholders::_1)); - // Allow every message - Mask32FilterBank f2(0, 0, 1, 1, 0, 0, 0); - - c->addFilter(f2); - c->init(); - protocol.start(); - - toSend1.canId = 0x200; - toSend1.length = 8; - toSend1.payload[0] = 0xffffffffffffffff; - toSend1.payload[1] = 2; - toSend1.payload[2] = 78022; - toSend1.payload[3] = 0xfffffffffffff; - toSend1.payload[4] = 23; - toSend1.payload[5] = 3234; - toSend1.payload[6] = 12; - toSend1.payload[7] = 0; - // std::thread firstSend(sendData, &protocol, &toSend1); - - Thread::sleep(10); - toSend2.canId = 0x100; - toSend2.length = 4; - toSend2.payload[0] = 0xffffff; - toSend2.payload[1] = 2; - toSend2.payload[2] = 0x123ff; - toSend2.payload[3] = 1; - std::thread secondSend(sendData, &protocol, &toSend2); - - /* code */ - - TRACE("start \n"); - while (true) - { - protocol.send(toSend1); - Thread::sleep(SLP); - - // TRACE("received packet \n"); - } -}