You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止PlatformIO中ARM GCC优化掉未显式调用的函数

问题背景

使用NUCLEO-F411RE开发板,在VSCode + PlatformIO环境下基于CMSIS框架开发时,遇到编译优化无法关闭的问题:

  • 同一份代码在STM32CubeIDE环境下编译烧录后可正常运行
  • PlatformIO环境下编译无报错,但烧录后设备完全无响应,初步判断是未被显式调用的函数被编译器优化移除
  • 已尝试在代码开头加#pragma GCC optimize("O0")、在platformio.ini的编译选项中加-O0、设置build_type = debug,甚至将__io_putchar的返回值声明为volatile,均未解决问题

测试代码

#pragma GCC optimize("O0")

#include "stm32f411xe.h"
#include <stdint.h>
#include <stdio.h>
#define GPIOAEN     (1U<<0)
#define GPIOCEN     (1U<<2)
#define PIN5        (1U<<5)
#define PIN13       (1U<<13)
#define LED_PIN     PIN5
#define BTN_PIN     PIN13
#define UART2EN     (1U<<17)
void uart_2_init(void);
void uart2_write(int ch);
void ms_delay(int ms)
{
   while (ms-- > 0) {
      volatile int x=500;
      while (x-- > 0)
         __asm("nop");
   }
}
int __io_putchar(int ch){
    uart2_write(ch);
    return ch;
}

int main(){
    RCC->AHB1ENR |= (GPIOAEN | GPIOCEN);

    GPIOA->MODER |= (1U<<10);
    GPIOA->MODER &=~(1U<<11);

    GPIOC->MODER &=~(1U<<26);
    GPIOC->MODER &=~(1U<<27);
    uart_2_init();
    while (1)
    {
        ms_delay(200);
        printf("hello again from stm32\n\r");

    }

}
void uart_2_init(void){
    RCC->AHB1ENR |= GPIOAEN;
    RCC->APB1ENR  |= UART2EN;

    //PA2 and PA3 alternate
    GPIOA->MODER &=~(1U<<4);
    GPIOA->MODER |= (1U<<5);
    //Alternate function selection
    GPIOA->AFR[0] |= (1U<<8);
    GPIOA->AFR[0] |= (1U<<9);
    GPIOA->AFR[0] |= (1U<<10);
    GPIOA->AFR[0] &=~(1U<<11);
    // USART2 settings
    USART2->BRR = 0x0683;
    USART2->CR1 = 0x0008;
    USART2->CR2 = 0x0000;
    USART2->CR3 = 0x0000;
    USART2->CR1 |=0x2000;
}

void uart2_write(int ch){
    while (!(USART2->SR & 0x0080)){}
    USART2->DR = (ch & 0xFF);

}

原有platformio.ini配置

[env:nucleo_f411re]
platform = ststm32
board = nucleo_f411re
framework = cmsis
build_flags =
  -Wall
  -O0
  -v
build_type = debug

无效尝试

将__io_putchar返回值声明为volatile,修改后代码如下,仍无法解决问题:

volatile int __io_putchar(int ch){
    uart2_write(ch);
    return ch;
}

解决方法

按以下步骤逐一排查配置即可解决:

  1. 废弃返回值加volatile的错误写法
    volatile修饰函数返回值仅标记返回值本身为易变类型,完全不会阻止编译器对函数的优化、链接时移除操作,这个写法本身没有任何作用。
  2. 正确覆盖PlatformIO默认优化等级
    手动在build_flags里加的-O0会被PlatformIO ststm32平台默认注入的编译参数(Debug模式默认-Og,Release模式默认-Os)覆盖,优先级不足。不需要在build_flags里硬写优化参数,直接用PlatformIO内置的optimize配置项指定优化等级即可,修改后的platformio.ini参考:
    [env:nucleo_f411re]
    platform = ststm32
    board = nucleo_f411re
    framework = cmsis
    optimize = -O0
    build_flags =
      -Wall
      -v
    build_type = debug
    
    编译时加了-v参数,可以直接在编译日志里查看gcc调用命令,确认参数中只有-O0、没有-Og/-Os/-O2等其他优化参数,就说明优化等级配置生效。
  3. 给非显式调用的函数加used属性
    类似__io_putchar这类被库函数(这里是newlib的printf)隐式调用的函数,编译器在编译当前源文件时无法识别到调用关系,会判定为死代码直接移除。需要给这类函数加GCC的used属性,强制编译器保留该函数,正确写法如下:
    int __attribute__((used)) __io_putchar(int ch){
        uart2_write(ch);
        return ch;
    }
    
    所有中断服务函数、回调函数、库对接桩函数如果没有被显式调用,都建议加这个属性避免被优化。
  4. (可选)关闭链接时死段回收
    如果完成以上配置后仍有函数被移除,可以在build_flags中追加以下参数,关闭编译器分段、链接时回收未引用段的默认逻辑:
    -fno-function-sections
    -fno-data-sections
    -Wl,--no-gc-sections
    
    加完后所有写的函数、全局变量都会被完整保留到最终固件里,不会被链接器删除。

内容的提问来源于stack exchange,提问作者Lasse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:18:26