Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
173bf6d
sys_can: configurable FDCAN pins, bounded init wait, drain RX FIFO
tridge Jun 27, 2026
16661d3
bootloader: run DroneCAN concurrently with bit-banged serial
tridge Jun 27, 2026
983b1f8
bootloader: v3 deviceInfo with devinfo magic address
tridge Jun 27, 2026
6b78cab
build: data-driven custom board targets from Inc/targets.h
tridge Jun 27, 2026
f2c0d7c
bootloader: consume per-board CAN pins, RGB LED and RAM limit
tridge Jun 27, 2026
7fe25af
bootloader: clear bl_serial_active ~10s after last good 4-way command
tridge May 27, 2026
bbfcae9
targets: add SEQURE_G431_CAN, TBS_12S/16S_L431_CAN, TBS_12S_F415_CAN,…
tridge Jun 27, 2026
aded5ad
bootloader: drive CAN_TERM_PIN from EEPROM byte 183 on DroneCAN start
tridge Jun 27, 2026
0057fed
bootloader: honour USE_HSE / USE_LSE / USE_MSI on L431
tridge Jun 27, 2026
670cfd0
targets: add VIMDRONES_L431_CAN, VIMDRONES_NANO_L431_CAN, VIMDRONES_S…
tridge Jun 27, 2026
6bace55
bl_update: disable interrupts for the flash_bootloader() sequence
tridge Jun 27, 2026
de01d0b
bootloader: switch DroneCAN time tracking from uint64_t to uint32_t
tridge Jun 27, 2026
7b50501
bootloader: gate can_print() behind DRONECAN_DEBUG
tridge Jun 27, 2026
8d22fca
sys_can_at32: initialise filter_mode in can_filter_init_struct
tridge Jun 27, 2026
57bf83b
build: enable -flto and -fdata-sections in the bootloader build
tridge Jun 27, 2026
78b1a3b
bootloader: accumulate uptime from 1Hz ticks to survive micros32 wrap
tridge Jun 27, 2026
334060f
bootloader: drain CAN TX queue from interrupt, enable bxCAN TXFP
tridge Jun 27, 2026
bb0f264
bootloader: fall back to non-CAN boot when no CAN cable present
tridge Jun 27, 2026
fe0311e
bootloader: init CAN early so the no-CAN fallback window sees the bus…
tridge Jun 26, 2026
f1ed9ef
bootloader: implement DroneCAN parameter GetSet for CAN_NODE / ESC_INDEX
tridge Jun 27, 2026
fff7693
bootloader: expose full DroneCAN parameter list (T_UINT8/16/BOOL/STRING)
tridge Jun 27, 2026
d986bd3
feat: hold gate-driver ENABLE/nSLEEP low in bootloader
AlexKlimaj Aug 3, 2026
29b746c
bootloader: restore DShot/dual-protocol merge resolutions after rebase
AlexKlimaj Aug 4, 2026
0a764a2
fix(F051): remove stray #endif/} after bl_gate_driver_off
AlexKlimaj Aug 4, 2026
060c417
feat(ARK_G431_CAN): report UAVCAN hardware 0.71 (board_id 71)
AlexKlimaj Aug 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions Inc/targets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
per-board bootloader configuration.

Each board is a "#ifdef <BOARDNAME> ... #endif" block, named and formatted as
a subset of the matching board block in the main AM32 firmware repo
(../AM32/Inc/targets.h). The build system (make/parse_targets.py) scans this
file and creates one bootloader target per board:

AM32_<MCU>_BOOTLOADER_<TARGET_TAG>_CAN

e.g. the ARK_G431_CAN block below generates AM32_G431_BOOTLOADER_ARKG4_CAN.
Building that target passes -D<BOARDNAME> (here -DARK_G431_CAN), which
activates this board's block. targets.h is force-included into every
translation unit, so with no board define every block expands to nothing and
generic builds are unaffected.

Required fields per board:
FILE_NAME - quoted board name; the MCU is the token matching a known MCU
family (G431, L431, ...) and a trailing _CAN marks a CAN build
TARGET_TAG - short tag placed in the build-target name (the "pin" slot)
USE_P<port><n> - the bit-banged comms pin (e.g. USE_PB4)

