查看原文
其他

在 STM32 上使用 C++ 指南

嵌入式ARM 2021-01-31

简介

本文描述了如何使用在搭载了 RT-Thread 系统的 STM32 平台上使用 C++,包括 C++ 的配置和应用等。并给出了在STM32F411 NUCLEO开发板上验证的代码示例。


硬件平台简介


本文基于意法半导体 STM32F411 NUCLEO开发板,给出了 C++ 的具体应用示例代码,由于RT-Thread上层应用API的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。

STM32F411 NUCLEO是意法半导体推出的一款基于ARM Cortex-M4内核的开发板,最高主频为100Mhz,该开发板具有丰富的板载资源,可以充分发挥STM32F411RE 的芯片性能。

STM32F411RE从属于销量名列前茅的STM32F4系列,众所周知,F4是STM32主打高性能和数字信号处理的“轻奢”系列。

“奢侈”在F4作为内核为Cortex-M4 (DSP+FPU)的MCU,可选180MHz 主频、2M Flash/384KB RAM、Chrom-ART加速器、MPI-DSI接口、延伸到125度的工作温度、DFSDM数字滤波器以及各种常见的音频(SAI)、连接(Ethernet、Camera、USB)、控制(CAN、UART、I2C)、存储(FMC、2/4/8 bits SPI、SDMMC)外设。

“轻”在价格让人“轻松”、尺寸“轻巧”(不到3mm*3mm的封装)、功耗“轻微”。

如何在 STM32 上使用 C++


准备工作


1、下载 RT-Thread 源码

2、下载 ENV 工具

3、进入rt-thread\bsp\stm32f411-st-nucleo 目录,检查 BSP rtconfig.py 文件和 SConstruct 文件是否支持 C++ 配置,如下图所示

检查 rtconfig.py 文件中对 C++ 的支持

检查 SConstruct 文件中对 C++ 的支持

打开 C++ 支持:

打开 Env 工具,在 Env 命令行中输入 menuconfig,进入配置界面,使用 menuconfig 工具(学习如何使用)配置工程。在 menuconfig 配置界面依次选择 RT-Thread Components ---> C++ features ---> Support C++ features,如图所示:


编译工程: scons --target=mdk5 1. 生成 mdk5 工程,将示例代码附带的 main.cpp 替换掉 BSP 中的 main.c 并重新加入到工程中,如图所示:

编译,下载程序,在终端输入 help 命令可以看到 test_cpp 已经添加成功了。


运行 C++ 程序:

在终端输入 test_cpp 运行结果如下图所示。




C++ 全局对象构造函数的调用



RT-Thread 中对全局对象构造函数的实现中,以 GNUC 为例,在 rt-thread\components\cplusplus 目录下的 crt_init.c 文件中对 C++ 进行了系统初始化, 在特定的 BSP 目录下,连接脚本文件 link.lds 为 C++ 全局构造函数的代码分配了段,使 C++ 全局对象构造函数链接后能够存放在指定的段中。如下图所示:

crt_init.c 文件完成了 C++ 系统的初始化工作

C++ 系统初始化部分:


1RT_WEAK int cplusplus_system_init(void)
2
{
3    typedef void(*pfunc)();
4    extern pfunc __ctors_start__[];
5    extern pfunc __ctors_end__[];
6    pfunc *p;
7
8    for (p = __ctors_start__; p < __ctors_end__; p++)
9        (*p)();
10
11    return 0;
12}
13INIT_COMPONENT_EXPORT(cplusplus_system_init);


在 cplusplus_system_init 函数中,将全局对象的构造函数依次链接到了链接脚本文件中为其分配的段中,并且调用了 RT-Thread 组件自动初始化的宏 INIT_COMPONENT_EXPORT,所以在链接的时候,C++全局对象构造函数所产生的目标文件就被链接到了__ctors_start____ctors_end__组成的段中。

链接脚本中为 C++ 全局构造函数分配的段部分:



1PROVIDE(__ctors_start__ = .);
2KEEP (*(SORT(.init_array.*)))
3KEEP (*(.init_array))
4PROVIDE(__ctors_end__ = .);



__ctors_start__ 分配了 C++ 全局构造函数段的起始地址, __ctors_end__ 分配了 C++ 全局构造函数段的结束地址,所以全局构造函数在系统初始化的时候,就会被链接到这里分配的段地址中。

RT-Thread C++ 异常说明


同样,在链接脚本文件 link.lds 中,也为 C++ 异常分配了段地址:


1    __exidx_start = .;
2    ARM.exidx :
3    {
4        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
5        _sidata = .;
6    } > CODE
7    __exidx_end = .;



__exidx_start 分配了 C++ 异常的起始地址, __exidx_end 分配了 C++ 异常的结束地址,当异常产生的时候,就会被分配到指定的段地址中。

这里以一个 C++ 除零异常的抛出和捕获为例:


1   #include<math.h>
2
3    #define MIN_VALUE                 (1e-4)                          
4    #define IS_DOUBLE_ZERO(d)         (abs(d) < MIN_VALUE)
5
6    double div_func(double x, double y)                     
7    
{
8        if (IS_DOUBLE_ZERO(y))
9        {
10            throw y;                                           /* throw exception */
11        }
12
13        return x / y;                                  
14    }
15
16    void throw_exceptions(void *args)
17    
{
18        try                                            
19        {
20            div_func(63);
21            rt_kprintf("there is no err\n");
22            div_func(40);                                   /* create exception*/
23            rt_kprintf("you can run here?\n");
24        }
25        catch(double)                                         /* catch exception */     
26        {
27            rt_kprintf("error of dividing zero\n");
28        }
29    }
30
31    MSH_CMD_EXPORT(throw_exceptions, throw cpp exceptions);



当除零异常发生的时候 div_func 函数会抛出一个异常,在 throw_exceptions 函数中会去捕获这个异常。

下载代码,并在终端输入 throw_exceptions 运行结果如下图所示。

到这一步为止,如何在搭载了 RT-Thread 系统的 STM32 平台上如何使用 C++ 的介绍就结束了。


参考资料



1、ENV 用户手册

https://www.rt-thread.org/document/site/programming-manual/env/env/


2、STM32F411-ST-NUCLEO BSP 源码

https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32f411-st-nucleo

(请将以上链接复制至外部浏览器打开)


本文转自公众号:RTThread物联网操作系统


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存