首先從最基本的 Sim 目標(biāo)開始,其他目標(biāo)都將在此基礎(chǔ)進(jìn)行更改。
根據(jù)本筆記的仿真一節(jié)進(jìn)行設(shè)置,獲得基本的仿真功能。這個(gè)時(shí)候如果點(diǎn)擊 debug 選項(xiàng)是可以進(jìn)入 Debug 模式的。
但因?yàn)檫@是進(jìn)階的文章,要稍微高大上一些,所以直接從 uCOS II 工程開始,關(guān)于 uCOS II 的移植可以參考網(wǎng)上資料,也可以參考本筆記的系統(tǒng)章節(jié),不再詳述,因?yàn)楸竟?jié)主要講解的是模板的建立,是一個(gè)綜合性很強(qiáng)的內(nèi)容,所以如果基礎(chǔ)薄弱的需要多學(xué)習(xí)其他基礎(chǔ)內(nèi)容,不懂的上網(wǎng)搜索或者查看本筆記是否有相關(guān)的內(nèi)容。
言歸正傳,因?yàn)椴捎昧饲度胧讲僮飨到y(tǒng),所以為了更好的移植性,所以 main 函數(shù)主要就是啟動一個(gè)操作系統(tǒng),并創(chuàng)建一個(gè)啟動任務(wù),這個(gè)啟動任務(wù)優(yōu)先級最高,并且承擔(dān)了裸機(jī)開發(fā)時(shí)進(jìn)行底層初始化功能。這是最基本的功能,但是因?yàn)槭悄0?,?dāng)然要加入一些常用的代碼了:
?
1、調(diào)試停止
這個(gè)是為了方便調(diào)試的,目的是當(dāng)單片機(jī)處于調(diào)試模式時(shí),可以停止一些模塊的時(shí)鐘(這其實(shí)是硬件調(diào)試的時(shí)候才需要的功能,只不過在這里先加上了),這樣就不必?fù)?dān)心發(fā)生內(nèi)核停止了,模塊還在工作的情況,比如 TIM1 輸出 PWM 實(shí)驗(yàn)中,Debug 模式下,單片機(jī)停止運(yùn)行,如果沒有這句代碼(可以根據(jù)需要修改該代碼,不必一樣),那么 TIM1 還是會輸出 PWM 的,但是一旦加上該語句,那么當(dāng)內(nèi)核停止工作時(shí),TIM1 時(shí)鐘也停止,這樣定時(shí)器就不會輸出 PWM 波了,方便觀察現(xiàn)象。
2、中斷分組
當(dāng)系統(tǒng)需要使用中斷時(shí),首先需要設(shè)置中斷分組情況,根據(jù)實(shí)際情況設(shè)置分組。一般可以設(shè)置為組 2,兩位搶占優(yōu)先級,兩位子優(yōu)先級,如果不設(shè)置分組,默認(rèn)情況下為全部 bit 用于搶占優(yōu)先級(雖然 CM3 內(nèi)核支持 200 多個(gè)中斷向量,但實(shí)際的單片機(jī)根本沒有這么多中斷,也不能支持 200 多的中斷嵌套,所以只有部分 bit 位是有效的,比如 STM32F103 系列單片機(jī)實(shí)際上只用了高四位(用高四位是為了兼容,最大支持 16 層中斷嵌套),所以能設(shè)置 5 種分組情況)
通過設(shè)置該分組后通過 MDK 仿真可以看到默認(rèn)的分組情況已經(jīng)發(fā)生改變了:
默認(rèn)分組
更改分組后
?
3、串口
串口應(yīng)該是最常用的外設(shè)了,工程模板需要包含,并且添加一些字符處理函數(shù)、printf 重定向等。為了更有效率的接收發(fā)送數(shù)據(jù),可以采用 DMA,使用 DMA 接收和發(fā)送數(shù)據(jù)將最大程度的解放 CPU,并且可以開啟串口空閑中斷,這樣就可以接收任意長度的串口數(shù)據(jù)了,當(dāng)然每個(gè)數(shù)據(jù)幀之間必須插入空閑幀,這個(gè)時(shí)間可以根據(jù)處理情況適當(dāng)延長。
突發(fā)的情況下數(shù)據(jù)傳輸量可能很大,可以采用隊(duì)列接收數(shù)據(jù),這樣就不用擔(dān)心接收到的數(shù)據(jù)在沒處理完成前被后來的數(shù)據(jù)沖掉了。STM32F4 系列單片機(jī)有雙緩沖,可以一邊接收數(shù)據(jù),一邊處理數(shù)據(jù),可以好好利用一下,并且可以開啟 FIFO,這樣能最大程度的保證接收效率的問題,當(dāng)然開啟了 FIFO 在操作上更麻煩一些了。關(guān)于 STM32F4 的 DMA 可以查看本筆記的?DMA章節(jié)。
但是如果說你沒有串口模塊,也是有辦法解決數(shù)據(jù)輸出問題的,就是使用 ITM 的方式,感興趣的查看這個(gè)章節(jié)。
?
4、總線協(xié)議
最常用的總線協(xié)議有單總線、I2C、SPI,這些總線協(xié)議講解網(wǎng)上一大堆,可以直接拿來使用,如果覺得別人的代碼效率不高,也可以自己寫,這樣可以加深對總線協(xié)議的理解,當(dāng)對一種協(xié)議理解比較透徹時(shí),再學(xué)習(xí)其他協(xié)議時(shí)也就事半功倍了,如果你的工作是經(jīng)常和芯片打交道,建議你把這些協(xié)議都試著寫一下,畢竟這應(yīng)該算是最簡單的協(xié)議了吧,當(dāng)然了,如果你的能力提高了,可以加入 CAN 協(xié)議、SDIO 協(xié)議、USB 協(xié)議等等,首先第一步應(yīng)該是利用單片機(jī)的內(nèi)部模塊嘗試配置成功,如果你不僅僅滿足于模塊的使用的話,可以嘗試采用模擬的方式去實(shí)現(xiàn)它。這里要注意的一點(diǎn)是,STM32 硬件 I2C 有一些 BUG,使用前必須好好研究一下,如果項(xiàng)目緊急的話,那么使用模擬的 I2C 會是不錯(cuò)的選擇。
5、添加一些必要的文件
比如 cortexm3_macro 文件,這兩個(gè)文件(.c 和 .h)包含了一些 Cotex-M3 特殊的匯編指令;比如 core_cm3 文件,這兩個(gè)文件包含了內(nèi)核功能的操作函數(shù);如果對數(shù)學(xué)計(jì)算能力高的,可以使用官方針對內(nèi)核優(yōu)化過的 DSP 算法庫,而不用標(biāo)準(zhǔn)庫函數(shù),將大大提高計(jì)算能力,如果說 STM32F1 的計(jì)算能力在使用官方優(yōu)化的算法庫下還是不能達(dá)到計(jì)算要求,那么可以采用 STM32F4 系列,這個(gè)系列用了硬件浮點(diǎn)運(yùn)算單元,效率提高上百倍,當(dāng)然了,也要使用官方的文件才能達(dá)到,標(biāo)準(zhǔn)庫應(yīng)該是沒有利用上這個(gè)硬件單元的。這里要注意的一點(diǎn)是,當(dāng)在 uCOS 下使用硬件浮點(diǎn)運(yùn)算單元時(shí),可能會出現(xiàn)問題,這個(gè)問題可以上網(wǎng)解決,也可以在本筆記的 uCOS II 系統(tǒng)篇獲得解決方法。注意,如果一些已經(jīng)驗(yàn)證過的文件,比如官方代碼庫、自己寫的驅(qū)動函數(shù)等等這些,覺得不需要再改變了,那么可以設(shè)置為只讀屬性,這樣是為了當(dāng)你使用字符替換時(shí)不會意外的修改這些文件,因?yàn)槟悴恢滥阋鎿Q的名字是否也出現(xiàn)在其他文件中,如果你不小心采用了 Current project 這種方式進(jìn)行全局替換的話,那么有可能會將別的文件內(nèi)的字符也替換掉,如果真的如此,你只能一個(gè)一個(gè)文件撤銷了,那是很麻煩的事情。
6、一些必要的宏和調(diào)試信息
經(jīng)常使用的一些宏定義可以添加到一個(gè)單獨(dú)的文件中,比如說位帶操作的宏定義,兩三個(gè)數(shù)比較大小的宏定義等等,這些你認(rèn)為需要的宏定義都可以添加到里面,不要害怕添加到里面會影響效率,沒有的事,即使你加入一些沒有調(diào)用的函數(shù)也不會占用你的代碼空間的(如果是 51 的話,如果你在工程中出現(xiàn)沒有調(diào)用的函數(shù),會被警告,但 ARM 不會),這些沒有調(diào)用的函數(shù)是不會鏈接到你的目標(biāo)文件的(但是你不知道你的代碼是會用在 51 工程還是 ARM 工程,不閑麻煩的話,就如 uCOS 源碼一般,加入一些宏開關(guān)吧)。這些宏更是如此,效率問題根本不存在的,大膽的添加你需要的宏就是了。當(dāng)你經(jīng)歷了大量的修修改改工作時(shí),你就會知道多寫一些宏定義是有多重要了。新手會感慨一個(gè)簡單的數(shù)據(jù)卻用了一堆的宏定義去表示,煩不勝煩;而高手看到代碼里到處都是直接的數(shù)字表示,卻是分分鐘有重寫代碼的沖動!
設(shè)置固件版本信息:
輸出產(chǎn)品信息:
?
7、多使用 ##
可能初學(xué)者一般都沒用過 ##,但是如果你用過 ## ,并且善于用 ##,那么你會喜歡的,尤其是當(dāng)你需要寫大量類似的代碼的時(shí)候更是如此,因?yàn)樗芎艽蟪潭鹊奶岣吣銓懘a的效率。關(guān)于 ## 的使用可以看本筆記的相關(guān)內(nèi)容。當(dāng)然使用了 ## 會使代碼變得不直觀,那么你可以使用字符替換的功能以簡化代碼的書寫。
?
8、工程基本配置
開啟實(shí)時(shí)語法錯(cuò)誤窗口:這樣可以更快的發(fā)現(xiàn)語法錯(cuò)誤,而不需要編譯后才發(fā)現(xiàn):
Debug 模式下,開啟一些必要的觀察窗口:
輸出一些必要的文件,如果想要輸出 HEX 文件,那么勾選即可
Periodic Window Update 勾選上表示能實(shí)時(shí)刷新窗口,這樣即使程序正在運(yùn)行,Watch Windows 窗口的數(shù)據(jù)也是會實(shí)時(shí)更新的。
生成 bin 文件(具體如何生成 bin 文件可以參考本筆記相關(guān)章節(jié))
代碼優(yōu)化級別設(shè)置為最低(方便調(diào)試,這樣一些代碼就不會被優(yōu)化掉了),開啟 C99 模式(方便寫代碼,比如局部變量可以不必局限在代碼最前面定義),如果有些宏定義是全局的,也可以在該窗口設(shè)置,比如開啟參數(shù)檢查功能的宏可以在這里添加:USE_FULL_ASSERT。每個(gè)宏之間采用英文逗號隔開。
因?yàn)殚_啟了參數(shù)檢查功能,所以需要實(shí)現(xiàn)一個(gè)函數(shù)原型:
void assert_failed(uint8_t* file, uint32_t line);
原型類似如下:
以上函數(shù)原型是根據(jù)我個(gè)人的習(xí)慣寫的,如果斷言失敗將進(jìn)入該函數(shù),首先會輸出哪里有錯(cuò)誤,當(dāng)然考慮到可能沒有實(shí)現(xiàn)串口的情況,所以使用 sprintf 函數(shù)將調(diào)試信息輸出到 DebugInfo 中,這樣就可以直接使用 Watch 窗口查看這個(gè)數(shù)組的內(nèi)容了,并能很快定位到底是哪一個(gè)地方出現(xiàn)了問題,進(jìn)而排除該問題。最后使用軟件斷點(diǎn) __breakpoint(0) 將程序停止,這樣就不會在全速運(yùn)行狀態(tài)下明明已經(jīng)斷言失敗了,還傻乎乎的在那里干等著。
STM32F4 的仿真模板其他的都和 F1 仿真類似,只是因?yàn)?MDK 對 STM32F4 的支持并不怎么好,所以會有比較多的問題存在,這個(gè)我準(zhǔn)備用單獨(dú)一小節(jié)詳細(xì)討論。
9、擁有自己的模板文件
比如我的頭文件模板:
頭文件注意在正式內(nèi)容外加上這個(gè):
這應(yīng)該是用于兼容 C++ 代碼的,萬一你的代碼會在 C++ 項(xiàng)目中用到,就不必修改了。????然后是頭文件:
函數(shù)體說明:
STM32F1 和 STM32F4 底層代碼兼容:
到此,整個(gè) Simu 目標(biāo)即完成了,接下來開始添加 FLASH 目標(biāo)。
關(guān)注魚鷹,更多精彩!