Optional fields (see the main firmware repo for the full set):
CAN_RX_PORT/CAN_RX_PIN, CAN_TX_PORT/CAN_TX_PIN - FDCAN1 pins (default PA11/PA12)
CAN_TERM_PIN / CAN_TERM_POLARITY - CAN termination pin, driven
from EEPROM byte 183
(eepromBuffer.can.term_enable)
when the bootloader starts
DroneCAN. Encode the pin with
GPIO_PORT_PIN(portnum, pinnum)
where portnum is 0=A,1=B,2=C.
USE_RGB_LED + RED_PORT/RED_PIN, GREEN_PORT/GREEN_PIN, BLUE_PORT/BLUE_PIN
GATE_DRIVER_OFF_PORT / GATE_DRIVER_OFF_PIN - smart gate-driver run pin
(DRV8350 ENABLE, DRV8328
nSLEEP). Driven low for the
whole bootloader so the
driver stays in sleep.
USE_HSE / HSE_VALUE / USE_HSE_BYPASS - external high-speed
oscillator (see the
per-MCU Mcu/<mcu>/Inc/
blutil.h bl_clock_config()
for which MCUs honour it)
USE_LSE / USE_LSE_BYPASS - external low-speed
32.768 kHz crystal (L431)
USE_MSI - free-running MSI clock
(L431, no LSE/HSE)
RAM_LIMIT_KB - max app stack-pointer RAM (default 64)

BOARD_FLASH_SIZE=128, DRONECAN_SUPPORT=1 and AM32_MCU come automatically from
the generated _CAN build, so they need not be repeated here.

To disable a board without deleting it, comment out its FILE_NAME line with //
or add DISABLE_BUILD to it (matching the main firmware repo convention).
*/

/*
encode a (port, pin) pair as a single 16-bit value, used by CAN_TERM_PIN.
Matches the same-named macro in the main firmware's Inc/targets.h so per-board
blocks here can be byte-identical to their main-firmware counterparts.
*/
#define GPIO_PORT_PIN(portnum, pinnum) ((portnum)<<8|(pinnum))

#ifdef ARK_G431_CAN
#define FILE_NAME "ARK_G431_CAN" // parser: MCU=G431, CAN build
#define TARGET_TAG ARKG4 // -> AM32_G431_BOOTLOADER_ARKG4_CAN
#define USE_PB4 // bit-banged comms pin
/* Must match ARK32 Inc/targets.h. PX4 board_id = (major << 8) | minor = 71. */
#define DRONECAN_HW_VERSION_MAJOR 0
#define DRONECAN_HW_VERSION_MINOR 71

// FDCAN1 pins: RX PA11, TX PB9 (AF9)
#define CAN_RX_PORT GPIOA
#define CAN_RX_PIN LL_GPIO_PIN_11
#define CAN_TX_PORT GPIOB
#define CAN_TX_PIN LL_GPIO_PIN_9

// RGB LED on PC6 (red), PC7 (green), PC8 (blue), active low
#define USE_RGB_LED
#define RED_PORT GPIOC
#define RED_PIN LL_GPIO_PIN_6
#define GREEN_PORT GPIOC
#define GREEN_PIN LL_GPIO_PIN_7
#define BLUE_PORT GPIOC
#define BLUE_PIN LL_GPIO_PIN_8

// DRV8350H ENABLE (PC9): hold low so the gate driver stays in sleep in BL
#define GATE_DRIVER_OFF_PORT GPIOC
#define GATE_DRIVER_OFF_PIN LL_GPIO_PIN_9

// CAN termination pin on PC12, active high
#define CAN_TERM_PIN GPIO_PORT_PIN(2, 12) // PC12
#define CAN_TERM_POLARITY 1

// 8MHz external oscillator (bypass mode)
#define USE_HSE
#undef HSE_VALUE
#define HSE_VALUE 8000000
#define USE_HSE_BYPASS 0

// G491 has 112KB RAM; allow app stack pointer above the 64KB default
#define RAM_LIMIT_KB 112
#endif

#ifdef SEQURE_G431_CAN
#define FILE_NAME "SEQURE_G431_CAN" // parser: MCU=G431, CAN build
#define TARGET_TAG SEQUREG4 // -> AM32_G431_BOOTLOADER_SEQUREG4_CAN
#define USE_PA2 // from HARDWARE_GROUP_G4_D (INPUT_PIN PA2)

// FDCAN1 pins default to PA11/PA12; SEQURE uses the defaults, so no CAN_*
// overrides are needed here.

// CAN termination pin on PB7, active high. PB7 -> AN of the ISOM8610 (U2), a
// normally-open opto-emulator switch across the 120R between CAN_H/CAN_L: PB7
// high drives forward current AN->CAT, closing the switch (termination on).
#define CAN_TERM_PIN GPIO_PORT_PIN(1, 7) // PB7
#define CAN_TERM_POLARITY 1

// 8MHz external HSE crystal (USE_HSE_BYPASS 0 = crystal, not oscillator)
#define USE_HSE
#undef HSE_VALUE
#define HSE_VALUE 8000000
#define USE_HSE_BYPASS 0
#endif

