it sounds like it should be solved by now, but I struggled a lot and learned in the process, so here’s what I went through so you may not have to. it’s mostly a ramble but you may find some pointers there if you ever get stuck
part 1. test DFU
I have a WeAct blackpill with a STM32F411 on it, and I know it should support DFU, so let’s try that first
> build as normal
> locate .elf
> fire up cubeprg
> reboot board with boot0 pressed and nrst
> refresh, connect to device
> troubleshooting: $ sudo dfu-util --list -v <-- show all DFU-compatible devices
> load .elf in cubeprg, hit download
> maybe need to reboot board
> now which virtual COM port are we using? --> $ sudo dmesg | grep tty | tail -n 10
> ttyACM0 in this case
> connect to it --> win already
part 2. mistakes
I went further and messed around trying to flash tinyuf2 straight away etc, not knowing what I was doing
eventually I realised I bricked my MCU, so I had to find a way to erase the whole FLASH if that’s even possible turns out it is, like so:
On the STM32F411CEU6 chip the following sequence of commands, performed with BOOT0 held high while running the first command and at least until the chip reboots, can erase the tinyuf2 bootloader with flash write protection enabled:
$ dfu-util -d 0483:df11 -a 0 -v -s :force:unprotect
$ dfu-util -d 0483:df11 -a 0 -v -s :force:mass-erase
part 3. UF2
Okay now let’s try to install a bootlader which will let me easily flash the firmware with a simple drag and drop on a USB device. the TP2040 does that out of the box (I think?) so that would be nice and also how hard can it be?
follow instructions to clone and build tinyuf2, then flash over DFU
note: board name is in lowercase: $ make BOARD=stm32f411ce_blackpill all
$ sudo dfu-util -a 0 -s 0x08000000:leave -D tinyuf2-stm32f411ce_blackpill.bin
UF2 drag and drop of this program works: blink_stm32f411_uf2 (thanks mate it helped me a ton to have that stupid blinky properly compiled!)
part 4. my own code flashed using uf2
ok tinyuf2 expects ‘-f 0x57755a57’ from uf2conv in my case!!
now to compile my code in a way tinyuf2 can jump do we need to mess with memory layouts.
#define VECT_TAB_OFFSET 0x00010000U in system_stm32f4xx.c
in the .ld file:
FLASH (rx) : ORIGIN = 0x8010000, LENGTH = 448K
–> still not working
ok let’s debug that crap
ST-link hot plug to play with registers: I can blink the led. power, ST-link, hardware all good. that’s a relief
checked that PC was stuck in an error handler, coming from system clock config
I love debugging clocks
commenting out that setup and flashing using ST-LINK works, my beautiful stupid blinky loaded by tinyuf2 actually runs, amazing
now let’s figure out this clock setup thing
even though not used, cube IDe somehow configures PLLQ to 5 which seems to be the reason for the crash. setting it to 4 works.
paring down system clock config.
increasing HSE timeout to 1000ms (1s) –> nope
switching to HSI! –> nope
no HAL for GPIOs, direct with resgiuster and dummy loops? –> yep
seems to work on double NRST but not single?????
ok here’s where we stand:
- HSI setup, no HAL for GPIOs: works on double NRST
- HSI setup, HAL for GPIOs: works on double NRST
- HSE setup, HAL for GPIOs: works on double NRST
wait I’m confused, turns out that figuring out whether it MY code which runs or the stupid uf2 bootlader just by judging LED blinking speeds on a misconfigured clock is NOT THE WAY
ok let me sleep now
part 5. success
I’m back, I really need to sort out that systemclock crap
working theory: tinyUF2 sets up the clocks specifically using HSE and PLL. when my app tries to change PLL on the fly, it crashes.
tinyUF2 setup:
static inline void clock_init(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/* Enable Power Control clock */
__HAL_RCC_PWR_CLK_ENABLE();
/* The voltage scaling allows optimizing the power consumption when the device is
clocked below the maximum system frequency, to update the voltage scaling value
regarding system frequency refer to product datasheet. */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
/* Enable HSE Oscillator and activate PLL with HSE as source */
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 = HSE_VALUE/1000000;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 7;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | 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;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
which sets the SYSCLK at 84MHz and the USB clock at 48MHz
if my systemclockconfig does the same, it crashes (tries to change/re-initialise the PLL live does crash?)
let’s try a 2 step process:
- custom systemclockconfig2 to use HSI
- then let the auto generated code do its thing using HSE and PLL
WORKSSSSS
summary:
- change VTOR:
#define VECT_TAB_OFFSET 0x00010000Usince we jump to0x08010000(and the bootloader is at0x08000000) - change LD table:
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
/*FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K*/
FLASH (rx) : ORIGIN = 0x8010000, LENGTH = 448K
}
- uncomment
#define USER_VECT_TAB_ADDRESSinsystem_stm32f4xx.c - uf2conv:
$ uf2conv -c -f 0x57755a57 -b 0x08010000 -o app.uf2 app.bin
–> not the -f family (shoudl match how tinyuf2 was compiled), and get the -b address right
- in main app: tinyuf2 sets up its own clocks using HSE and PLL for USB mgmt. changing this on the fly crashes. you need to have a temporary fallback to HSI without PLL and then let
SystemClock_Configdo its thing. sample HSI16 setup:
void SystemClock_Config_HSI_16(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler_HSI_16();
}
/** 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_HSI;
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_0) != HAL_OK)
{
Error_Handler_HSI_16();
}
}