diff --git a/.gitignore b/.gitignore index e43b0f9..8cf7846 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ +# Ignore .pre-commit-config.yaml +.pre-commit-config.yaml + .DS_Store diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c4767f4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,34 @@ +repos: + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: 7d85583be209cb547946c82fbe51f4bc5dd1d017 + hooks: + - id: clang-format + args: + [ + "--style={BasedOnStyle: Google, SortIncludes: false, ColumnLimit: 120}", + ] + files: \.(cpp|hpp)$ + stages: [commit] + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.5.1 + hooks: + - id: prettier + files: \.(js|ts|jsx|tsx|css|less|html|json|markdown|md|yaml|yml)$ + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.1 + hooks: + - id: ruff + types_or: [python, pyi] + args: [--fix] + stages: [commit] + - id: ruff-format + stages: [commit] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml diff --git a/Components/DataBroker/Inc/DataBroker.hpp b/Components/DataBroker/Inc/DataBroker.hpp new file mode 100644 index 0000000..0605787 --- /dev/null +++ b/Components/DataBroker/Inc/DataBroker.hpp @@ -0,0 +1,195 @@ +/** + ******************************************************************************** + * @file DataBroker.hpp + * @author Shivam Desai + * @date Nov 23, 2024 + * @brief + ******************************************************************************** + */ + +#ifndef DATA_BROKER_HPP_ +#define DATA_BROKER_HPP_ + +/************************************ + * INCLUDES + ************************************/ +#include "Publisher.hpp" +#include "SensorDataTypes.hpp" +#include "Command.hpp" +#include "DataBrokerMessageTypes.hpp" +#include "SystemDefines.hpp" +#include "Mutex.hpp" +#include +#include +#include +#include + +/************************************ + * MACROS AND DEFINES + ************************************/ + +/************************************ + * TYPEDEFS + ************************************/ + +/************************************ + * CLASS DEFINITIONS + ************************************/ +class DataBroker { + public: + /** + * @brief Publish data of a certain type + * NOTE: You must ensure that there is a publisher for that type + */ + template + static void Publish(T* dataToPublish) { + if (subscriberListLock.Lock(SUBSCRIBER_LIST_MUTEX_TIMEOUT)) { + Publisher* publisher = getPublisher(); + if (publisher != nullptr) { + publisher->Publish(dataToPublish); + } else { + SOAR_ASSERT("Data Publisher not found \n"); + } + subscriberListLock.Unlock(); + return; + } else { + SOAR_PRINT("Could Not Subscribe to Data Broker Publisher \n"); + } + return; + } + + /** + * @brief Subscribe to a certain type of data in the system + * @param taskToSubscribe Task Handle of the task that will receive + * and handle the data. (i.e. -> Subscribe(this)) + */ + template + static void Subscribe(Task* taskToSubscribe) { + if (subscriberListLock.Lock(SUBSCRIBER_LIST_MUTEX_TIMEOUT)) { + Publisher* publisher = getPublisher(); + if (publisher != nullptr) { + publisher->Subscribe(taskToSubscribe); + } else { + SOAR_ASSERT("Data Publisher not found \n"); + } + subscriberListLock.Unlock(); + return; + } else { + SOAR_PRINT("Could Not Subscribe to Data Broker Publisher \n"); + } + return; + } + + /** + * @brief Unsubscribe to a certain type of data in the system + * @param taskToUnsubscribe Task Handle of the task that will stop + * receiving the data. (i.e. -> Unsubscribe(this)) + */ + template + static void Unsubscribe(Task* taskToUnsubscribe) { + if (subscriberListLock.Lock(SUBSCRIBER_LIST_MUTEX_TIMEOUT)) { + Publisher* publisher = getPublisher(); + if (publisher != nullptr) { + publisher->Unsubscribe(taskToUnsubscribe); + } else { + SOAR_ASSERT("Data Publisher not found \n"); + } + subscriberListLock.Unlock(); + return; + } else { + SOAR_PRINT("Could Not Unsubscribe to Data Broker Publisher \n"); + } + return; + } + + /** + * @brief This API can be used to offload the data from the databroker message + * into a new object in the receiving task + * @param cm the Command object that contains the databroker message + */ + template + static constexpr T ExtractData(const Command& cm) { + if (cm.GetCommand() != DATA_BROKER_COMMAND) { + SOAR_ASSERT("Not a Data Broker Command!\n"); + } + + Publisher* publisher = getPublisher(); + DataBrokerMessageTypes messageType = DataBroker::getMessageType(cm); + + if (messageType != publisher->GetPublisherMessageType()) { + const std::string errorMessage = "Trying to unpack the wrong type of message. You are trying to use " + + DataBrokerMessageType::ToString(publisher->GetPublisherMessageType()) + + " instead of " + DataBrokerMessageType::ToString(messageType) + "\n\n"; + + const char* messageCStr = errorMessage.c_str(); + SOAR_PRINT(messageCStr); + SOAR_ASSERT(false, ""); + } + + // The data allocated by this command ptr will be freed when cm.Reset()] + // is called. So we do not have to free this memory here + T* dataPtr = reinterpret_cast(cm.GetDataPointer()); + + T data{}; + + std::memcpy(&data, dataPtr, sizeof(T)); + + return data; + } + + /** + * @brief This API can be use to get the type of data broker message contained + * in the message. + * All the message types can be found in DataBrokerMessageTypes.hpp + * @param cm the Command object that contains the databroker message + */ + static DataBrokerMessageTypes getMessageType(const Command& cm) { + return static_cast(cm.GetTaskCommand()); + } + + private: + // Deleting the default constructor as this class is not + // instanceable + DataBroker() = delete; + + // Deleting the copy constructor to prevent copies + DataBroker(const DataBroker& obj) = delete; + + // Deleting assignment operator to prevent assignment operations + DataBroker& operator=(DataBroker const&) = delete; + + // Mutex to access the Subscriber List + inline static Mutex subscriberListLock{}; + // Mutex lock wait time + static constexpr uint16_t SUBSCRIBER_LIST_MUTEX_TIMEOUT = 1000; + + /** + * @brief Checks if the 2 template types are the same + */ + template + static constexpr bool matchType() { + return std::is_same_v; + } + + /** + * @brief Returns the correct Publisher object for a template type + */ + template + static constexpr auto getPublisher(void) { + if constexpr (matchType()) { + return &IMU_Data_publisher; + } else { + SOAR_ASSERT(false, "This publisher type does not exist, you must create it"); + return (Publisher*)nullptr; + } + } + + // List of Publishers + inline static Publisher IMU_Data_publisher{DataBrokerMessageTypes::IMU_DATA}; + +}; +/************************************ + * FUNCTION DECLARATIONS + ************************************/ + +#endif /* DATA_BROKER_HPP_ */ diff --git a/Components/DataBroker/Inc/Publisher.hpp b/Components/DataBroker/Inc/Publisher.hpp new file mode 100644 index 0000000..c664c44 --- /dev/null +++ b/Components/DataBroker/Inc/Publisher.hpp @@ -0,0 +1,107 @@ +/** + ******************************************************************************** + * @file Publisher.hpp + * @author Shivam Desai + * @date Nov 23, 2024 + * @brief + ******************************************************************************** + */ + +#ifndef PUBLISHER_HPP_ +#define PUBLISHER_HPP_ + +/************************************ + * INCLUDES + ************************************/ +#include +#include +#include +#include "Task.hpp" +#include "Subscriber.hpp" +#include "SystemDefines.hpp" + +/************************************ + * MACROS AND DEFINES + ************************************/ + +/************************************ + * TYPEDEFS + ************************************/ + +/************************************ + * CLASS DEFINITIONS + ************************************/ +template +class Publisher { + public: + // Constructor + Publisher(DataBrokerMessageTypes messageType) { publisherMessageType = messageType; } + + // subscribe + bool Subscribe(Task* taskToSubscribe) { + // Check if subscriber already exists + for (Subscriber& subscriber : subscribersList) { + if (subscriber.getSubscriberTaskHandle() == taskToSubscribe) { + return true; + } + } + + // Add the subscriber + for (Subscriber& subscriber : subscribersList) { + if (subscriber.getSubscriberTaskHandle() == nullptr) { + subscriber.Init(taskToSubscribe); + return true; + } + } + + SOAR_ASSERT(true, "Failed to add subscriber\n"); + return false; + } + + // unsubscribe + bool Unsubscribe(Task* taskToUnsubscribe) { + for (Subscriber& subscriber : subscribersList) { + if (subscriber.getSubscriberTaskHandle() == taskToUnsubscribe) { + subscriber.Delete(); + return true; + } + } + + SOAR_ASSERT(true, "Subscriber not Deleted\n"); + return false; + } + + // publish + void Publish(T* dataToPublish) { + for (const Subscriber& subscriber : subscribersList) { + if (subscriber.getSubscriberTaskHandle() != nullptr) { + // create command + uint16_t messageType = static_cast(publisherMessageType); + + Command brokerData(DATA_BROKER_COMMAND, messageType); + + uint8_t* messsageData = reinterpret_cast(dataToPublish); + + // copy data to command + brokerData.CopyDataToCommand(messsageData, sizeof(T)); + + subscriber.getSubscriberQueueHandle()->Send(brokerData); + } + } + } + + DataBrokerMessageTypes GetPublisherMessageType() { return publisherMessageType; } + + private: + // list of subscribers + Subscriber subscribersList[MaxSubscribers] = {}; + + // message type for system routing + DataBrokerMessageTypes publisherMessageType = DataBrokerMessageTypes::INVALID; +}; + +/************************************ + * FUNCTION DECLARATIONS + ************************************/ + +#endif /* PUBLISHER_HPP_ */ diff --git a/Components/DataBroker/Inc/Subscriber.hpp b/Components/DataBroker/Inc/Subscriber.hpp new file mode 100644 index 0000000..3352b80 --- /dev/null +++ b/Components/DataBroker/Inc/Subscriber.hpp @@ -0,0 +1,59 @@ +/** + ******************************************************************************** + * @file Subscriber.hpp + * @author Shivam Desai + * @date Nov 23, 2024 + * @brief + ******************************************************************************** + */ + +#ifndef SUBSCRIBER_HPP_ +#define SUBSCRIBER_HPP_ + +/************************************ + * INCLUDES + ************************************/ +#include "Task.hpp" +#include "SystemDefines.hpp" + +/************************************ + * MACROS AND DEFINES + ************************************/ + +/************************************ + * TYPEDEFS + ************************************/ + +/************************************ + * CLASS DEFINITIONS + ************************************/ +class Subscriber { + public: + void Init(Task* subscriberTaskHandle) { + if (taskHandle != nullptr || taskQueue != nullptr) { + SOAR_ASSERT(false, "You cannot overwrite a subscriber"); + return; + } + taskHandle = subscriberTaskHandle; + taskQueue = taskHandle->GetEventQueue(); + } + + void Delete() { + taskHandle = nullptr; + taskQueue = nullptr; + } + + inline const Task* getSubscriberTaskHandle() const { return taskHandle; } + + inline Queue* getSubscriberQueueHandle() const { return taskQueue; } + + private: + Task* taskHandle = nullptr; + Queue* taskQueue = nullptr; +}; + +/************************************ + * FUNCTION DECLARATIONS + ************************************/ + +#endif /* SUBSCRIBER_HPP_ */ diff --git a/Components/SystemTypes/DataBrokerMessageTypes.hpp b/Components/SystemTypes/DataBrokerMessageTypes.hpp new file mode 100644 index 0000000..23f4965 --- /dev/null +++ b/Components/SystemTypes/DataBrokerMessageTypes.hpp @@ -0,0 +1,78 @@ +/** + ******************************************************************************** + * @file DataBrokerMessageTypes.hpp + * @author Shivam Desai + * @date Nov 23, 2024 + * @brief + ******************************************************************************** + */ + +#ifndef DATA_BROKER_MESSAGE_TYPES_HPP_ +#define DATA_BROKER_MESSAGE_TYPES_HPP_ + +/************************************ + * INCLUDES + ************************************/ +#include +#include + +/************************************ + * MACROS AND DEFINES + ************************************/ + +/************************************ + * TYPEDEFS + ************************************/ +enum class DataBrokerMessageTypes : uint8_t { + INVALID = 0, + IMU_DATA, + GPS_DATA, + BARO_DATA, + FILTER_DATA, + MAG_DATA, +}; + +namespace DataBrokerMessageType { +/************************************ + * CLASS DEFINITIONS + ************************************/ + +/************************************ + * FUNCTION DECLARATIONS + ************************************/ +std::string ToString(DataBrokerMessageTypes messageType); + +inline std::string ToString(DataBrokerMessageTypes messageType) { + switch (messageType) { + case DataBrokerMessageTypes::IMU_DATA: { + std::string type{"IMU_DATA"}; + return type; + } + case DataBrokerMessageTypes::GPS_DATA: { + std::string type{"GPS_DATA"}; + return type; + } + case DataBrokerMessageTypes::BARO_DATA: { + std::string type{"BARO_DATA"}; + return type; + } + case DataBrokerMessageTypes::FILTER_DATA: { + std::string type{"FILTER_DATA"}; + return type; + } + case DataBrokerMessageTypes::MAG_DATA: { + std::string type{"MAG_DATA"}; + return type; + } + case DataBrokerMessageTypes::INVALID: + [[fallthrough]]; + default: { + std::string type{"INVALID"}; + return type; + } + } +} + +} // namespace DataBrokerMessageType + +#endif /* DATA_BROKER_MESSAGE_TYPES_HPP_ */ diff --git a/Components/SystemTypes/SensorDataTypes.hpp b/Components/SystemTypes/SensorDataTypes.hpp new file mode 100644 index 0000000..21cc20a --- /dev/null +++ b/Components/SystemTypes/SensorDataTypes.hpp @@ -0,0 +1,58 @@ +/** + ******************************************************************************** + * @file SensorDataTypes.hpp + * @author Shivam Desai + * @date Nov 3, 2024 + * @brief General sensor data structure to pass around in the system or + *log it to flash memory + ******************************************************************************** + */ + +#ifndef SENSORDATATYPES_HPP_ +#define SENSORDATATYPES_HPP_ + +#include + +/************************************ + * MACROS AND DEFINES + ************************************/ + +/************************************ + * TYPEDEFS + ************************************/ + +/** + * @param accelX The acceleration in the X axis relative to the sensor + * @param accelY The acceleration in the Y axis relative to the sensor + * @param accelZ The acceleration in the Z axis relative to the sensor + */ +struct IMUData { + uint32_t accelX; + uint32_t accelY; + uint32_t accelZ; +}; + +/** + * @param Temperature. Can be any where from -2147483648 to 2147483647 + */ +struct ThermocoupleData { + int32_t temperature; +}; + +struct GPSData{ + uint32_t gps; +}; + +struct BaroData{ + uint32_t baro; +}; + +struct FilterData{ + uint32_t filter; +}; + +struct MagData{ + uint32_t mag; +}; + +#endif /* SENSORDATATYPES_HPP_ */ diff --git a/Components/SystemTypes/SystemCommunicationTypes.hpp b/Components/SystemTypes/SystemCommunicationTypes.hpp new file mode 100644 index 0000000..e3d046b --- /dev/null +++ b/Components/SystemTypes/SystemCommunicationTypes.hpp @@ -0,0 +1,22 @@ +/** + ******************************************************************************** + * @file SystemCommunicationTypes.hpp + * @author Shivam Desai + * @date Nov 3, 2024 + * @brief Data structures to pass around other system information such as + * commands, events or state information + ******************************************************************************** + */ + +#ifndef SYSTEMCOMMUNICATIONTYPES_HPP_ +#define SYSTEMCOMMUNICATIONTYPES_HPP_ + +/************************************ + * MACROS AND DEFINES + ************************************/ + +/************************************ + * TYPEDEFS + ************************************/ + +#endif /* SYSTEMCOMMUNICATIONTYPES_HPP_ */ diff --git a/Components/SystemTypes/SystemConfigTypes.hpp b/Components/SystemTypes/SystemConfigTypes.hpp new file mode 100644 index 0000000..578f097 --- /dev/null +++ b/Components/SystemTypes/SystemConfigTypes.hpp @@ -0,0 +1,18 @@ +/** + ******************************************************************************** + * @file SystemConfigTypes.hpp + * @author Shivam Desai + * @date Nov 3, 2024 + * @brief Defines that allow the system to build with different + *functionality by changing the value of the define in this file + ******************************************************************************** + */ + +#ifndef SYSTEMCONFIGTYPES_HPP_ +#define SYSTEMCONFIGTYPES_HPP_ + +/************************************ + * MACROS AND DEFINES + ************************************/ + +#endif /* SYSTEMCONFIGTYPES_HPP_ */ diff --git a/Core/Command.cpp b/Core/Command.cpp index 035a080..9b8f8ab 100644 --- a/Core/Command.cpp +++ b/Core/Command.cpp @@ -100,7 +100,7 @@ uint8_t* Command::AllocateData(uint16_t dataSize) statAllocationCounter += 1; //TODO: May want to print out whenever we have an imbalance in statAllocationCounter by more than ~5 or so. - CUBE_ASSERT(statAllocationCounter < MAX_NUMBER_OF_COMMAND_ALLOCATIONS); + SOAR_ASSERT(statAllocationCounter < MAX_NUMBER_OF_COMMAND_ALLOCATIONS); return this->data; } return nullptr; diff --git a/Core/CubeUtils.cpp b/Core/CubeUtils.cpp index 33b7d67..f7a929b 100644 --- a/Core/CubeUtils.cpp +++ b/Core/CubeUtils.cpp @@ -170,14 +170,14 @@ int32_t Utils::ExtractIntParameter(const char* msg, uint16_t identifierLen) { // Handle a command with an int parameter at the end if (static_cast(strlen(msg)) < identifierLen+1) { - CUBE_PRINT("Int parameter insufficient length\r\n"); + SOAR_PRINT("Int parameter insufficient length\r\n"); return ERRVAL; } // Extract the value and attempt conversion to integer const int32_t val = Utils::StringToLong(&msg[identifierLen]); if (val == ERRVAL) { - CUBE_PRINT("Int parameter invalid value\r\n"); + SOAR_PRINT("Int parameter invalid value\r\n"); } return val; diff --git a/Core/Inc/PQueue.hpp b/Core/Inc/PQueue.hpp index c751b1a..b44687e 100644 --- a/Core/Inc/PQueue.hpp +++ b/Core/Inc/PQueue.hpp @@ -107,7 +107,7 @@ class PQueue { bool operator<(const PriorityQueueItem& other) const { if(priority_ == other.priority_) { #ifdef PQUEUE_ENABLE_SEQN_CIRCULAR_CHECK - CUBE_ASSERT(pSeqN_ != nullptr, "PQueue null seqn pointer"); + SOAR_ASSERT(pSeqN_ != nullptr, "PQueue null seqn pointer"); seq_t seqN = *pSeqN_; if((order_ < seqN && other.order_ < seqN) || (order_ >= seqN && other.order_ >= seqN)) { @@ -267,10 +267,10 @@ bool PQueue::ReceiveWait(T& item) { template void PQueue::HandleConsistencyError() { // Print an error - CUBE_PRINT("ERROR: PQueue Data Consistency\r\n"); + SOAR_PRINT("ERROR: PQueue Data Consistency\r\n"); // Count the error, if it exceeds the max, we must reset the system - CUBE_ASSERT(++errCount_ > PQUEUE_ERROR_COUNT_MAX, + SOAR_ASSERT(++errCount_ > PQUEUE_ERROR_COUNT_MAX, "PQueue data consistency faults exceeded limits"); // Pop/Add items to the RT queue until it matches that of the priority queue diff --git a/Core/Mutex.cpp b/Core/Mutex.cpp index b016faf..b2ff5e9 100644 --- a/Core/Mutex.cpp +++ b/Core/Mutex.cpp @@ -18,7 +18,7 @@ Mutex::Mutex() { rtSemaphoreHandle = xSemaphoreCreateMutex(); - CUBE_ASSERT(rtSemaphoreHandle != NULL, "Semaphore creation failed."); + SOAR_ASSERT(rtSemaphoreHandle != NULL, "Semaphore creation failed."); } diff --git a/Core/Queue.cpp b/Core/Queue.cpp index 33c3d94..c216b07 100644 --- a/Core/Queue.cpp +++ b/Core/Queue.cpp @@ -59,7 +59,7 @@ bool Queue::SendToFront(Command& command) if (xQueueSendToFront(rtQueueHandle, &command, DEFAULT_QUEUE_SEND_WAIT_TICKS) == pdPASS) return true; - CUBE_PRINT("Could not send data to front of queue!\n"); + SOAR_PRINT("Could not send data to front of queue!\n"); command.Reset(); return false; @@ -79,7 +79,7 @@ bool Queue::Send(Command& command, bool reportFull) if (xQueueSend(rtQueueHandle, &command, DEFAULT_QUEUE_SEND_WAIT_TICKS) == pdPASS) return true; - if (reportFull) CUBE_PRINT("Could not send data to queue!\n"); + if (reportFull) SOAR_PRINT("Could not send data to queue!\n"); command.Reset(); diff --git a/Core/Timer.cpp b/Core/Timer.cpp index 25c1cb3..0a3df33 100644 --- a/Core/Timer.cpp +++ b/Core/Timer.cpp @@ -22,7 +22,7 @@ Timer::Timer() // The timer ID is specified as (void *)this to provide a unique ID for each timer object - however this is not necessary for polling timers. // The timer is created in the dormant state. rtTimerHandle = xTimerCreate("Timer", timerPeriod, pdFALSE, (void *)this, DefaultCallback); - CUBE_ASSERT(rtTimerHandle, "Error Occurred, Timer not created"); + SOAR_ASSERT(rtTimerHandle, "Error Occurred, Timer not created"); timerState = UNINITIALIZED; } @@ -36,7 +36,7 @@ Timer::Timer() Timer::Timer(void (*TimerDefaultCallback_t)( TimerHandle_t xTimer )) { rtTimerHandle = xTimerCreate("Timer", timerPeriod, pdFALSE, (void *)this, TimerDefaultCallback_t); - CUBE_ASSERT(rtTimerHandle, "Error Occurred, Timer not created"); + SOAR_ASSERT(rtTimerHandle, "Error Occurred, Timer not created"); timerState = UNINITIALIZED; } @@ -47,10 +47,10 @@ Timer::Timer(void (*TimerDefaultCallback_t)( TimerHandle_t xTimer )) Timer::~Timer() { if (xTimerDelete(rtTimerHandle, DEFAULT_TIMER_COMMAND_WAIT_PERIOD*2) == pdPASS) { - CUBE_PRINT("Timer has been deleted \n\n"); + SOAR_PRINT("Timer has been deleted \n\n"); } else { - CUBE_PRINT("WARNING, FAILED TO DELETE TIMER! \n\n"); + SOAR_PRINT("WARNING, FAILED TO DELETE TIMER! \n\n"); } } @@ -142,7 +142,7 @@ bool Timer::Stop() bool Timer::ResetTimer() { if (timerState == UNINITIALIZED) { - CUBE_PRINT("Cannot Restart as timer has not yet started!"); + SOAR_PRINT("Cannot Restart as timer has not yet started!"); return false; } if (ChangePeriodMs(timerPeriod) == true) { @@ -158,7 +158,7 @@ bool Timer::ResetTimer() bool Timer::ResetTimerAndStart() { if (timerState == UNINITIALIZED) { - CUBE_PRINT("Cannot Restart as timer has not yet started!"); + SOAR_PRINT("Cannot Restart as timer has not yet started!"); return false; } if (ChangePeriodMsAndStart(timerPeriod) == true) { @@ -223,12 +223,12 @@ void Timer::SetAutoReload(bool setReloadOn) if (setReloadOn == true){ vTimerSetReloadMode(rtTimerHandle, pdTRUE); //Testing purposes - CUBE_PRINT("Set to Auto Reload\n\n"); + SOAR_PRINT("Set to Auto Reload\n\n"); } if (setReloadOn == false){ vTimerSetReloadMode(rtTimerHandle, pdFALSE); //Testing purposes - CUBE_PRINT("Set to One Shot\n\n"); + SOAR_PRINT("Set to One Shot\n\n"); } } diff --git a/CubeDefines.cpp b/CubeDefines.cpp index 362dfee..43cdb65 100644 --- a/CubeDefines.cpp +++ b/CubeDefines.cpp @@ -53,7 +53,7 @@ void cube_print(const char* str, ...) else { // Print out that we could not acquire the VA list mutex - CUBE_ASSERT(false, "Could not acquire VA_LIST mutex"); + SOAR_ASSERT(false, "Could not acquire VA_LIST mutex"); } #endif } diff --git a/CubeDefines.hpp b/CubeDefines.hpp index e075e76..80b53a4 100644 --- a/CubeDefines.hpp +++ b/CubeDefines.hpp @@ -51,11 +51,11 @@ constexpr uint16_t ASSERT_TAKE_MAX_TIME_MS = 500; // Max time in ms to ta // Assert macro, use this for checking all possible program errors eg. malloc success etc. supports a custom message in printf format // This is our version of the stm32f4xx_hal_conf.h 'assert_param' macro with support for optional messages -// Example Usage: CUBE_ASSERT(ptr != 0, "Pointer on loop index %d is null!", index); -#define CUBE_ASSERT(expr, ...) ((expr) ? (void)0U : cube_assert_debug(false, (const char *)__FILE__, __LINE__, ##__VA_ARGS__)) +// Example Usage: SOAR_ASSERT(ptr != 0, "Pointer on loop index %d is null!", index); +#define SOAR_ASSERT(expr, ...) ((expr) ? (void)0U : cube_assert_debug(false, (const char *)__FILE__, __LINE__, ##__VA_ARGS__)) -// CUBE_PRINT macro, acts as an interface to the print function which sends a packet to the UART Task to print data -#define CUBE_PRINT(str, ...) (cube_print(str, ##__VA_ARGS__)) +// SOAR_PRINT macro, acts as an interface to the print function which sends a packet to the UART Task to print data +#define SOAR_PRINT(str, ...) (cube_print(str, ##__VA_ARGS__)) /** * @brief Malloc inline function, wraps malloc for multi-platform support, asserts successful allocation @@ -68,7 +68,7 @@ inline uint8_t* cube_malloc(uint32_t size) { #else uint8_t* ret = (uint8_t*)pvPortMalloc(size); #endif - CUBE_ASSERT(ret, "cube_malloc failed"); + SOAR_ASSERT(ret, "cube_malloc failed"); return ret; } diff --git a/CubeTask.cpp b/CubeTask.cpp index d7590f3..894c093 100644 --- a/CubeTask.cpp +++ b/CubeTask.cpp @@ -14,7 +14,7 @@ void CubeTask::InitTask() { // Make sure the task is not already initialized - CUBE_ASSERT(rtTaskHandle == nullptr, "Cannot initialize UART task twice"); + SOAR_ASSERT(rtTaskHandle == nullptr, "Cannot initialize UART task twice"); // Start the task BaseType_t rtValue = @@ -26,7 +26,7 @@ void CubeTask::InitTask() (TaskHandle_t*)&rtTaskHandle); //Ensure creation succeded - CUBE_ASSERT(rtValue == pdPASS, "CUBETask::InitTask() - xTaskCreate() failed"); + SOAR_ASSERT(rtValue == pdPASS, "CUBETask::InitTask() - xTaskCreate() failed"); } /** @@ -65,13 +65,13 @@ void CubeTask::HandleCommand(Command& cm) #endif break; default: - CUBE_PRINT("CUBETask - Received Unsupported DATA_COMMAND {%d}\n", cm.GetTaskCommand()); + SOAR_PRINT("CUBETask - Received Unsupported DATA_COMMAND {%d}\n", cm.GetTaskCommand()); break; } break; } default: - CUBE_PRINT("CUBETask - Received Unsupported Command {%d}\n", cm.GetCommand()); + SOAR_PRINT("CUBETask - Received Unsupported Command {%d}\n", cm.GetCommand()); break; } diff --git a/Drivers/DMATransfer_README.md b/Drivers/DMATransfer_README.md new file mode 100644 index 0000000..c105f38 --- /dev/null +++ b/Drivers/DMATransfer_README.md @@ -0,0 +1,81 @@ +# DMA Transfer Driver + +**Author:** Javier +**Date:** 2026-01-10 +**Status:** Active + +## Overview + +The **DMA Transfer Driver** is a header-only abstraction layer designed to simplify Direct Memory Access (DMA) transfers for STM32 peripherals. It provides a single, unified static interface (`DMAControl::Transfer`) that automatically handles protocol-specific HAL calls for **SPI**, **I2C**, and **UART**. + +This driver also manages **Cache Coherence** for Cortex-M7 (STM32H7) and Cortex-M4 (STM32G4) devices, ensuring that data located in D-Cache is properly cleaned (flushed) to RAM before transmission or invalidated (refreshed) from RAM after reception. + +## Features + +* **Unified Interface:** Uses C++ `if constexpr` templates to detect the handle type (`SPI_HandleTypeDef`, `I2C_HandleTypeDef`, `UART_HandleTypeDef`) and call the matching HAL function. +* **Cache Management:** Automatically calls `SCB_CleanDCache_by_Addr` and `SCB_InvalidateDCache_by_Addr` if the architecture requires it (H7/G4). +* **Zero-Overhead:** Being header-only template library, the compiler optimizes unused branches, resulting in no runtime performance penalty compared to raw HAL calls. + +## Directory Structure + +* **Header:** `SoarOS/Drivers/Inc/DMATransfer.hpp` +* **Documentation:** `SoarOS/Drivers/DMATransfer_README.md` + +## API Reference + +```cpp +static HAL_StatusTypeDef Transfer( + HandleType* handle, // Pointer to the HAL handle (e.g., &hspi1, &hi2c1, &huart2) + uint16_t devAddr, // Device Address (Used for I2C only; set to 0 for others) + uint8_t* txData, // Pointer to Transmit Buffer (nullptr if receive-only) + uint8_t* rxData, // Pointer to Receive Buffer (nullptr if transmit-only) + uint16_t size // Number of bytes to transfer +); +``` + +## Usage + +### Include the Header + +```cpp +#include "DMATransfer.hpp" +``` + +### I2C + +```cpp +// Example: Write 4 bytes to an EEPROM at address 0x50 +uint8_t data[] = {0x00, 0x01, 0xAA, 0xBB}; +DMAControl::Transfer(&hi2c1, (0x50 << 1), data, nullptr, 4); + +// Example: Read 10 bytes from a sensor at address 0x68 +uint8_t rxBuffer[10]; +DMAControl::Transfer(&hi2c1, (0x68 << 1), nullptr, rxBuffer, 10); +``` + +### SPI + +```cpp +uint8_t txBuf[] = {0xCA, 0xFE}; +uint8_t rxBuf[2]; + +// Full Duplex Transfer +DMAControl::Transfer(&hspi1, 0, txBuf, rxBuf, 2); +``` + +### UART + +```cpp +// Transmit Message via DMA +uint8_t msg[] = "Hello World"; +DMAControl::Transfer(&huart1, 0, msg, nullptr, 11); + +// Receive Data via DMA +uint8_t inputBuf[64]; +DMAControl::Transfer(&huart1, 0, nullptr, inputBuf, 64); +``` + +### NOTES + +* This Driver Requires HAL handles. It is not directly compatible with LL pointers without a wrapper. +* DMA channels should be enabled and linked to peripherals in main.c / msp.c before calling this function. diff --git a/Drivers/Inc/DMATransfer.hpp b/Drivers/Inc/DMATransfer.hpp new file mode 100644 index 0000000..1b36111 --- /dev/null +++ b/Drivers/Inc/DMATransfer.hpp @@ -0,0 +1,71 @@ + /** + ******************************************************************************** + * @file DMATransfer.hpp + * @author Javier + * @date 2026-01-10 + * @brief Header for the DMA Transfer Driver. + ******************************************************************************** + */ + +#ifndef CUBE_DMATRANSFER_HPP +#define CUBE_DMATRANSFER_HPP + +/************************************ + * INCLUDES + ************************************/ +//#include "main.h" +#include "SystemDefines.hpp" +#include "cmsis_os.h" +#include + +/************************************ + * CLASS DEFINITIONS + ************************************/ +class DMAControl { +public: + template + static HAL_StatusTypeDef Transfer(HandleType* handle, uint16_t devAddr, uint8_t* txData, uint8_t* rxData, uint16_t size) { + + // H7 / G4 Cache Management + #if defined(STM32H7) || defined(STM32G4) + // Cache To RAM before DMA + if (txData != nullptr) SCB_CleanDCache_by_Addr((uint32_t*)txData, size); + // Clean Cache to Force read RAM + if (rxData != nullptr) SCB_InvalidateDCache_by_Addr((uint32_t*)rxData, size); + #endif + + // === SPI Logic === + if constexpr (std::is_same_v) { + // SPI Transfer (Full-Duplex) + return HAL_SPI_TransmitReceive_DMA(handle, txData, rxData, size); + } + + // === I2C Logic === + if constexpr (std::is_same_v) { + + // Transmit + if (txData != nullptr && rxData == nullptr) { + return HAL_I2C_Master_Transmit_DMA(handle, devAddr, txData, size); + } + + // Receive + else if (rxData != nullptr && txData == nullptr) { + return HAL_I2C_Master_Receive_DMA(handle, devAddr, rxData, size); + } + } + // === UART Logic === + if constexpr (std::is_same_v) { + // Transmit + if (txData != nullptr && rxData == nullptr) { + return HAL_UART_Transmit_DMA(handle, txData, size); + } + // Receive + else if (rxData != nullptr && txData == nullptr) { + return HAL_UART_Receive_DMA(handle, rxData, size); + } + } + return HAL_ERROR; + } +}; + +#endif /* CUBE_DMATRANSFER_HPP */ \ No newline at end of file diff --git a/Drivers/Inc/UARTDriver.hpp b/Drivers/Inc/UARTDriver.hpp index e278339..29174f9 100644 --- a/Drivers/Inc/UARTDriver.hpp +++ b/Drivers/Inc/UARTDriver.hpp @@ -13,28 +13,6 @@ #include "SystemDefines.hpp" #include "cmsis_os.h" -// Example code on how to declare UART Driver Instances in the HPP -#if EXAMPLE_CODE -/* UART Driver Instances ------------------------------------------------------------------*/ -class UARTDriver; - -namespace Driver { - extern UARTDriver uart1; - extern UARTDriver uart2; - extern UARTDriver uart3; - extern UARTDriver uart5; -} - -/* UART Driver Aliases ------------------------------------------------------------------*/ -namespace UART { - constexpr UARTDriver* Umbilical_RCU = &Driver::uart1; - constexpr UARTDriver* Radio = &Driver::uart2; - constexpr UARTDriver* Conduit_PBB = &Driver::uart3; - // UART 4 (GPS) uses HAL - constexpr UARTDriver* Debug = &Driver::uart5; -} -#endif - /* UART Receiver Base Class ------------------------------------------------------------------*/ /** * @brief Any classes that are expected to receive using a UART driver diff --git a/Drivers/UARTDriver_README.md b/Drivers/UARTDriver_README.md index 121091f..2c48dfd 100644 --- a/Drivers/UARTDriver_README.md +++ b/Drivers/UARTDriver_README.md @@ -50,7 +50,7 @@ something like cpp_USART5_IRQHandler(), for example: * RunInterface.cpp */ -#include "main_system.hpp" +#include "main_avionics.hpp" #include "UARTDriver.hpp" extern "C" { diff --git a/README.md b/README.md index 42a9b80..39460e4 100644 --- a/README.md +++ b/README.md @@ -72,11 +72,11 @@ Add the Cube++ repository into the root folder as a submodule using one of these SSH ``` -git submodule add git@github.com:cjchanx/CubePlusPlus.git Cube++ +git submodule add git@github.com:UCSOAR/SoarOperatingSystem.git SoarOS ``` HTTPS ``` -git submodule add https://github.com/cjchanx/cubeplusplus-examples.git Cube++ +git submodule add https://github.com/UCSOAR/SoarOperatingSystem.git SoarOS ``` Please add the following to include paths: @@ -90,13 +90,13 @@ Please ensure the following folders are not in the exclude from build option: - (none for this version) ### Codebase Setup -An example project utilizing Cube++ with basic CUBE_PRINT support, in addition to a Debug receive task for parsing input data on the debug line can be found at https://github.com/cjchanx/cubeplusplus-examples/tree/main/Basic_Debug +An example project utilizing Cube++ with basic SOAR_PRINT support, in addition to a Debug receive task for parsing input data on the debug line can be found at https://github.com/cjchanx/cubeplusplus-examples/tree/main/Basic_Debug - It is recommended to setup a new folder called `Components` or `Modules` in the root where all the code goes. - There are a few files that you should have in `Components`/`Modules`: - SystemDefines.hpp : An example can be found [here](https://github.com/cjchanx/cubeplusplus-examples/blob/main/Basic_Debug/Components/SystemDefines.hpp) - - main_system.cpp : This can be named anything you want, but should contain the run_main() function that is the entry point for your codebase, example [here](https://github.com/cjchanx/cubeplusplus-examples/blob/main/Basic_Debug/Components/main_system.cpp) - - main_system.hpp : Header file for main_system.cpp, example [here](https://github.com/cjchanx/cubeplusplus-examples/blob/main/Basic_Debug/Components/main_system.hpp) + - main_avionics.cpp : This can be named anything you want, but should contain the run_main() function that is the entry point for your codebase, example [here](https://github.com/cjchanx/cubeplusplus-examples/blob/main/Basic_Debug/Components/main_avionics.cpp) + - main_avionics.hpp : Header file for main_avionics.cpp, example [here](https://github.com/cjchanx/cubeplusplus-examples/blob/main/Basic_Debug/Components/main_avionics.hpp) - SysCore/Inc/RunInterface.hpp : Header file for the run interface which allows C code to call into the C++ codebase without errors, example [here](https://github.com/cjchanx/cubeplusplus-examples/blob/main/Basic_Debug/Components/Core/Inc/RunInterface.hpp) - SysCore/RunInterface.cpp : Code file for the run interface, example [here](https://github.com/cjchanx/cubeplusplus-examples/blob/main/Basic_Debug/Components/Core/RunInterface.cpp) - Setup Debug UART