你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

在 STM32 上使用 C++ 指南

[复制链接]
eefishing 发布时间:2019-6-21 12:32

简介

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

3 r: X" c; s' A3 _: o+ R) r硬件平台简介
+ R" K% d4 E7 U  g$ `1 R& o9 [8 w
本文基于意法半导体 STM32F411 NUCLEO开发板,给出了 C++ 的具体应用示例代码,由于RT-Thread上层应用API的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。5 Y% Z- A7 }! J% `2 T) r
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的封装)、功耗“轻微”。

1.png

如何在 STM32 上使用 C++
) j% P- J8 J4 n. D9 v7 e; h# h
准备工作

3 E- J8 d" J# h$ i- ~

1、下载 RT-Thread 源码

2、下载 ENV 工具

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

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

2.png

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

3.png

打开 C++ 支持:

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

4.png

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

5.png

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

; N9 z) b; N# u  S# Y/ j

6.png

运行 C++ 程序:

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

8.png


8 [5 `1 j0 j  L( i# P

% ~! w8 }5 }. gC++ 全局对象构造函数的调用
6 _  G  P# x, w; q) N1 ]7 a2 M# N$ N( m0 E& B; P# `6 `4 Z

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

7.png

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

C++ 系统初始化部分:

1RT_WEAK int cplusplus_system_init(void)# e" H6 u* v* N0 {" G- }
2{: ~$ l2 g+ h' w6 ^
3    typedef void(*pfunc)();' W# G+ }  O& E9 t/ h
4    extern pfunc __ctors_start__[];' \1 U* h, J) X: R( V  E' ]
5    extern pfunc __ctors_end__[];
* O9 }  t6 S# N1 t2 A 6    pfunc *p;
) W# A' P/ H, y: z0 S" {+ Z: P 7# I1 o3 ?4 v& @
8    for (p = __ctors_start__; p < __ctors_end__; p++)
; A" U: u* A: F 9        (*p)();' w0 z2 c' \9 `  ]( I9 ]
10
2 m' n# F2 z& ]( {11    return 0;
( `) ~: I/ C2 ^+ [+ r7 N12}2 a% _  [4 o; _
13INIT_COMPONENT_EXPORT(cplusplus_system_init);

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

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

$ @5 F) |; z( N6 g- N1PROVIDE(__ctors_start__ = .);# O5 ^; m9 f2 M1 D) Z5 P0 q2 K) D  Z
2KEEP (*(SORT(.init_array.*)))
2 T3 \, ]. ^. {# K, t. b3KEEP (*(.init_array))9 v0 o" i" P% l- F% a1 m
4PROVIDE(__ctors_end__ = .);
1 J/ P0 e- d5 K; S

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

RT-Thread C++ 异常说明4 _& S1 }7 a3 q1 h

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

1    __exidx_start = .;( P+ m( Y7 B. J3 B  Q% h: a9 n8 P+ |
2    ARM.exidx :
) f8 g. c6 z: R5 I" ~3    {
7 y: B) P( {8 N1 F5 A! x4        *(.ARM.exidx* .gnu.linkonce.armexidx.*)+ u0 D) D/ n+ K8 K' d% |& j
5        _sidata = .;! Z7 e4 N, S, x* I, p
6    } > CODE: n1 ^9 b; `1 a" n: \2 @, |9 }
7    __exidx_end = .;
! R- p. s, j% a- d0 V9 z) l0 K4 @0 ^( _: a% ^3 W- w

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

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

1   #include<math.h>$ {% z7 ^0 v- U
2  v- `, u5 V7 S
3    #define MIN_VALUE                 (1e-4)                          
6 E; ^& P( P3 B. b# u 4    #define IS_DOUBLE_ZERO(d)         (abs(d) < MIN_VALUE)6 @& t4 {* F/ G
5$ c$ l6 s( P8 r  c0 S) [
6    double div_func(double x, double y)                     & a& f% [! ]/ ]; J) o, R: `) Q
7    {
1 S# X' _: @4 l! p" ]% i. G 8        if (IS_DOUBLE_ZERO(y))
! q) ^6 _1 a' @  F 9        {
7 q1 ]1 G+ R4 E7 V10            throw y;                                           /* throw exception */
+ v  m: z9 ~7 f11        }3 F/ m5 |4 z- a) `8 H& }
12
# C0 S$ \" a: e0 s' L  j13        return x / y;                                  . e; B0 \& w% J! E( q6 f
14    }
* b, Q7 L$ f5 T8 I/ F" @5 v; \15
8 ~  z8 X& i6 o16    void throw_exceptions(void *args)% I) X  C  m: P& E
17    {
3 O: Z, l6 y# h# \7 [6 \& G) G* q$ z18        try                                            + h3 I; s1 a; r7 W9 l4 T1 |8 B4 N; E
19        {5 O3 q, ^% Q$ R: y8 |
20            div_func(6, 3);% G- n0 P5 w- J$ ]+ J  x% E2 ?9 a8 U
21            rt_kprintf("there is no err\n");
* v1 L! r: m. R# O- D22            div_func(4, 0);                                   /* create exception*/9 Z5 n( K! L' ^
23            rt_kprintf("you can run here?\n");
2 @; i5 N# Q& f9 K; Z0 \! r1 I24        }
( m( v# b3 e& d8 o25        catch(double)                                         /* catch exception */     1 J  t4 M0 |- t( d2 _) e' Q
26        {6 Y7 a$ g: E5 N2 _
27            rt_kprintf("error of dividing zero\n");
8 v. W5 C$ [- [, E: I. _; ?, n4 k$ a28        }( M0 }; l$ s/ n* E# [, F; X) z
29    }2 l* F$ I4 c3 X( L) n, q: s) `
303 J5 R3 A  `% H3 u- t6 O
31    MSH_CMD_EXPORT(throw_exceptions, throw cpp exceptions);
3 F! R: m3 \/ o1 v6 d' R; z
/ T/ L: X% V5 }. \" r) G* n4 Y# d

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

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

11.png

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

参考资料2 {# U$ g4 ^8 D8 i* G

1、ENV 用户手册# Z+ Y; Y6 ?; h! m9 k7 c; v

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

. N# J* @+ G) f7 P2 z

2、STM32F411-ST-NUCLEO BSP 源码

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

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


# a; }2 c% ?# }3 D, b
赞 收藏 1 评论0 发布时间:2019-6-21 12:32

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版