本篇將介紹MDK下99%用戶都不知道的萬(wàn)能printf方法。
一、說(shuō)在前面的話
你聽(tīng)說(shuō)過(guò)J-Link的RTT么?官方的宣傳是這樣的:

簡(jiǎn)單來(lái)說(shuō),只要擁有了J-Link,你就可以享受以下的便利:
無(wú)需占用USART或者USB轉(zhuǎn)串口工具,將printf重定位到一個(gè)由J-LINK提供的虛擬串口上;
支持任何J-LINK聲稱(chēng)支持的芯片
高速通信,不影響芯片的實(shí)時(shí)響應(yīng)
它的缺點(diǎn)也是明顯的:
你必須擁有一個(gè)J-Link,如果你使用的是 CMSIS-DAP或者ST-Link之類(lèi)的第三方調(diào)試工具,就無(wú)法享受這一福利;
你必須在工程中手動(dòng)插入一段代碼
曾幾何時(shí),J-Link的這一福利讓多少非J-Link用戶羨慕嫉妒恨,看看手中的ST-Link、ULINKpro和各類(lèi)廉價(jià)的CMSIS-DAP板載調(diào)試器——“隔壁鄰居的小孩都饞哭了”
如果我告訴你,其實(shí)MDK中內(nèi)置了一種非常簡(jiǎn)單廉價(jià)的方式,可以讓你實(shí)現(xiàn)類(lèi)似的功能,并具有以下特點(diǎn):
支持所有的調(diào)試仿真器,哪怕自己手搓的CMSIS-DAP都行
MDK原生功能,連CMSIS-Pack都不用安裝
點(diǎn)幾下鼠標(biāo)就可以通過(guò)RTE完成部署
除了簡(jiǎn)單的初始化函數(shù)外,無(wú)需手動(dòng)插入代碼
可以將你的printf輸出直接打印在MDK的Debug (printf) View窗口中
你是否心動(dòng)了呢?
二、部署從未如此簡(jiǎn)單
2.1RTE配置
依次通過(guò)菜單 Project->Manage->Run-Time Environment 打開(kāi)RTE配置窗口:

找到并展開(kāi)Compiler選項(xiàng)卡,勾選Event Recorder,并確保Variant下拉列表選中的是默認(rèn)的DAP。

展開(kāi)Compiler下的I/O,勾選STDOUT,并在Variant下拉列表中選擇 EVR——這里EVR是Event Recorder的縮寫(xiě)。單擊確定后,我們會(huì)在工程管理器中看到以下的內(nèi)容:

至此,所需的工具都已經(jīng)成功地加入到工程中了。
雖然這里EventRecorderConf.h是一個(gè)可以編輯的狀態(tài),但實(shí)踐中,我們基本不用去碰他——使用默認(rèn)配置即可。
2.2服務(wù)初始化
在包含main()函數(shù)的C代碼文件中,按照如下的格式添加對(duì)頭文件的包含:
#include#if defined(RTE_Compiler_EventRecorder) # include #endif
在main()函數(shù)中添加對(duì)EventRecorder服務(wù)的初始化:
void main(void)
{
...
#if defined(RTE_Compiler_EventRecorder)
&& defined(RTE_Compiler_IO_STDOUT_EVR)
EventRecorderInitialize(0, 1);
#endif
...
}
如果你從未使用過(guò)EventRecorder也不必驚慌,這段代碼的主要作用是為printf專(zhuān)門(mén)開(kāi)啟一個(gè)數(shù)據(jù)通道。
理論上,到這里,我們就已經(jīng)完成了部署,可以在進(jìn)入調(diào)試模式后,通過(guò)MDK的Debug (printf) View窗口來(lái)觀察printf的輸出結(jié)果了。比如,我們?cè)趍ain()函數(shù)中打印一個(gè) "hello world ":
#include#include #if defined(RTE_Compiler_EventRecorder) # include #endif void main(void) { ... #if defined(RTE_Compiler_EventRecorder) && defined(RTE_Compiler_IO_STDOUT_EVR) EventRecorderInitialize(0, 1); #endif ... printf("Hello World "); ... }
編譯,一切順利的話,進(jìn)入調(diào)試模式后通過(guò)菜單View->Serial Windows->Debug (printf) View打開(kāi)窗口:

運(yùn)行后,可以在Debug (printf) View窗口中看到如下的結(jié)果:
三、常見(jiàn)問(wèn)題
如果你的工程中從未提供過(guò)對(duì) ".bss.noinit"數(shù)據(jù)段的處理,那么很可能會(huì)發(fā)現(xiàn)通過(guò)上述方法實(shí)現(xiàn)的printf輸出似乎不是很穩(wěn)定——時(shí)有時(shí)無(wú)——處于一種薛定諤的狀態(tài)。
這是由于EventRecorder有一段數(shù)據(jù)放置在了 “.bss.noinit” section中——以求芯片復(fù)位后不會(huì)破壞其中原有的內(nèi)容。
如果你的工程沒(méi)有專(zhuān)門(mén)針對(duì) “.bss.noinit” 的處理,那么就會(huì)在進(jìn)入調(diào)試模式后,從Command窗口中看到類(lèi)似如下的信息:

即:
Warning: Event Recorder not located in uninitialized memory!
如果遇到這種情況應(yīng)該怎么辦呢?
打開(kāi)工程配置窗口“Options for Target”,切換到“Linker”選項(xiàng)卡:

首先,一定要確保你勾選了圖中的“Use Memory Layout from Target Dialog”選項(xiàng)。在這一前提下,再次取消對(duì)它的勾選:

