操作系統中,中斷是很重要的組成部分。出現某些意外情況需主機干預時,機器能自動停止正在運行的程序并轉入處理新情況的程序,處理完畢后又返回原被暫停的程序繼續運行。
有了中斷系統才可以不用一直輪詢(polling)是否有事件發生,系統效率才得以提高。
一般在系統中,中斷控制分為三個部分:「模塊、中斷控制器和處理器」。
其中模塊通常由寄存器控制是否使能中斷和中斷觸發條件等;中斷控制器可以管理中斷的優先級等,而處理器則由寄存器設置用來響應中斷。
作為 ARM 系統中通用中斷控制器的是 GIC(Generic Interrupt Controller),目前有四個版本,V1~V4(V2最多支持8個ARM core,V3/V4支持更多的ARM core,主要用于ARM64系統結構)。
【注意】對于一些老的ARM處理器,比如ARM11,Cortex-A8,中斷控制器一般是VIC(向量中斷控制器)。
下面以GIC-400為例,它更適合嵌入式系統,符合v2版本的GIC architecture specification。GIC-400通過AMBA(Advanced Microcontroller Bus Architecture)片上總線連接到一個或者多個ARM處理器上。
GIC中斷控制器全局圖
從上圖可以看出, GIC 是聯系外設中斷和 CPU 的橋梁,也是各 CPU 之間中斷互聯的通道(也帶有管理功能),它負責檢測、管理、分發中斷,可以做到:
ARM CPU 對外的連接只有2 個中斷: 「IRQ和FIQ」 ,相對應的處理模式分別是一般中斷(IRQ )處理模式和快速中斷(FIQ )處理模式。所以GIC 最后要把中斷匯集成2 條線,與CPU 對接。
GIC中斷控制器結構
分發器:負責各個子中斷使能,設置觸發方式,優先級排序,分發到哪個 CPU 上; 接口:負責總的中斷的使能,狀態的維護。
分發器的主要的作用是檢測各個中斷源的狀態,控制各個中斷源的行為,分發各個中斷源產生的中斷事件到指定的一個或者多個CPU接口上。雖然分發器可以管理多個中斷源,但是它總是把優先級最高的那個中斷請求送往CPU接口。分發器對中斷的控制包括:
分發器可以管理若干個中斷源,這些中斷源用ID來標識,我們稱之interrupt ID。
CPU接口主要用于和CPU進行接口。
主要功能包括:
key中斷管理模塊圖
以上圖為例,該圖是按鍵產生的中斷信號要到達cpu所要經過的路徑。
軟件中斷SWI,是一條CPU指令,用以自陷一個中斷。由于軟中斷指令通常要運行一個切換CPU至內核態的子例程,它常被用作實現系統調用(System call)。
GIC 中斷類型有3種:SGI(Software-generated interrupt)、PPI(Private peripheral interrupt )、SPI(Shared peripheral interrupt)。
通常為邊沿觸發和電平觸發。
Note:電平觸發是在高或低電平保持的時間內觸發, 而邊沿觸發是由高到低或由低到高這一瞬間觸發;在GIC中PPI和SGI類型的中斷可以有相同的中斷ID。
舉兩個例子說明:
1)UART 接收到一包數據,產生了一個中斷給GIC,GIC可以將該中斷分配給CPU0-7中任何一個處理;假設該中斷分配給CPU0處理了,那么在中斷處理函數里面會把接收到的數據從UART FIFO讀出。可以想象一下,如果CPU0在讀數據時,另外一個CPU也在處理該中斷,恰巧也在讀數據,那么CPU0讀到的數據是不全的。這就是1-N model中斷,或者說SPI中斷。
2)比如CPU0給CPU1-7發送中斷,想告知對方自己正在處理某個進程A。這種場景下,CPU1-7都接收到中斷,都進入中斷處理函數,CPU1-7獲取到CPU0的信息后,在進程調度時,就可以繞開進程A,而自己調度其他進程。
注:這個例子只是說明N-N model,實際上進程調度不都全是這樣的。
當GIC接收到一個中斷請求,將其狀態設置為Pending。重新產生一個掛起狀態的中斷不影響該中斷狀態。中斷處理順序: ① GIC決定該中斷是否使能,若沒有被使能對GIC沒有影響; ② 對于每個Pending中斷,GIC決定目標處理器; ③ 對于每個處理器 ,Distributor根據它擁有的每個中斷優先級信息決定最高優先級的掛起中斷,將該中斷傳遞給目標CPU Interface; ④ GIC Distributor將一個中斷傳遞給CPU Interface后,該CPU Interface決定該中斷是否有足夠的優先級將中斷請求發給CPU; ⑤ 當CPU開始處理該異常中斷,它讀取GICC_IAR應答中斷。讀取的GICC_IAR獲取到中斷ID,對于SGI,還有源處理器ID。中斷ID被用來查找正確的中斷處理程序。
GIC識別讀過程后,將改變該中斷的狀態: a) 當中斷狀態變為active時,如果該中斷掛起狀態持續存在或者中斷再次產生,中斷狀態將從Pending轉化為pending & active b) 否則,中斷狀態將從pending狀態變為active
⑥ 當中斷完成中斷處理后,它需要通知GIC處理已經完成。這個過程稱為 priority drop and interrupt deactivation: a) 總是需要向EOIR寄存器寫入一個有效的值(end of interrupt register) b) 也需要接著向GICC_DIR寫入值(deactivate interrupt register)
軟件可以通過給每一個中斷源分配優先級值來配置中斷優先級。優先級的值是個8位的無符號二進制數,GIC支持最小16和最大256的優先級級別。如果GIC實現的優先級少于256,那么優先級字段的低階位為RAZ/WI。這就意味著實現的優先級字段個數范圍是4~8,如下圖所示:
Effect of not implementing some priority field bits
Note: 1)、如何確定優先級字段所支持的優先級位? 通過軟件往可寫GICD_IPRIORITYn優先級字段寫入0XFF,然后回讀出該字段的值便可以確定優先級字段所支持的優先級位(因為有些位沒實現是RAZ/WI) 2)、ARM 推薦在檢查中斷優先級范圍之前先: ? 對于外設中斷,軟件先禁用該中斷 ? 對于SGI,軟件先檢查該中斷確定為inactive
在一個active中斷處理完之前,CPU interface支持發送更高優先級的掛起中斷到目標處理器。這種情況必要條件如下:
CPU interface的GICC_PMR寄存器定義了目標處理器的優先級閥值,GIC僅上報優先級高于閥值的pending中斷給目標處理器。寄存器初始值為0,屏蔽所有的中斷。
下面我們來分析FS4412開發板的第一個中斷設備按鍵。
key
由該電路圖可得:
以下是key2與soc的連接,
key與soc的連接
可以看到key2復用了GPIX1_1這個引腳,同時該引腳還可以作為中斷【XEINT9】使用。
順便看下GPXCON寄存器的配置
GPX1CON
由上圖所示,
key與soc的關系圖如下圖所示:
按鍵中斷寄存器配置流程
由上圖所示:
CPU處理完中斷,需要清除中斷,對于按鍵來說,有3個寄存器需要操作:
清中斷
由上圖所示:
前面分析了按鍵連接的是GPX1_1,現在我們來看下對應的寄存器應該如何配置
將GPX1_1引腳的上拉和下拉禁止
GPX1PUD[3:2]= 0b00;
GPX1CON
將GPX1_1引腳功能設置為中斷功能
GPX1CON[7:4] = 0xf
EXT_INT41CON
配置成成下降沿觸發:
EXT_INT41CON[6:4] = 0x2
EXT_INT41_MASK
中斷使能寄存器
EXT_INT41_MASK[1] = 0b0
EXT_INT41_PEND
當GPX1_1引腳接收到中斷信號,中斷發生,中斷狀態寄存器EXT_INT41_PEND 相應位會自動置1 注意:中斷處理完成的時候,需要清除相應狀態位。置1清0.
EXT_INT41_PEND[1] =0b1
根據外設中斷名稱EINT9來查看該中斷對應的GIC中維護的HW id。【所有的中斷源在芯片廠商設計的時候都分配了唯一的一個ID,GIC通過該ID來驅動中斷源】
查看芯片手冊(datasheet -- 9.2表)
GIC中斷源表
通過【9.2中斷源表】找到和外設中斷標示對應的中斷控制器中斷標識(GPIO有32個可被喚醒寄存器)其對應「EINT[9],中斷ID為57」,這是非常重要的,在后面的寄存器設置中起很大作用;
1) ICDISER使能相應中斷到分配器
ICDISER
ICDISER用于使能相應中斷到分配器,一個bit控制一個中斷源,一個ICDISER可以控制32個中斷源,這里INT[9] 對應的中斷ID為57,所以在ICDSER1中進行設置,57/32=1余25,所以這里在ICDISER1第25位置一。
ICDISER.ICDISER1 |= (0x1 << 25); //57/32 =1...25 取整數(那個寄存器) 和余數(哪位)
ICDIPTR
ICDIPTR
選擇cpu
ICDIPTR寄存器每8個bit 控制一個中斷源,其中CPU0可以處理160個中斷源,所以需要40個寄存器。要選擇cpu0第一個bit必須是1。
設置SPI[25]/ID[57]由cpu0處理,57/4=16余1 所以選擇寄存器ICDIPTR14的第2個字節[15:8]。
//SPI 25 interrupts are sent to processor 0
//57/4 = 14..1 14號寄存器的[15:8]
ICDIPTR.ICDIPTR14 |= 0x01<<8;
還寄存器用于使能分配器。
ICDDCR =1;
ICCPMR
CPU0.ICCPMR = 0xFF;//設置cpu0 中斷屏蔽優先級為255 最低,所有中斷都能響應)
ICCICR
EXYNOS 4412一共有4個cpu,用4個寄存器分別來控制4個cpu,每個寄存器的bit[0]用于全局控制對應的cpu。我們選擇cpu0處理中斷,將bit[0]置1即可。
CPU0.ICCICR |= 0x1;
使能中斷到CPU。
ICCIAR
當中斷發生之后,中斷的HW id值會由硬件寫入到寄存器ICCIAR[9:0]中;對于SGIs來說,多處理器環境下,CPU的interface值寫入到[12:10]中。
讀取HW id:
int irq_num;
irq_num = CPU0.ICCIAR&0x3ff; //獲取中斷號
要處理中斷異常,必須安裝異常向量表,異常的處理流程可以參考前面的文章《6. 從0開始學ARM-異常、異常向量表、swi》
異常向量表地址是可以修改的,比如uboot在啟動的時候,會從flash中搬運代碼到RAM中,而flash的異常向量表地址和ram的地址肯定不一樣,所以搬運完代碼后,就必須要修改對應的異常向量表地址。
修改異常向量表的地址的需要借助協處理器指令mcr:
ldr r0,=0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
上述命令是將地址0x40008000設置為異常向量表的地址,關于mcr指令,我們沒有必要深究,知道即可。
RAM中異常向量表地址我們選用的是0x40008000,以下是exynos4412 地址空間分布。
exynos4412 地址分布
.text
.global _start
_start:
b reset
ldr pc,_undefined_instruction
ldr pc,_software_interrupt
ldr pc,_prefetch_abort
ldr pc,_data_abort
ldr pc,_not_used
ldr pc,=irq_handler
ldr pc,_fiq
reset:
ldr r0,=0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
init_stack:
//初始化棧
……
b main //跳轉至c的main函數
irq_handler: //中斷入口函數
sub lr,lr,#4
stmfd sp!,{r0-r12,lr}
.weak do_irq
bl do_irq
ldmfd sp!,{r0-r12,pc}^
stacktop: .word stack+4*512//棧頂
.data
stack: .space 4*512 //棧空間
中斷入口函數do_irq()
void do_irq(void)
{
static int a = 1;
int irq_num;
irq_num = CPU0.ICCIAR&0x3ff; //獲取中斷號
switch(irq_num)
{
case 57:
printf("in the irq_handler\n");
//清GPIO中斷標志位
EXT_INT41_PEND = EXT_INT41_PEND |((0x1 << 1));
//清GIC中斷標志位
ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25);
break;
}
//清cpu中斷標志
CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num;位
}
實現按鍵中斷的初始化函數key_init():
void key_init(void)
{
GPX1.CON =GPX1.CON & (~(0xf << 4)) |(0xf << 4); //配置引腳功能為外部中斷
GPX1.PUD = GPX1.PUD & (~(0x3 << 2)); //關閉上下拉電阻
EXT_INT41_CON = EXT_INT41_CON &(~(0xf << 4))|(0x2 << 4); //外部中斷觸發方式
EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1)); //使能中斷
ICDDCR = 1; //使能分配器
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (0x1 << 25); //使能相應中斷到分配器
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8))|(0x1 << 8); //選擇CPU接口
CPU0.ICCPMR = 255; //中斷屏蔽優先級
CPU0.ICCICR = 1; //使能中斷到CPU
return ;
}
除了中斷方式之外我們還可以通過輪詢方式讀取按鍵的信息,原理如下:
循環檢測GPX1_1引腳輸入的電平,為低電壓時,按鍵按下,為高電平時,按鍵抬起。
GPX1.CON = GPX1.CON &(~(0xf<<4)) ;
GPX1.PUD = GPX1.PUD & ~(0x3 << 2);
int main (void)
{
led_init();
pwm_init();
GPX1.CON = GPX1.CON &(~(0xf<<4))|0x0<<4;
while(1)
{
if(!(GPX1.DAT & (0x1<<1))) // 返回為真,按鍵按下
{
delay_ms(10);
if(!(GPX1.DAT & (0x1<<1))) //二次檢測,去抖
{
GPX2.DAT |= 0x1 << 7; //Turn on LED2
delay_ms(500);
beep_on();
GPX2.DAT &= ~(0x1<<7); //Turn off LED2
delay_ms(500);
while(!(GPX1.DAT & (0x1<<1)));
beep_off();
}
}
}
return 0;
}
更多 ARM Linux干貨,請關注 一口Linux
在產品開發中,調試工具是不可或缺的開發利器,除了日常常見的ARM的CMSIS-DAP/ULINK,IAR的I-JET,P&E的U-multilink/Cyclone, 各個半導體廠商自定義的XX-LINK(例如LPC-link, ST-Link,等)之外,還有兩個是神一樣的存在,一個是SEGGER的J-LINK,支持與IAR/KEIL等各種編譯環境無縫銜接,性能穩如老狗, 價格低廉, 所以在嵌入式工程師中的普及率非常高,被稱為居家旅行匱贈好友之佳品。另外一個是一聽名字就感覺高大上的勞特巴赫Lauterbach,作為專業第三方調試工具廠商,以其強大的TRACE32開發調試工具享譽全球,TRACE32開發工具擁有非常豐富和強大的功能,包括基本調試配置、RTOS、多核系統、虛擬目標調試、能耗分析以及強大的腳本語言等,而且可以支持市場中使用的80多種常見的微處理架構。
當然Lauterbach性能的強大,對應的代價就是價格也比JLINK多出一個數量級,但是對于大部分嵌入式開發者來說,最常用的還是基本的調試功能,包括斷點,寄存器查看,內存/Flash的查看,本文從斷點使用的角度講解如何使用JLINK配合IAR支持的各種斷點用出點勞德巴赫的一些強大功能。
斷點的概念非常簡單,目的簡單來說就是在指定指令或者代碼行中斷程序的執行,實現上可以是硬件斷點(通過編程FPB實現)也可以是軟件斷點(通過插入BKPT斷點指令實現 ),具體二者的底層實現這里不展開討論。常規的斷點調試(本質上是代碼斷點)是在想觀察哪里的問題時就在對應的代碼地址雙擊設置斷點,并且一旦運行到斷點位置會讓程序自動暫停運行,然后觀察感興趣的變量,內存或者寄存器,這種斷點調試功能能幫助開發者解決大部分的問題,但是其也有很大的局限性。
因為很多時候我們只想知道某段代碼是否運行過,而不能讓程序停下來,譬如說調試BLE協議棧,無法去單步運行,否則會打斷BLE主機和從機之間的通訊時序,而導致整個系統功能出現問題。還有中斷處理函數的調試,程序一旦停下了也就失去了其他所有中斷的后續響應,再比如兩個設備通信,一旦一方采用常規斷點的方式調試,可能會打斷正常的通信過程。所以通常的做法是添加串口打印或者ITM半主機打印輸出log信息到顯示屏,但是這種方式會帶來額外的軟硬件開銷(對于串口打印來說是兩個UART pin腳+UART驅動函數,對于ITM打印輸出是一個SWO+ITM驅動),甚至因為引入新的代碼導致程序出錯。除此之外,還有3種場景是這種普通斷點無法滿足的,第一個是同一段循環體運行N次才停下來,第二個是當變量被寫入新的數據或者被讀取時停下來,第三個是實時記錄斷點所在行某個特定變量或者地址的值,并在時間軸上以圖形的形式顯示出來,方便分析和對比。
以上這些功能在這些功能在勞德巴赫中是最基本功能,同樣在IAR中也提供了不同形式的斷點類型和組合,只是日常習慣了只用了其代碼斷點的功能,沒能充分發揮IAR的強大斷點功能,針對以上4種問題在IAR中可以分別用日志斷點、條件斷點、讀寫權限的數據斷點、數據日志斷點逐個擊破,從而避免了額外添加代碼的繁瑣,也能為解決隱藏bug提供更加靈活的手段。尤其是其中的讀寫權限的數據斷點,筆者曾經就是使用這種辦法幫客戶解決了兩個埋藏的很深大bug,其中一個是查找某個關鍵變量在哪里被意外修改,通過設置條件斷點+stack callback迅速定位到了肇事代碼段,另一種是客戶代碼意外堆棧溢出調查,當時的做法是在堆棧大小的90%地址靠近棧頂處設置一個寫觸發的數據斷點,當某層調用過程中堆棧接近溢出時,設置的數據斷點會被觸發而停止應用程序,從而迅速找到堆棧是在哪層調用溢出的,從而解決問題。
嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和內容,導致工資要不上去!
無償分享大家一個資料包,差不多150多G。里面學習內容、面經、項目都比較新也比較全!某魚上買估計至少要好幾十。
點擊這里找小助理0元領取:嵌入式物聯網學習資料(頭條)
總結下來,在 IAR 中,主要有以下幾種斷點,下面逐一介紹。
這種斷點就是前面提到的最常用的斷點,也是最簡單的斷點。開發則只需要在反匯編窗口中選擇C行或ASM指令并切換斷點。一旦遇到斷點,用戶應用程序將停止。這時候可以查看變量、標志和寄存器的值。換句話說,開發者擁有完全的控制權。對于這種普通代碼斷點,其數量受限于硬件斷點的數量,例如對于 Arm Cortex-M,通常有6-8個硬件斷點,但如果使用軟件斷點或在RAM中運行應用程序,則可以不受限制。使用時只需選擇顯示View -> Breakpoints 窗口,就可以啟用或禁用斷點。
默認情況下,IDE 將設置代碼斷點,而且是auto類型,可以通過Option->Debugger->JLINK/JTrace->Breakpoint去設置硬件斷點還是軟件斷點。如果開發者有 I-jet,可以在右鍵單擊代碼行時明確選擇一個 flash斷點。注意斷點符號中的“F”。Flash 斷點功能在適用于 Arm 的IAR7.60 或更高版本中可用。
條件斷點是代碼斷點與某些標志或變量作為條件的組合。設置斷點后,同樣可以再次使用View -> Breakpoints 窗口查看所有斷點,也可以通過右鍵單擊并選擇Edit option來設置額外參數。
設置斷點條件所使用的語法類似于C語法,可以使用==、>=和 <=。例如,如果您希望應用程序在計數器等于 10 時在斷點處停止,您可以使用“counter==10”。這在中斷例程中需要斷點時非常有用。如果沒有設置條件,應用程序就會一直被停止,影響到系統的正常工作,使用標志或變量作為條件使事情變得容易得多。甚至用戶還可以使用跳過計數器和條件檢查(如true或changed)來實現更復雜的斷點停止條件設置。該方法可以解決上面提到的第二種問題。
與其他斷點相比,數據斷點有點不同,因為是對特定內存地址、標志、變量或寄存器的讀寫訪問的監控。使用時只需右鍵單擊標志或變量并選擇選項Set data Breakpoint。默認情況下,對該變量,特定地址,寄存器的任何讀取和寫入訪問都會觸發斷點。如果你想添加額外的設置,你可以通過View->Breakpoints 窗口和Edit 選項來完成. 除了讀寫訪問之外,還可以監控數據是否匹配來作為斷點的觸發條件,這意味著寫或讀訪問只會在數據匹配時觸發暫停。另外,通過選擇編輯按鈕,開發者還可以打開一個額外的窗口,可以選擇絕對地址甚至源代碼所在行。對于變量或標志,建議使用自動大小。如果需要監控更大的范圍,則應手動設置監控的地址范圍或者變量范圍,譬如說監控一個結構體的數據變化,使用這種數據斷點也是可以實現的,但需要用戶正確設置變量,特定地址,寄存器等監控對象的Size。使用這種方法可以解決前文提到的第三種問題。
此處需要特別提一下,數據斷點對于調試被應用程序破壞的標志和變量非常有用。筆者曾經就是使用這種辦法在客戶解決了兩個埋藏的很深大bug,其中一個是查找某個關鍵變量在哪里被意外修改,通過設置條件斷點+stack callback迅速定位到了肇事代碼段,另一種是客戶端的意外堆棧溢出調查,當時的做法是在堆棧大小的90%地址靠近棧頂處設置一個數據斷點,當堆棧溢出接近時,設置的數據斷點會被觸發而停止應用程序,從而迅速找到問題的根源,至于如何設置,此處暫不展開。
除了具有讀寫訪問權限的數據斷點外,開發者還可以使用數據日志斷點。這種斷點的好處在于可以在時間線中監視和以圖形方式繪制內存中特定變量或地址的值,使顯示更加直觀,用戶還可以在同一個時間軸上顯示和比較兩個或多個變量,從而在邏輯上排查問題。設置的方法就是View->Breakpoints 窗口和Edit 選項,然后選擇set Data Log Breakpoint for counter即可,使用這種方法可以解決前文提到的第四種問題。
時間線以及附加數據日志和數據日志摘要可在探針選項下找到,例如如下面的屏幕截圖所示。
除了代碼和數據斷點之外,還有一種日志斷點,這是一個特殊的斷點,因為它只會臨時暫時停止應用程序以打印消息,然后繼續代碼的運行。一旦運行到設置的日志斷點,它會顯示如下用戶預先設定的消息,告知用戶某個函數事件被觸發。這種方式的好處在于,無需額外添加串口打印或者ITM半主機打印輸出log信息到顯示屏,無需額外的軟硬件開銷,便可實現基本的信息打印,方便開發者跟蹤程序的執行流程。
如下圖所示,每次斷點命中時,調試日志窗口中都會顯示一條消息。添加的計數器可以了解應用程序通過該部分源代碼的次數。通過這種辦法可以解決前面提到的第一個問題,即不停止代碼又能獲知感興趣的代碼段是否被執行過,以及執行的次數,兵不血刃,無需添加任何額外的代碼。
除了代碼的調試,IAR還支持先進的電源調試技術,可以監控功耗,并將其與源代碼相關聯。這也使得添加電源斷點成為可能,可以設置一個閾值,如 25mA,一旦能量高于該值,調試器將被觸發停止。設置閾值非常簡單, 只需要打開J-Link->PowerLog 窗口,然后設置值和所需選項,如上圖或下圖所示。通過這種分析,可以直觀的看出代碼執行過程中的功耗值,下面的時間線窗口不是必需的,但它可以為提供正在使用的能量提供一個時間參考。
至此,介紹完了IAR支持的6種不同的斷點類型和使用方法,也順帶針對性的解決了前文中提到的日常調試遇到的四個問題。如果在日常調試過程中靈活運用以上的這幾種斷點,對于日常調試提高開發速度和解決一些深藏的bug(例如前文提到的大型程序中變量被莫名修改,堆棧溢出追蹤等) 很有幫助。當然勞特巴赫之所以賣的這么貴,必然有其強大之處,尤其是強大的腳本編程,多核系統,能耗分析以及對芯片內部操作的開放度,能給開發者最大的操作靈活度。但就日常的斷點調試看,IAR+JLINK的組合也基本能滿足大部分的需求,畢竟就地取材最方便。
原文鏈接:https://mp.weixin.qq.com/s/3b2dsi6Gh05nS4Gfo9lHgQ
文章轉載自:痞子衡嵌入式
文章來源于:如何使用JLINK配合IAR的斷點功能用出點勞德巴赫的感覺
原文鏈接:如何使用JLINK配合IAR的斷點功能用出點勞德巴赫的感覺
版權聲明:本文來源于網絡,免費傳達知識,版權歸原作者所有,如涉及作品版權問題,請聯系我進行刪除