#ifdef TBS_12S_L431_CAN
#define FILE_NAME "TBS_12S_L431_CAN" // parser: MCU=L431, CAN build
#define TARGET_TAG TBS12SL4 // -> AM32_L431_BOOTLOADER_TBS12SL4_CAN
#define USE_PA2 // from HARDWARE_GROUP_L4_C (INPUT_PIN PA2)

// bxCAN pins are fixed at PA11/PA12 in the L431 bootloader CAN driver.

// CAN termination pin on PB3, active high (see commit applying CAN_TERM)
#define CAN_TERM_PIN GPIO_PORT_PIN(1, 3) // PB3
#define CAN_TERM_POLARITY 1

// LSE-disciplined MSI clock (32.768 kHz crystal, not bypass)
#define USE_LSE
#define USE_LSE_BYPASS 0
#endif

#ifdef TBS_16S_L431_CAN
#define FILE_NAME "TBS_16S_L431_CAN" // parser: MCU=L431, CAN build
#define TARGET_TAG TBS16SL4 // -> AM32_L431_BOOTLOADER_TBS16SL4_CAN
#define USE_PA2 // from HARDWARE_GROUP_L4_A (INPUT_PIN PA2)

// bxCAN pins are fixed at PA11/PA12 in the L431 bootloader CAN driver.

// CAN termination pin on PB3, active high
#define CAN_TERM_PIN GPIO_PORT_PIN(1, 3) // PB3
#define CAN_TERM_POLARITY 1

// LSE-disciplined MSI clock (external 32.768 kHz clock, bypass mode)
#define USE_LSE
#define USE_LSE_BYPASS 1
#endif

#ifdef TBS_12S_F415_CAN
#define FILE_NAME "TBS_12S_F415_CAN" // parser: MCU=F415, CAN build
#define TARGET_TAG TBS12SF4 // -> AM32_F415_BOOTLOADER_TBS12SF4_CAN
#define USE_PB4 // from HARDWARE_GROUP_AT_D (INPUT_PIN PB4)

// AT32 CAN pins are fixed at PA11/PA12 (CAN1_GMUX_0000) in the F415 driver.

// CAN termination pin on PB3, active high
#define CAN_TERM_PIN GPIO_PORT_PIN(1, 3) // PB3
#define CAN_TERM_POLARITY 1
#endif

#ifdef TBS_F415_CAN
#define FILE_NAME "TBS_F415_CAN" // parser: MCU=F415, CAN build
#define TARGET_TAG TBSF4 // -> AM32_F415_BOOTLOADER_TBSF4_CAN
#define USE_PA2 // from HARDWARE_GROUP_AT_H (INPUT_PIN PA2)

// AT32 CAN pins are fixed at PA11/PA12 (CAN1_GMUX_0000) in the F415 driver.
// Main firmware block has no CAN_TERM_PIN, so we don't set one here either.
#endif

#ifdef VIMDRONES_L431_CAN
#define FILE_NAME "VIMDRONES_L431_CAN" // parser: MCU=L431, CAN build
#define TARGET_TAG VIML4 // -> AM32_L431_BOOTLOADER_VIML4_CAN
#define USE_PA2 // from HARDWARE_GROUP_L4_B (INPUT_PIN PA2)

// bxCAN pins are fixed at PA11/PA12 in the L431 bootloader CAN driver.
// Main firmware block has no oscillator or CAN_TERM_PIN settings; the
// bootloader defaults to HSI16 (the main firmware does too via the
// fall-through path in Mcu/l431/Src/peripherals.c).
#endif

#ifdef VIMDRONES_NANO_L431_CAN
#define FILE_NAME "VIMDRONES_NANO_L431_CAN" // parser: MCU=L431, CAN build
#define TARGET_TAG VIMNANO // -> AM32_L431_BOOTLOADER_VIMNANO_CAN
#define USE_PA2 // from HARDWARE_GROUP_L4_B (INPUT_PIN PA2)

// bxCAN pins are fixed at PA11/PA12 in the L431 bootloader CAN driver.

// 24 MHz external HSE clock (main firmware doesn't set USE_HSE_BYPASS, so
// the bootloader default applies = bypass mode = external clock source).
#define USE_HSE
#undef HSE_VALUE
#define HSE_VALUE 24000000
#endif

#ifdef VIMDRONES_S50_L431_CAN
#define FILE_NAME "VIMDRONES_S50_L431_CAN" // parser: MCU=L431, CAN build
#define TARGET_TAG VIMS50 // -> AM32_L431_BOOTLOADER_VIMS50_CAN
#define USE_PA2 // from HARDWARE_GROUP_L4_B (INPUT_PIN PA2)

