用C++開發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住嗎!哈哈!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章