mirror of
https://git.intern.spaceteamaachen.de/ALPAKA/TACOS.git
synced 2025-06-12 09:36:00 +00:00
263 lines
11 KiB
Markdown
263 lines
11 KiB
Markdown
# TACOS
|
|
|
|
This is the Trajectory Analysis Control OS (TACOS) that serves as a framework for flight computer development. TACOS offers a state machine, a CAN bus interface, a watchdog and other HAL features through it's submodules. It runs on cmsis-rtos2 FreeRTOS on STM32 microcontrollers with C++ (maybe future versions will offer external C interfaces to support a wide array of languages...).
|
|
|
|
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 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:
|
|
|
|
1. ```Import -> General -> Import an Existing STM32CubeMX Configuration File (.ioc)```
|
|
2. Select the .ioc file from the ioc-collection
|
|
3. Enter the project name and location you want to save the project to
|
|
4. Select C++ as the target language
|
|
5. Click "Finish"
|
|
|
|
|
|
### 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`.
|
|
|
|
Now your project should look like this:
|
|
```
|
|
...
|
|
App/
|
|
├── Inc/
|
|
│ ├── sta/
|
|
│ │ └── config.hpp
|
|
├── Src/
|
|
│ └── startup.cpp
|
|
Libs/
|
|
...
|
|
```
|
|
|
|
### 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
|
|
cd Libs
|
|
git submodule add https://git.intern.spaceteamaachen.de/ALPAKA/TACOS.git
|
|
git submodule add https://git.intern.spaceteamaachen.de/ALPAKA/sta-core.git
|
|
git submodule add https://git.intern.spaceteamaachen.de/ALPAKA/rtos2-utils.git
|
|
```
|
|
|
|
Make sure that you add the include paths for TACOS, sta-core and rtos2-utils to the project with the following steps:
|
|
1. Right click your project in the `Project Explorer` and select `Properties -> C/C++ General -> Paths and Symbols -> Includes -> GNU C -> Add...`
|
|
2. Select `Add to all languages` and `Is a workspace path`
|
|
3. Click on `Workspace` and select a folder from the `YOUR_PROJECT_FOLDER/(Libs|App)` directory
|
|
- Always select the `include` or `Inc` folder for the include paths
|
|
- If the path you want to add is not in the list, refresh the project with `F5` in the `Project Explorer` and try again
|
|
4. Repeat for TACOS, sta-core, rtos2-utils and the App folder
|
|
5. Right click your project in the `Project Explorer` and select `Properties -> C/C++ General -> Paths and Symbols -> Source Location -> Add Folder...`
|
|
- Add the `App` and `Libs` folders
|
|
|
|
> [!NOTE]
|
|
> You often want to add more submodules during development. Here, a faster way to add the include path for a library is to right click the library's include folder in the `Project Explorer` and select `Add/remove include path`.
|
|
|
|
### Starting TACOS
|
|
|
|
Navigate to the `Core/Src/freertos.c` file and add the following code to the `StartDefaultTask` function:
|
|
```cpp
|
|
void StartDefaultTask(void *argument)
|
|
{
|
|
/* USER CODE BEGIN StartDefaultTask */
|
|
extern void startTACOS(void *);
|
|
startTACOS(argument);
|
|
|
|
/* Infinite loop */
|
|
for(;;)
|
|
{
|
|
osDelay(1);
|
|
}
|
|
/* USER CODE END StartDefaultTask */
|
|
}
|
|
```
|
|
|
|
This will start the TACOS startup and initialize all TACOS threads (which will then initialize yours).
|
|
|
|
### Configuring TACOS
|
|
In order to use TACOS, you need to provide a configuration file in the path `sta/config.hpp`. The following code is an example for a TACOS-project using default configuration:
|
|
```cpp
|
|
#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_ENABLED
|
|
#define STA_DEBUGGING_ENABLED
|
|
|
|
// Enable Features
|
|
|
|
// Statemachine settings. How many states does your statemachine have?
|
|
#define STA_TACOS_NUM_STATES 3
|
|
|
|
// Uses the default configuration for TACOS.
|
|
#include<sta/tacos/configs/default.hpp>
|
|
|
|
#endif /* INC_STA_CONFIG_HPP_ */
|
|
```
|
|
PS: For not officially supported chips use this as the include:
|
|
```cpp
|
|
#include <sta/devices/stm32/mcu/common.hpp>
|
|
#define STA_MCU_LITTLE_ENDIAN
|
|
#define STA_PLATFORM_STM32
|
|
```
|
|
|
|
### 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
|
|
#ifndef INC_TASKS_SPAM_TASK_HPP_
|
|
#define INC_TASKS_SPAM_TASK_HPP_
|
|
|
|
#include <sta/tacos.hpp>
|
|
|
|
namespace tasks
|
|
{
|
|
class SpamTask : public sta::tacos::TacosThread {
|
|
public:
|
|
SpamTask();
|
|
|
|
// One time function that is called when the thread is created.
|
|
void init() override;
|
|
|
|
// Repeatable function that is called every time the thread is executed.
|
|
void func() override;
|
|
};
|
|
} // namespace tasks
|
|
|
|
#endif /* INC_TASKS_SPAM_TASK_HPP_ */
|
|
```
|
|
This code defines a new thread that inherits from `TacosThread` and implements the `init` and `func` functions. The `init` function is called once when the thread is created and the `func` function is called every time the thread is executed.
|
|
|
|
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>
|
|
|
|
namespace tasks {
|
|
SpamTask::SpamTask() :
|
|
TacosThread("SPAM", osPriorityNormal){}
|
|
|
|
void SpamTask::init() {
|
|
// Nothing to init...
|
|
}
|
|
|
|
void SpamTask::func() {
|
|
// Print "Hello World" every second.
|
|
STA_DEBUG_PRINTLN("Hello World");
|
|
this->periodicDelay(1); // Execute this function with 1 Hz.
|
|
}
|
|
} // namespace tasks
|
|
```
|
|
|
|
> [!WARNING]
|
|
> A thread's priority must be strictly lower than the statemachine's priority. Unless manually changed, this is always `osPriorityHigh`.
|
|
|
|
To start this thread, we first need to fill out the `startup.cpp` file. This file may look like this:
|
|
```cpp
|
|
#include <sta/tacos.hpp>
|
|
#include <tasks/spam_task.hpp>
|
|
|
|
#include <sta/debug/debug.hpp>
|
|
|
|
namespace sta
|
|
{
|
|
namespace tacos
|
|
{
|
|
void startup()
|
|
{
|
|
// ###### Register different threads for different states here. ######
|
|
// Register a "Spam Task" thread for all states except 1 and 2.
|
|
sta::tacos::addThread<tasks::SpamTask>(ALL_STATES - state_set{1,2});
|
|
|
|
STA_DEBUG_PRINTF("The answer to everything is %d", 42);
|
|
}
|
|
} // namespace tacos
|
|
} // namespace sta
|
|
```
|
|
|
|
The function `startup()` is a weakly implemented function that is executed right before TACOS initializes its statemachine task. It serves as an entry point for the user to initialize all busses, threads and rtos2-utils stuff that is needed for the application to fulfill its purpose.
|
|
|
|
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!
|
|
|
|
### \[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.
|
|
2. Add the following code to the `sta/config.hpp` file:
|
|
```
|
|
#define STA_TACOS_CAN_BUS_ENABLE
|
|
```
|
|
PS: For not officially supported chips add this:
|
|
```
|
|
#define STA_STM32_CAN_HANDLE {YOUR_HCAN_HANDLE}
|
|
```
|
|
|
|
There are two options for handling incoming CAN messages:
|
|
1. If `#define STA_CAN_BUS_FWD_ENABLE` is set, the messages will be forwarded to the task with the ID of the message.
|
|
- Tasks set their ID with `setID(uint32_t id)` in their constructor.
|
|
- From here they can handle the message by going through their `CAN_queue_` with `CanSysMsg msg; CAN_queue_.get(&msg);`
|
|
2. All messages will trigger the weakly defined handleSysMessage callback.
|
|
- This could be implemented like this:
|
|
```cpp
|
|
namespace sta
|
|
{
|
|
namespace tacos
|
|
{
|
|
bool handleSysMessage(CanMsgHeader &header, uint8_t *payload)
|
|
{
|
|
// Print the message ID and the first byte of the payload.
|
|
//(please don't do this in production, it will crash the system sooner or later)
|
|
STA_DEBUG_PRINTF("> ID: %d", header.sid);
|
|
|
|
switch (header.sid)
|
|
{
|
|
// State transition message
|
|
case STA_TACOS_CAN_BUS_SYS_MSG_ID:
|
|
// First byte of payload is the origin state, second byte is the destination state
|
|
tacos::setState(payload[0], payload[1], 0, true);
|
|
return true;
|
|
|
|
case MODULE_SW_RESET_CAN_ID:
|
|
HAL_NVIC_SystemReset();
|
|
|
|
return true; // :)
|
|
|
|
// ...
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return false; // I know, i know, this is not necessary, but it's good practice. And you know what they say about good practice: Do it!
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## 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, CAN-Bus and inter-thread communication.
|
|
|
|
### 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 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.
|
|
|
|
A state transition can be triggered by calling the functions `requestState()`, `forceState()` or `setStateTimed()` that are provided in `sta/tacos.hpp`. Additionally, state transitions can be triggered remotely using the CAN-Bus. This is discussed in more detail in the section discussing the CAN Bus.
|
|
|
|
### 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. |