不知有没有人对C++开发STM32感兴趣的,来讨论些问题

如题所述

  因为一开始我自学的就是C++,后来玩单片机以后才使用C语言来开发东西,也写过一些程序了,
  但是我的体会就是,C语言没有C++那么方便,最近才在学STM32,看到ARM编译器能够支持C++,挺爽的,就开始尝试把实验代码的工程改成C++的方式
  出现了一些问题,我不太会查资料,网上讨论这个的也比较少,所以就发个帖子大家一起讨论下

  先说一下,为什么要用C++,(说的可能不太清楚,对没学过C++的哥们就不好意思啊呃。。。)
  1. 最简单的,函数重载, 这个语法特性就比C语言的好,还有运算符重载,这些都可以让代码变得更直观(这个等下我用我刚刚拓展的代码来演示一下)
  2. 命名空间,C语言写大程序一般每个模块都会有个前缀,但是写起来麻烦,其实有时候在一个地方只会用到一个模块的内容,所以用using xxx::xxx感觉会比较方便些。
  3. 类的支持,尤其是成员函数什么的使用起来方便,还有constructor,不用每次手动调用初始化。
  (关于这个还是有一点问题我没有解决的,好像是全部类变量的初始化问题,我比较健忘,记不清了)
  4.inline关键字,可以解决宏的一些缺陷 (有热心网友指出,用C也有__inline关键字)
  5.引用参数的支持,比直接使用指针省事放心些
  6.模板的支持,(这个在我之前发过的一个关于宏的帖子里有提到一点,也有提供代码,这个帖子的附件里面也有代码)
  7.至少,网上说的,C++具有C的全部优点
  关于我上面说的优点,也有网友对我的观点不屑,
  有的说C++效率低下(关于这个我并不理解,因为我反汇编看过,没有明显的区别,除非你使用了继承多态的特性可能会有问题,
  但是,在有些地方就需要使用这些特性,特别是在比较大的系统里,比如GUI,就算是用C语言去模拟面象对象的实现过程效率也是差不多的)
  还有网友称C++的一些语法特性为语法糖,没多大用处(呵呵,这个就看个人喜好了,就像学过汇编的哥们可能就喜欢写汇编,不喜欢用C语言一样)

  我也不打算说服所有的人,我只是希望有兴趣的人能和我一起交流意见,我之前发了那个帖子之后,就有一位华工刚毕业的师兄感兴趣,通过邮件和我交流
  只可惜那个时候我一直没什么时间继续解决那些问题。趁这个下午有点时间,我就改了一下之前的代码,再到这里发帖,希望有兴趣的人能一起交流。

  下面是我遇到的一些问题,有的已经解决,有的还没解决:

  1. 以前的库函数怎么使用?
  在函数声明前加上 extern "C" 就可以了,因为C++支持函数重载之类的东东,所以生成的目标代码的名字和C会有些不同,用extern "C"强制成C语言的名字规则。
  现在新版本的库好像已经加上了,这里顺便问一下,新版本的库在官网的哪里下载?我不要一点就直接弹出下载的链接

  新版的STM32的库的头文件前面是
  #ifdef __cplusplus
  extern "C" {
  #endif

  后面
  #ifdef __cplusplus
  }
  #endif
  这就是为了兼容C++的。

  2. 中断函数进不去呀?
  中断函数定义前也要加上 extern "C"。中断函数,名字已经固定了(在你使用MDK给的启动代码的情况下),
  而C++对函数的生成的目标代码的命名方式和C语言不一样(为了支持重载),
  然后我是以这种方式来解决的,
  #define ARMAPI extern "C"
  ARMAPI void EXTI15_10_IRQHandler(void)
  {
  ...
  }

  3. 对库里面定义的结构体有警告: warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
  这个没有关系的,我用这段代码屏蔽了中间警告
  #ifdef __cplusplus
  extern "C" {
  //消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
  #pragma diag_remark 368
  #endif

  ...
  ...

  #ifdef __cplusplus
  //恢复368号警告
  #pragma diag_default 368
  }

  4. C++有 new 关键字,类似C的malloc,但是这个是需要有内存管理提供动态内存的,这个在一个没有操作系统的MCU上咋整呀?
  其实关于使用C++的,国外已经有成功的例子了(只是IDE用的不是MDK。。。),我从网友提供的链接去下载了源代码:
  http://andybrown.me.uk/ws/2011/12/28/stm32plus-a-c-library-for-stm32-development/
  (PS:看了他们的代码库,我发现我以前写的文件系统实在是弱啊。。。没关系,会继续完善的)

  人家是这么搞的,利用函数重载,重载了new操作符,(软件仿真会跳进重载的函数里)
  ,自己只要提供malloc和free以及堆的一些初始化就行了(这个一般要上了OS才有内存管理的吧。。。)

  /* * Implement C++ new/delete operators using the heap */
  void *operator new(size_t size)
  { return malloc(size); }

  void *operator new[](size_t size)
  { return malloc(size); }

  void operator delete(void *p)
  { free(p); }

  void operator delete[](void *p)
  { free(p); }

  还有些没解决的问题

  5. 定义全局类变量后会有错误:
  ..\output\stm32test.axf: Error: L6218E: Undefined symbol __cpp_initialize__aeabi_ (referred from anon$$obj.o).
  class A
  {
  public :
  A()
  {
  int x, y;
  x = 5;
  y = 7;
  x = y;
  y = x + 8;
  }
  };
  A a;

  有些地方可能不清楚,原帖子在这个链接:
  http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=5401612&bbs_page_no=1&search_mode=3&search_text=Pony279&bbs_id=9999

  然后是我刚刚做的设置IO口模式的几个函数的测试代码,因为实现代码比较长,我不会帖上来,
  在附件里面有完整的代码,测试代码在gpio_test_120309a.cpp文件里,相关的代码全部在 MYLIB文件夹里面也有。
  (自己在学习的时候整理的函数都在那里面,其实内容比较少。。。)
  我这里只是为了演示使用的便利性的提高和代码的阅读性的提高。

  SetMode函数实现是利用了函数重载的,和默认参数。如果不知道什么是重载的,先不要去想语法,直接看代码,看得懂不?
  int main(void)
  {
  {using namespace gpio; //在这个代码block里面使用gpio的命名空间。

  SetMode(GPIOA, Pin(0), OUTPUT); //设置PA0为输出模式,有默认参数 开漏输出, 这里没有写出来
  SetMode(GPIOA, Pin(1), OUTPUT, OPEN_DRAIN, _50M);
  SetMode(GPIOA, Pin(2), OUTPUT, PUSH_PULL, _50M);
  SetMode(GPIOA, Pin(3), INPUT); //有默认参数,上拉输入,没有写出来。
  SetMode(GPIOA, Pin(8), INPUT, PULL, DOWN);
  SetMode(GPIOB, Pin(3), INPUT);
  SetMode(GPIOB, Pin(8), INPUT, PULL, DOWN);
  }
  //仿真结果和预期一致。
  while(1);
  }

  函数重载是语法上允许同名函数的存在,但是需要这两个函数的参数列表是不同的,使用的时候,编译器会知道你要调用的是哪个函数。
  在写代码的时候对于一个同样功能但是参数类型不同的函数,如果需要起不同的名字,不论是写代码还是读代码都会比较痛苦的,

  我上面好像提了几次代码的阅读性的问题,因为我一直很重视代码的阅读性,这一点从我之前发的那个关于宏的帖子大家也可以知道。
  因为,我想老鸟应该有体会,其实程序猿花在读代码上的时间,是远大于写代码的时间的。

  好像扯远了。。。就先到这里吧。。。
温馨提示:答案为网友推荐,仅供参考
第1个回答  2016-02-22
最近正在写这个,mdk C++的。
第2个回答  2015-03-09
表示在关注