我們會(huì)看到,MDK基于當(dāng)前的Memory Layout,為我們?cè)贠ut目錄下生成了一個(gè)與工程同名的鏈接腳本(比如圖中的工程名叫example,因此生成的鏈接腳本為example.sct)。
單擊Edit按鈕,可以看到腳本的內(nèi)容:

先別著急半路開(kāi)香檳——該文件是系統(tǒng)自動(dòng)生成的,如果我們不移動(dòng)它的位置,那么只要哪次手抖勾選了“Use Memory Layout from Target Dialog”,它的內(nèi)容就會(huì)立即被覆蓋掉——意味著我們?cè)诤罄m(xù)步驟中所做的修改就會(huì)付諸東流。
為了避免該問(wèn)題,應(yīng)該將它從Object目錄中移動(dòng)到工程目錄下。具體步驟為:右鍵單擊腳本文件名:

選擇“Open Container Folder”來(lái)打開(kāi)文件所在目錄:

找到Scatter Script腳本文件后,將其拷貝到上一級(jí)目錄下(也就是工程目錄):

重新打開(kāi)工程配置窗口:

確保我們“沒(méi)有”選中“Use Memory Layout from Target Dialog”選項(xiàng),并在Scatter File文本框中直接填寫(xiě)我們剛剛拷貝出來(lái)的腳本文件名(由于我們直接放在工程目錄下,因此這里直接用相對(duì)路徑"./example.scat"或者"example.scat"就行)。單擊OK保存配置。
打開(kāi)example.sct,在RW_IRAM1后面追加如下的代碼:
ZI_RAM_UNINIT +0 UNINIT {
.ANY (.bss.noinit)
}
效果大約類(lèi)似這樣:

保存后重新編譯,再次進(jìn)入 Debug 模式,問(wèn)題就應(yīng)該解決了。
這里步驟的核心思想是在scatter script內(nèi)緊接著為RW和ZI的execution region為.bss.noinit提供一個(gè)屬性為UNINIT的專(zhuān)屬execution region。
在領(lǐng)會(huì)精神的情況下,如果你的工程原本就使用了scatter script也可以如法炮制。俗話說(shuō)解鈴還須系鈴人,如果你還是不知道怎么處理,那么就去找 你工程中scatter script的作者吧。
值得強(qiáng)調(diào)的是:如果你的MDK版本太老,為了確保最佳的用戶體驗(yàn),還是推薦盡快升級(jí)吧。您可以在關(guān)注【裸機(jī)思維】公眾號(hào)后發(fā)送關(guān)鍵字【MDK】來(lái)獲取其最新的網(wǎng)盤(pán)鏈接。
四、說(shuō)在后面的話
總的來(lái)說(shuō),MDK通過(guò)EventRecorder為我們提供了一個(gè)通用便捷的方式來(lái)重定向printf——無(wú)論你使用什么調(diào)試仿真器,甚至是FVP,都可以享受來(lái)自“MDK”的陽(yáng)光普照。
對(duì)很多有分發(fā)自己工程作為模板的小伙伴來(lái)說(shuō),使用該方法后將不再限制用戶必須使用J-Link之類(lèi)的工具,而是可以放開(kāi)手腳,獲得了“開(kāi)袋即食”的調(diào)試體驗(yàn)。
最后強(qiáng)調(diào)一下哦,EventRecorder只在調(diào)試階段有意義,如果我們需要在產(chǎn)品的正常工作模式下使用printf,還是老老實(shí)實(shí)把Compiler->IO->STDOUT配置為User:

實(shí)現(xiàn)stdout_putchar()函數(shù)——用它來(lái)發(fā)送字符到具體的外設(shè)吧,比如:
int stdout_putchar(int ch)
{
if ('
' == ch) {
int temp = '
';
while(Driver_USART0.Send(&temp, 1) != ARM_DRIVER_OK);
}
if (Driver_USART0.Send(&ch, 1) == ARM_DRIVER_OK) {
return ch;
}
return -1;
}
-
usb
+關(guān)注
關(guān)注
60文章
8462瀏覽量
285484 -
串口
+關(guān)注
關(guān)注
15文章
1626瀏覽量
83204 -
MDK
+關(guān)注
關(guān)注
4文章
211瀏覽量
33713 -
J-Link
+關(guān)注
關(guān)注
0文章
91瀏覽量
24005 -
Printf
+關(guān)注
關(guān)注
0文章
84瀏覽量
14799
原文標(biāo)題:MDK下99%用戶都不知道的萬(wàn)能printf方法
文章出處:【微信號(hào):Ithingedu,微信公眾號(hào):安芯教育科技】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
要學(xué)習(xí)protel了,不知道該學(xué)習(xí)是99還是***,更不知道在哪里找
電子萬(wàn)能試驗(yàn)機(jī)與液壓萬(wàn)能試驗(yàn)機(jī)的區(qū)別
MDK下99%用戶都不知道的萬(wàn)能printf方法
winxp萬(wàn)能聲卡驅(qū)動(dòng),程序下載
萬(wàn)能電視遙控器使用方法
不知道電動(dòng)車(chē)電池的型號(hào)怎么辦?
萬(wàn)能遙控器設(shè)置方法_萬(wàn)能遙控器代碼
萬(wàn)能轉(zhuǎn)換開(kāi)關(guān)選型_萬(wàn)能轉(zhuǎn)換開(kāi)關(guān)使用
關(guān)于人工智能的日常應(yīng)用很多人都不知道
怎樣調(diào)整地磅萬(wàn)能遙控器的四角誤差
萬(wàn)能材料試驗(yàn)機(jī),如何進(jìn)行整機(jī)檢定?
MDK下99%用戶都不知道的萬(wàn)能printf方法
評(píng)論