在编译stm32 arm gcc工程中遇到的一些关于C++的问题
环境 : vscode Embeded IDE插件。
工程 : 使用cubemx生成的stm32 gcc工程。
我在尝试移植以前在esp-idf写的一个用c++实现的ssd1306组件,遇到了几个问题。首先是virtual析构器无法通过编译,这个是小问题,查询万能的互联网发现是没有链接stdc++库,在链接器后面加入-lstdc++就能解决。(但是我还有其他类,没有使用virtual修饰析构器,却能正常编译通过)。
编译器报错: undefined reference to `operator delete(void)'*
报错位置: Ssd1306::~Ssd1306()
c:/arm gnu toolchain arm-none-eabi/12.2 mpacbti-rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld.exe: warning: ./build/Debug/MiniWitch.elf has a LOAD segment with RWX permissions
c:/arm gnu toolchain arm-none-eabi/12.2 mpacbti-rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld.exe: ./build/Debug/USER/device/ssd1306/src/ssd1306.o: in function `Ssd1306::~Ssd1306()':
d:\UnderwayProject\MiniWitch\firmware/./USER/device/ssd1306/src/ssd1306.cpp:18: undefined reference to `operator delete(void*)'
c:/arm gnu toolchain arm-none-eabi/12.2 mpacbti-rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld.exe: ./build/Debug/USER/device/ssd1306/src/ssd1306.o:(.rodata._ZTI7Ssd1306+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
在链接器中加入stdc++就可以处理以上报错。
第二个遇到的问题比较麻烦,以前用C语言写单片机用的比较多,虽然有面向对象的思想,但是实例一般都是直接做为静态变量。这次因为图简单,直接用了个静态实例实现单例模式,结果在构造的时候出了大问题。
构造器
static Ssd1306 ssd1306( &oled_spi4_handle );
Ssd1306::Ssd1306(Ssd1306_hal_handle_t *hal_t){
_hal_t = *hal_t;
// _isSelfBuf = false;
_hal_t.init(_hal_t.ctx);
softInit();
}
构造器的底层调用
static void ssd1306_delay(uint16_t ms){
if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
{
vTaskDelay( ms / portTICK_PERIOD_MS );
}else{
HAL_Delay(ms);
}
}
这个用于初始化ssd1306的delay函数看上去没什么大问题,结果运行的时候直接阻塞…。定位了一下原因,hal_timer压根没起来,用openOCD debug了一下,发现了问题。
LoopFillZerobss:
cmp r2, r4
bcc FillZerobss
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl sdk_main
bx lr
.size Reset_Handler, .-Reset_Handler
重点就是__libc_init_array函数,这个应该是c++标准库中的函数,用于构造(初始化)一些静态或者全局实例的,在进入sdk_main之前,程序就跑到了Ssd1306的实例的构造器中去了,而构造器依赖了hal库,hal库在sdk_main中才进行了初始化,导致在delay直接阻塞…,当然softInit里面也调用了hal的一些spi相关的接口,没有在delay阻塞后面也会导致指针踩空然后挂掉。
我当前想到的办法就是不使用静态或者全局的实例,在sdk_main运行完后直接new到全局去,但是单片机这点小heap确实有点难顶。所以后期打算直接改启动文件,将hal库的初始化单独封装起来,在 libc_init_array 之前调用,hal库全部由c语言实现,应该问题不大,libc_init_array调用完之后,在进入用户的main函数。