Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This document covers how LaminarOS is structured within its Github repo.

Requirements

CMake and GNU are required to build this library, please follow this guide for the necessary tools: Firmware Development Environment Setup

Folder Structure

The following is the current folder structure of LaminarOS is meant to be generic due to allow continuous expansion. The main components of LaminarOS consist of the LOS github repo. This folder structure should not have to change too much in the future.

LaminarOS
-CMakeLists.txt
-/Los_Interface
-Src
-Inc
-/Los_Drivers
-Src
-Inc
-/Los_Core
-Src
-Inc
-/Boardfiles
-/nucleof401re
-/nucleol552zeq

-/Tools

Lets go through each directory and present what type of files should be going in there. Although not a directory, the CMakeLists.txt is the file responsible for compiling every single file within the LOS project into a library (.a) file (you do not have to know how this file works unless major changes are to be made to the folder structure).

As for LOS_Interface, LOS_Driver, and LOS_Core, each of these directories has a Src and Inc folder. No further folder should be added to these directories. The Src folder are for source files and the Inc folder are for the header files.

It is likely that any configuration header files will go into the LOS_Interface/Inc/ directory.

The Boardfiles directory holds subdirectories to the board package that LOS should have support for. Within those subdirectories, should lie the STM32CubeIDE generated project along with all the generated files.

Finally, the Tools directory holds any scripts or software tools that would be useful for development. Currently, there is a build bash script that will build the CMakeLists.txt and compile the code all in one step with argument to select the board package to build for.

Adding Support for a New Board Package

How the CMake Works

Proposal

LOS will act as a “library” for ZP software and any other projects that would like to utilize the interface. To avoid any unforeseeable issues with building LOS within other projects in STM32CubeIDE (remember the project files are within the LOS repo) we will be using CMake and Makefiles to build our project.

LOS will have a local CMake file that will build interface, drivers, and core along with all boardfiles. A CMake arg can be used to build for the different boardfile projects.

Other software (such as ZP) that will be using LOS will have their own CMake to build their project files along with LOS.
Since main.c will be in LOS in the boardifles directory, it has to have some way to initialize the freeRTOS threads from ZP sw without know of their existence to avoid having it depend on anything from ZP sw. To do this, LOS interface will define a weak function (“LOS_init_threads”?) that will be overridden by ZP sw. Ideally, some part of ZPsw overides this function and call the LOS interface to initialize the freeRTOS threads for all state machines, most notable AM, PM, and TM. Then in main() the function will be called, initializing all threads and the kernel will be started. This way LOS doesn’t have to know about the threads being run, not depending on ZP swfollowing (as of March 9, 2023):

  • boardfiles (folder)

  • LOS_Core (folder)

  • LOS_Driver (folder)

  • LOS_Interface (folder)

  • Tools (folder)

  • CMakeLists.txt (file)

boardfiles

The boardfiles folder is where all the STM32CubeIDE or STM32CubeMX generated code goes for all the different board packages we support. For example, LOS supports the NucleoL552ZEQ STM developement board and so there is a nucleol552zeq folder which was the location given to the STM32CubeIDE for it to generate its files to.

If another board package is desired to be supported, it should be added here.

LOS_Core

The LOS_Core folder is where the LOS_Core files go. At the time of writing, LOS_Core has not started development but the folder structure within will likely be very similar to boardfiles.

LOS_Driver

The LOS_Driver folder is where all the drivers go. Each “class” of driver has its own folder of which would contain further Src and Inc folders. Since our drone could use multiple different types of the same sensor, they are first clomped together in the same “class” of driver but each would have their own .c and .h file along with a generic .c and .h file in order to be used generically.

For example, if we had 3 different types of airspeed sensor, airspeedX, airspeedY, and airspeedZ, each requiring their own driver, the folder structure would look like the following:

  • Airspeed

    • Inc

      • airspeed.c

      • airspeedX.c

      • airspeedY.c

      • airspeedZ.c

    • Src

      • airspeed.h

      • airspeedX.h

      • airspeedY.h

      • airspeedZ.h

The reason behind having a generic airspeed.c and airspeed.h file will be explained in more detail in the Driver Development section [insert link].

LOS_Interface

