一、概述
????本文面向已經(jīng)懂得軟件基本操作的職業(yè)老手,如果是未使用過(guò)該軟件的小鮮肉,請(qǐng)移步基礎(chǔ)篇。這里以STM32芯片為例對(duì)工具進(jìn)行講解,其他品牌的芯片在調(diào)試方面上可能存在差異。
二、軟件說(shuō)明
????Keil提供了包括C編譯器、宏匯編、鏈接器、庫(kù)管理和一個(gè)功能強(qiáng)大的仿真調(diào)試器等在內(nèi)的完整開發(fā)方案,通過(guò)一個(gè)集成開發(fā)環(huán)境(μVision)將這些部分組合在一起。
????目前軟件對(duì)中文的支持不友好,不建議安裝網(wǎng)上的一些漢化包之類的。另外建立的工程文件路徑也盡量不要存在中文,否則可能會(huì)出現(xiàn)一些異常。
演示版本:5.24a
三、軟件使用
3.1 基本調(diào)試操作
????首先點(diǎn)擊"Debug->Start/Stop Debug Session"或下圖2位置,進(jìn)入調(diào)試。
????如果前面工程配置里選擇了復(fù)位調(diào)試,則進(jìn)入調(diào)試后,會(huì)停在main函數(shù)頭部
????復(fù)位(Reset):對(duì)程序進(jìn)行復(fù)位操作,根據(jù)燒錄器不同的復(fù)位方式配置會(huì)觸發(fā)不同的復(fù)位類型。
????全速運(yùn)行(Run):使當(dāng)前程序開始正常全速運(yùn)行,直到程序遇到斷點(diǎn)時(shí)停止。
????停止運(yùn)行(Stop):當(dāng)程序全速運(yùn)行時(shí),點(diǎn)擊此按鍵可停止程序運(yùn)行,停的是當(dāng)前點(diǎn)擊時(shí)的程序運(yùn)行位置。
????單步調(diào)試(Step):根據(jù)當(dāng)前調(diào)試的窗口的語(yǔ)言,執(zhí)行單條語(yǔ)句。如果遇到函數(shù),則會(huì)進(jìn)入函數(shù)內(nèi)部。如果是在反匯編窗口中,則只執(zhí)行一條匯編指令。
????單步跳過(guò)調(diào)試(Step Over):如果是在C語(yǔ)言窗口中,則是按單條語(yǔ)句執(zhí)行,與單步調(diào)試不同的是,遇到函數(shù)不會(huì)進(jìn)入函數(shù)內(nèi)部,而是直接全速運(yùn)行函數(shù),并跳到下一條語(yǔ)句。
????單步返回調(diào)試(Step Out):如果是在C語(yǔ)言窗口中,則是直接全速運(yùn)行當(dāng)前函數(shù)后面所有內(nèi)容,直到函數(shù)返回上一級(jí)。
????插入/移除斷點(diǎn):如果當(dāng)前光標(biāo)所在行未有斷點(diǎn),則插入斷點(diǎn)(前提是當(dāng)前行可以插入,如果無(wú)法插入會(huì)顯示一個(gè)感嘆號(hào)),在有斷點(diǎn)的情況下則是移除斷點(diǎn)。插入斷點(diǎn)后,當(dāng)前行前面會(huì)有個(gè)紅圓表示斷點(diǎn)位置。也可以通過(guò)直接點(diǎn)擊紅圓位置進(jìn)行插入/移除斷點(diǎn)的操作。另一種斷點(diǎn)方式,是通過(guò)指令來(lái)控制,當(dāng)然也可以使用Keil提供的界面化操作,設(shè)置某個(gè)變量讀或?qū)憰r(shí)觸發(fā)斷點(diǎn)。不過(guò)目前貌似有部分芯片不支持這種操作。注:斷點(diǎn)最多只能打7個(gè)。
????使能/禁止斷點(diǎn):開啟或禁止當(dāng)前光標(biāo)所在行的斷點(diǎn)。禁止后紅圓變成白圓。
????禁止所有斷點(diǎn):禁止當(dāng)前所有的斷點(diǎn)。
????刪除所有斷點(diǎn):刪除所有斷點(diǎn)。
3.1 調(diào)試窗口
- 變量查看窗口——Watch1,Watch2
????通過(guò)"View->Watch Windows->Watch1、Watch2"可以選擇打開Watch窗口,也可以在工具欄這里打開。再點(diǎn)擊一次則可以關(guān)閉。
????通過(guò)選中一個(gè)變量,右鍵添加入對(duì)應(yīng)的Watch窗口,可以追蹤查看當(dāng)前變量的變化狀態(tài)。注意,只有全局變量可以全程監(jiān)視,臨時(shí)變量只有在進(jìn)入當(dāng)前函數(shù)中才可監(jiān)視到其數(shù)據(jù),用static關(guān)鍵詞修飾的變量無(wú)法監(jiān)視。
????如果當(dāng)前變量沒有實(shí)時(shí)更新,則需要點(diǎn)擊"View->Periodic Window Update"將其勾選上。
????在"Watch"窗口中,可以查看當(dāng)前變量名稱、值、數(shù)據(jù)類型,如果當(dāng)前變量類型為結(jié)構(gòu)體,則可以以對(duì)應(yīng)的結(jié)構(gòu)形式進(jìn)行展開查看。
- 內(nèi)存查看窗口——Memory
????通過(guò)"View->Memory Windows->Memory1/2/3/4"打開Memory窗口,也可以通過(guò)工具欄
這個(gè)圖標(biāo)打開。打開的狀態(tài)下再按一次則可關(guān)閉。
????在Memory窗口中輸入想要查看內(nèi)存的起始地址,另外右上角的鎖可以把當(dāng)前界面鎖定下來(lái)。
????另外如果查看的是Ram的地址,那其中的數(shù)據(jù)也可以直接通過(guò)此窗口進(jìn)行修改。
- 系統(tǒng)視窗——System Viewer Windows(這個(gè)根據(jù)不同芯片會(huì)有不同的展示)
????可以在"Peripherals"選項(xiàng)欄中選擇"System Viewer"系統(tǒng)視窗中對(duì)應(yīng)的外設(shè),選擇"Core Peripherals"則是內(nèi)核調(diào)試窗口。另外系統(tǒng)視窗也可以通過(guò)工具欄中這個(gè)位置可以打開。
????這個(gè)窗口用于查看當(dāng)前單片機(jī)外設(shè)及內(nèi)核寄存器的值,在調(diào)試外設(shè)底層時(shí)經(jīng)常會(huì)使用到。
????在這個(gè)窗口中可以直接修改外設(shè)寄存器的值,當(dāng)然部分只讀寄存器是無(wú)法修改的,有些則是需要在特定條件下才可以設(shè)置生效,具體就得看對(duì)應(yīng)的芯片手冊(cè)里寄存器的說(shuō)明了。
- 調(diào)度關(guān)系窗口——Call Stack Window
????可以在"View->Call Stack Window"打開此窗口,也可以在工具欄這個(gè)位置打開 。
????這個(gè)窗口用于查看當(dāng)前程序調(diào)度關(guān)系,當(dāng)出現(xiàn)有進(jìn)入硬件錯(cuò)誤異常調(diào)試時(shí)可以快速定位到是哪里觸發(fā)的異常。這個(gè)窗口是的調(diào)度關(guān)系是從當(dāng)前程序堆棧里獲取的數(shù)據(jù)并將其圖形化,所以如果當(dāng)前堆棧數(shù)據(jù)被破壞,則此窗口也將無(wú)法查看調(diào)度關(guān)系。
????該窗口里顯示的調(diào)度關(guān)系是從下至上調(diào)用的,最上面的表示當(dāng)前程序所處的函數(shù)。展開對(duì)應(yīng)的函數(shù),可以查看各層調(diào)用函數(shù)跳轉(zhuǎn)之前保存的一些臨時(shí)變量等信息。
- 寄存器窗口——Register Window
????該窗口可在"View->Registers Window"處打開,也可以在工具欄這個(gè)位置打開。
????這個(gè)窗口用于查看當(dāng)前內(nèi)核的相關(guān)寄存器,如匯編里常說(shuō)的15個(gè)通用寄存器。當(dāng)然調(diào)試中比較常用的是其中的SP、LR、PC三個(gè)寄存器。SP為當(dāng)前棧的地址位置,PC為當(dāng)前程序地址,LR為函數(shù)跳轉(zhuǎn)前的地址,即當(dāng)前函數(shù)返回的地址。
????另外"Banked"中的MSP為當(dāng)前程序系統(tǒng)主棧,PSP則為操作系統(tǒng)的任務(wù)棧,這兩者的區(qū)別是,如果使用了操作系統(tǒng),則當(dāng)前任務(wù)中的所有調(diào)度關(guān)系使用的是任務(wù)棧,而類似中斷這種內(nèi)核的操作使用的是主棧;如果未使用操作系統(tǒng),則只會(huì)使用主棧,不會(huì)使用任務(wù)棧。
????通過(guò)"Internal"可以查看當(dāng)前是處在中斷還是任務(wù)中,Mode為"Thread"表示是在線程/任務(wù)中,或者是函數(shù)中(非中斷),為"Handle"表示是在中斷中;Privilege為"Privelege"表示當(dāng)前處于特權(quán)模式。Stack為"PSP"表示當(dāng)前使用的是任務(wù)棧,為"MSP"則表示使用的是主棧。
- 反匯編調(diào)試窗口——Disassembly Window
????該窗口在"View->Disassembly Window"中可以打開,也可以在工具欄中這個(gè)位置打開。
????該窗口是通過(guò)bin文件反匯編出來(lái)的匯編文件(匯編跟二進(jìn)制原本就是一一對(duì)應(yīng)的關(guān)系)。當(dāng)設(shè)置了優(yōu)化等級(jí)后,部分C語(yǔ)言的調(diào)試會(huì)變得困難(匯編跟C語(yǔ)言不是一一對(duì)應(yīng),而程序運(yùn)行又是完全根據(jù)匯編來(lái)走的),此時(shí)可能需要使用匯編窗口進(jìn)行調(diào)試。
- 命令窗口——Command Window
????該窗口在"View->Command Window"中可以打開,也可能在工具欄這個(gè)位置打開。
????這個(gè)窗口可用來(lái)輸入一些控制命令,比如保存輸出當(dāng)前內(nèi)存地址等。在命令窗口中輸入如下導(dǎo)出指令,按下回車即可導(dǎo)出數(shù)據(jù)
save filename startAddr,endAddr
filename:導(dǎo)出數(shù)據(jù)的文件名,無(wú)論后綴是什么,導(dǎo)出的格式都是十六進(jìn)制文件。沒有輸入路徑時(shí),文件自動(dòng)保存在當(dāng)前工程根目錄下。
startAddr, endAddr:需要導(dǎo)出數(shù)據(jù)的起始地址和結(jié)束地址,也可以通過(guò)表達(dá)式寫出來(lái)。
例如:
save ExportData.hex 0x08000000, 0x08000000+0x2000
- 函數(shù)地址表——Symbols Window
????該窗口在"View->Symbols Window"中打開,也可能在工具欄這個(gè)位置打開。
????可以查看當(dāng)前所有程序的函數(shù)調(diào)用關(guān)系及其所在地址。
- 串口調(diào)試窗口——Serial Windows
????該窗口在"View->Serial Windows"中打開,也可以在工具欄這個(gè)位置打開。
暫未使用過(guò),后續(xù)再添加。
- 邏輯分析窗口——Analysis Windows
????該窗口在"View->Analysis Windows"中打開,也可以在工具欄這個(gè)位置打開。
????這個(gè)貌似只能在軟件模擬仿真中使用,如果使用硬件調(diào)試,需要硬件支持。
- 跟蹤窗口——Trace Windows
????該窗口可以在"View->Trace Windows"中打開,也可以在工具欄這個(gè)位置打開。
暫時(shí)未使用過(guò),后續(xù)再添加。
四、調(diào)試應(yīng)用
- HardFault(硬件錯(cuò)誤)
????這個(gè)可以算是最常見的一個(gè)問題了,在開發(fā)過(guò)程中多多少少會(huì)遇到過(guò)程序死機(jī)的問題,而死機(jī)的大部分原因都是進(jìn)入的HardFault中斷,即常說(shuō)的硬件錯(cuò)誤中斷。要想知道這個(gè)怎么調(diào)試,首先得清楚這是什么,怎么觸發(fā)。
????觸發(fā)原因:內(nèi)存溢出,堆棧溢出,數(shù)組越界,中斷錯(cuò)誤,除0(在某些編譯器下會(huì)有錯(cuò)誤)等。前面三個(gè),可以歸結(jié)為都是內(nèi)存異常操作導(dǎo)致,但因?yàn)槠涑霈F(xiàn)方式不一樣,所以調(diào)試方式也不同。
????從現(xiàn)象反推,當(dāng)出現(xiàn)這個(gè)錯(cuò)誤時(shí),第一時(shí)間查看函數(shù)的調(diào)度關(guān)系,看最后是死在哪個(gè)位置。如果不是堆棧溢出,一般來(lái)說(shuō)是可以直接查到進(jìn)入硬件錯(cuò)誤前的最后執(zhí)行的代碼位置的。當(dāng)然內(nèi)存溢出跟數(shù)組越界也有可能導(dǎo)致無(wú)法查看調(diào)度關(guān)系,因?yàn)檫@個(gè)調(diào)度關(guān)系就是從程序運(yùn)行棧里取出數(shù)據(jù)進(jìn)行展示,所以當(dāng)棧數(shù)據(jù)被破壞,則無(wú)法使用此方式進(jìn)行調(diào)試。
????知道死機(jī)位置后(其實(shí)大概率就是因?yàn)槟硞€(gè)異常指針的引用導(dǎo)致的問題),此時(shí)就去查找異常指針出現(xiàn)的原因。首先從邏輯層面看,異常指針是否是因?yàn)槟硞€(gè)邏輯給指針賦了個(gè)錯(cuò)誤值。
????其次是數(shù)據(jù)越界的角度來(lái)看,在Map文件中查找該指針的內(nèi)存地址,查看其內(nèi)存前后是否存在一些數(shù)組或結(jié)構(gòu)體,然后去檢查前后數(shù)組或結(jié)構(gòu)體的操作是否存在下標(biāo)溢出,指針偏移錯(cuò)誤等問題。
????還有最后一種,就是直接從內(nèi)存里獲取數(shù)據(jù)作為指針地址進(jìn)行引用,此類用法一般是在日志操作或GUI中比較常用,這種情況就要去內(nèi)存數(shù)據(jù)來(lái)源是否存在問題。
????除以上三種可能性外,還有一種可能對(duì)一些人是涉及知識(shí)盲區(qū)的,就是引用地址沒有地址對(duì)齊。這一部分是涉及內(nèi)核的一些知識(shí)。這里簡(jiǎn)單講下,對(duì)于M0內(nèi)核,指針引用地址需要根據(jù)其引用的數(shù)據(jù)類型進(jìn)行對(duì)齊。比如以下代碼:
int main(void)
{
uint32_t *p = 0x20000001;
*p = 20; /* 這句一執(zhí)行就會(huì)導(dǎo)致異常 */
}
還有一種是操作指針本身的地址沒有4字節(jié)對(duì)齊,也會(huì)出現(xiàn)問題。如下代碼:
/* 實(shí)際這樣子定義編譯器會(huì)報(bào)錯(cuò)(在Keil中編譯),這里只是為了直觀表示 */
uint32_t *p __attribute__((at(0x20000001)));
int main(void)
{
p = 0x20000010; /* 這句一執(zhí)行就會(huì)導(dǎo)致異常 */
}
- 復(fù)位
????復(fù)位有幾種類型,一是看門狗復(fù)位,二是軟件復(fù)位,三是硬件復(fù)位。復(fù)位類型可以通過(guò)芯片自帶的復(fù)位寄存器進(jìn)行查看。不過(guò)查看前需要手動(dòng)清除所有復(fù)位標(biāo)志,不然其復(fù)位標(biāo)志會(huì)一直保留著。
????先講下看門狗復(fù)位,當(dāng)單片機(jī)開啟看門狗后,很多問題都會(huì)變成復(fù)位問題,比如上面說(shuō)的HardFault,因?yàn)镠ardFault也是一個(gè)中斷,只是默認(rèn)中斷里是一個(gè)While(1)的死循環(huán),所以當(dāng)進(jìn)入中斷后,一段時(shí)間沒有喂狗操作,就會(huì)觸發(fā)復(fù)位?;蛘咭恍┎僮飨萑胨姥h(huán)的,均是同理。這里我們把這一類問題都?xì)w為死循環(huán)問題。處理方式,先把看門狗關(guān)掉,然后調(diào)試看停在哪個(gè)死循環(huán)中,如果是HardFault,那就看上面硬件錯(cuò)誤的處理方式。如果是其他死循環(huán),那就看是什么條件觸發(fā)的。死循環(huán)的問題相對(duì)來(lái)說(shuō)比較好找。
????另外一種比較難處理的看門狗復(fù)位問題,莫過(guò)于某些操作時(shí)間過(guò)長(zhǎng),導(dǎo)致喂狗不及時(shí)。比如讀寫Flash時(shí),通常會(huì)關(guān)閉中斷,當(dāng)大量讀寫時(shí),其操作時(shí)間不可小靚,未開看門狗的情況下會(huì)有肉眼可見的程序卡頓,開了看門狗的情況下則通常會(huì)觸發(fā)程序復(fù)位。這種類型的問題,通過(guò)關(guān)閉看門狗可能也無(wú)法定位到具體位置,因?yàn)槌绦蜻€可以正常執(zhí)行,只是在某些程序段會(huì)變得比較卡頓。對(duì)于這種問題,最好的方式是通過(guò)代碼對(duì)比,通過(guò)對(duì)比原本沒出問題的代碼和出問題代碼的差異性,鎖定問題大體出現(xiàn)的位置,再通過(guò)程序執(zhí)行時(shí)間進(jìn)行估算。也可以借助一個(gè)獨(dú)立的定時(shí)器,在一些時(shí)間操作較長(zhǎng)的可疑之處計(jì)時(shí)。比如程序調(diào)用了某個(gè)底層未開源函數(shù),那可以在調(diào)用前后打印定時(shí)器的計(jì)數(shù),來(lái)計(jì)算函數(shù)運(yùn)行的時(shí)間。當(dāng)然也可以通過(guò)Keil自帶的調(diào)試計(jì)數(shù)值來(lái)計(jì)算運(yùn)行時(shí)間。
????軟件復(fù)位就比較好找了,一般是需要人為調(diào)試內(nèi)核的復(fù)位接口進(jìn)行復(fù)位,所以只要查看是哪些位置觸發(fā)的調(diào)用復(fù)位函數(shù)的條件就可以鎖定問題點(diǎn)。
????硬件復(fù)位就只能從外圍電路進(jìn)行切入了,考慮干擾、連錫等問題。當(dāng)然有些硬件復(fù)位是通過(guò)一個(gè)硬件看門狗進(jìn)行復(fù)位的,如果是這種應(yīng)用,那參考內(nèi)部看門狗的問題排查方式。
- 邏輯時(shí)序類調(diào)試
????時(shí)序類的用斷點(diǎn)調(diào)試法就很難做到了,特別是那種時(shí)序要求很嚴(yán)格的。就比如Modbus通信,協(xié)議是規(guī)定了一幀數(shù)據(jù)中每?jī)蓚€(gè)字節(jié)間隔時(shí)間不能超過(guò)1.5字符。所以想要在一幀數(shù)據(jù)中,按一個(gè)字節(jié)一個(gè)字節(jié)斷點(diǎn)調(diào)試從機(jī)是不可能的,主機(jī)不會(huì)給你休息的時(shí)間。這時(shí)候就必須得添加一些測(cè)試代碼了,添加測(cè)試代碼最重要的一個(gè)原則,是不能變更原本的功能。所以一般在數(shù)據(jù)流向的關(guān)鍵路徑上添加一些監(jiān)控變量,通過(guò)監(jiān)控變量的變化來(lái)識(shí)別時(shí)序是否出現(xiàn)錯(cuò)誤。
????另外也可以使用邏輯分析窗口,把對(duì)應(yīng)的變量添加進(jìn)窗口中,通過(guò)時(shí)間變化查看變量對(duì)應(yīng)的變化關(guān)系,以此來(lái)判斷邏輯時(shí)序是否正常。
- 內(nèi)存調(diào)試
????如果有涉及boot或日志記錄功能的編寫,那肯定會(huì)涉及大量?jī)?nèi)存的對(duì)比及調(diào)試,這時(shí)候可以利用上面提到的小技巧,在命令窗口那里輸入save filename.hex StartAddr, EndAddr把對(duì)應(yīng)的內(nèi)存數(shù)據(jù)打印出來(lái)。
- 底層外設(shè)調(diào)試
????這個(gè)打開對(duì)應(yīng)外設(shè)的寄存器界面,對(duì)著芯片用戶手冊(cè)查看每個(gè)寄存器的功能進(jìn)行調(diào)試,只有對(duì)寄存器功能熟悉了才有對(duì)應(yīng)的調(diào)試手段。
五、注意事項(xiàng)
1、有時(shí)候在watch窗口中,變量值不會(huì)刷新,這時(shí)候就需要查看一下"View->Periodic Window Update"是否已勾選,如果沒勾選,變量只有在第一次添加或停止調(diào)試時(shí)才會(huì)刷新。另外當(dāng)窗口里一次性加載了一個(gè)很大的數(shù)組,當(dāng)展開數(shù)組時(shí),變量刷新也會(huì)變得很慢,并且軟件會(huì)變卡頓。
2、當(dāng)選擇了非0級(jí)優(yōu)化時(shí),調(diào)試可能會(huì)變得困難,具體表現(xiàn)在斷點(diǎn)調(diào)試。比如現(xiàn)在下面的代碼,代碼優(yōu)化的關(guān)系,有可能把case0、1、2里的return 1都合并成一行,導(dǎo)致運(yùn)行調(diào)試時(shí),無(wú)論當(dāng)前程序進(jìn)入了哪個(gè)分支,使用斷點(diǎn)時(shí)都只會(huì)進(jìn)其中一個(gè)。所以當(dāng)開啟代碼優(yōu)化等級(jí)后,需要注意斷點(diǎn)調(diào)試將變得不可信。另外優(yōu)化編譯后,有部分代碼也將無(wú)法打斷點(diǎn)(被優(yōu)化的代碼)。
switch (xx)
{
case 0:
{
do_something0();
return 1;
}
case 1:
{
do_something1();
return 1;
}
case 2:
{
do_something2();
return 1;
}
default:
{
return 0;
}
}
此時(shí)應(yīng)該去看匯編的實(shí)現(xiàn),其執(zhí)行順序與匯編一致。
3、目前發(fā)現(xiàn)有部分工程在一些電腦上調(diào)試時(shí),打斷點(diǎn)后在刪除斷點(diǎn)之前退出調(diào)試,會(huì)導(dǎo)致Keil崩潰,只能結(jié)束進(jìn)程重啟。
4、當(dāng)開啟內(nèi)部看門狗并且未打開調(diào)試關(guān)看門狗功能時(shí),停止運(yùn)行一段時(shí)間后會(huì)復(fù)位。
5、在全速運(yùn)行時(shí),有時(shí)打斷點(diǎn)會(huì)無(wú)效,取消斷點(diǎn)也無(wú)效,貌似是Keil本身的問題。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-777853.html
六、相關(guān)知識(shí)
????Keil5軟件使用-基礎(chǔ)使用篇、Keil5軟件使用-進(jìn)階工程配置篇、Keil軟件包-知識(shí)寶藏庫(kù)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-777853.html
到了這里,關(guān)于【工具使用】Keil5軟件使用-進(jìn)階調(diào)試篇的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!