When you’re getting started with STM32 microcontrollers, one of the best ways to dive in is by building a simple LED Blinky project. In this post, I’ll walk you through every step to create a new STM32CubeIDE project for the Nucleo-F446RE board, manually write the initialization code using STM32 HAL APIs, and explain some important challenges you might face based on real-world experience.
This guide is targeted at absolute beginners and early learners working with the STM32 Nucleo-F446RE board.
While STM32CubeIDE offers graphical tools like STM32CubeMX, writing the HAL initialization manually gives you a deeper understanding of how the microcontroller boots, configures clocks, and interacts with peripherals.
Let’s go step-by-step.
a) Click on File → New → STM32 Project.
b) Board Selector → search for NUCLEO-F446RE
.
c) Select it and click Next.
a) Enter a project name, e.g., BlinkyManual
.
b) Ensure Targeted project type is STM32Cube
c) Click Next
d) Make sure “copy only necessary library files” is selected
e) Click Finish.
a) CubeIDE might ask whether to initialize peripherals with the default mode, select Yes.
b) Now you will see the configuration screen like this
c) Click on Pinout and select “Clear pinout”. This will clear the default configurations so we can manually code it as required
d) Now click on the Code generate button.
e) You will see the project structure like this with the bare minimum code.
Here’s the full working code we wrote:
/*
* main.c
*
* Created on: Apr 23, 2025
* Author: erraj
*/
#include "stm32f4xx_hal.h"
void SystemClock_Config(void);
void GPIO_Config(void);
void Error_Handler(void);
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Config();
while(1){
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
HAL_Delay(1000); // 1 second delay
}
}
void GPIO_Config(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
Error_Handler();
}
}
void Error_Handler(void) {
while (1) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
HAL_Delay(100);
}
}
See the code in action. The green led on board is blinking at a specified time interval.
Let’s understand what each section does:
HAL_Init()
HAL_Delay()
.main()
.SystemClock_Config()
Error_Handler()
.GPIO_Config()
while(1)
HAL_GPIO_TogglePin()
to flip the pin status.HAL_Delay(1000)
to wait for 1 second.Error_Handler()
Writing everything manually sounds easy, but in practice, I faced some important issues:
HAL_Delay()
stopped working, LED always ON.HAL_Delay()
Not WorkingHAL_Init()
and don’t touch startup files.Problem | Cause | Solution |
---|---|---|
LED stays ON without blinking | Missing HAL_Init() or bad clock config | Check that HAL_Init() and SystemClock_Config() are called before GPIO code |
Build error: undefined reference to HAL_Delay | Core system files were deleted | Create new project, don’t delete startup files |
No COM port showing on PC | USB driver issue | Install ST-LINK USB drivers manually from STMicroelectronics site |
Project settings window looks different | STM32CubeIDE version mismatch | Always use latest STM32CubeIDE |
Very slow blinking | Clock misconfigured | Verify PLL and system clock settings |
Setting up STM32 projects manually in CubeIDE is a powerful learning experience.
You understand how clock trees, GPIOs, and HAL APIs interact at a deeper level.
But it also teaches you important habits, like:
Once you master manual setup, you’ll be much more confident working with advanced peripherals like UART, I2C, Timers, ADC, and more!