diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml index 4089cb2..f807aa8 100644 --- a/.settings/language.settings.xml +++ b/.settings/language.settings.xml @@ -5,7 +5,7 @@ - + @@ -16,7 +16,7 @@ - + diff --git a/App/Inc/sta/config.hpp b/App/Inc/sta/config.hpp index f46a2d7..eee17cb 100644 --- a/App/Inc/sta/config.hpp +++ b/App/Inc/sta/config.hpp @@ -31,7 +31,9 @@ // Settings for TACOS #define STA_TACOS_MANAGER_PRIORITY osPriorityNormal #define STA_TACOS_STATEMACHINE_PRIORITY osPriorityNormal -#define STA_TACOS_NUM_STATES 4 + +// Statemachine settings. Here, we only have a single state which is also the initial state. +#define STA_TACOS_NUM_STATES 1 #define STA_TACOS_INITIAL_STATE 0 #endif /* INC_STA_CONFIG_HPP_ */ diff --git a/App/Src/startup.cpp b/App/Src/startup.cpp index be10ea9..884a1f3 100644 --- a/App/Src/startup.cpp +++ b/App/Src/startup.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include @@ -22,17 +24,47 @@ namespace sta { void onStatemachineInit() { - STA_DEBUG_PRINTLN("Starting statemachine task!"); + Statemachine::instance()->setStateFunction([](uint16_t state) -> uint16_t { + // ###### Implement your state transition function here! ###### + + /** + * This function should return some state within 0 and STA_TACOS_NUM_STATES (specified in the config.hpp). + * Return the same state as given as the argument for no state transition. Make sure, that this function is not + * computationally expensive. + */ + + return state; + }); + + Statemachine::instance()->setTimerFunction([](uint16_t state, uint16_t lastState, uint32_t event) { + // ###### Set the failsafe and lockout timers here ###### + + /** + * This function is called after a state transition from lastState to state. Event corresponds to EventFlag enum in the Statemachine + * and encode how the state change happened. + * + * Setting a timer here is fully optional and depends on your system's specifications. + */ + + uint32_t someFailsafeMillis = 42; + uint16_t someNextState = STA_TACOS_INITIAL_STATE; + uint32_t someLockoutMillis = 21; + + // Start the failsafe timer to force a state transition to someNextState after someFailsafeMillis milliseconds. + Statemachine::instance()->setFailsafeTimer(someFailsafeMillis, someNextState); + + // Start the lockout timer to block a state transition for the first someLockoutMillis milliseconds. + Statemachine::instance()->setLockoutTimer(someLockoutMillis); + }); + } void onManagerInit() { - STA_DEBUG_PRINTLN("Starting manager task!"); + // ###### Register different threads for different states here. ###### - Manager::instance()->registerThread(std::make_shared("0"), {0}); - Manager::instance()->registerThread(std::make_shared("1"), {1}); - Manager::instance()->registerThread(std::make_shared("2"), {2}); - Manager::instance()->registerThread(std::make_shared("3"), {3}); + // The dummy task runs for state 0. + Manager::instance()->registerThread(std::make_shared("Dummy"), {0}); } } // namespace tacos } // namespace sta diff --git a/App/Src/tasks/dummy.cpp b/App/Src/tasks/dummy.cpp index 9b7adb0..5046319 100644 --- a/App/Src/tasks/dummy.cpp +++ b/App/Src/tasks/dummy.cpp @@ -29,8 +29,8 @@ namespace demo void DummyTask::func() { STA_DEBUG_PRINTLN(this->getName()); - // STA_DEBUG_PRINT("Executing "); - // STA_DEBUG_PRINTLN(this->getName()); + + osDelay(1000); } } // namespace demo diff --git a/Core/Inc/FreeRTOSConfig.h b/Core/Inc/FreeRTOSConfig.h index 0a78574..20e6f05 100644 --- a/Core/Inc/FreeRTOSConfig.h +++ b/Core/Inc/FreeRTOSConfig.h @@ -68,7 +68,7 @@ #define configCPU_CLOCK_HZ ( SystemCoreClock ) #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES ( 56 ) -#define configMINIMAL_STACK_SIZE ((uint16_t)128) +#define configMINIMAL_STACK_SIZE ((uint16_t)256) #define configTOTAL_HEAP_SIZE ((size_t)15360) #define configMAX_TASK_NAME_LEN ( 16 ) #define configUSE_TRACE_FACILITY 1 @@ -77,6 +77,7 @@ #define configQUEUE_REGISTRY_SIZE 8 #define configCHECK_FOR_STACK_OVERFLOW 1 #define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_MALLOC_FAILED_HOOK 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #define configRECORD_STACK_HIGH_ADDRESS 1 @@ -94,7 +95,7 @@ #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY ( 48 ) #define configTIMER_QUEUE_LENGTH 10 -#define configTIMER_TASK_STACK_DEPTH 256 +#define configTIMER_TASK_STACK_DEPTH 512 /* The following flag must be enabled only when using newlib */ #define configUSE_NEWLIB_REENTRANT 1 diff --git a/Core/Inc/stm32f4xx_hal_conf.h b/Core/Inc/stm32f4xx_hal_conf.h index a695a07..14f10bb 100644 --- a/Core/Inc/stm32f4xx_hal_conf.h +++ b/Core/Inc/stm32f4xx_hal_conf.h @@ -222,7 +222,7 @@ /* Section 2: PHY configuration section */ /* DP83848_PHY_ADDRESS Address*/ -#define DP83848_PHY_ADDRESS 0x01U +#define DP83848_PHY_ADDRESS /* PHY Reset delay these values are based on a 1 ms Systick interrupt*/ #define PHY_RESET_DELAY 0x000000FFU /* PHY Configuration delay */ @@ -252,10 +252,10 @@ #define PHY_JABBER_DETECTION ((uint16_t)0x0002U) /*!< Jabber condition detected */ /* Section 4: Extended PHY Registers */ -#define PHY_SR ((uint16_t)0x10U) /*!< PHY status register Offset */ +#define PHY_SR ((uint16_t)) /*!< PHY status register Offset */ -#define PHY_SPEED_STATUS ((uint16_t)0x0002U) /*!< PHY Speed mask */ -#define PHY_DUPLEX_STATUS ((uint16_t)0x0004U) /*!< PHY Duplex mask */ +#define PHY_SPEED_STATUS ((uint16_t)) /*!< PHY Speed mask */ +#define PHY_DUPLEX_STATUS ((uint16_t)) /*!< PHY Duplex mask */ /* ################## SPI peripheral configuration ########################## */ diff --git a/Core/Src/freertos.c b/Core/Src/freertos.c index 64b2b36..3eae141 100644 --- a/Core/Src/freertos.c +++ b/Core/Src/freertos.c @@ -75,6 +75,7 @@ void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ /* Hook prototypes */ void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName); +void vApplicationMallocFailedHook(void); /* USER CODE BEGIN 4 */ void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName) @@ -85,6 +86,22 @@ void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName) } /* USER CODE END 4 */ +/* USER CODE BEGIN 5 */ +void vApplicationMallocFailedHook(void) +{ + /* vApplicationMallocFailedHook() will only be called if + configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook + function that will get called if a call to pvPortMalloc() fails. + pvPortMalloc() is called internally by the kernel whenever a task, queue, + timer or semaphore is created. It is also called by various parts of the + demo application. If heap_1.c or heap_2.c are used, then the size of the + heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in + FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used + to query the size of free heap space that remains (although it does not + provide information on how the remaining heap might be fragmented). */ +} +/* USER CODE END 5 */ + /** * @brief FreeRTOS initialization * @param None diff --git a/Core/Src/main.c b/Core/Src/main.c index 75a8446..5ccb8a8 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -99,6 +99,7 @@ int main(void) /* Start scheduler */ osKernelStart(); + /* We should never get here as control is now taken by the scheduler */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ diff --git a/Core/Src/stm32f4xx_hal_msp.c b/Core/Src/stm32f4xx_hal_msp.c index ed524b2..27650fc 100644 --- a/Core/Src/stm32f4xx_hal_msp.c +++ b/Core/Src/stm32f4xx_hal_msp.c @@ -20,6 +20,7 @@ /* Includes ------------------------------------------------------------------*/ #include "main.h" + /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ diff --git a/Libs/rtos2-utils b/Libs/rtos2-utils index d591560..067a48c 160000 --- a/Libs/rtos2-utils +++ b/Libs/rtos2-utils @@ -1 +1 @@ -Subproject commit d591560c95a4eae525cbbbdb4e4bc15642852473 +Subproject commit 067a48c30953fb5ee2c9f442f9a93a062c16ac1e diff --git a/TACOS-Demo Debug.cfg b/TACOS-Demo Debug.cfg new file mode 100644 index 0000000..940ce37 --- /dev/null +++ b/TACOS-Demo Debug.cfg @@ -0,0 +1,42 @@ +# This is an genericBoard board with a single STM32F411RETx chip +# +# Generated by STM32CubeIDE +# Take care that such file, as generated, may be overridden without any early notice. Please have a look to debug launch configuration setup(s) + +source [find interface/stlink-dap.cfg] + + +set WORKAREASIZE 0x8000 + +transport select "dapdirect_swd" + +set CHIPNAME STM32F411RETx +set BOARDNAME genericBoard + +# Enable debug when in low power modes +set ENABLE_LOW_POWER 1 + +# Stop Watchdog counters when halt +set STOP_WATCHDOG 1 + +# STlink Debug clock frequency +set CLOCK_FREQ 8000 + +# Reset configuration +# use hardware reset, connect under reset +# connect_assert_srst needed if low power mode application running (WFI...) +reset_config srst_only srst_nogate connect_assert_srst +set CONNECT_UNDER_RESET 1 +set CORE_RESET 0 + +# ACCESS PORT NUMBER +set AP_NUM 0 +# GDB PORT +set GDB_PORT 3333 + + + +# BCTM CPU variables + +source [find target/stm32f4x.cfg] + diff --git a/TACOS-Demo.ioc b/TACOS-Demo.ioc index 1d7d028..677ca1f 100644 --- a/TACOS-Demo.ioc +++ b/TACOS-Demo.ioc @@ -3,12 +3,14 @@ CAD.formats= CAD.pinconfig= CAD.provider= FREERTOS.FootprintOK=true -FREERTOS.IPParameters=Tasks01,configUSE_NEWLIB_REENTRANT,configRECORD_STACK_HIGH_ADDRESS,configCHECK_FOR_STACK_OVERFLOW,Mutexes01,FootprintOK,configTIMER_TASK_PRIORITY +FREERTOS.IPParameters=Tasks01,configUSE_NEWLIB_REENTRANT,configRECORD_STACK_HIGH_ADDRESS,configCHECK_FOR_STACK_OVERFLOW,Mutexes01,FootprintOK,configTIMER_TASK_PRIORITY,configMINIMAL_STACK_SIZE,configUSE_MALLOC_FAILED_HOOK FREERTOS.Mutexes01=uartMutex,Static,uartMutex_cb FREERTOS.Tasks01=defaultTask,24,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL FREERTOS.configCHECK_FOR_STACK_OVERFLOW=1 +FREERTOS.configMINIMAL_STACK_SIZE=256 FREERTOS.configRECORD_STACK_HIGH_ADDRESS=1 FREERTOS.configTIMER_TASK_PRIORITY=48 +FREERTOS.configUSE_MALLOC_FAILED_HOOK=1 FREERTOS.configUSE_NEWLIB_REENTRANT=1 File.Version=6 KeepUserPlacement=false @@ -28,8 +30,8 @@ Mcu.PinsNb=3 Mcu.ThirdPartyNb=0 Mcu.UserConstants= Mcu.UserName=STM32F411RETx -MxCube.Version=6.8.0 -MxDb.Version=DB.6.0.80 +MxCube.Version=6.9.2 +MxDb.Version=DB.6.0.92 NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false NVIC.ForceEnableDMAVector=true @@ -76,6 +78,8 @@ ProjectManager.RegisterCallBack= ProjectManager.StackSize=0x400 ProjectManager.TargetToolchain=STM32CubeIDE ProjectManager.ToolChainLocation= +ProjectManager.UAScriptAfterPath= +ProjectManager.UAScriptBeforePath= ProjectManager.UnderRoot=true ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_USART2_UART_Init-USART2-false-HAL-true RCC.AHBFreq_Value=16000000 diff --git a/Tacos/include/sta/tacos/statemachine.hpp b/Tacos/include/sta/tacos/statemachine.hpp index 9ecefd9..bf5fa4c 100644 --- a/Tacos/include/sta/tacos/statemachine.hpp +++ b/Tacos/include/sta/tacos/statemachine.hpp @@ -42,7 +42,7 @@ namespace sta namespace tacos { typedef std::function state_fn; - typedef std::function timer_fn; + typedef std::function timer_fn; class Statemachine : public TacosThread @@ -135,6 +135,7 @@ namespace sta private: uint16_t currentState_; + uint16_t failsafeState_; RtosTimer lockoutTimer_; RtosTimer failsafeTimer_; diff --git a/Tacos/include/sta/tacos/thread.hpp b/Tacos/include/sta/tacos/thread.hpp index b809fc1..54e66f2 100644 --- a/Tacos/include/sta/tacos/thread.hpp +++ b/Tacos/include/sta/tacos/thread.hpp @@ -61,13 +61,16 @@ namespace sta /** * @brief This function is executed first when this thread is started. */ - virtual void init() = 0; + virtual void init(); /** * @brief The body of the thread's loop. Has to be implemented by the user. */ virtual void func() = 0; + + virtual void cleanup(); + private: /** @@ -75,8 +78,10 @@ namespace sta */ static void entry_point(void* arg); + private: osThreadId_t instance_; osThreadAttr_t attribs_; + bool running_; }; } } diff --git a/Tacos/src/manager.cpp b/Tacos/src/manager.cpp index d670d2e..17755cc 100644 --- a/Tacos/src/manager.cpp +++ b/Tacos/src/manager.cpp @@ -11,6 +11,8 @@ #include +#include + namespace sta { @@ -41,6 +43,8 @@ namespace sta void Manager::stopThreads(uint16_t state) { + std::set> terminated; + for (uint16_t other = 0; other < STA_TACOS_NUM_STATES; ++other) { if (other == state) @@ -51,13 +55,11 @@ namespace sta for (std::shared_ptr thread : threads_[other]) { // If the thread is currently running but not part of the set of threads that should be running... - if (thread->isRunning() && threads_[state].count(thread) == 0) + if (thread->isRunning() && terminated.count(thread) == 0 && threads_[state].count(thread) == 0) { // ...politely request termination. thread->requestTermination(); - - STA_DEBUG_PRINT("KILLING "); - STA_DEBUG_PRINTLN(thread->getName()); + terminated.emplace(thread); } } } @@ -67,8 +69,8 @@ namespace sta { uint16_t state = Statemachine::instance()->getCurrentState(); - startThreads(state); stopThreads(state); + startThreads(state); } void Manager::init() @@ -80,8 +82,6 @@ namespace sta { Statemachine::stateChangeEvent.wait(Statemachine::EventFlags::ALL, osWaitForever); - STA_DEBUG_PRINTLN("UPDATING THREADS"); - // Start all new tasks and stop all the tasks that aren't supposed to be running. updateThreads(); } diff --git a/Tacos/src/statemachine.cpp b/Tacos/src/statemachine.cpp index fdb9955..b1a9154 100644 --- a/Tacos/src/statemachine.cpp +++ b/Tacos/src/statemachine.cpp @@ -17,6 +17,7 @@ namespace sta Statemachine::Statemachine() : TacosThread{"Statemachine", STA_TACOS_STATEMACHINE_PRIORITY}, currentState_{STA_TACOS_INITIAL_STATE}, + failsafeState_{STA_TACOS_INITIAL_STATE}, lockoutTimer_{[](void *){}, nullptr}, failsafeTimer_{[](void *){}, nullptr}, transitionFunc_{[](uint16_t) -> uint16_t { return Statemachine::instance()->getCurrentState(); }}, @@ -39,15 +40,21 @@ namespace sta // Check if a state change is desired. Block if the lockoutTimer is still running. if (next != currentState_ && !lockoutTimer_.isRunning()) { + if (failsafeTimer_.isRunning()) + { + failsafeTimer_.stop(); + } + // Call user space code to set the timers again. timerFunc_(next, currentState_, EventFlags::NORMAL); // Update the state and trigger the global state changed event. currentState_ = next; Statemachine::stateChangeEvent.set(EventFlags::NORMAL); + Statemachine::stateChangeEvent.clear(EventFlags::ALL); } - Statemachine::stateChangeEvent.clear(EventFlags::ALL); + osThreadYield(); } uint16_t Statemachine::getCurrentState() const @@ -74,16 +81,16 @@ namespace sta STA_ASSERT(nextState < STA_TACOS_NUM_STATES); STA_ASSERT(!failsafeTimer_.isRunning()); - failsafeTimer_.setCallback([nextState](void *) { + failsafeTimer_.setCallback([nextState](void* arg) { Statemachine::instance()->forceStateTransition(nextState); - }, nullptr); + }, &failsafeState_); failsafeTimer_.start(millis); } void Statemachine::setLockoutTimer(uint32_t millis) { - STA_ASSERT(!failsafeTimer_.isRunning()); + STA_ASSERT(!lockoutTimer_.isRunning()); lockoutTimer_.setCallback([](void *) { }, nullptr); lockoutTimer_.start(millis); @@ -91,6 +98,17 @@ namespace sta void Statemachine::forceStateTransition(uint16_t state) { + if (failsafeTimer_.isRunning()) + { + failsafeTimer_.stop(); + } + + if (lockoutTimer_.isRunning()) + { + lockoutTimer_.stop(); + } + + timerFunc_(state, currentState_, EventFlags::FORCED); currentState_ = state; Statemachine::stateChangeEvent.set(EventFlags::FORCED); diff --git a/Tacos/src/thread.cpp b/Tacos/src/thread.cpp index ececb73..b42fd99 100644 --- a/Tacos/src/thread.cpp +++ b/Tacos/src/thread.cpp @@ -22,13 +22,15 @@ namespace sta TacosThread::TacosThread(const char* name, osPriority_t prio) : RtosThread(RtosHandle(Handle::Deferred(&instance_))), instance_{ NULL }, - attribs_{ .name = name, .priority = prio } + attribs_{ .name = name, .priority = prio }, + running_{false} {} TacosThread::TacosThread() : RtosThread(RtosHandle(Handle::Deferred(&instance_))), instance_{ NULL }, - attribs_{ } + attribs_{ }, + running_{false} {} void TacosThread::entry_point(void* arg) @@ -43,16 +45,21 @@ namespace sta { STA_ASSERT(!isRunning()); - instance_ = osThreadNew(entry_point, this, &attribs_); + // If this is the first time starting the thread, it has to be started via rtos first. + if (instance_ == NULL) + { + instance_ = osThreadNew(entry_point, this, &attribs_); - STA_ASSERT(instance_ != NULL); + STA_ASSERT(instance_ != NULL); + } - // SetFlag(THREAD_START); + // Send a thread start signal. + sysNotify(STA_RTOS_THREAD_FLAG_START); } bool TacosThread::isRunning() { - return instance_ != NULL; + return running_; } osThreadId_t TacosThread::getInstance() @@ -67,26 +74,37 @@ namespace sta return attribs_.name; } + void TacosThread::init() {} + void TacosThread::loop() { - // The thread has to wait until the startup has been completed. - rtos::waitForStartupEvent(); - - // wait(THREAD_START) - - init(); - - // Run the thread until the termination flag is set. - while ((getFlags() & STA_RTOS_THREAD_FLAG_TERMINATE) == 0) + // Infinite loop allowing quick restarts of this thread after termination. + while (true) { - func(); - } + // The thread has to wait until the startup has been completed. + rtos::waitForStartupEvent(); - instance_ = NULL; + // Wait for a thread start flag. + wait(STA_RTOS_THREAD_FLAG_START); + + running_ = true; + init(); + + // Run the thread until the termination flag is set. + while (!checkTerminationRequest()) + { + func(); + } + + cleanup(); + running_ = false; + } osThreadExit(); } + void TacosThread::cleanup() {} + bool TacosThread::operator==(const TacosThread& other) const { return std::strcmp(this->getName(), other.getName()) == 0;