LaminarOS Repo Structure
Overview
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: https://uwarg-docs.atlassian.net/wiki/spaces/ZP/pages/565215233
Folder Structure
The folder structure of LaminarOS is meant to be generic due to allow continuous expansion. The main components of LaminarOS consist of the following (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:
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:
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.