Building Custom Firmware
Rationale​
Prebuilt targets may not include the features you wish to use. If a target already exists, it is relatively simple to build your own custom firmware.
This guide provides a high level overview. It is not a detailed development guide.
Prerequisite​
You need a working development environment. There is developer / build environment documentation for the major platforms (Linux, MacOS, Windows).
Target Specific Files​
Overview​
For basic configuration changes, the files are found under src/main/targets/NAME. At the top level this includes a separate directory for each target.
This article considers a prototype flight controller QUARKVISION that was never put into production.
$ cd inav/src/main/target
$ ls QUARKVISION
CMakeFiles CMakeLists.txt README.md target.h
cmake_install.cmake config.c target.c
The CMakefles directory and cmake_install.cmake are generated by the build system; the other files are:
CMakeLists.txt: Mandatory, defines the target name and variantsREADME.md: Optional, information about the targettarget.h: Mandatory, defines capabilities and definitions (e.g. sensors, pin definitions)target.c: Mandatory, defines timers and usage (e.g. for motors and servos)config.c: Mandatory, defines configuration defaults (RX type etc).
CMakeLists.txt​
The QUARKVISION example contains a single line:
target_stm32f405xg(QUARKVISION HSE_MHZ 16 SKIP_RELEASES)
Here is the definition for:
- The processor class
stm32f405xg - The target name
QUARKVISION - Additional parameters
HSE_MHZ 16(an optional parameter defining a non-default high-speed external (HSE) oscillator clock required by this board) andSKIP_RELEASESas this is not an official target.
If we had other variants, for example a V2 variant with different sensors, we could add another line, for example:
target_stm32f405xg(QUARKVISION_V2 HSE_MHZ 16 SKIP_RELEASES)
We can then reference the QUARKVISION_V2 in target.c or target.h to handle the different capabilities of each variation.
See the developer documentation for more information, including a more detailed CMakeLists.txt and a list of processor options.
target.h​
target.h contains hardware definitions, a fragment is below:
#pragma once
#define TARGET_BOARD_IDENTIFIER "QRKV"
#define USBD_PRODUCT_STRING "QuarkVision"
...
#define BEEPER PC15
#define BEEPER_INVERTED
#define USE_UART_INVERTER
#define INVERTER_PIN_UART2 PB2 // PB2 used as inverter select GPIO
#define INVERTER_PIN_UART2_RX PB2 // PB2 used as inverter select GPIO
#define MPU6000_CS_PIN PC1
#define MPU6000_SPI_BUS BUS_SPI2
#define USE_IMU_MPU6000
#define IMU_MPU6000_ALIGN CW270_DEG
// MPU6000 interrupts
#define USE_EXTI
#define GYRO_INT_EXTI PC0
#define USE_MPU_DATA_READY_SIGNAL
//*************** MAG *****************************
#define USE_MAG
#define MAG_I2C_BUS BUS_I2C3
#define USE_MAG_HMC5883
#define USE_MAG_MAG3110
#define USE_MAG_QMC5883
#define USE_MAG_AK8963
#define USE_MAG_AK8975
...
Of note:
TARGET_BOARD_IDENTIFIERthis should be unique in the first 4 bytes. If the mythicalQUARKVISION_V2existed, it would need a separate ID. For example:
#if defined(QUARKVISION_V2)
# define TARGET_BOARD_IDENTIFIER "QVV2"
#else
# define TARGET_BOARD_IDENTIFIER "QRKV"
#endif
This pattern is required for each variation and the features affected by the variation.
The file fragment then defines hardware options:
- Pins, Busses
- Sensors, for example the
#define USE_MAGstanza which defines theMAG_I2C_BUSI2C bus for the compass, and the different compass types supported on this board.
There will similar stanzas for all the available sensor components.
target.c​
target.c defines the timer and channel usage. The association between timers and channels is provided by the vendor MCU documentation. This defines an array of timerHardware_t, in turn defined by the DEF_TIM (define timer) macro.
...
timerHardware_t timerHardware[] = {
DEF_TIM(TIM1, CH3, PA10, TIM_USE_PPM, 0, 0), // S1_IN_PPM A01
DEF_TIM(TIM8, CH2, PC7, TIM_USE_ANY, 0, 0), // SSERIAL1 RX c07
DEF_TIM(TIM8, CH1, PC6, TIM_USE_ANY, 0, 0), // SSERIAL1 TX
DEF_TIM(TIM2, CH1, PA15, 0, 0, 0), // LED A15
DEF_TIM(TIM3, CH3, PB0, TIM_USE_OUTPUT_AUTO, 0, 0), // S1_OUT
DEF_TIM(TIM3, CH4, PB1, TIM_USE_OUTPUT_AUTO, 0, 0), // S2_OUT
DEF_TIM(TIM12, CH1, PB14, TIM_USE_OUTPUT_AUTO, 0, 0), // S3_OUT
DEF_TIM(TIM12, CH2, PB15, TIM_USE_OUTPUT_AUTO, 0, 0), // S4_OUT
DEF_TIM(TIM11, CH1, PB9, TIM_USE_OUTPUT_AUTO, 0, 0), // S5_OUT
DEF_TIM(TIM10, CH1, PB8, TIM_USE_OUTPUT_AUTO, 0, 0), // S6_OUT
DEF_TIM(TIM3, CH2, PB5, TIM_USE_OUTPUT_AUTO, 0, 0), // S7_OUT
DEF_TIM(TIM3, CH1, PB4, TIM_USE_OUTPUT_AUTO, 0, 0), // S8_OUT
DEF_TIM(TIM8, CH3, PC8, TIM_USE_OUTPUT_AUTO, 0, 0), // S9_OUT
DEF_TIM(TIM2, CH2, PB3, TIM_USE_OUTPUT_AUTO, 0, 0), // S10_OUT
};
...
The parameters are:
TIMn: The timerCHn: The channelPxy: The hardware (MCU) pin- The usage function(s) available in this pin. Note that each timer is assigned a rate defined by function, so it is inadvisable to have both
MOTORandSERVOdefinition on the same timer.TIM_USE_OUTPUT_AUTOwill let INAV assign the output to eitherMOTORorSERVOautomatically. - The final two parameters (
flags,dmavarare hardware specific / required for DMA (e.g. DSHOT), which is turn is defined by#define USE_DSHOTintarget.h. See vendor's technical definitions perhaps compared to comparable targets. The example target here does not defineUSE_DSHOTand the values are 0. These parameters provide a DMA descriptor table compatible with Betaflight.
Adding a new source file​
If a new source file is added outside of the target/NAME directory, it must be added to the top level src/main/CMakeLists.txt.
Further reading​
- Read the fine source, base your target / changes on a supported target with similar characteristics
- Read the developer / build environment documentation
Other recommendations​
Use a separate branch​
$ git checkout -b my_super_special_branch
This will isolate your work from the base repo and facilitate making a pull request if you decide to contribute your changes back to the project.
Building​
Build the target.
$ make -j $(nproc) QUARKVISION
## or (using ninja as the build manager)
$ ninja QUARKVISION
...
[365/366] Linking C executable bin/QUARKVISION.elf
Memory region Used Size Region Size %age Used
FLASH: 519160 B 896 KB 56.58%
FLASH_CONFIG: 0 GB 128 KB 0.00%
RAM: 76476 B 128 KB 58.35%
CCM: 13852 B 64 KB 21.14%
BACKUP_SRAM: 0 GB 4 KB 0.00%
MEMORY_B1: 0 GB 0 GB
[366/366] cd /home/jrh/Projects/fc/ina.../inav/build/inav_6.0.0_QUARKVISION.hex
Fix any errors / warnings​
If you changes introduce compiler warnings, please fix them. Submissions (pull requests) with compiler errors / warnings will not be accepted. If your changes overflow the flash size, consider removing unwanted features in target.h. Your target.h can always #undef generic features from src/main/target/common.h (e.g. unwanted RX or telemetry options).
Commit your changes​
You can now commit the changes to your branch, e.g. git commit -a -m "my descriptive commit message" ; otherwise if one wanted to update to the upstream the source tree (e.g.)
git pull
git will complain that there are uncommitted changes and won't perform the update.
- Commit to your private branch as above ; or
$ git reset --hardbefore pulling ; or- Stash away the original files and restore them after pulling.
The developer documentation has more information on synchronising a custom branch with upstream Github.
Other tools and resources​
Migrate Betaflight Targets​
There is a script in the repo that can help automate conversion of Betaflight targets to INAV. The developer, @mosca, will be grateful for any reports of success (or failure).
Paweł Spychalski has also made YouTube videos on the subject.