1. 程式人生 > >用C++開發STM32程式

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