The LOS_Interface folder is where all the LOS interfaces go. The folder contains 3 additional folders:

  • Common

  • Inc

  • Src

The Inc and Src folders contain the source and header folders for the different interfaces. Additional interfaces can easily be added here.

The Common folder is where each supported board package will have specific code. Most importantly, the config.h and config.c file. This file contains the configuration information of the drone being used. Specifically, which driver is being used for each type of sensor/peripheral. You should notice that for each sensor or peripheral, the specific class for that peripheral is declared THEN it is added to an array of its generic class.

For example, if we look again at the airspeed example in the LOS_Driver section. If we decided to use airspeed sensor Y then the code in config.cpp would look something like this:

Code Block
breakoutModewide
languagec
const int NUM_AIRSPEED_SENSORS = 1; //number of airspeed sensors being used

AirspeedY airspeed_sensor_Y(param0, param1, param2); //declaring the specific airspeed sensor we want to use

Airspeed airspeeds[NUM_AIRSPEED_SENSORS] = {&airspeed_sensor_y}; //inputting the specific airspeed driver in the generic array

There are many reasons why we do this (of which will be explained in the Driver Development document) but what you have to know here is this is where all the drivers are declared here to be used by the interface files. Funny thing is, the interface files only use the airspeeds array to reference the actual driver, which is of type Airspeed so they themselves don’t even know which drivers are being used, to them they are all the same.

Tools

The tools folder is for any common scripts that are used for development. Most importantly, it is home to build.bash which is the build script that will build LaminarOS using the CMakeLists.txt.

CMakeLists.txt

The CMakeLists.txt file contains configuration used by the compiler on how to compile our code. This file is meant to be written in a way that it will recursively pickup new files in existing folders, but if a new folder is added (for example when adding a new type of driver), then this file has to be modified accordingly.

This is by no means a tutorial on how to development with CMake but lets go through when we have to modify this file and what to add.

Here is an example of a CMakeLists.txt file I got from some branch in LaminarOS:

Code Block
breakoutModewide
cmake_minimum_required(VERSION 3.2.0)

project(LaminarOS C CXX)

set(CMAKE_VERBOSE_MAKEFILE ON)

set(INCLUDE_PATHS   ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Interface/Inc
                    ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Core/Inc

                    # Include the config header folder
                    ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Interface/Common/${FOLDER_NAME}/Inc

                    ## Any new driver folders Inc dirs must be added here ##
                    ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Driver/RC_Receiver/Inc
                    ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Driver/Motor_Channel/Inc
                    ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Driver/RC_Sender/Inc

                    ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Drivers/${FAMILY_NAME}_HAL_Driver/Inc
                    ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Core/Inc
                    ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Drivers/CMSIS/Device/ST/${FAMILY_NAME}/Include
                    ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Drivers/CMSIS/Include
                    ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Middlewares/Third_Party/FreeRTOS/Source
                    ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2
                    ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_${PORTABLE_NAME}
                    ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Middlewares/Third_Party/FreeRTOS/Source/include
)
set(LOS_INCLUDES ${INCLUDE_PATHS} PARENT_SCOPE)
include_directories(
    ${INCLUDE_PATHS}
)

set(HAL_DRIVERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Drivers)
set(HAL_DRIVERS_C_SOURCES "${HAL_DRIVERS_DIR}/${FAMILY_NAME}_HAL_Driver/Src/*.c")

set(FREE_RTOS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Middlewares/Third_Party/FreeRTOS/Source)
set(FREE_RTOS_C_SOURCES "${FREE_RTOS_DIR}/*.c"
                        "${FREE_RTOS_DIR}/CMSIS_RTOS_V2/*.c"
                        "${FREE_RTOS_DIR}/portable/GCC/ARM_${PORTABLE_NAME}/*.c"
                        "${FREE_RTOS_DIR}/portable/MemMang/*.c")

set(HAL_CORE ${CMAKE_CURRENT_SOURCE_DIR}/boardfiles/${FOLDER_NAME}/Core)
set(HAL_CORE_C_SOURCES "${HAL_CORE}/Src/*.c")
set(HAL_CORE_CXX_SOURCES "${HAL_CORE}/Src/*.cpp")

