这个现象说明软件仿真和实际运行一定存在着一些区别,虽然目前的代码没有使用多少硬件资源。经过思考和猜测,最后终于发现,原来是因为,我在工程里设置了 noinit(以前手贱设置的= =),所以上电时在 SRAM 中的那些没有声明初始值的全局变量不会被清零(刚上电时 SRAM 中的数据都是不确定的)。而软件仿真时,模拟的 SRAM 中的所有内容都已经事先被软件清零了。(下图中IRAM1右边的 NoInit 对应的那个框框本来有个我打的勾勾的,现在我把它去掉了)
去掉 noinit 后,再编译下载,cout 成功执行了!!!也许这应该归为 MDK 使用的 C/C++ 库的 BUG 吧,为啥一定要初始化清零呢?何必呢?就算这个问题不归为 C/C++ 库的 BUG,那么就应该归为 MDK 软件仿真的 BUG 了。
3. 其他问题
其实到这里,主要问题都已经解决了。不过我还遇到了其他问题,想和大家分享一下经验技巧,继续看吧
1)部分 flash 写坏了 L
我继续下载调试程序的时候发现编程软件提示我 flash 在 45k的时候写坏了(在调试过程中我把优化改到了最低,所以代码膨胀到 45K以上了,使用 C++ 的库对 STM32 来说还是挺臃肿的啊)。不过换芯片神马的会比较苦逼,最关键的是现在是凌晨 2 点 L,上哪找 STM32去。于是我找到了一个比较可行的办法,让链接器在放代码时避开那个位置,还好能用 Y^_^Y
2)隐藏基本的软硬件初始化
另外,作为一个工程模板,我希望一些基本的硬件初始化和软件初始化代码应该在用户的程序执行之前进行。这样做可以事先建立一个基本的执行环境,对用户隐藏一些不必要的细节,例如我的测试代码里面,主函数就一句输出 “hello world!”,没有任何硬件初始化的代码。这样就方便了用户代码的编写,也让程序的结构变得更加直观。但是这样做就需要保证 __main 中进行 C/C++ 运行时库初始化的时候不可以把我已经初始化过一些全局变量清零。由于又不能 noinit,所以我选择了在 _clock_init 里进行初始化。_clock_init 是 C语言标准库里的函数,它是在清零全局变量之后,初始化用户定义的全局变量之前进行的,而且这个函数允许用户对其进行重新实现。其实我只是借个地方初始化而已。
//由于不能使用 no_init,而 sys 对象又必须在所有对象之前初始化
// _clock_init 这里是个初始化的好地方
ARMAPI void _clock_init(){
sys.Init();
return ;
}
到这里,使用 cout 的全部工作就完成了!!!
4. 测试结果
最后程序输出的信息很简单,就是 Hello world 啦
注:前三行是自己在系统初始化时加的调试信息,且系统时延迟 1s 启动的,让开发板上 PA8 对应的红色 LED 也亮 1s, 以示意系统启动,这样在下载完程序后串口调试助手才有足够的时间打开串口
又让我回想起当初入门 C++ 的感觉了呢 Y^_^Y
5. 继续拓展?
既然 cout 能实现了,那么 cin 当然也是能够实现的,只要你实现好 fgetc 这个函数就行了。而且,有兴趣的同学还可以把 Fatfs 中的文件系统函数和 C 语言标准库的函数结合起来,还有还有,可以把 RTC 和 的相关函数结合起来,在 STM32 上完善 C/C++ 标准库的使用!还等什么?有兴趣就自己写代码试试吧!
stm32_cppTest-20121116 - C++ cout 的实现测试.zip
2.28 MB, 下载次数: 1141
MDK 开发Stm32 上使用 C++ cout 对象的实现.pdf
221.51 KB, 下载次数: 1115再发个使用 cin 和 string 测试的例子。
// 测试代码
#include "iostream"
#include "string.h"
using namespace std;
int main(void) {
cout
int i;
while(1){
cout>s;
cout
cout>i;
cout
cout
}
直接用串口调试助手发送数据就可以,但是需要在后面发多一个空格或者回车例如
以前用过 SecureCRT,有点类似串口调试助手,它可以直接在编辑框上键入字符,软件会自动把数据从串口发送出去。但是我在 Mini 板上用 SecureCRT 的时候发现它会对自动下载电路产生影响。
然后改了一下我在一年前写超简陋的串口调试助手的代码,发现可以用,不过存在的问题比较多,连中文都不能显示,使用不方便代码也比较丑陋。。。当时只是写来练手的。。。
代码里连删除这一类的按键也没有做特殊处理,不过改下代码拉出来体验一下命令行的感觉还是可以的
Serial.zip
2.11 MB, 下载次数: 371
stm32_cppTest - 20121116 - cin cout.zip
2.34 MB, 下载次数: 482
转载:MDK 开发Stm32 上使用 C++ cout 对象的实现
==============================================================
Keil for ARM与C++
1. 如果你的程序中使用了C++全局变量,那么*不要*使用MicroLIB,否则Keil会说某某Symbol找不到
2. 不使用MicroLIB带来的一个问题是KEIL会使用semihosting SWI完成sys_io(例如printf的时候),我们需要一个retarget.c来禁止semihosting。KEIL提供该文件的模版(包括最小版和完全版,见下文),改改就是了
3. retarget.c也有最小版和完全版;最小版除实现fputc及辅助函数用于printf外,只实现了sys_io中的_sys_close;完全版还实现了_sys_open,_sys_read,_sys_write,等等。如果实现了sys_io中除_sys_close以外的任意一个,那么就必须同时实现其他函数。即,要么最小版,要么完全版,不存在中间版。当使用C++标准库时可能需要完全版:例如使用complex template时就必须使用完全版的retarget.c,因为complex class实现了“<>”运算符重载,需要_sys_open等函数。当然我们一般不需要完整的函数内容,只要让编译器看到函数定义就行了。
4. 使用new和delete:参考帮助文件的Libraries and floating point support guide -> The ARM C and C++ libraries -> Stack pointer initialization and heap bounds
一般来说KEIL或厂家(如ST)提供的启动文件已包含该项支持;将汇编启动文件中的heap size项改改就好了。
5. 顺便说下如何动态控制printf的精度(同样用于snprintf,etc):使用%*控制。例如printf("%.*f ", 2, 1.234)
6. 科学记数法打印:%e
==============================================================
如何在MDK中使用C++,整理的经验
一:C++引用C文件
注意:C++文件能引用C文件,但是C文件不能引用C++文件
1:在C头文件中加上extern修饰符:
1. #ifdef __cplusplus
2. extern "C" {
3. #endif
4.
5.
6. 。。。。。。。。。。。。。这里写c语言代码
7.
8.
9. #ifdef __cplusplus
10. }
11. #endif
这样在使用C++调用时就使用C++编译器编译,c语言调用时就是用c语言方式编译
2:编写一个C++风格的头文件,在这里添加extern修饰符:
(一般用在调用已经封装好的库文件或者无法或不想修改.c文件所引用的头文件时)
1. // CStack.h
2. extern "C" {
3. #include "Stack.h";
4. }
或者是直接在需要引用c头文件的cpp文件中
1. // .cpp
2. extern "C" {
3. #include "delay.h";
4. }
二:在新建cpp文件之后,MDK可能会把它识别为image文件
现象如下:
此处这个iic.cpp便被识别成了image文件,main.cpp则是正常的。对它单机右键,选择option for file ”iic.cpp”即可查看。
解决:将File Type 修改为C++ sourcefile 即可。
三:C++中相对于C独有的new以及堆地址设置及内存分配问题。
(当然,如果不必使用new功能,则可以不分配堆空间)
[1] [2] [3]
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。