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;