Merge pull request 'feat/documentation' (#46) from feat/documentation into main

Reviewed-on: https://git.intern.spaceteamaachen.de/ALPAKA/TACOS/pulls/46
Reviewed-by: lars.wilko.sentse <lars.wilko.sentse@noreply.git.intern.spaceteamaachen.de>
This commit is contained in:
lars.wilko.sentse 2025-01-18 21:10:45 +00:00
commit 0340543d0b
2 changed files with 150 additions and 23 deletions

164
README.md
View File

@ -4,11 +4,11 @@ This is the Trajectory Analysis Control OS (TACOS) that serves as a framework fo
To use TACOS one should implement threads, which fulfill the various roles of the module in the App directory. TACOS utilizes [ALPAKA](https://git.intern.spaceteamaachen.de/ALPAKA) features, in particular requiring [sta-core](https://git.intern.spaceteamaachen.de/ALPAKA/sta-core) and [rtos2-utils](https://git.intern.spaceteamaachen.de/ALPAKA/rtos2-utils), as such it requires these to be in it's include path.
## Setting up a TACOS project
## Setting up a TACOS Project
### Setting up the project
### Setting up the Project
First one must create a new CubeIDE project with FreeRTOS. To avoid doing that however we recommend using the [ioc-collection](https://git.intern.spaceteamaachen.de/ALPAKA/ioc-collection) to get a preconfigured IOC for the STM microcontroller you are using. From here follow the following steps:
First one must create a new CubeIDE project with FreeRTOS. To avoid doing that however we recommend using the [ioc-collection](https://git.spaceteamaachen.de/ALPAKA/ioc-collection) to get a preconfigured IOC for the STM microcontroller you are using. From here follow the following steps:
1. ```Import -> General -> Import an Existing STM32CubeMX Configuration File (.ioc)```
2. Select the .ioc file from the ioc-collection
@ -17,7 +17,7 @@ First one must create a new CubeIDE project with FreeRTOS. To avoid doing that h
5. Click "Finish"
### Setting up the folder structure
### Setting up the Folder Structure
Now it is necessary to setup the dependencies and include paths for TACOS. For this first create a new folder in the project directory called `Libs`. Then create another folder in the project directory called `App` with the subfolders `Inc` and `Src`. Now also create a folder called `sta` in the `Inc` folder. Finally add the empty files `App/Inc/sta/config.hpp` and `App/Src/startup.cpp`.
@ -34,7 +34,7 @@ Libs/
...
```
### Setting up the dependencies
### Setting up the Dependencies
First it is recommended to initialize a git repository in the project folder with `git init`. Then add the TACOS, sta-core and rtos2-utils repositories as submodules in the `Libs` folder with the following commands:
```bash
@ -84,26 +84,16 @@ 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_
// Using a board with an ASEAG module present.
#define STA_STM32_ASEAG
// Use the STM32F407 microprocessor.
#include <sta/devices/stm32/mcu/STM32F407xx.hpp>
// Doesn't really do too much right now. Has to be added for successful compilation.
#define STA_PRINTF_USE_STDLIB
// Enable debug serial output and assertions.
#define STA_ASSERT_FORCE
#define STA_ASSERT_ENABLED
#define STA_DEBUGGING_ENABLED
// Enable Features
#define STA_RTOS_SYSTEM_EVENTS_ENABLE
// #define STA_RTOS_SYSTEM_WATCHDOG_ENABLE
// #define STA_RTOS_WATCHDOG_ENABLE
// #define STA_TACOS_WATCHDOG_FREQUENCY 10000
#define STA_CAN_BUS_ENABLE
// Statemachine settings.
// Statemachine settings. How many states does your statemachine have?
#define STA_TACOS_NUM_STATES 3
// Uses the default configuration for TACOS.
@ -111,14 +101,36 @@ In order to use TACOS, you need to provide a configuration file in the path `sta
#endif /* INC_STA_CONFIG_HPP_ */
```
PS: For not officially supported chips use this as the include:
> [!WARNING]
> If you want to use debug printing (enabled using the macro `STA_DEBUGGING_ENABLED`) in a TACOS project, you should also enable float formatting under `Project Settings -> C/C++ Build -> MPU/MCU Settings -> Use float with printf from newlib-nano`. This allows you to format print floats using `STA_DEBUG_PRINTF`. If this setting is not enabled, your software will crash when debug printing floats.
The configuration file shown in the example above initializes the project assuming that you are working on a STM32 Nucleo of type F407. Typically, you are using a different microcontroller, however. In this case you can replace the line
```cpp
#include <sta/devices/stm32/mcu/STM32F407xx.hpp>
```
with the include
```cpp
#include <sta/devices/stm32/mcu/STM32_YOUR_MCU_HERE.hpp>
```
So far, only a few chips are officially supported. For not officially supported chips use this as the include:
```cpp
#define STA_STM32_SWD_USART_IDX <IDX OF YOUR UART>
#include <sta/devices/stm32/mcu/common.hpp>
#define STA_MCU_LITTLE_ENDIAN
#define STA_PLATFORM_STM32
```
> [!NOTE]
> The definition of `STA_STM32_SWD_USART_IDX` allows you to specify which UART handle to use for debug printing. If undefined, a default handle for Nucleos will be used. You can also add the macro `STA_STM32_ASEAG` instead if you are a cool kid using ASEAG-based hardware 😎.
### Implementing your own threads
> [!WARNING]
> The definition of `STA_STM32_SWD_USART_IDX` has to be placed _before_ the include `cpp #include <sta/devices/stm32/mcu/STM32_YOUR_MCU_HERE.hpp>`
> [!WARNING]
> Make sure you actually enable the UART bus in the under `Pinout & Configuration -> Connectivity` in the IOC.
### Implementing Your Own Threads
Let's create a simple thread that prints "Hello World" every second. First create a new file in the `App/Inc/tasks` folder called `spam_task.hpp`. Then add the following code:
```cpp
@ -148,6 +160,7 @@ This code defines a new thread that inherits from `TacosThread` and implements t
Now create a new file in the `App/Src/tasks` folder called `spam_task.cpp` and add the following code:
```cpp
#include <tasks/spam_task.hpp>
#include <sta/debug/debug.hpp>
namespace tasks {
SpamTask::SpamTask() :
@ -195,7 +208,7 @@ The function `startup()` is a weakly implemented function that is executed right
And that's it! Now you have a thread that prints "Hello World" every second. Simply build the project and flash it to your microcontroller and be amazed by the Spam!
### Setting up the CAN Bus
### \[Optional\] 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.
@ -250,5 +263,116 @@ namespace sta
}
```
## TACOS Usage Guide
Almost all of the important aspects of working with TACOS have already been discussed when setting up the project itself. The following sections will give you an in-depth explanation of how to use the statemachine, inter-thread communication and the CAN-Bus.
### Using the Statemachine
The statemachine forms the heart and soul of a TACOS-based project. Upon initialization, TACOS starts a statemachine that manages the system state and the currently active threads. As seen before, whenever we pass a new thread to TACOS we also have to provide all states in which the thread should run. After each state transition from state $ x $ to state $ y $ the statemachine task performs two actions:
1. All threads that should run in state $ y $ but are not currently running are started.
2. All threads that should not run in state $ y $ but are currently running are stopped.
> [!IMPORTANT]
> The statemachine does not immediately stop a thread and deletes it from memory. Instead, the thread is allowed to finish the current execution of its `func` before entering a blocked state. This allows the thread to release all its resources.
In order to fully understand the statemachine, we have to take a look at the _lockout_ and _failsafe timer_. These lockout and failsafe timers are the result of design choices made during early stages of STAHR. The goal was to combine the state estimation (i.e. sensor fusion using a Kalman filter) with timer-based safety mechanisms. For example, our goal was to block any state transition to the state `DROGUE` before 60 seconds after liftoff. Additionally, a timer was started to automatically switch to state `DROGUE` after 120 seconds after liftoff.
These safety mechanisms resulted in the implementation of the lockout and failsafe timer in TACOS:
1. **Lockout Timer**: The lockout timer can be started after a state transition. As long as it is running, all state transitions are blocked by the statemachines, unless the user actively chooses to bypass the safety mechanism using `forceState()`.
2. **Failsafe Timer** The failsafe timer can be used to schedule a state transition after a certain period of time has elapsed. This transition will be blocked if the lockout timer is running at that time. The failsafe timer obeys the following rules:
- A timed state transition can be requested even when the lockout timer is active. It only matters if the lockout timer is running at the end of the time span of the lockout timer.
- If a state transition is triggered before the end of the time span, the failsafe timer is stopped.
A state transition can be triggered by calling the functions `requestState()`, `forceState()` or `setStateTimed()` that are provided in `sta/tacos.hpp`. Take a look at `include/sta/README.md` for more details on the functions. Additionally, state transitions can be triggered remotely using the CAN-Bus, however, this is discussed in more detail in the section on the CAN Bus.
#### Example Usage
Generally, the state transitions are requested in the `startup()` function or in TacosThread instances implemented by the user. It is good practise to give your states names by defining an enum in a header file `states.hpp` that can be included everywhere in your project.
```cpp
#ifndef MY_PROJECT_STATES_HPP
#define MY_PROJECT_STATES_HPP
namespace my_project
{
enum class States : uint16_t
{
STARTUP = 0,
PING = 1,
PONG = 2
};
}
#endif // MY_PROJECT_STATES_HPP
```
This gives us three states: `STARTUP`, `PING` and `PONG`. Generally, these names have no meaning for TACOS but they make your software more readable. Next, we define two modified tasks based on `SpamTask` for our project:
```cpp
#include <tasks/ping_task.hpp>
#include <tasks/pong_task.hpp>
#include <path/to/states.hpp>
namespace tasks {
PingTask::PingTask() :
TacosThread("PING", osPriorityNormal){}
void PingTask::func() {
sleep(100);
STA_DEBUG_PRINTLN("PING");
sta::tacos::requestState(my_project::PING, my_project::PONG);
}
PongTask::PongTask() :
TacosThread("PONG", osPriorityNormal){}
void PongTask::func() {
sleep(100);
STA_DEBUG_PRINTLN("PONG");
sta::tacos::requestState(my_project::PONG, my_project::PING);
}
} // namespace tasks
```
> [!IMPORTANT]
> Generally, you want both tasks to be implemented in separate .cpp files.
Using these two threads we can implement our `startup()` function:
```cpp
#include <sta/tacos.hpp>
#include <tasks/spam_task.hpp>
#include <path/to/states.hpp>
#include <sta/debug/debug.hpp>
namespace sta
{
namespace tacos
{
void startup()
{
// Register a "PingTask" thread for the state PING.
sta::tacos::addThread<tasks::PingTask>({my_project::PING});
sta::tacos::addThread<tasks::PongTask>({my_project::PONG});
// Start with the spam after one second.
sta::tacos::setStateTimed(my_project::STARTUP, my_project::PING, 1000);
}
} // namespace tacos
} // namespace sta
```
The resulting program switches between the states `PING` and `PONG` and alternately outputs "PING" and "PONG" via UART. While this is just a toy example, building more complicated applications is not much harder!
### Using Inter-Thread Communication
> [!IMPORTANT]
> Coming soon!
### Further information
To look into other function of TACOS please consult the READMEs in the include folder or the doxygen documentation. Also consult the sta-core and rtos2-utils READMEs for further information on the features that TACOS uses.

View File

@ -15,7 +15,7 @@ Retrieves the current state of the TACOS state machine.
void requestState(uint32_t from, uint32_t to, uint32_t lockout = 0, bool publish = true)
```
Requests a state transition. Invalid state transitions will be dismissed. First come, first serve.
Requests a state transition. Invalid state transitions will be dismissed. First come, first serve. Can be blocked by the lockout timer.
- **Parameters**:
- `from`: The starting state for the transition.
@ -27,7 +27,7 @@ Requests a state transition. Invalid state transitions will be dismissed. First
void forceState(uint32_t from, uint32_t to, uint32_t lockout = 0, bool publish = true)
```
Forces a state transition. Will be ignored if already in the given state. Triggers instantly.
Forces a state transition. Will be ignored if already in the given state. Triggers instantly. Ignores the lockout timer.
- **Parameters**:
- `from`: The starting state for the transition.
@ -41,7 +41,7 @@ Forces a state transition. Will be ignored if already in the given state. Trigge
void setStateTimed(uint32_t from, uint32_t to, uint32_t millis, uint32_t lockout = 0, bool publish = false)
```
Requests a state transition after a specified time.
Requests a state transition after a specified time. Equivalent to calling `requestState` after a specified time.
- **Parameters**:
- `from`: The starting state.
@ -49,6 +49,9 @@ Requests a state transition after a specified time.
- `millis`: The wait time in milliseconds before requesting the transition.
- `lockout`: (Optional) A timer for blocking subsequent transitions.
> [!IMPORTANT]
> If there is already a timed transition scheduled, calling `setStateTimed` will overwrite this request.
---
```cpp