用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住嗎!哈哈!