MDK中用C++開發STM32

​作者:良知猶存

轉載授權以及圍觀:歡迎添加微信:Allen-Iverson-me-LYN

前言

    最近想開發一段單片機的代碼,代碼本身有很多的重複元素,這重複定義的一些結構體使用起來有些繁瑣,所以就想用C++開發,C++的繼承 模板類可以很容易的解決這些問題。因爲在單片機運行,習慣用MDK或者IAR這些軟件。但是這些軟件都是默認C開發的,用C++開發需要重新配置,有些麻煩。但是我還是試了試,做了一個小demo供大家參考。

代碼文件我傳到我的github中去了,大家有興趣可以參考一下

https://github.com/conscience-still/MDK-Cplusplus--LED

一、STM32CubeMX生成底層代碼

    因爲是做一個demo,不需要很複雜,就用cubemx生成了一個簡單的串口和IO控制的MDK代碼,用了精簡的LL庫,具體實現就不講了,詳細操作可以看我博客CubeMX配置的一些文章。我的博客名是:良知猶存

 

二、進行IDE的C++配置(去掉C環境的配置)

1.首先打開MDK軟件,去掉use microlib 勾選,這個一個C的依賴庫,但比標準的庫小,它可以減少C代碼的大小。CubeMX生成的文件默認選擇此項。因爲這個精簡庫不支持C++,所以我們需要去掉此項功能。

2.Options for Target 再點C/C++  在下邊的Misc Controls 中輸入—cpp

 

3.去掉C99 mode選項

三、代碼中C++的編寫注意

   1. IDE中的編譯器的這個工程時候,當文件後綴是C的時候IDE會使用C編譯器進行編譯,如果文件後綴是CPP則IDE使用C++編譯器進行編譯,工程包含的頭文件是使用C++編譯器進行編譯的,不過頭文件聲明的還是C文件的符號,所以IDE會無法正確編譯鏈接。此時我們應該將頭文件所有聲明C符號的部分用預編譯宏加extern "C" { }的形式包含起來,告訴編譯器該段要使用C編譯器進行編譯。只包含需要進行C編譯的部分即可

#ifndef __MAIN_H#define __MAIN_H#ifdef __cplusplusextern "C" {#endif/* Includes ------------------------------------------------------------------*/#include "stm32f0xx_ll_crs.h"#include "stm32f0xx_ll_rcc.h"#include "stm32f0xx_ll_bus.h"#include "stm32f0xx_ll_system.h"#include "stm32f0xx_ll_exti.h"#include "stm32f0xx_ll_cortex.h"#include "stm32f0xx_ll_utils.h"#include "stm32f0xx_ll_pwr.h"#include "stm32f0xx_ll_dma.h"#include "stm32f0xx_ll_usart.h"#include "stm32f0xx_ll_gpio.h"#ifdef __cplusplus}#endif

 2.設置需要C++編譯的文件,這時候有兩種方法實現。

   1>.在代碼文件的界面,選擇文件右擊選擇Option for Files "你點擊的文件",然後設置file type爲需要的C++

   2>.直接將文件改爲.cpp文件,重新添加,此時候IDE自動進行C++編譯

 

第二種方法簡單快捷,但是第一種方法雖然麻煩,但是有個好處,我們不需要修改文件名稱,這樣STM32CubeMX下一次生成代碼就不會在生成相應名稱的C代碼了。

 

 3.將中斷服務函數添加 extern "C" 的標識,因爲C++中無法直接識別中斷函數,所以用C的方法進行設備編譯。而在Cpp文件中引入C的部分代碼,需要進行extern "C" { }進行修飾,否則不能通過編譯鏈接。

 

四、C++實現時候遇到的情況

   1.寫了個類沒有注意到寫成了虛函數,其他處也沒有繼承定義這個虛函數,導致編譯錯誤,爲什麼把這個問題寫出來呢,就是因爲MDK中C++的報錯沒怎麼遇到過,我查了挺長時間,才發現這個問題的。

 

c++test\c++test.axf: Error: L6218E:Undefined symbol vtable for STM32_TEST::TestGPIO (referred from main.o). 

把類中的虛函數改爲定義好的函數即可。

 

2.因爲我把串口初始化都放在類中實現,我想進行類的構造的時候進行串口數據的打印,但是網上查詢得知,MDK不支持std的流打印輸出,所以我就用sub和super補丁函數,進行系統main函數執行前進行串口的初始化。

 

這是一種特殊模式:用於有一個已經存在且不能被改變的函數 的情況。使用這兩個模式可以幫原函數打補丁。如存在一個函數foo();

 

$Sub$ $foo :定義的新功能函數,在foo()函數之前/後使用$Sub$$foo 可以添加一些新的程序代碼。

 

$Super$ $foo :就是原始的未修補的foo函數,使用這個$Super$ $foo函數將直接跳轉到foo()函數。


具體教程可以看ARM官網的資料學習哈,http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0377g/pge1362065967698.html。

因爲super與sub函數屬於c所以我們在cpp文件下需要添加extern“C”進行編譯才行,否則就要出現如下問題了,這些我都遇到過,給大家把雷趟了一遍。

 

 


3.最後的一個bug,STDIO的初始化。

本來一個簡單的C++程序就寫完了,主要就是運行環境,但是程序收錄進去之後無法工作,並且在硬件調試下明顯看到系統到了__main之後不知道跑哪裏去了,F5全速執行幾次程序纔有機會正常運行,這就很奇怪了,後來在網上找資料,終於找到問題所在了,在以爲博主的文章看到,他最後找到問題原來是:

事實上本人也找了近兩天的時間才找到解決辦法,一開始認爲是heap和stack沒有初始化好,嘗試了好久均未成功,後來在網上得到啓發,這個問題是出在STDIO初始化上。

如果要使用C/C++標準庫就要對其STDIO進行Retarget的,很簡單,但卻是非常關鍵的一步,就是這麼一回事啦。

我按照他的操作然後程序就可以正常運行了,下載ARM官方的retarget文件,並加入到工程當中。下載鏈接:

http://infocenter.arm.com/help/topic/com.arm.doc.faqs/attached/3844/retarget.c

 

然後將裏面的串口讀寫按照我現有的硬件需求進行重寫就可以了。如下代碼所示:

char UART_read(void);void UART_write(char ch);    char UART_read(void){  return 0;}void UART_write(char ch){  while(!(USART2->ISR & USART_ISR_TXE)){};   USART2->TDR = ch;}

五、最後測試的一些體驗與感想

  

    剛開始想用C++在MDK中開發是因爲,有些個需求的功能C++特別符合,但是在調試這個demo過程中,發現使用的單片機容量太小,一個<iostream>頭文件的包含就讓一個只有串口加幾組IO控制的最小程序代碼膨脹到了32K,而去掉該頭文件,代碼縮小到了5K。

 

    代碼過大是c++的依賴項過多,而C++ 中模板類 、虛擬繼承 、STL庫等精華由於依賴的問題都不建議在單片機中用,代碼膨脹的時候單片機吃不住。所以C++雖好,可不一定適合小容量的單片機,大家需要按照自己的功能進行有效的使用C++,精簡使用的依賴,這個可以通過每次編譯的生成的.map文件進行增該刪,其次對於C++中內存以及代碼擴增一些基礎知識需要熟悉,負責很容易代碼膨脹,導致我們的程序無法在單片機使用。

 

 這就是我分享的在MDK用C++開發的demo,裏面代碼是實踐過的,如果大家有什麼更好的思路,歡迎分享交流哈。

更多分享,掃碼關注我

微信:Allen-Iverson-me-LYN

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