diff --git a/make/mcu/STM32H7.mk b/make/mcu/STM32H7.mk
index e927bd7fb..8d165ba75 100644
--- a/make/mcu/STM32H7.mk
+++ b/make/mcu/STM32H7.mk
@@ -163,6 +163,7 @@ DEVICE_FLAGS += -DSTM32H743xx
DEFAULT_LD_SCRIPT = $(LINKER_DIR)/stm32_flash_h743_2m.ld
STARTUP_SRC = startup_stm32h743xx.s
TARGET_FLASH := 2048
+DEVICE_FLAGS += -DMAX_MPU_REGIONS=16
else ifeq ($(TARGET),$(filter $(TARGET),$(H750xB_TARGETS)))
DEVICE_FLAGS += -DSTM32H750xx
DEFAULT_LD_SCRIPT = $(LINKER_DIR)/stm32_flash_h750_128k.ld
@@ -181,6 +182,13 @@ TARGET_FLASH := FIRMWARE_SIZE
DEFAULT_LD_SCRIPT = $(LINKER_DIR)/stm32_flash_h750_exst.ld
endif
+ifeq ($(EXST),yes)
+# Upper 8 regions are reserved for a boot loader in EXST environment
+DEVICE_FLAGS += -DMAX_MPU_REGIONS=8
+else
+DEVICE_FLAGS += -DMAX_MPU_REGIONS=16
+endif
+
ifneq ($(DEBUG),GDB)
OPTIMISE_DEFAULT := -Os
OPTIMISE_SPEED := -Os
@@ -230,6 +238,8 @@ MCU_COMMON_SRC = \
drivers/persistent.c \
drivers/transponder_ir_io_hal.c \
drivers/audio_stm32h7xx.c \
+ drivers/memprot_hal.c \
+ drivers/memprot_stm32h7xx.c \
#drivers/accgyro/accgyro_mpu.c \
MCU_EXCLUDES = \
diff --git a/src/link/stm32_flash_h743_2m.ld b/src/link/stm32_flash_h743_2m.ld
index 392a0c02c..6bdb84d61 100644
--- a/src/link/stm32_flash_h743_2m.ld
+++ b/src/link/stm32_flash_h743_2m.ld
@@ -224,13 +224,40 @@ SECTIONS
.DMA_RAM (NOLOAD) :
{
+ . = ALIGN(32);
+ PROVIDE(dmaram_start = .);
_sdmaram = .;
_dmaram_start__ = _sdmaram;
KEEP(*(.DMA_RAM))
+ PROVIDE(dmaram_end = .);
_edmaram = .;
_dmaram_end__ = _edmaram;
} >D2_RAM
+ .DMA_RW_D2 (NOLOAD) :
+ {
+ . = ALIGN(32);
+ PROVIDE(dmarw_start = .);
+ _sdmarw = .;
+ _dmarw_start__ = _sdmarw;
+ KEEP(*(.DMA_RW))
+ PROVIDE(dmarw_end = .);
+ _edmarw = .;
+ _dmarw_end__ = _edmarw;
+ } >D2_RAM
+
+ .DMA_RW_AXI (NOLOAD) :
+ {
+ . = ALIGN(32);
+ PROVIDE(dmarwaxi_start = .);
+ _sdmarwaxi = .;
+ _dmarwaxi_start__ = _sdmarwaxi;
+ KEEP(*(.DMA_RW_AXI))
+ PROVIDE(dmarwaxi_end = .);
+ _edmarwaxi = .;
+ _dmarwaxi_end__ = _edmarwaxi;
+ } >RAM
+
.persistent_data (NOLOAD) :
{
__persistent_data_start__ = .;
diff --git a/src/link/stm32_flash_h750_128k.ld b/src/link/stm32_flash_h750_128k.ld
index 778afb4f4..f23057c10 100644
--- a/src/link/stm32_flash_h750_128k.ld
+++ b/src/link/stm32_flash_h750_128k.ld
@@ -225,6 +225,30 @@ SECTIONS
_edmaram = .;
_dmaram_end__ = _edmaram;
} >D2_RAM
+
+ .DMA_RW_D2 (NOLOAD) :
+ {
+ . = ALIGN(32);
+ PROVIDE(dmarw_start = .);
+ _sdmarw = .;
+ _dmarw_start__ = _sdmarw;
+ KEEP(*(.DMA_RW))
+ PROVIDE(dmarw_end = .);
+ _edmarw = .;
+ _dmarw_end__ = _edmarw;
+ } >D2_RAM
+
+ .DMA_RW_AXI (NOLOAD) :
+ {
+ . = ALIGN(32);
+ PROVIDE(dmarwaxi_start = .);
+ _sdmarwaxi = .;
+ _dmarwaxi_start__ = _sdmarwaxi;
+ KEEP(*(.DMA_RW_AXI))
+ PROVIDE(dmarwaxi_end = .);
+ _edmarwaxi = .;
+ _dmarwaxi_end__ = _edmarwaxi;
+ } >RAM
.persistent_data (NOLOAD) :
{
diff --git a/src/link/stm32_flash_h750_exst.ld b/src/link/stm32_flash_h750_exst.ld
index a5c1c9296..c42fdf669 100644
--- a/src/link/stm32_flash_h750_exst.ld
+++ b/src/link/stm32_flash_h750_exst.ld
@@ -241,13 +241,40 @@ SECTIONS
.DMA_RAM (NOLOAD) :
{
+ . = ALIGN(32);
+ PROVIDE(dmaram_start = .);
_sdmaram = .;
_dmaram_start__ = _sdmaram;
KEEP(*(.DMA_RAM))
+ PROVIDE(dmaram_end = .);
_edmaram = .;
_dmaram_end__ = _edmaram;
} >D2_RAM
+ .DMA_RW_D2 (NOLOAD) :
+ {
+ . = ALIGN(32);
+ PROVIDE(dmarw_start = .);
+ _sdmarw = .;
+ _dmarw_start__ = _sdmarw;
+ KEEP(*(.DMA_RW))
+ PROVIDE(dmarw_end = .);
+ _edmarw = .;
+ _dmarw_end__ = _edmarw;
+ } >D2_RAM
+
+ .DMA_RW_AXI (NOLOAD) :
+ {
+ . = ALIGN(32);
+ PROVIDE(dmarwaxi_start = .);
+ _sdmarwaxi = .;
+ _dmarwaxi_start__ = _sdmarwaxi;
+ KEEP(*(.DMA_RW_AXI))
+ PROVIDE(dmarwaxi_end = .);
+ _edmarwaxi = .;
+ _dmarwaxi_end__ = _edmarwaxi;
+ } >RAM
+
.persistent_data (NOLOAD) :
{
__persistent_data_start__ = .;
diff --git a/src/main/drivers/memprot.h b/src/main/drivers/memprot.h
new file mode 100644
index 000000000..4d6b07c24
--- /dev/null
+++ b/src/main/drivers/memprot.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of Cleanflight and Betaflight.
+ *
+ * Cleanflight and Betaflight are free software. You can redistribute
+ * this software and/or modify this software under the terms of the
+ * GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Cleanflight and Betaflight are distributed in the hope that they
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software.
+ *
+ * If not, see .
+ */
+
+#pragma once
+
+typedef struct mpuRegion_s {
+ uint32_t start;
+ uint32_t end; // Zero if determined by size member (MPU_REGION_SIZE_xxx)
+ uint8_t size; // Zero if determined from linker symbols
+ uint8_t perm;
+ uint8_t exec;
+ uint8_t shareable;
+ uint8_t cacheable;
+ uint8_t bufferable;
+} mpuRegion_t;
+
+extern mpuRegion_t mpuRegions[];
+extern unsigned mpuRegionCount;
+
+void memProtReset(void);
+void memProtConfigure(mpuRegion_t *mpuRegions, unsigned regionCount);
diff --git a/src/main/drivers/memprot_hal.c b/src/main/drivers/memprot_hal.c
new file mode 100644
index 000000000..d4ea19769
--- /dev/null
+++ b/src/main/drivers/memprot_hal.c
@@ -0,0 +1,107 @@
+/*
+ * This file is part of Cleanflight and Betaflight.
+ *
+ * Cleanflight and Betaflight are free software. You can redistribute
+ * this software and/or modify this software under the terms of the
+ * GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Cleanflight and Betaflight are distributed in the hope that they
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software.
+ *
+ * If not, see .
+ */
+
+#include
+
+#include "platform.h"
+
+#include "memprot.h"
+
+static void memProtConfigError(void)
+{
+ for (;;) {}
+}
+
+void memProtConfigure(mpuRegion_t *regions, unsigned regionCount)
+{
+ MPU_Region_InitTypeDef MPU_InitStruct;
+
+ if (regionCount > MAX_MPU_REGIONS) {
+ memProtConfigError();
+ }
+
+ HAL_MPU_Disable();
+
+ // Setup common members
+ MPU_InitStruct.Enable = MPU_REGION_ENABLE;
+ MPU_InitStruct.SubRegionDisable = 0x00;
+ MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
+
+ for (unsigned number = 0; number < regionCount; number++) {
+ mpuRegion_t *region = ®ions[number];
+
+ if (region->end == 0 && region->size == 0) {
+ memProtConfigError();
+ }
+
+ MPU_InitStruct.Number = number;
+ MPU_InitStruct.BaseAddress = region->start;
+
+ if (region->size) {
+ MPU_InitStruct.Size = region->size;
+ } else {
+ // Adjust start of the region to align with cache line size.
+ uint32_t start = region->start & ~0x1F;
+ uint32_t length = region->end - start;
+
+ if (length < 32) {
+ // This will also prevent flsl from returning negative (case length == 0)
+ length = 32;
+ }
+
+ int msbpos = flsl(length) - 1;
+
+ if (length == (1U << msbpos)) {
+ msbpos += 1;
+ }
+
+ MPU_InitStruct.Size = msbpos;
+ }
+
+ // Copy per region attributes
+ MPU_InitStruct.AccessPermission = region->perm;
+ MPU_InitStruct.DisableExec = region->exec;
+ MPU_InitStruct.IsShareable = region->shareable;
+ MPU_InitStruct.IsCacheable = region->cacheable;
+ MPU_InitStruct.IsBufferable = region->bufferable;
+
+ HAL_MPU_ConfigRegion(&MPU_InitStruct);
+ }
+
+ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
+}
+
+void memProtReset(void)
+{
+ MPU_Region_InitTypeDef MPU_InitStruct;
+
+ /* Disable the MPU */
+ HAL_MPU_Disable();
+
+ // Disable existing regions
+
+ for (uint8_t region = 0; region <= MAX_MPU_REGIONS; region++) {
+ MPU_InitStruct.Enable = MPU_REGION_DISABLE;
+ MPU_InitStruct.Number = region;
+ HAL_MPU_ConfigRegion(&MPU_InitStruct);
+ }
+
+ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
+}
diff --git a/src/main/drivers/memprot_stm32h7xx.c b/src/main/drivers/memprot_stm32h7xx.c
new file mode 100644
index 000000000..8e4fe2334
--- /dev/null
+++ b/src/main/drivers/memprot_stm32h7xx.c
@@ -0,0 +1,77 @@
+/*
+ * This file is part of Cleanflight and Betaflight.
+ *
+ * Cleanflight and Betaflight are free software. You can redistribute
+ * this software and/or modify this software under the terms of the
+ * GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Cleanflight and Betaflight are distributed in the hope that they
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software.
+ *
+ * If not, see .
+ */
+
+#include "platform.h"
+
+#include "memprot.h"
+
+// Defined in linker script
+extern uint8_t dmaram_start;
+extern uint8_t dmaram_end;
+
+extern uint8_t dmarwaxi_start;
+extern uint8_t dmarwaxi_end;
+
+mpuRegion_t mpuRegions[] = {
+#ifdef USE_ITCM_RAM
+ {
+ // Mark ITCM-RAM as read-only
+ // "For Cortex®-M7, TCMs memories always behave as Non-cacheable, Non-shared normal memories, irrespectiveof the memory type attributes defined in the MPU for a memory region containing addresses held in the TCM"
+ // See AN4838
+ .start = 0x00000000,
+ .end = 0, // Size defined by "size"
+ .size = MPU_REGION_SIZE_64KB,
+ .perm = MPU_REGION_PRIV_RO_URO,
+ .exec = MPU_INSTRUCTION_ACCESS_ENABLE,
+ .shareable = MPU_ACCESS_NOT_SHAREABLE,
+ .cacheable = MPU_ACCESS_NOT_CACHEABLE,
+ .bufferable = MPU_ACCESS_BUFFERABLE,
+ },
+#endif
+ {
+ // DMA transmit buffer in D2 SRAM1
+ // Reading needs cache coherence operation
+ .start = (uint32_t)&dmaram_start,
+ .end = (uint32_t)&dmaram_end,
+ .size = 0, // Size determined by ".end"
+ .perm = MPU_REGION_FULL_ACCESS,
+ .exec = MPU_INSTRUCTION_ACCESS_ENABLE,
+ .shareable = MPU_ACCESS_SHAREABLE,
+ .cacheable = MPU_ACCESS_CACHEABLE,
+ .bufferable = MPU_ACCESS_NOT_BUFFERABLE,
+ },
+#ifdef USE_SDCARD_SDIO
+ {
+ // A region in AXI RAM accessible from SDIO internal DMA
+ .start = (uint32_t)&dmarwaxi_start,
+ .end = (uint32_t)&dmarwaxi_end,
+ .size = 0, // Size determined by ".end"
+ .perm = MPU_REGION_FULL_ACCESS,
+ .exec = MPU_INSTRUCTION_ACCESS_ENABLE,
+ .shareable = MPU_ACCESS_NOT_SHAREABLE,
+ .cacheable = MPU_ACCESS_CACHEABLE,
+ .bufferable = MPU_ACCESS_NOT_BUFFERABLE,
+ },
+#endif
+};
+
+unsigned mpuRegionCount = ARRAYLEN(mpuRegions);
+
+STATIC_ASSERT(ARRAYLEN(mpuRegions) <= MAX_MPU_REGIONS, MPU_region_count_exceeds_limit);
diff --git a/src/main/drivers/system_stm32h7xx.c b/src/main/drivers/system_stm32h7xx.c
index 4b1887119..38d12c793 100644
--- a/src/main/drivers/system_stm32h7xx.c
+++ b/src/main/drivers/system_stm32h7xx.c
@@ -176,31 +176,6 @@ bool isMPUSoftReset(void)
void systemInit(void)
{
-#ifdef USE_ITCM_RAM
- // Mark ITCM-RAM as read-only
- HAL_MPU_Disable();
-
- // "For Cortex®-M7, TCMs memories always behave as Non-cacheable, Non-shared normal memories, irrespective of the memory type attributes defined in the MPU for a memory region containing addresses held in the TCM"
- // See AN4838
-
- MPU_Region_InitTypeDef MPU_InitStruct;
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
- MPU_InitStruct.BaseAddress = 0x00000000;
- MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
- MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RO_URO;
- MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
- MPU_InitStruct.Number = MPU_REGION_NUMBER0;
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
- MPU_InitStruct.SubRegionDisable = 0x00;
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
-
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
-#endif
-
-
// Configure NVIC preempt/priority groups
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITY_GROUPING);
diff --git a/src/main/io/asyncfatfs/asyncfatfs.c b/src/main/io/asyncfatfs/asyncfatfs.c
index 6f8413f10..871ad1382 100644
--- a/src/main/io/asyncfatfs/asyncfatfs.c
+++ b/src/main/io/asyncfatfs/asyncfatfs.c
@@ -457,9 +457,8 @@ typedef struct afatfs_t {
} initState;
#endif
-
#ifdef STM32H7
- uint8_t cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS] __attribute__((aligned(32)));
+ uint8_t *cache;
#else
uint8_t cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS];
#endif
@@ -512,6 +511,10 @@ typedef struct afatfs_t {
uint32_t rootDirectorySectors; // Zero on FAT32, for FAT16 the number of sectors that the root directory occupies
} afatfs_t;
+#ifdef STM32H7
+static DMA_RW_AXI uint8_t afatfs_cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS] __attribute__((aligned(32)));
+#endif
+
static afatfs_t afatfs;
static void afatfs_fileOperationContinue(afatfsFile_t *file);
@@ -3609,6 +3612,9 @@ afatfsError_e afatfs_getLastError(void)
void afatfs_init(void)
{
+#ifdef STM32H7
+ afatfs.cache = afatfs_cache;
+#endif
afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_INITIALIZATION;
afatfs.initPhase = AFATFS_INITIALIZATION_READ_MBR;
afatfs.lastClusterAllocated = FAT_SMALLEST_LEGAL_CLUSTER_NUMBER;
diff --git a/src/main/startup/system_stm32h7xx.c b/src/main/startup/system_stm32h7xx.c
index bbe262f55..49ba5584a 100644
--- a/src/main/startup/system_stm32h7xx.c
+++ b/src/main/startup/system_stm32h7xx.c
@@ -64,6 +64,8 @@
#include "stm32h7xx.h"
#include "drivers/system.h"
#include "platform.h"
+#include "string.h"
+#include "common/utils.h"
#include "build/debug.h"
@@ -508,96 +510,15 @@ void CRS_IRQHandler(void)
}
#endif
-void MPU_Config()
-{
- MPU_Region_InitTypeDef MPU_InitStruct;
+#include "build/debug.h"
- HAL_MPU_Disable();
+void systemCheckResetReason(void);
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
-
- // XXX FIXME Entire D2 SRAM1 region is setup as non-bufferable (write-through) and non-cachable.
- // Ideally, DMA buffer region should be prepared based on read and write activities,
- // and DMA buffers should be assigned to different region based on read/write activity on
- // the buffer.
- // XXX FIXME Further more, sizes of DMA buffer regions should tracked and size set as appropriate
- // using __start__ and __end__.
-
- MPU_InitStruct.BaseAddress = 0x30000000;
- MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;
-
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
-
- // As described in p.10 of AN4838.
-
- // Write through & no-cache config
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
- MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
- MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
-
- MPU_InitStruct.Number = MPU_REGION_NUMBER1;
- MPU_InitStruct.SubRegionDisable = 0x00;
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
-
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
-
-#ifdef USE_SDCARD_SDIO
- // The Base Address 0x24000000 is the SRAM1 accessible by the SDIO internal DMA.
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
- MPU_InitStruct.BaseAddress = 0x24000000;
-#if defined(USE_EXST)
- MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
-#else
- MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
-#endif
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
- MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
- MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
- MPU_InitStruct.Number = MPU_REGION_NUMBER2;
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
- MPU_InitStruct.SubRegionDisable = 0x00;
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
-#endif
-
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
-}
-
-/**
- * @brief Setup the microcontroller system
- * Initialize the FPU setting, vector table location and External memory
- * configuration.
- * @param None
- * @retval None
- */
-void resetMPU(void)
-{
- MPU_Region_InitTypeDef MPU_InitStruct;
-
- /* Disable the MPU */
- HAL_MPU_Disable();
-
-#if !defined(USE_EXST)
- uint8_t highestRegion = MPU_REGION_NUMBER15;
-#else
- uint8_t highestRegion = MPU_REGION_NUMBER7;// currently 8-15 reserved by bootloader. Bootloader may write-protect the firmware region, this firmware can examine and undo this at it's peril.
-#endif
-
-
- // disable all existing regions
- for (uint8_t region = MPU_REGION_NUMBER0; region <= highestRegion; region++) {
- MPU_InitStruct.Enable = MPU_REGION_DISABLE;
- MPU_InitStruct.Number = region;
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
- }
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
-}
+#include "drivers/memprot.h"
void SystemInit (void)
{
- resetMPU();
+ memProtReset();
initialiseMemorySections();
@@ -692,7 +613,7 @@ void SystemInit (void)
// Configure MPU
- MPU_Config();
+ memProtConfigure(mpuRegions, mpuRegionCount);
// Enable CPU L1-Cache
SCB_EnableICache();
diff --git a/src/main/target/common_pre.h b/src/main/target/common_pre.h
index b7e48dac2..c70f8fa1b 100644
--- a/src/main/target/common_pre.h
+++ b/src/main/target/common_pre.h
@@ -151,8 +151,10 @@
#ifdef USE_DMA_RAM
#define DMA_RAM __attribute__((section(".DMA_RAM")))
+#define DMA_RW_AXI __attribute__((section(".DMA_AXI_RW")))
#else
#define DMA_RAM
+#define DMA_RW_AXI
#endif
#define USE_BRUSHED_ESC_AUTODETECT // Detect if brushed motors are connected and set defaults appropriately to avoid motors spinning on boot