diff --git a/.gitignore b/.gitignore index b9309ce..64dcbf5 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ Release/ docs/html docs/latex +# Config +include/sta/config.hpp + diff --git a/README.md b/README.md index 51cdb5b..97ea1a7 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,10 @@ In order to use TACOS, you need to provide a configuration file in the path `sta #ifndef INC_STA_CONFIG_HPP_ #define INC_STA_CONFIG_HPP_ -// Use the STM32F411 microprocessor. -#include +// Using a board with an ASEAG module present. +#define STA_STM32_ASEAG +// Use the STM32F407 microprocessor. +#include // Doesn't really do too much right now. Has to be added for successful compilation. #define STA_PRINTF_USE_STDLIB @@ -44,10 +46,14 @@ In order to use TACOS, you need to provide a configuration file in the path `sta #define STA_ASSERT_FORCE #define STA_DEBUGGING_ENABLED -// Settings for the rtos-utils +// Enable Features #define STA_RTOS_SYSTEM_EVENTS_ENABLE // #define STA_RTOS_SYSTEM_WATCHDOG_ENABLE // #define STA_RTOS_WATCHDOG_ENABLE +#define STA_CAN_BUS_ENABLE + +// Statemachine settings. +#define STA_TACOS_NUM_STATES 3 // Uses the default configuration for TACOS. #include @@ -59,4 +65,18 @@ PS: For not officially supported chips use this as the include: #include #define STA_MCU_LITTLE_ENDIAN #define STA_PLATFORM_STM32 -``` \ No newline at end of file +``` +## Setting up the CAN Bus + +To enable the CAN Bus two things need to be done: +1. Enable CAN in the IOC with the RX0 and RX1 Interrupts enabled. +2. Add the following code to the `sta/config.hpp` file: +``` +#define STA_CAN_BUS_ENABLE +``` +PS: For not officially supported chips add this: +``` +#define STA_STM32_CAN_HANDLE {YOUR_HCAN_HANDLE} +``` + +After this messages will automatically be forwarded to the task with it's ID. To send messages use the interface defined in `tacos.hpp`. \ No newline at end of file diff --git a/include/sta/tacos.hpp b/include/sta/tacos.hpp index c5d6edc..4672dbf 100644 --- a/include/sta/tacos.hpp +++ b/include/sta/tacos.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -76,6 +77,32 @@ namespace sta return thread_ptr; } + + /** + * @brief Queue a message to be sent over the CAN bus. + * + * @param msg The message to be sent. + * @param timeout The time to wait for the message to be sent. + * + * @return bool True if the message was sent successfully. + * + * @ingroup tacos_api + */ + bool queueCanBusMsg(CanSysMsg & msg, uint32_t timeout); + + /** + * @brief Publish a state transition message to the CAN bus. + * + * @param from The state we want to transition from. + * @param to The state we want to transition to. + * @param lockout An optional timer blocking state transition for a given time. + * + * @return bool True if the message was sent successfully. + * + * @ingroup tacos_api + */ + bool publishState(uint32_t from, uint32_t to, uint32_t lockout = 0); + } // namespace tacos } diff --git a/include/sta/tacos/can_bus.hpp b/include/sta/tacos/can_bus.hpp new file mode 100644 index 0000000..216c370 --- /dev/null +++ b/include/sta/tacos/can_bus.hpp @@ -0,0 +1,121 @@ +#ifndef INCLUDE_TACOS_CAN_BUS_HPP_ +#define INCLUDE_TACOS_CAN_BUS_HPP_ + +#include + +#ifdef STA_CAN_BUS_ENABLE + +#include +#include +#include +#include + +#include +#include + + +/** + * @defgroup tacos_can_bus Can Bus Task + * @ingroup tacos + * @brief Can Bus task for TACOS. + */ + +namespace sta +{ + namespace tacos + { + /** + * @brief Can Bus implementation for Tacos. + * + * @ingroup tacos_can_bus + */ + class CanBus : public TacosThread + { + public: + + CanBus(); + + /** + * @brief Getter function for the singleton instance. + */ + static CanBus* instance() + { + static CGuard g; + + if (!_instance) + { + // Create the can bus singleton instance. + CanBus::_instance = new CanBus(); + } + + return _instance; + } + + /** + * @brief Place system message in CAN driver TX queue. + * + * @param msg Message to transmit + * @param timeout Timeout for placing message (0 = no wait, osWaitForever = blocking) + * @return True if message was queued successfully + */ + bool queueCanBusMsg(const CanSysMsg & msg, uint32_t timeout); + + /** + * @brief Retrieve system message from CAN driver TX queue. + * + * @param[out] msg Destination for retrieved message + * @param timeout Timeout for retrieving message (0 = no wait, osWaitForever = blocking) + * @return True if message was retrieved successfully + */ + bool getCanBusMsg(CanSysMsg * msg, uint32_t timeout); + + /** + * @brief Buffers incoming message and sets event to notify the system. + * + * @param fifo FIFO number of the received message. + */ + void canCallback(uint32_t fifo); + + void init() override; + void func() override; + + private: + static CanBus * _instance; + + class CGuard + { + public: + ~CGuard() + { + if( NULL != CanBus::_instance ) + { + delete CanBus::_instance; + CanBus::_instance = NULL; + } + } + }; + + sta::STM32CanController * canBusController_; + + CanSysMsg* canBusSysQueueBuffer_[STA_RTOS_CAN_BUS_QUEUE_LENGTH]; + uint8_t bufferIndex; + + RtosQueue canBusSysQueue_; + + AlpakaCanBus canBus_; + + static RtosEvent messageEvent; + }; + + /** + * @brief Callback function for handling received messages. Intended for state transitions. + */ + STA_WEAK + void handleSysMessage(CanMsgHeader & header, uint8_t * payload); + + } /* namespace tacos */ + +} /* namespace sta */ + +#endif /* STA_CAN_BUS_ENABLE */ +#endif /* INCLUDE_TACOS_CAN_BUS_HPP_ */ diff --git a/include/sta/tacos/configs/default.hpp b/include/sta/tacos/configs/default.hpp index 119c247..494a350 100644 --- a/include/sta/tacos/configs/default.hpp +++ b/include/sta/tacos/configs/default.hpp @@ -1,15 +1,21 @@ #ifndef STA_TACOS_CONFIGS_DEFAULT_HPP #define STA_TACOS_CONFIGS_DEFAULT_HPP -// Enable the watchdog provided by TACOS. -#define STA_TACOS_WATCHDOG_ENABLED - // Generally, we assume the TACOS threads to have the highest priorties. #define STA_TACOS_MANAGER_PRIORITY osPriorityHigh #define STA_TACOS_STATEMACHINE_PRIORITY osPriorityHigh #define STA_TACOS_WATCHDOG_PRIORITY osPriorityHigh +#define STA_TACOS_CAN_BUS_PRIORITY osPriorityHigh // Per default, we assume state 0 to be the initial state. #define STA_TACOS_INITIAL_STATE 0 +// Can Bus settings +#define STA_RTOS_CAN_BUS_QUEUE_LENGTH 5 +#define STA_RTOS_CAN_BUS_MAX_FILTER 14 +#define STA_RTOS_CAN_BUS_MAX_PAYLOAD_SIZE 8 + +// State transition message define with highest CAN priority +#define STA_TACOS_CAN_BUS_SYS_MSG_ID 0x0 + #endif // STA_TACOS_CONFIGS_DEFAULT_HPP diff --git a/include/sta/tacos/thread.hpp b/include/sta/tacos/thread.hpp index 82a07dd..6344d55 100644 --- a/include/sta/tacos/thread.hpp +++ b/include/sta/tacos/thread.hpp @@ -12,6 +12,8 @@ #include #include +#include +#include /** * @defgroup tacos_thread TACOS Thread @@ -126,6 +128,24 @@ namespace sta */ void sleep(uint32_t ticks); +#ifdef STA_CAN_BUS_ENABLE + /** + * @brief Set the ID of the CAN bus this thread is associated with. + * + * @param canID + */ + void setCanID(uint32_t canID); + + /** + * @brief Get the ID of the CAN bus this thread is associated with. + * + * @return can ID + */ + uint32_t getCanID(); + + sta::RtosQueue CAN_queue_; +#endif // STA_CAN_BUS_ENABLE + #ifdef STA_TACOS_WATCHDOG_ENABLED /** * @brief This macro wraps a given statement into waiting() and heartbeat() to make the code more readable. @@ -191,6 +211,9 @@ namespace sta bool running_; #ifdef STA_TACOS_WATCHDOG_ENABLED ThreadStatus status_; +#endif // STA_TACOS_WATCHDOG_ENABLED +#ifdef STA_CAN_BUS_ENABLE + uint32_t canID_; #endif // STA_TACOS_WATCHDOG_ENABLED bool terminate_; }; diff --git a/src/can_bus.cpp b/src/can_bus.cpp new file mode 100644 index 0000000..52656c6 --- /dev/null +++ b/src/can_bus.cpp @@ -0,0 +1,164 @@ +#include +#ifdef STA_CAN_BUS_ENABLE + +#include +#include +#include +#include + +extern CAN_HandleTypeDef STA_STM32_CAN_HANDLE; + +namespace sta +{ + namespace tacos + { + CanBus::CanBus() + : TacosThread{"Can Bus", STA_TACOS_CAN_BUS_PRIORITY}, + canBusController_(new STM32CanController(&STA_STM32_CAN_HANDLE)), + canBusSysQueue_(STA_RTOS_CAN_BUS_QUEUE_LENGTH), + canBus_{canBusController_, HAL_GetTick} + { + bufferIndex = 0; + for(int i = 0; i < STA_RTOS_CAN_BUS_QUEUE_LENGTH; i++){ + canBusSysQueueBuffer_[i] = nullptr; + } + } + + void CanBus::init() + { + canBusController_->start(); + + if (HAL_CAN_ActivateNotification(&STA_STM32_CAN_HANDLE, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK || + HAL_CAN_ActivateNotification(&STA_STM32_CAN_HANDLE, CAN_IT_RX_FIFO1_MSG_PENDING)) + { + Error_Handler(); + } + } + + void CanBus::func() + { + messageEvent.clear(STA_RTOS_CAN_ANY); + uint32_t flags = messageEvent.wait(STA_RTOS_CAN_ANY, osWaitForever); + + if (flags != static_cast(osErrorTimeout)) + { + STA_ASSERT_MSG((flags & osStatusReserved) == flags, "Unexpected error occurred in wait"); + + if (flags & STA_RTOS_CAN_FLAG_SYS_QUEUED) + { + // Take messages from queue until empty + CanSysMsg msg; + while (CanBus::_instance->getCanBusMsg(&msg, 0)) + { + canBus_.send(msg); + } + } + + if (flags & STA_RTOS_CAN_FLAG_MSG_AVAIL) + { + CanSysMsg sysMsg; + // Iterate through buffer and set back to nullptr after use + for(int i = 0; i < STA_RTOS_CAN_BUS_QUEUE_LENGTH; i++){ + if(canBusSysQueueBuffer_[i] != nullptr){ + sysMsg = *canBusSysQueueBuffer_[i]; + canBusSysQueueBuffer_[i] = nullptr; + + // Append to the correct thread's queue + for (std::shared_ptr thread : Manager::instance()->getActiveThreads()){ + if (thread->getCanID() == sysMsg.header.sid){ + thread->CAN_queue_.put(sysMsg); + break; + } + } + + if(sysMsg.header.sid == STA_TACOS_CAN_BUS_SYS_MSG_ID){ + // Handle system message + handleSysMessage(sysMsg.header, sysMsg.payload); + } + } + } + } + } + + } + + bool CanBus::queueCanBusMsg(const CanSysMsg& msg, uint32_t timeout) + { + // This technically should check if we are using a system message, but we just pretending that everything is one of those rn + //STA_ASSERT((msg.header.sid & ~STA_CAN_SID_SYS_BITS) == 0); + + if (canBusSysQueue_.put(msg, timeout)) + { + // Signal task + messageEvent.set(STA_RTOS_CAN_FLAG_SYS_QUEUED); + messageEvent.clear(STA_RTOS_CAN_ANY); + return true; + } + else + { + return false; + } + } + + void CanBus::canCallback(uint32_t fifo){ + if(messageEvent.get() != STA_RTOS_CAN_FLAG_MSG_AVAIL){ + // get here does not work since FreeRTOS is a buggy mess + messageEvent.set(STA_RTOS_CAN_FLAG_MSG_AVAIL); + + CanRxHeader rxHeader; //CAN Bus Receive Header + uint8_t canRX[8] = {0,0,0,0,0,0,0,0}; //CAN Bus Receive Buffer + + bool received_ = canBusController_->receiveFrame(fifo, &rxHeader, canRX); + + if(received_){ + CanSysMsg sysMsg; + sysMsg.header.sid = rxHeader.id.sid; + sysMsg.header.payloadLength = rxHeader.payloadLength; + + for(int i = 0; i < rxHeader.payloadLength; i++){ + sysMsg.payload[i] = canRX[i]; + } + + canBusSysQueueBuffer_[bufferIndex] = &sysMsg; + + bufferIndex++; + if (bufferIndex >= STA_RTOS_CAN_BUS_QUEUE_LENGTH) bufferIndex = 0; + } + } + } + + bool CanBus::getCanBusMsg(CanSysMsg * msg, uint32_t timeout) + { + return (canBusSysQueue_.get(msg, timeout)); + } + + CanBus* CanBus::_instance = nullptr; + + RtosEvent CanBus::messageEvent; + + } /* namespace tacos */ +} /* namespace sta */ + + +namespace sta { + + void CanBus_RxPendingCallback(uint32_t fifo){ + sta::tacos::CanBus::instance()->canCallback(fifo); + } + + namespace tacos + { + void handleSysMessage(CanMsgHeader & header, uint8_t * payload) + { + // This is a weak function that can be overridden by the user, + // if they want to handle system messages in a different way, i.e. ignore them + + STA_ASSERT(header.payloadLength == 2); + + // First byte of payload is the origin state, second byte is the destination state + tacos::setState(payload[0], payload[1]); + } + } // namespace tacos +} // namespace sta + +#endif // STA_CAN_BUS_ENABLE diff --git a/src/startup.cpp b/src/startup.cpp index 8556665..a841f00 100644 --- a/src/startup.cpp +++ b/src/startup.cpp @@ -29,7 +29,7 @@ #include #include #include - +#include // The UART mutex defined in freertos.c extern osMutexId_t uartMutexHandle; @@ -39,8 +39,6 @@ extern osMutexId_t uartMutexHandle; * @ingroup tacos * @brief Functions that are called during startup. */ - - namespace sta { #ifdef STA_DEBUGGING_ENABLED @@ -125,6 +123,25 @@ namespace sta Watchdog::instance()->start(); } #endif // STA_TACOS_WATCHDOG_ENABLED + +#ifdef STA_CAN_BUS_ENABLE + /** + * @brief Function that is called before the Can Bus task is started. Override it to adjust + * the Can bus to your specifications. + * + * @ingroup tacos_startup + */ + STA_WEAK + void onCanBusInit() + {} + + void initCanBus() + { + onCanBusInit(); + + CanBus::instance()->start(); + } +#endif //STA_CAN_BUS_ENABLE } // namespace tacos @@ -144,6 +161,10 @@ namespace sta #ifdef STA_TACOS_WATCHDOG_ENABLED tacos::initWatchdog(); #endif // STA_TACOS_WATCHDOG_ENABLED + +#ifdef STA_CAN_BUS_ENABLE + tacos::initCanBus(); +#endif // STA_CAN_BUS_ENABLE } } // namespace rtos } // namespace sta diff --git a/src/tacos.cpp b/src/tacos.cpp index 45aa708..b4e4ba3 100644 --- a/src/tacos.cpp +++ b/src/tacos.cpp @@ -7,7 +7,6 @@ #include - namespace sta { namespace tacos @@ -26,6 +25,23 @@ namespace sta { Statemachine::instance()->requestTimedStateTransition(from, to, millis, lockout); } + + bool queueCanBusMsg(CanSysMsg & msg, uint32_t timeout){ + return CanBus::instance()->queueCanBusMsg(msg, timeout); + } + + bool publishState(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */){ + CanSysMsg msg; + msg.header.sid = STA_TACOS_CAN_BUS_SYS_MSG_ID; + msg.header.payloadLength = 2; + msg.payload[0] = from; + msg.payload[1] = to; + + msg.header.eid = 0; + msg.header.format = 0; + + return CanBus::instance()->queueCanBusMsg(msg, lockout); + } } // namespace tacos } // namespace sta diff --git a/src/thread.cpp b/src/thread.cpp index 41e2b16..61bf62a 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -27,6 +27,10 @@ namespace sta #ifdef STA_TACOS_WATCHDOG_ENABLED status_{ThreadStatus::STOPPED}, #endif // STA_TACOS_WATCHDOG_ENABLED +#ifdef STA_CAN_BUS_ENABLE + CAN_queue_{STA_RTOS_CAN_BUS_QUEUE_LENGTH}, + canID_{0}, +#endif // STA_CAN_BUS_ENABLE terminate_{false} { STA_ASSERT(stack_size >= 0); @@ -165,6 +169,16 @@ namespace sta TacosThread::~TacosThread(){} +#ifdef STA_CAN_BUS_ENABLE + void TacosThread::setCanID(uint32_t canID){ + canID_ = canID; + } + + uint32_t TacosThread::getCanID(){ + return canID_; + } +#endif // STA_CAN_BUS_ENABLE + } // namespace tacos } // namespace sta