TACOS/README.md
2024-12-07 13:53:57 +00:00

246 lines
9.6 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_
// Use the STM32F407 microprocessor.
#include <sta/devices/stm32/mcu/STM32F407xx.hpp>
// Enable debug serial output and assertions.
#define STA_ASSERT_ENABLED
#define STA_DEBUGGING_ENABLED
// Statemachine settings.
#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
```
> [!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`.
### 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>
#include <sta/debug/debug.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!
### 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!
}
}
}
```
### 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.