RoboMaster

标题: 【分享帖】用C++11开发STM32程序 [打印本页]

作者: 放弃=给你自由    时间: 2017-12-14 22:00
标题: 【分享帖】用C++11开发STM32程序
今天这篇文章有点复杂,大家要注意一点看啦!
我们知道KEIL是支持C++的,网上一搜索也能找到一些使用C++的方法,无非是在Keil里的options->C/C++->Misc Controls里添加—cpp,如果要支持c++11,还需要指定—cpp11。事实上这样的C++并不是完整意义的上的C++,本人测试过,有好多C++的新功能都是没有办法实现的。这里需要注明的是,在KEIL5.18a以前的版本(包括5.18a)所支持的Arm Compiler只有ARM Compiler 5以及更低的版本,C++11支持不完整,而对C++11有完整支持就必须要使用Arm Compiler 6 即 AC6。
为了使用对C++11有完整支持的Arm Compiler 6(AC6),今天所使用的KEIL MDK版本至少应用为5.20版本以上(Arm Compiler 6.4)
本文中本人使用的AC6为6.7版本,为KEIL MDK 4.24a所自带的AC6编译器
这里需要注意的是AC6仅支持以下系统
  • Windows Server 2012, 64-bit only.
  • Windows 7 Enterprise SP1.
  • Windows 7 Professional SP1.
  • Windows 8.1, 64-bit only.
  • Windows 10, 64-bit only.

所以大家在试验之前,一定要检查下自己所使用的环境,否则就会浪费时间啦。
关于MDK的下载以及和谐办法,大家自行百度解决啦~
在开始之前,有个东西要了解,那就是microlib,不知道大家知道不知道,本来想写一篇关于microlib的文章,想必这是大家最熟悉的陌生人了。使用STM32CubeMX生成的MDK工程都会自动链接这个系统自带的库。

                               
登录/注册后可看帖子
它就是Code Generation里的Use MicroLIB,默默地被勾上。
那么他最主要的作用是什么呢?
  • 创建栈空间
  • 创建堆空间,如果需要的话,这样才可以使用malloc等一些函数
  • 初始化用户可能用到的系统库
  • 调用用户的main函数
  • Microlib不支持exit函数

如果是C/C++ standardlib 还支持
  • 支持应用程序使用ISO定义的函数
  • 可以捕捉运行时错误并发送信号,如果需要,在错误发生时或行程序退出时还可以停止运行

然而真正的C++开发是不能链接microlib的,因为他只是标准C library的一个精简集。网上能查到microlib的一些限制,这里列举一些出来
  • Microlib和标准的IOS C库不兼容,所以不支持有些ISO所提供的特性或者功能不完整
  • 仅对C99库提供有限的函数支持
  • Microlib不支持C++
  • 不支持位置独立的代码
  • 不支持单个或双个的内存区域模型
  • 不支持Mutex以及不支持宽字符

正常情况下,在STM32CubeMX通过成的.s文件里可以看到一个__main函数,这个就是microlib的入口地址,他会完成上述的初始化动作,最后跳转到我们熟悉的main。

                               
登录/注册后可看帖子
刚才也说过我们要实现真正的C++编程,就不能链接microlib,如果不链接microlib,就会默认链接到我们的C/C++标准库。
现在开始,首先依然是使用STM32CubeMX生成一个带串口的工程,阿圆有依旧是STM32F437ZGT6,工程名为ARMCCTest,要使用完整的C++11特性就必须使用AC6,这里把ARM Compiler设置为V6.7,并勾掉Use MicroLib

                               
登录/注册后可看帖子
这里根据ARM官方的建议,检查下Short enums/wchar是否勾上

                               
登录/注册后可看帖子
这样就设置好了。
但是呢,如果就这样去编译,会有一堆的编译错误
主要是__weak编译失败

                               
登录/注册后可看帖子
AC6己经不支持直接声明 __weak了,需要使用 __attribute__((weak))替代。这里不建议使用全局替代的方法, 如下图所示

                               
登录/注册后可看帖子
因为如果你的的工程里有包含了C++文件,这种方法可能把系统库里的__weak也给替换了,曾经吃过大亏!结果都重装KEIL了
好的,为了测试C++11的功能,我们新建一个CppTest.cpp文件,为了保持和C的兼容性呢我们把main挪到了cpp文件里,将原来Keil生成的main改为cmain即可

                               
登录/注册后可看帖子

                               
登录/注册后可看帖子
这里看到有一个Test类,这就是我们需要对C++11特性进行测试的类

                               
登录/注册后可看帖子
这里面还包含了一个Base和Derived类

                               
登录/注册后可看帖子
这样一个简单的C++测试用例就写好了!
但是!这样是不能执行的!一旦执行系统在跳到__main时之后就跑飞了!
大家可以想一想这是怎么一回事?
留白
留白
留白
好啦,不卖关子啦,事实上本人也找了近两天的时间才找到解决办法,一开始认为是heap和stack没有初始化好,尝试了好久均未成功,后来在网上得到启发,这个问题是出在STDIO初始化上。
如果要使用C/C++标准库就要对其STDIO进行Retarget的,很简单,但却是非常关键的一步,就是这么一回事啦。
下载ARM官方的retarget文件,并加入到工程当中
下载链接
http://infocenter.arm.com/help/topic/com.arm.doc.faqs/attached/3844/retarget.c
稍微进行小修改,把它重定向到串口就可以啦!

                               
登录/注册后可看帖子
现在就可以把代码编译运行一下,从SSCOM看到代码正常运行并输出了log

                               
登录/注册后可看帖子
都运行成功了!
要问为什么没有使用std::cout,我也觉得很奇怪啦
../Src/CppTest.cpp(44): error: no member named 'cout' in namespace 'std'
std::cout<< "adsaf" ;
~~~~~^
上面只是简单测试了C++ vector容器,智能指针,auto变量和lambda表达式,当然C++11的内容比这要广泛得多,大家可自行测试!
不过要使用上完整的C++11代价也是非常大的(未开启优化 -O0编译)!上面的代码几乎不做什么有用的功能其大小竟然达到了可怕的231K!

                               
登录/注册后可看帖子
大家还Hold住吗!哈哈!