// bxCAN pins are fixed at PA11/PA12 in the L431 bootloader CAN driver.

// 24 MHz external HSE clock (same as VIMDRONES_NANO, see above).
#define USE_HSE
#undef HSE_VALUE
#define HSE_VALUE 24000000
#endif
24 changes: 20 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ endef

MCU_TYPES := $(sort $(foreach mcu,$(MCU_BUILDS),$(call base_mcu,$(mcu))))

# custom per-board targets defined in Inc/targets.h, parsed by make/parse_targets.py.
# BOARD_TARGETS entries are "BUILD|TAG|BOARDDEFINE", e.g. G431_CAN|ARKG4|ARK_G431_CAN
BOARD_TARGETS := $(shell python3 make/parse_targets.py builds)
BOARD_MCUS := $(shell python3 make/parse_targets.py mcus)
# make sure each custom board's per-MCU makefile gets included below
MCU_TYPES := $(sort $(MCU_TYPES) $(BOARD_MCUS))

# Function to include makefile for each MCU type
define INCLUDE_MCU_MAKEFILES
$(foreach MCU_TYPE,$(MCU_TYPES),$(eval include $(call lc,$(MCU_TYPE))makefile.mk))
Expand All @@ -47,8 +54,11 @@ LIBS := -lnosys

# Compiler options
CFLAGS_BASE := -fsingle-precision-constant -fomit-frame-pointer -ffast-math --specs=nosys.specs
CFLAGS_BASE += -I$(MAIN_INC_DIR) -g3 -Os -ffunction-sections -funsigned-char
CFLAGS_BASE += -I$(MAIN_INC_DIR) -g3 -Os -ffunction-sections -fdata-sections -funsigned-char
CFLAGS_BASE += -flto
CFLAGS_BASE += -Wall -Wextra -Wundef -Werror -Wno-unused-parameter
# force-include per-board config; inert for builds with no board define
CFLAGS_BASE += -include $(MAIN_INC_DIR)/targets.h

CFLAGS_COMMON := $(CFLAGS_BASE)

Expand All @@ -62,7 +72,10 @@ CFLAGS_COMMON := $(CFLAGS_BASE)
CFLAGS_ARM_ONLY := -fno-optimize-crc

# Linker options
LDFLAGS_COMMON := -specs=nano.specs $(LIBS) -Wl,--gc-sections -Wl,--print-memory-usage
# -fdata-sections lets --gc-sections drop unused globals (companion to -ffunction-sections).
# -flto enables link-time optimisation (cross-TU inlining + DCE); pass it on both
# the compile and link command lines so the linker gets the IR not raw object code.
LDFLAGS_COMMON := -specs=nano.specs $(LIBS) -Wl,--gc-sections -Wl,--print-memory-usage -flto

# configure some directories that are relative to wherever ROOT_DIR is located
OBJ := obj
Expand Down Expand Up @@ -137,6 +150,7 @@ BLU_BUILDS :=
define CREATE_BOOTLOADER_TARGET
$(eval BUILD := $(1))
$(eval PIN := $(2))
$(eval BOARD := $(3))
$(eval MCU := $$(call base_mcu,$$(1)))
$(eval EXTRA_CFLAGS := $(call get_cflags,$(1)))
$(eval ELF_FILE := $(BIN_DIR)/$(call BOOTLOADER_BASENAME_VER,$(BUILD),$(PIN)).elf)
Expand Down Expand Up @@ -170,7 +184,7 @@ $(eval SRC_DRONECAN := $(if $(call has_can_suffix,$(1)),$(SRC_DRONECAN_$(MCU))))
-include $(DEP_FILE)
-include $(BLU_DEP_FILE)

$(ELF_FILE): CFLAGS_BL := $$(MCU_$(MCU)) $$(CFLAGS_$(MCU)) $$(CFLAGS_BASE) -DBOOTLOADER -DUSE_$(PIN) $(EXTRA_CFLAGS) -DAM32_MCU=\"$(MCU)\" $$(CFLAGS_DRONECAN) $(xCFLAGS_ARM) $(xCFLAGS_4K)
$(ELF_FILE): CFLAGS_BL := $$(MCU_$(MCU)) $$(CFLAGS_$(MCU)) $$(CFLAGS_BASE) -DBOOTLOADER -DUSE_$(PIN) $(if $(BOARD),-D$(BOARD)) $(EXTRA_CFLAGS) -DAM32_MCU=\"$(MCU)\" $$(CFLAGS_DRONECAN) $(xCFLAGS_ARM) $(xCFLAGS_4K)
$(ELF_FILE): LDFLAGS_BL := $$(LDFLAGS_COMMON) $$(LDFLAGS_$(MCU)) -T$(xLDSCRIPT)
$(ELF_FILE): $$(SRC_$(MCU)_BL) $$(SRC_BL) $$(SRC_DRONECAN)
$$(QUIET)echo building bootloader for $(BUILD) with pin $(PIN)
Expand All @@ -184,7 +198,7 @@ $(ELF_FILE): $$(SRC_$(MCU)_BL) $$(SRC_BL) $$(SRC_DRONECAN)
$(H_FILE): $(BIN_FILE)
$$(QUIET)python3 bl_update/make_binheader.py $(BIN_FILE) $(H_FILE)