set(INTERFACE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Interface)
set(INTERFACE_C_SOURCES "${INTERFACE_DIR}/Src/*.c" "${INTERFACE_DIR}/Common/${FOLDER_NAME}/Src/*.c")
set(INTERFACE_CXX_SOURCES "${INTERFACE_DIR}/Src/*.cpp" "${INTERFACE_DIR}/Common/${FOLDER_NAME}/Src/*.cpp")

set(DRIVERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Driver)
set(DRIVERS_C_SOURCES "${DRIVERS_DIR}/*/Src/*.c")
set(DRIVERS_CXX_SOURCES "${DRIVERS_DIR}/*/Src/*.cpp")

set(CORE_Dir ${CMAKE_CURRENT_SOURCE_DIR}/LOS_Core)
set(CORE_C_SOURCES "${CORE_Dir}/Src/*.c")
set(CORE_CXX_SOURCES "${CORE_Dir}/Src/*.cpp")

file(GLOB_RECURSE C_SOURCES ${HAL_DRIVERS_C_SOURCES}
                            ${HAL_CORE_C_SOURCES}
                            ${INTERFACE_CXX_SOURCES}
                            ${DRIVERS_CXX_SOURCES}
                            ${CORE_C_SOURCES}
                            ${FREE_RTOS_C_SOURCES})                            
message("MESSAGE: ${C_SOURCES}")                         
file(GLOB_RECURSE CXX_SOURCES ${HAL_CORE_CXX_SOURCES} 
                              ${INTERFACE_CXX_SOURCES}
                              ${DRIVERS_CXX_SOURCES}
                              ${CORE_CXX_SOURCES})

add_library(${PROJECT_NAME} ${C_SOURCES} ${CXX_SOURCES})

Lines 7-30

When ever you see the Set function being used, it is essentially creating a variable with the name being the first parameter and setting it to anything that comes after. Think of it like a macro in C, whenever you see the variable name, just replace it exactly with everything it is set to.

Here we are using the include_directories function and giving it a bunch of file paths to “include” folders throughout LaminarOS. This function essentially sets the scope of what the compiler can see and has access to.a

For example, if you have a file named foo.h and you #include foo.h somewhere where you want to use it, if you do not add the file path to the folder that contains foo.h you will get an error message like the following: foo.h: No such file or directory.

Lines 32-55

Here we are just setting a bunch of variables to the file paths of all the source files (.c) we want to compile. From lines 32-43, we are setting variables for the STM32CubeIDE generated files. From lines 45-55 we are setting variables for all the sources files of LOS Core, Drivers, and Interface.

In these file paths, you will notice the * symbol being used. This is known as a “wildcard”. A good explanation is found here. Basically it saves us from writing a bunch of very similar file paths and allows us to keep our file paths generic enough so that we don’t have to modify the CMake file every time we add a file.

Lines 57-67

Here we are using the file function which will, using the GLOB_RECURSE flag, recursively looking for files that match the format of the given file paths and set it to the variable name provided. This is when we actually find the source files.

Line 69

Finally, we use the add_library function to compile all the sources files into a library.

Board Package Specific Build Information

You should notice that some of the variables used within the CMakeLists.txt are not actually defined anywhere in that file. For example, FOLDER_NAME, FAMILY_NAME, etc. This is because these variables, along with many other build option variables are located in what is called a “toolchain file”.

These files are located within boardfiles/<boardpackage> and are named <boardpackage.cmake>. For example, within the nucleol552zeq board package, there lies a nucleol552zeq.cmake file. This is how one CMakeLists.txt file can build for multiple different board packages.

When To Add to the CMakeLists.txt

The main scenario where you will have to modify the CMakeLists.txt file is when you add a new folder for a generic driver. This should be put underneath the comment on line 13.

Other than this, unless the folder structure of LaminarOS changes, you probably don’t have to make any modifications. That being said, if you ever get some weird errors about files/folders not being found, the issue is probably in the CMakeLists.txt file.

Adding Support for a New Board Package

To add support for new board packages, one would simply create a new folder in the boardfile folder named <boardpackage>. Then, you need to create the <boardpackage>.cmake file. For the most part you can copy and past an existing .cmake file and change the variable names for the stuff that is different.

Beware, for some of the compiler options, it may be different depending on what processor is used.