作者: nephilim    时间: 2017-12-15 00:03
学习学习
话说,visual studio 2017 + visualgdb 这个组合开发OSDK STM32有人尝试过吗??
作者: 随性而为    时间: 2017-12-29 13:17
66666666666
作者: gelascer    时间: 2018-1-2 19:33
QAQ然而不会C++
作者: 龙小越    时间: 2018-1-3 18:52
准备尝试好厉害的样子
作者: prophet27    时间: 2018-1-4 12:25
高大上的样子LOL
作者: prophet27    时间: 2018-1-4 13:45
好东西66666666666666666
作者: Starrs    时间: 2018-1-4 18:13
赞   学习学习
作者: 1627471414    时间: 2018-1-7 13:02
学习学习

作者: HYB    时间: 2018-1-7 21:16
66666666666
作者: 龙小越    时间: 2018-1-10 19:07
请问CUBE重新编译的工程还需要重新配置吗?
作者: 放弃=给你自由    时间: 2018-1-10 23:03
龙小越 发表于 2018-1-10 19:07
请问CUBE重新编译的工程还需要重新配置吗?

不需要
作者: 小陈陈    时间: 2018-1-14 09:44

6666666666666666666666666666
作者: NCSTMFW    时间: 2018-1-15 17:02
666666666666
作者: NCSTMFW    时间: 2018-1-15 17:02
然而不会c++
作者: dannyzhou97    时间: 2018-1-16 15:03
66666666666
作者: Dru2Sky    时间: 2018-1-16 18:11
厉害厉害 学习了
作者: HJHOJBK    时间: 2018-1-18 00:29
666666666666
作者: Future2017    时间: 2018-1-18 09:54
赞的。。。。。。。。。
作者: loading......    时间: 2018-1-20 15:41
6666666666666
作者: HJHOJBK    时间: 2018-1-20 22:19
666666666666
作者: fatmouse    时间: 2018-1-24 15:36

作者: 兴趣使然    时间: 2018-1-25 20:54
666666666666666666666666666
作者: wmd    时间: 2018-3-17 10:48
最近也在探索STM32用C++开发的方法,看了楼主的方法 感觉受益良多呀
这类教程和探索比较少 感觉大家都有自己的方法
在这里我可以分享一下我对应的各类处理方法和一些坑过我的地方:
像那个__weak其实可以不用全局替换 考虑到尽量不去碰库里面的文件,
因为cube重新生成的话这些没有在User code范围内的修改都会被覆盖,
所以对于这个情况我的方法是在main.h里面加一个宏定义:#define __weak __attribute__((weak))
这样即可以搞定所有的__weak,因为这些库都include了main.h
另外,如果stm32开发要用到中断的话一定要在stm32f4xx_it.c的开头与结尾加上extern "C" 和大括号扩住(这个文件的开头和结尾都有user code段) 因为中断的跳转向量表是写在汇编的startup里面的 然后C++因为要考虑重载函数 所以函数的链接符号会和C的不一样 为了能正确的进入中断必须保持这些中断函数按照C的方式引出

还有其他一些比较稀少的外部情况问题都可以按照main.h加修补宏定义的方式来解决

我这边偶尔也会有探索C++编译时解决不了的问题,如果楼主还在弄的话不如加个好友交流一下?
作者: wmd    时间: 2018-3-17 10:59
再补充一些 我为了保持和Cube的高度相关,当时是直接把main.c设置为C++源文件了 这样就算cube重新生成的时候也不需要修改很多其他的东西.不过偶尔加第三方库的时候main.c直接当成c++文件用还是有一点问题的 这个感谢楼主的经验 我到时候再移植一波 然后刚刚说的stm32f4xx_it.c要加extern "C"的时候也要先把这个文件在keil里面直接设置成C++文件.这样中断就可以使用C++特性了
作者: 舟田鱼    时间: 2018-8-10 16:37
大神做的是工程以C++为主,需要调用C文件中的函数。但是我现在做的是工程以C为主,要调用C++文件中的内容,感觉也是个大坑,还要自己慢慢摸索。
作者: 1094167453    时间: 2018-10-5 13:46
66666666666
作者: 薅电机狂魔    时间: 2019-7-26 23:23
Keil官方有一个迁移到新编译器的教程
http://www.keil.com/appnotes/files/apnt_298.pdf

这个BBS讲得也很好
http://firebbs.cn/thread-22706-1-1.html

其实生成的代码大不一定代表运行速度慢。事实上,用新编译器编译出来的代码,运行速度更快!
而且新编译器对静态检查更完善,可以找到很多bug
作者: 薅电机狂魔    时间: 2019-7-26 23:26
对了,语言最好选择GNU版本的,比如gnu99,不然会有很多很多预编译指令报错
作者: 地平线的光    时间: 2019-9-23 10:09
学习了!
作者: szthomas    时间: 2019-10-11 11:16
厉害啊啊啊啊啊啊啊啊




欢迎光临 RoboMaster (https://bbs.robomaster.com/) Powered by Discuz! X3.2