$(BLU_ELF_FILE): CFLAGS_BLU := -DAM32_MCU=\"$(MCU)\" $$(MCU_$(MCU)) $$(CFLAGS_$(MCU)) $$(CFLAGS_BASE) -DBOOTLOADER -DUSE_$(PIN) $(EXTRA_CFLAGS) -Wno-unused-variable -Wno-unused-function $(xCFLAGS_ARM)
$(BLU_ELF_FILE): CFLAGS_BLU := -DAM32_MCU=\"$(MCU)\" $$(MCU_$(MCU)) $$(CFLAGS_$(MCU)) $$(CFLAGS_BASE) -DBOOTLOADER -DUSE_$(PIN) $(if $(BOARD),-D$(BOARD)) $(EXTRA_CFLAGS) -Wno-unused-variable -Wno-unused-function $(xCFLAGS_ARM)
$(BLU_ELF_FILE): LDFLAGS_BLU := $$(LDFLAGS_COMMON) $$(LDFLAGS_$(MCU)) -T$(xBLU_LDSCRIPT)
$(BLU_ELF_FILE): $$(SRC_$(MCU)_BL) $$(SRC_BLU) $(H_FILE)
$$(QUIET)echo building bootloader updater for $(BUILD) with pin $(PIN)
Expand Down Expand Up @@ -279,6 +293,8 @@ endef

# ARK 4IN1 F051: PB4 signal (same as generic PB4), PA15 = DRV8328 nSLEEP held low
$(eval $(call CREATE_BOARD_BOOTLOADER_TARGET,F051,ARK4IN1,PB4,-DGATE_DRIVER_OFF_PORT=GPIOA -DGATE_DRIVER_OFF_PIN_NUM=15))
# custom per-board targets from Inc/targets.h (BUILD|TAG|BOARDDEFINE)
$(foreach B,$(BOARD_TARGETS),$(eval $(call CREATE_BOOTLOADER_TARGET,$(word 1,$(subst |, ,$(B))),$(word 2,$(subst |, ,$(B))),$(word 3,$(subst |, ,$(B))))))

bootloaders: $(ALL_BUILDS)

Expand Down
12 changes: 8 additions & 4 deletions Mcu/f051/Inc/blutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,19 @@ static inline void bl_gpio_init(void)

/*
Hold the smart gate-driver run pin low for the whole bootloader stay
(e.g. DRV8328 nSLEEP on ARK 4IN1). Optional board CFLAGS:
-DGATE_DRIVER_OFF_PORT=GPIOA -DGATE_DRIVER_OFF_PIN_NUM=15
(see Makefile ARK4IN1 product).
(e.g. DRV8328 nSLEEP on ARK 4IN1). Supports either:
-DGATE_DRIVER_OFF_PORT=GPIOA -DGATE_DRIVER_OFF_PIN_NUM=15 (Makefile product)
or GATE_DRIVER_OFF_PORT / GATE_DRIVER_OFF_PIN (targets.h LL pin mask)
*/
static inline void bl_gate_driver_off(void)
{
#if defined(GATE_DRIVER_OFF_PORT) && defined(GATE_DRIVER_OFF_PIN_NUM)
#if defined(GATE_DRIVER_OFF_PORT) && (defined(GATE_DRIVER_OFF_PIN) || defined(GATE_DRIVER_OFF_PIN_NUM))
LL_GPIO_InitTypeDef s = {0};
#if defined(GATE_DRIVER_OFF_PIN)
const uint32_t pin = GATE_DRIVER_OFF_PIN;
#else
const uint32_t pin = (1U << (GATE_DRIVER_OFF_PIN_NUM));
#endif
if (GATE_DRIVER_OFF_PORT == GPIOA) {
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
} else if (GATE_DRIVER_OFF_PORT == GPIOB) {
Expand Down
Loading
Loading