Development boards
Board-specific settings that are not derivable from the part number. Panel resolutions and TouchGFX configuration are on the Display and TouchGFX page.
External loaders
Boards with external QSPI or Octo-SPI flash need an external loader before
STM32CubeProgrammer or the debugger can write to it. Without one, downloads to
addresses at or above 0x90000000 silently do nothing, and the application
appears to flash successfully but has no assets.
| Board | External loader |
|---|---|
| STM32F746G-DISCO | W25Q128JVEIQ_STM32F746G-DISCO.stldr |
| STM32F769I-DISCO | MX25L512G_STM32F769I-DISCO.stldr |
| STM32H7B3I-EVAL | MX25LM51245G_STM32H7B3I-EVAL-REVB.stldr |
| STM32H7B3I-DK | MX25LM51245G_STM32H7B3I-DISCO.stldr |
The -EVAL and -DK variants of the same part carry different loader files —
STM32H7B3I-EVAL-REVB versus STM32H7B3I-DISCO above — because the flash is
wired to different pins on each board. Picking the loader by part number rather
than by board is a common reason a download appears to succeed but leaves the
external flash blank. This bites ST demos that run from external flash, such as
ClockAndWeather on the STM32H7B3I-DK.
In STM32CubeIDE the loader is set under Debug Configurations ▸ Debugger ▸ External Loaders. Leave Initialize unchecked unless the flash needs setting
up before the first access — enabling it on a board that does not need it can
hang the download.
STM32F746G-DISCO
Project creation from Board Selector ▸ STM32F746G-DISCO:
Project Name: new-project
Targeted Language: C++
Initialize all peripherals with their default Mode: Yes
Enable Memory Protection Unit (MPU): true
FREERTOS:
Interface: CMSIS_V1
Advanced settings:
USE_NEWLIB_REENTRANT: Enabled
Pinout:
- Search: PI1 # LD1, the user LED
Define: GPIO_Output
USE_NEWLIB_REENTRANT matters as soon as more than one thread calls into the C
library — printf, malloc, strtok. Without it they share a single reentrancy
structure and corrupt each other's state, producing failures far from the call
site.
Blink, from the default FreeRTOS task:
void StartDefaultTask(void const * argument)
{
/* init code for USB_HOST */
MX_USB_HOST_Init();
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_SET);
osDelay(500);
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_RESET);
osDelay(500);
}
/* USER CODE END 5 */
}
Use osDelay rather than HAL_Delay inside a task. HAL_Delay busy-waits and
never yields, so it starves every lower-priority thread for the full duration.
STM32F769I-DISCO
- Flashing takes several minutes. This is the external loader programming QSPI flash, not a hang.
- Panel depends on the board revision — see Panel variants.
Board MB1166 revision A-09 corresponds to:
LCD FRIDA FRD397B25009-D-CTK replaced by FRIDA FRD400B25025-A-CTK
STM32H7B3I-EVAL
Hardware:
LCD: ROCKTEK RK070ER9427-CT # 800 x 480
Board Selector: STM32H7B3I-EVAL
FREERTOS:
Interface: CMSIS_V1
Advanced settings:
USE_NEWLIB_REENTRANT: Enabled
SYS:
Timebase Source: TIM7 # keep SysTick for the RTOS
Computing:
CRC: Activated # required by TouchGFX
Debug Configurations:
External Loader:
Loader: MX25LM51245G_STM32H7B3I-EVAL-REVB.stldr
Enabled: true
Initialize: false
Open TouchGFX/ApplicationTemplate.part and generate code from it.
SD card initialisation on this board has been unreliable across MCU Package
versions. If HAL_SD_Init fails or returns intermittently, check the release
notes for your package version before debugging the driver.
Project templates
CubeMX offers three starting points, and the difference matters more than it first appears.
MCU-based versus board-based
A board-based project pre-fills everything the schematic determines. A MCU-based project starts empty and you supply it all.
Core/Inc/main.h gains the board's pin names:
/* Private defines -----------------------------------------------------------*/
#define LCD_B0_Pin GPIO_PIN_4
#define LCD_B0_GPIO_Port GPIOE
// ...
Core/Inc/stm32f7xx_hal_conf.h sets the tick priority:
#define TICK_INT_PRIORITY ((uint32_t)0U) /*!< tick interrupt priority */
Priority 0 is the highest on Cortex-M. The HAL tick needs it so that
HAL_Delay and HAL timeouts still advance when called from another interrupt
handler. Under FreeRTOS this is the one interrupt that is allowed above
configMAX_SYSCALL_INTERRUPT_PRIORITY, precisely because it never calls kernel
APIs — see Interrupts.
Core/Src/main.c gets a clock configuration matching the fitted crystal:
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* Initialize all configured peripherals */
MX_GPIO_Init();
void SystemClock_Config(void)
{
// ...
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 10;
RCC_OscInitStruct.PLL.PLLN = 210;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 2;
The project file *.ioc records which board it came from:
# ...
board=STM32F746G-DISCO
boardIOC=true
# ...
This is the CubeMX project file — extension .ioc, not .ico. Keep it in
version control; it is the source of truth for everything CubeMX regenerates.
Initialising all peripherals
Answering yes to Initialize all peripherals with their default Mode adds every
peripheral the board exposes. main.c then holds the full list:
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC3_Init(void);
static void MX_CRC_Init(void);
static void MX_DCMI_Init(void);
static void MX_DMA2D_Init(void);
static void MX_ETH_Init(void);
static void MX_FMC_Init(void);
static void MX_I2C1_Init(void);
static void MX_I2C3_Init(void);
static void MX_LTDC_Init(void);
static void MX_QUADSPI_Init(void);
static void MX_RTC_Init(void);
static void MX_SAI2_Init(void);
static void MX_SDMMC1_SD_Init(void);
static void MX_SPDIFRX_Init(void);
static void MX_SPI2_Init(void);
static void MX_TIM1_Init(void);
/* ... TIM2, TIM3, TIM5, TIM8, TIM12 ... */
static void MX_USART1_UART_Init(void);
static void MX_USART6_UART_Init(void);
void StartDefaultTask(void const * argument);
and the corresponding calls, followed by the RTOS start-up:
/* Configure the peripherals common clocks */
PeriphCommonClock_Config();
MX_ADC3_Init();
MX_CRC_Init();
/* ... */
MX_FATFS_Init();
/* Create the thread(s) */
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 4096);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
Additional files appear alongside:
| Path | Added for |
|---|---|
Core/Inc/FreeRTOSConfig.h | RTOS configuration |
Core/Src/freertos.c | Task definitions and hooks |
Core/Src/stm32f7xx_hal_timebase_tim.c | HAL tick moved off SysTick |
Core/Src/stm32f7xx_hal_msp.c | Per-peripheral MSP init |
Core/Src/stm32f7xx_it.c | Interrupt handlers |
FATFS/App, FATFS/Target | File system |
Middlewares/, USB_HOST/ | Middleware stacks |
stm32f7xx_it.c declares the handles the handlers dispatch to:
/* External variables --------------------------------------------------------*/
extern HCD_HandleTypeDef hhcd_USB_OTG_FS;
extern DMA2D_HandleTypeDef hdma2d;
extern LTDC_HandleTypeDef hltdc;
extern TIM_HandleTypeDef htim6;
main.h also picks up the post-init callback for peripherals whose GPIO setup
must run after the peripheral itself is configured:
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
Initialising everything is convenient for exploring a board and wasteful for a product: each peripheral costs flash, RAM and start-up time, and every unused one is a pin you cannot repurpose without regenerating. Start narrow for real work.