前言
最近想开发一段单片机的代码,代码本身有很多的重复元素,这重复定义的一些结构体使用起来有些繁琐,所以就想用C++开发,C++的继承 模板类可以很容易的解决这些问题。因为在单片机运行,习惯用MDK或者IAR这些软件。但是这些软件都是默认C开发的,用C++开发需要重新配置,有些麻烦。但是我还是试了试,做了一个小demo供大家参考。
代码文件我传到我的github中去了,大家有兴趣可以参考一下
https://github.com/conscience-still/MDK-Cplusplus--LED
一、STM32CubeMX生成底层代码
因为是做一个demo,不需要很复杂,就用cubemx生成了一个简单的串口和IO控制的MDK代码,用了精简的LL库,具体实现就不讲了,详细操作可以看我博客CubeMX配置的一些文章。我的博客名是:良知犹存
二、进行IDE的C++配置(去掉C环境的配置)
1.首先打开MDK软件,去掉use microlib 勾选,这个一个C的依赖库,但比标准的库小,它可以减少C代码的大小。CubeMX生成的文件默认选择此项。因为这个精简库不支持C++,所以我们需要去掉此项功能。
2.Options for Target 再点C/C++ 在下边的Misc Controls 中输入—cpp
3.去掉C99 mode选项
三、代码中C++的编写注意
1. IDE中的编译器的这个工程时候,当文件后缀是C的时候IDE会使用C编译器进行编译,如果文件后缀是CPP则IDE使用C++编译器进行编译,工程包含的头文件是使用C++编译器进行编译的,不过头文件声明的还是C文件的符号,所以IDE会无法正确编译链接。此时我们应该将头文件所有声明C符号的部分用预编译宏加extern "C" { }的形式包含起来,告诉编译器该段要使用C编译器进行编译。只包含需要进行C编译的部分即可
#ifndef __MAIN_H#define __MAIN_H
#ifdef __cplusplusextern "C" {#endif
/* Includes ------------------------------------------------------------------*/#include "stm32f0xx_ll_crs.h"#include "stm32f0xx_ll_rcc.h"#include "stm32f0xx_ll_bus.h"#include "stm32f0xx_ll_system.h"#include "stm32f0xx_ll_exti.h"#include "stm32f0xx_ll_cortex.h"#include "stm32f0xx_ll_utils.h"#include "stm32f0xx_ll_pwr.h"#include "stm32f0xx_ll_dma.h"#include "stm32f0xx_ll_usart.h"#include "stm32f0xx_ll_gpio.h"
#ifdef __cplusplus}#endif
2.设置需要C++编译的文件,这时候有两种方法实现。
1>.在代码文件的界面,选择文件右击选择Option for Files "你点击的文件",然后设置file type为需要的C++
2>.直接将文件改为.cpp文件,重新添加,此时候IDE自动进行C++编译
第二种方法简单快捷,但是第一种方法虽然麻烦,但是有个好处,我们不需要修改文件名称,这样STM32CubeMX下一次生成代码就不会在生成相应名称的C代码了。
3.将中断服务函数添加 extern "C" 的标识,因为C++中无法直接识别中断函数,所以用C的方法进行设备编译。而在Cpp文件中引入C的部分代码,需要进行extern "C" { }进行修饰,否则不能通过编译链接。
四、C++实现时候遇到的情况
1.写了个类没有注意到写成了虚函数,其他处也没有继承定义这个虚函数,导致编译错误,为什么把这个问题写出来呢,就是因为MDK中C++的报错没怎么遇到过,我查了挺长时间,才发现这个问题的。
c++testc++test.axf: Error: L6218E:Undefined symbol vtable for STM32_TEST::TestGPIO (referred from main.o).
把类中的虚函数改为定义好的函数即可。
2.因为我把串口初始化都放在类中实现,我想进行类的构造的时候进行串口数据的打印,但是网上查询得知,MDK不支持std的流打印输出,所以我就用sub和super补丁函数,进行系统main函数执行前进行串口的初始化。
这是一种特殊模式:用于有一个已经存在且不能被改变的函数 的情况。使用这两个模式可以帮原函数打补丁。如存在一个函数foo();
$Sub$ $foo :定义的新功能函数,在foo()函数之前/后使用$Sub$$foo 可以添加一些新的程序代码。
$Super$ $foo :就是原始的未修补的foo函数,使用这个$Super$ $foo函数将直接跳转到foo()函数。
具体教程可以看ARM官网的资料学习哈,http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0377g/pge1362065967698.html。
因为super与sub函数属于c所以我们在cpp文件下需要添加extern“C”进行编译才行,否则就要出现如下问题了,这些我都遇到过,给大家把雷趟了一遍。
3.最后的一个bug,STDIO的初始化。
本来一个简单的C++程序就写完了,主要就是运行环境,但是程序收录进去之后无法工作,并且在硬件调试下明显看到系统到了__main之后不知道跑哪里去了,F5全速执行几次程序才有机会正常运行,这就很奇怪了,后来在网上找资料,终于找到问题所在了,在以为博主的文章看到,他最后找到问题原来是:
事实上本人也找了近两天的时间才找到解决办法,一开始认为是heap和stack没有初始化好,尝试了好久均未成功,后来在网上得到启发,这个问题是出在STDIO初始化上。
如果要使用C/C++标准库就要对其STDIO进行Retarget的,很简单,但却是非常关键的一步,就是这么一回事啦。CSDN:回忆浅离
我按照他的操作然后程序就可以正常运行了,下载ARM官方的retarget文件,并加入到工程当中。下载链接:
http://infocenter.arm.com/help/topic/com.arm.doc.faqs/attached/3844/retarget.c
然后将里面的串口读写按照我现有的硬件需求进行重写就可以了。如下代码所示:
char UART_read(void);void UART_write(char ch); char UART_read(void){ return 0;}void UART_write(char ch){ while(!(USART2->ISR & USART_ISR_TXE)){}; USART2->TDR = ch;}
五、最后测试的一些体验与感想
刚开始想用C++在MDK中开发是因为,有些个需求的功能C++特别符合,但是在调试这个demo过程中,发现使用的单片机容量太小,一个 代码过大是c++的依赖项过多,而C++ 中模板类 、虚拟继承 、STL库等精华由于依赖的问题都不建议在单片机中用,代码膨胀的时候单片机吃不住。所以C++虽好,可不一定适合小容量的单片机,大家需要按照自己的功能进行有效的使用C++,精简使用的依赖,这个可以通过每次编译的生成的.map文件进行增该删,其次对于C++中内存以及代码扩增一些基础知识需要熟悉,负责很容易代码膨胀,导致我们的程序无法在单片机使用。
上一篇:Windows下Keil MDK5配置STM32开发环境
下一篇:STM32F429的USB有坑?
推荐阅读最新更新时间:2024-11-07 12:30
设计资源 培训 开发板 精华推荐
- AD5348 并行接口、八路电压输出、12 位 DAC 的典型应用
- L7806C 稳压器用于光控制器的典型应用 (Vo(min) = Vxx+VBE)
- 【立创开发板】梁山派阿克曼转向智能小车
- LT6654BHS6-2.5 具有升压输出电流和电流限制的电压基准的典型应用
- LT1171IT、-5.2/2.5A 负降压转换器的典型应用
- ch552t硅胶键加波轮编码器多媒体键盘
- 使用 Microchip Technology 的 MICRF001 的参考设计
- 使用 Analog Devices 的 LTC1623 的参考设计
- 使用 ADM3061EARZ-R7 3 至 5.5 V、±12 kV IEC ESD 保护、50 Mbps RS-485 收发器的典型应用
- 使用 Diodes Incorporated 的 PAM2309 的参考设计
- MPS电机研究院 让电机更听话的秘密! 第一站:电机应用知识大考!
- 最能打的国产芯们
- ADI学霸型讲师又来了,探讨MEMS传感器和工业设备的故障监测
- ADI有奖下载活动之22:针对电子测试和测量的RF和微波解决方案
- 答题赢好礼|以人为本、生命无价 ADI 楼宇烟雾探测技术
- 轻松注册世健eSHOP,百份奖品大派送!惊喜不断,好礼无限!ipad air2等你拿!
- 是德科技:五招教您最小化合格/不合格的误判风险在线直播预报名、看直播、填问卷、好礼相送!
- 英飞凌&英恒|如何选择一颗合适的车用MOSFET
- 下载 Intel白皮书 多重好礼等你拿!
- TI CC1310网络研讨会诚邀您预报名参加,惊喜奖不停!