In this post, I’ll share the debugging process I went through in Phase 2. Looking at just the conclusion, the cause was really simple. But as with all debugging, you have no choice but to find the cause through trial and error. I think it’s worth recording how I solved it.
Given the bootloader and the application code, I flashed both files. However, it hung forever when jumping to the application.
1
2
3
4
5
6
7
8
9
10
11
========================================
BOOTLOADER v1.0
========================================
Running at address: 0x08000661
Bootloader running...
Bootloader running... (LED blinks 3 times)
Attempting to jump to application...
Preparing to jump to application at 0x08010000...
App Stack Pointer: 0x20030000
App Entry Point: 0x08011711
Jumping to application NOW!
And there is no message from the application. Let’s look at the code.
main.c Since I was working in STM32CubeIDE, I used the IDE’s automatic code configuration feature when first writing the code. I configured my board (STM32F429I-DISC1) and set up the default peripheral modes to create the basic skeleton of main.c.
Then I added the following features:
_write function for printf output over UARTjump_to_application GPIO, TIM1, MX_USART1_UART, and SystemClock_Config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/* BOOTLOADER Core/Src/main.c
Copy and paste, running this code will not guarantee a normal build. This code only works in STM32CubeIDE with STM32F429I board
*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_USART1_UART_Init(void);
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
void jump_to_application(uint32_t app_address)
{
printf("Preparing to jump to application at 0x%08lX...\r\n", app_address);
// 1. Read the application's vector table
// First entry: Initial Stack Pointer
// Second entry: Reset Handler (entry point)
uint32_t app_stack_pointer = *((__IO uint32_t*)app_address);
uint32_t app_entry_point = *((__IO uint32_t*)(app_address + 4));
printf(" App Stack Pointer: 0x%08lX\r\n", app_stack_pointer);
printf(" App Entry Point: 0x%08lX\r\n", app_entry_point);
// 2. Sanity check: Is the stack pointer valid?
// It should point to RAM (0x20000000 - 0x20030000 for STM32F429)
if ((app_stack_pointer < 0x20000000) || (app_stack_pointer > 0x20030000))
{
printf("ERROR: Invalid stack pointer! Application may not be valid.\r\n");
return; // Don't jump to invalid application
}
printf("Jumping to application NOW!\r\n\r\n");
HAL_Delay(100); // Give UART time to send the message
// 3. Disable interrupts
__disable_irq();
// 4. Deinitialize HAL (optional but good practice)
HAL_RCC_DeInit();
HAL_DeInit();
// 5. Disable SysTick
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
// 6. Set the vector table address to the application's vector table
SCB->VTOR = app_address;
// 7. Set the stack pointer to the application's initial stack pointer
__set_MSP(app_stack_pointer);
// 8. Jump to the application's reset handler
void (*app_reset_handler)(void) = (void (*)(void))app_entry_point;
app_reset_handler();
// Should never reach here
while (1);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_USART1_UART_Init();
printf("\r\n");
printf("========================================\r\n");
printf(" BOOTLOADER v1.0 \r\n");
printf("========================================\r\n");
printf("Running at address: 0x%08lX\r\n", (uint32_t)&main);
printf("Bootloader running...\r\n");
printf("\r\n");
// Blink LED a few times to show bootloader is running
printf("Bootloader running... (LED blinks 3 times)\r\n");
for (int i = 0; i < 3; i++)
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(200);
}
printf("\r\n");
printf("Attempting to jump to application...\r\n");
HAL_Delay(500); // Brief pause
// Jump to application!
jump_to_application(0x08010000);
// If we reach here, jump failed
printf("\r\n");
printf("ERROR: Failed to jump to application!\r\n");
printf("Staying in bootloader mode.\r\n");
while (1)
{
// Slow blink indicates bootloader fallback mode
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13);
HAL_Delay(500);
}
}
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_SCALE3);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 72;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 3;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
// Initializes the CPU, AHB and APB buses clocks
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();
}
}
static void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, NCS_MEMS_SPI_Pin|CSX_Pin|OTG_FS_PSO_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(ACP_RST_GPIO_Port, ACP_RST_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, RDX_Pin|WRX_DCX_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOG, LD3_Pin|LD4_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : NCS_MEMS_SPI_Pin CSX_Pin OTG_FS_PSO_Pin */
GPIO_InitStruct.Pin = NCS_MEMS_SPI_Pin|CSX_Pin|OTG_FS_PSO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : B1_Pin MEMS_INT1_Pin MEMS_INT2_Pin TP_INT1_Pin */
GPIO_InitStruct.Pin = B1_Pin|MEMS_INT1_Pin|MEMS_INT2_Pin|TP_INT1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_EVT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : ACP_RST_Pin */
GPIO_InitStruct.Pin = ACP_RST_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(ACP_RST_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : OTG_FS_OC_Pin */
GPIO_InitStruct.Pin = OTG_FS_OC_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_EVT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(OTG_FS_OC_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : BOOT1_Pin */
GPIO_InitStruct.Pin = BOOT1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(BOOT1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : TE_Pin */
GPIO_InitStruct.Pin = TE_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(TE_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : RDX_Pin WRX_DCX_Pin */
GPIO_InitStruct.Pin = RDX_Pin|WRX_DCX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/*Configure GPIO pins : LD3_Pin LD4_Pin */
GPIO_InitStruct.Pin = LD3_Pin|LD4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6) {
HAL_IncTick();
}
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
void assert_failed(uint8_t *file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
}
main.c Here is the application code. I omitted header files or init functions because they are the same ones in the bootloader main.c code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* APPLICATION Core/Src/main.c
Copy and paste, running this code will not guarantee a normal build. This code only works in STM32CubeIDE with STM32F429I board
*/
int main(void)
{
/* MCU Configuration */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("\r\n");
printf("========================================\r\n");
printf(" APPLICATION v1.0 \r\n");
printf("========================================\r\n");
printf("Running at address: 0x%08lX\r\n", (uint32_t)&main);
printf("Application is running!\r\n");
printf("\r\n");
/* USER CODE END 2 */
/* Infinite loop */
while (1)
{
/* USER CODE BEGIN 3 */
// Blink LED FAST (application pattern - different from bootloader)
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13); // Green LED
HAL_Delay(100); // 100ms on, 100ms off (MUCH FASTER than bootloader)
/* USER CODE END 3 */
}
}
// ...
Claude’s first diagnosis was:
0x08010000)system_stm32f4xx.c Items 1 through 4 can be easily resolved if the settings are corrected. On the other hand, it a problem occurs with item 5, it can be judged that there is an issue with the code itself. If you added debugging code to the application and it still doesn’t work, it means there is a problem in the initialization process of the application.
As Claude says,
Let’s also make the application more robust. Sometimes the issue is that UART isn’t initialized properly in the application. In your Application’s
main.c, try adding a simple LED indicator BEFORE any printf:
The following code is Claude’s suggestion.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* APPLICATION Core/Src/main.c modified version
Copy and paste, running this code will not guarantee a normal build. This code only works in STM32CubeIDE with STM32F429I board
*/
int main(void)
{
/* MCU Configuration */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
// IMMEDIATE LED SIGNAL - Application started!
// Flash LED very fast 10 times to show we reached application
for (int i = 0; i < 10; i++)
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(50);
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(50);
}
MX_USART1_UART_Init(); // Now init UART
/* USER CODE BEGIN 2 */
printf("\r\n");
printf("========================================\r\n");
printf(" APPLICATION v1.0 \r\n");
printf("========================================\r\n");
while (1)
{
/* USER CODE BEGIN 3 */
// Blink LED FAST (application pattern - different from bootloader)
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13); // Green LED
HAL_Delay(100); // 100ms on, 100ms off (MUCH FASTER than bootloader)
/* USER CODE END 3 */
}
}
// And the rest of the code ...
I first tested flashing Application.bin to 0x08010000. And the application runs. Here is the output I got:
1
2
3
4
5
========================================
APPLICATION v1.0
========================================
Running at address: 0x080105AD
Application is running!
It wasn’t the problem with the application code itself. LED blinks very fast and then the blink rate slows a bit down. It means there is no problem with UART init or run.
Also, I read memory address from 0x08010000 using STM32CubeProgrammer:
1
2
0x08010000: 20030000
0x08010004: 0801173D
As shown, application.bin was flashed to memory correctly.
I cleaned the project and rebuilt it. However, after flashing both binary files and pressing reset button, I saw a slow LED blink from the bootloader but no LED activity afterwards. It indicates that the application still isn’t running properly.
| Slow LED blink (200ms) | O | Bootloader working |
|---|---|---|
| Fast LED blink (50ms) | X | Application not working |
Claude suspected the problem might be related to complex peripheral initialization. Its reasoning was:
When you flash the application standalone (without bootloader), it works because it’s the first thing to run. But when jumping from the bootloader, peripherals are already initialized by the bootloader and might be in a conflicting state!
- Bootloader initializes: GPIO, UART, USB_HOST, DMA2D, LTDC, FMC (SDRAM), etc.
- Bootloader deinitializes HAL with
HAL_DeInit()andHAL_RCC_DeInit()- Bootloader jumps to Application
- Application tries to initialize the same peripherals again
- Conflict occurs - application crashes or hangs
The issue is likely with complex peripherals like FMC (external SDRAM), USB_HOST, LTDC, or DMA2D which might not fully deinitialize.
And it created a minimal application to test the jump mechanism, and modify the application’s main.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* APPLICATION modified ver.2
Copy and paste, running this code will not guarantee a normal build. This code only works in STM32CubeIDE with STM32F429I board
*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
for (int i = 0; i < 10; i++)
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(50);
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(50);
}
while (1)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13); // Green LED
HAL_Delay(100); // 100ms on, 100ms off (MUCH FASTER than bootloader)
}
}
// ...
I eventually left only GPIO, with no UART or other peripheral initialization. However, it still didn’t jump to the application, or hung somewhere in the application. A weird thing was that the application itself worked fine when it was flashed to 0x08000000 without the bootloader.
This time, Claude asked several diagnostic questions:
Open Basic-Application/STM32F429ZITX_FLASH.ld, and check the MEMORY section. See if in this section:
1
2
3
4
5
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
FLASH (rx) : ORIGIN = 0x????????, LENGTH = ????K
}
It says:
1
FLASH (rx) : ORIGIN = 0x08010000, LENGTH = 960K
In Basic-Application/Core/Src/system_stm32f4xx.c, check if the VECT_TAB_OFFSET is:
1
#define VECT_TAB_OFFSET 0x00010000U
Using STM32CubeProgrammer:
0x08000000 0x20030000 (stack pointer)0x08010000 0x20030000 (stack pointer)0x08010004 0x0801XXXX (reset handler address)Create an application SO simple it CAN’T fail - no HAL_Delay, just raw GPIO toggling:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/* APPLICATION modified ver.3 - without HAL, just raw GPIO toggling
Copy and paste, running this code will not guarantee a normal build. This code only works in STM32CubeIDE with STM32F429I board
*/
#include "main.h"
void SystemClock_Config(void);
int main(void)
{
// Minimal initialization
HAL_Init();
SystemClock_Config();
// Enable GPIOG clock
__HAL_RCC_GPIOG_CLK_ENABLE();
// Configure PIN 13 as output
GPIOG->MODER &= ~(3 << (13 * 2)); // Clear mode bits
GPIOG->MODER |= (1 << (13 * 2)); // Set as output
// Infinite loop - toggle LED with software delay
while (1)
{
GPIOG->ODR |= (1 << 13); // LED ON
for (volatile int i = 0; i < 1000000; i++); // Software delay
GPIOG->ODR &= ~(1 << 13); // LED OFF
for (volatile int i = 0; i < 1000000; i++); // Software delay
}
}
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_SCALE3);
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 = 4;
RCC_OscInitStruct.PLL.PLLN = 72;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 3;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}
void Error_Handler(void) { while(1); }
Claude gave five diagnostic questions. Its approach was to start from the most basic checks (flash, memory address, linker script) and only then move on to code modifications:
There are two lessons to learn from Claude. First, programmers often get so absorbed in their code that they forget to start testing from the basic settings. Second, the best approach is cause elimination method when the cause of a bug is uncertain.
Now Claude’s approach was to put HAL_Delay() back again to the code. I modified the previous ultra-simple main.c to use HAL_Delay instead of software delays:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "main.h"
void SystemClock_Config(void);
int main(void)
{
// Minimal initialization
HAL_Init(); // This enables SysTick for HAL_Delay
SystemClock_Config();
// Enable GPIOG clock
__HAL_RCC_GPIOG_CLK_ENABLE();
// Configure PIN 13 as output
GPIOG->MODER &= ~(3 << (13 * 2));
GPIOG->MODER |= (1 << (13 * 2));
// Infinite loop with HAL_Delay
while (1)
{
GPIOG->ODR |= (1 << 13); // LED ON
HAL_Delay(100); // Use HAL_Delay
GPIOG->ODR &= ~(1 << 13); // LED OFF
HAL_Delay(100); // Use HAL_Delay
}
}
// Same SystemClock_Config as before
void SystemClock_Config(void) { /* ... */ }
void Error_Handler(void) { while(1); }
Yes, I modified the ultra-simple main.c to use HAL_Delay() instead of software delays. And it didn’t work. Now we were pretty sure that HAL_Delay() was the problem.
HAL_Delay() Why did HAL_Delay fail after the bootloader jump? HAL_Delay() depends on the SysTick timer, which generates interrupts every 1ms to increment a counter.
The problem chain:
HAL_Init())
1
2
3
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
__disable_irq(); HAL_Init() which should re-enable SysTickHAL_Delay() waits forever for a counter that never incrementsThe fix would be to explicitly re-enable interrupts after HAL_Init() in the application:
1
2
HAL_Init();
__enable_irq(); // ← This re-enables global interrupts!
Right after HAL_Init(), add __enable_irq() in the application.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "main.h"
void SystemClock_Config(void);
int main(void)
{
// Minimal initialization
HAL_Init(); // This sets up SysTick
// CRITICAL: Re-enable interrupts!
__enable_irq(); // ← ADD THIS LINE!
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init(); // If you want UART back
// Fast blink 10 times
for (int i = 0; i < 10; i++)
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(50); // Works now!
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(50);
}
printf("Application running!\r\n");
while (1)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13);
HAL_Delay(100);
}
}
// ...
But again, this still didn’t fix the problem.