:從硬件觸發(fā)到C語言響應(yīng))
1. 中斷到底是什么先別急著看代碼我們從廚房燒水說起你家灶臺上正燒著一壺水水還沒開但你得去接個重要電話。你不會一直守在灶臺前盯著水壺——那樣太低效。你會把水壺放上去調(diào)好火候然后去做別的事回消息、切菜、甚至躺沙發(fā)上刷會兒短視頻。突然“?!币宦暽陧懰_了。你立刻放下手頭的事快步走回廚房關(guān)火、提壺、倒水——整個過程不到3秒。等水倒完你又回到沙發(fā)上繼續(xù)刷視頻。這個“哨聲一響就暫停當(dāng)前事、立刻處理開水”的行為就是中斷最原始、最本質(zhì)的生活映射。它不是程序跳轉(zhuǎn)不是函數(shù)調(diào)用更不是輪詢等待它是硬件主動發(fā)起的“緊急插隊請求”是單片機在執(zhí)行主任務(wù)比如控制LED閃爍、讀取傳感器數(shù)據(jù)、驅(qū)動電機時被外部或內(nèi)部事件“臨時打斷”轉(zhuǎn)而執(zhí)行一段專用響應(yīng)代碼中斷服務(wù)函數(shù)處理完再無縫切回原來的位置繼續(xù)干活。很多人學(xué)中斷卡在第一步以為中斷是“讓程序停下來等某個信號”結(jié)果寫出來的代碼要么永遠等不到觸發(fā)要么一觸發(fā)就死機重啟。根本原因在于沒理解中斷的三要素閉環(huán)觸發(fā)源 → 響應(yīng)機制 → 恢復(fù)現(xiàn)場。這三者缺一不可且必須嚴(yán)格匹配硬件特性。比如你家水壺哨聲再響如果灶臺沒裝自動關(guān)火裝置相當(dāng)于沒配置NVIC使能或者你耳朵聾了CPU未開全局中斷、或者你正在浴室洗澡聽不見中斷優(yōu)先級被更高級事件屏蔽那“中斷”就形同虛設(shè)。本文不堆寄存器定義不列大段匯編也不照搬《單片機原理》教材里的抽象框圖。我會用51、STM32、PY32F003三類主流芯片的真實場景拆解中斷從物理信號到C語言函數(shù)的完整鏈路為什么按鍵抖動要濾波為什么串口接收要用空閑中斷判斷幀結(jié)束為什么定時器中斷里不能調(diào)用printfNVIC向量表地址怎么算GPIO八種模式中哪些能觸發(fā)中斷這些在實驗室調(diào)試時讓你抓耳撓腮的問題背后全是同一套底層邏輯。適合剛點亮第一個LED的新手也適合寫過百行中斷代碼卻總在穩(wěn)定性上栽跟頭的工程師。2. 中斷的底層邏輯硬件信號如何變成C語言函數(shù)調(diào)用2.1 中斷不是軟件功能而是CPU的硬件特權(quán)機制很多初學(xué)者誤以為“中斷是C語言提供的高級功能”這是致命誤區(qū)。中斷的本質(zhì)是CPU內(nèi)核的硬件電路設(shè)計。以ARM Cortex-M系列STM32/PY32F003為例當(dāng)某個外設(shè)如GPIO、UART、TIM檢測到有效電平變化上升沿/下降沿或狀態(tài)滿足條件如串口接收緩沖區(qū)非空它會通過專用總線向CPU內(nèi)核的嵌套向量中斷控制器NVIC發(fā)送一個高電平脈沖信號。這個信號不是數(shù)據(jù)而是“請立即處理”的硬件握手請求。NVIC收到請求后會做三件事查向量表根據(jù)中斷號如EXTI0_IRQn6USART1_IRQn37查NVIC向量表找到對應(yīng)中斷服務(wù)函數(shù)ISR的入口地址保存現(xiàn)場自動將當(dāng)前程序計數(shù)器PC、程序狀態(tài)寄存器xPSR、通用寄存器R0-R3/R12/LR等壓入主棧MSP或進程棧PSP確保返回時能精確恢復(fù)執(zhí)行點跳轉(zhuǎn)執(zhí)行將PC指向ISR函數(shù)地址開始執(zhí)行用戶寫的中斷服務(wù)代碼。提示這個過程完全由硬件完成耗時固定Cortex-M3/M4約12個周期。你寫的C函數(shù)只是被硬件“拉過去執(zhí)行”而非主動調(diào)用。這也是為什么中斷服務(wù)函數(shù)必須短小精悍——它搶占的是主程序的CPU時間片任何阻塞操作如while(1)、delay_ms()、printf都會導(dǎo)致主程序長時間停擺甚至引發(fā)中斷嵌套溢出。對比51單片機8051架構(gòu)其機制更原始沒有NVIC靠CPU內(nèi)部的中斷標(biāo)志位IE0/IE1/TI/RI和固定的中斷入口地址0x0003、0x000B等。當(dāng)INT0引腳檢測到下降沿CPU在每個指令周期末采樣IE0標(biāo)志若為1則自動跳轉(zhuǎn)到0x0003地址執(zhí)行。但同樣需要手動清標(biāo)志位IE00否則會反復(fù)觸發(fā)——這正是新手常犯的“按鍵按一次觸發(fā)十次”的根源。2.2 觸發(fā)源從物理引腳到中斷線的信號鏈路中斷的起點永遠是物理世界。以最常見的按鍵中斷為例信號路徑如下機械按鍵 → PCB走線 → MCU GPIO引腳 → 輸入緩沖器 → 施密特觸發(fā)器 → 邊沿檢測電路 → EXTI線 → NVIC其中每一步都可能成為故障點按鍵抖動機械觸點閉合時產(chǎn)生10~20ms的毫秒級振蕩若直接接中斷一次按下會被識別為數(shù)十次觸發(fā)。這就是為什么“smart200定時中斷濾波”成為剛需——用10ms定時器中斷定期采樣按鍵電平連續(xù)3次讀取相同值才確認(rèn)有效。GPIO模式選擇必須配置為浮空輸入/上拉輸入如STM32的GPIO_MODE_IT_FALLING若設(shè)為推挽輸出引腳無法感知外部電平變化若設(shè)為模擬輸入則邊沿檢測電路被旁路。EXTI線映射STM32的GPIOA~G的Pin0共用EXTI0線Pin1共用EXTI1線。這意味著PA0、PB0、PC0同時接按鍵時只能有一個能觸發(fā)EXTI0中斷——必須通過軟件判斷具體是哪個端口的Pin0。再看串口接收中斷當(dāng)USART接收移位寄存器收到完整起始位8位數(shù)據(jù)停止位后硬件置位RXNE接收數(shù)據(jù)寄存器非空標(biāo)志。此時若USART_CR1寄存器的RXNEIE位為1NVIC即觸發(fā)中斷。但問題來了一幀數(shù)據(jù)可能包含多個字節(jié)而RXNE只在每個字節(jié)接收完成后置位一次。若用普通RXNE中斷需頻繁進出ISR效率低下。于是高手都用空閑中斷IDLE當(dāng)接收線持續(xù)一個字符時間無活動即檢測到停止位后無新起始位硬件置位IDLE標(biāo)志此時可一次性讀取DMA緩沖區(qū)中的全部數(shù)據(jù)完美解決幀邊界判斷難題。2.3 響應(yīng)機制NVIC配置決定中斷能否真正生效即使硬件信號正確送達NVIC若配置缺失中斷依然靜默。以STM32F103為例啟用EXTI0中斷需四步缺一不可使能GPIO時鐘RCC-APB2ENR | RCC_APB2ENR_IOPAEN;否則GPIO寄存器讀寫無效配置GPIO模式GPIOA-CRL 0xFFFFFFF0; GPIOA-CRL | 0x00000008;PA0設(shè)為浮空輸入映射EXTI線AFIO-EXTICR[0] 0xFFFFFF0F; AFIO-EXTICR[0] | 0x00000000;PA0→EXTI0配置NVICEXTI-IMR | EXTI_IMR_MR0; EXTI-FTSR | EXTI_FTSR_TR0; NVIC_EnableIRQ(EXTI0_IRQn);使能中斷線、設(shè)置下降沿觸發(fā)、開啟NVIC通道。漏掉任意一步現(xiàn)象都是“按鍵按下LED不閃”。常見錯誤包括忘記使能AFIO時鐘導(dǎo)致EXTICR寄存器寫無效、NVIC優(yōu)先級設(shè)為0但未開全局中斷__enable_irq()未調(diào)用、或使用HAL庫時未調(diào)用HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0)。注意NVIC向量表地址不是固定值。Cortex-M默認(rèn)從0x00000000開始但實際項目常重映射到SRAM0x20000000或FLASH0x08000000。向量表偏移量由SCB-VTOR寄存器控制。若你修改了鏈接腳本將中斷向量表放在0x08002000卻未設(shè)置VTORCPU仍會從0x00000000讀取錯誤的地址導(dǎo)致跳轉(zhuǎn)到非法內(nèi)存——這就是“程序跑飛”的典型原因。3. 實操拆解從51單片機到STM32三類典型中斷場景3.1 場景一51單片機按鍵中斷——理解“清標(biāo)志”與“防抖”的硬約束以STC89C52為例實現(xiàn)PA0按鍵控制LED亮滅。核心代碼如下#include reg52.h sbit LED P1^0; sbit KEY P3^2; // INT0引腳 void main() { IT0 1; // 設(shè)置INT0為下降沿觸發(fā) EX0 1; // 使能INT0中斷 EA 1; // 開全局中斷 while(1); } void ext0_isr() interrupt 0 { // 中斷號0對應(yīng)INT0 // 關(guān)鍵必須先延時消抖再讀取電平 for(int i0; i1000; i); // 簡單軟件延時1ms if(KEY 0) { // 確認(rèn)按鍵確實按下 LED ~LED; // 切換LED狀態(tài) while(KEY 0); // 等待按鍵釋放防止重復(fù)觸發(fā) } }這段代碼看似簡單卻暗藏三個生死細(xì)節(jié)interrupt 0語法51編譯器通過此關(guān)鍵字將函數(shù)綁定到0x0003地址無需手動填向量表IT01必須在EA1之前若先開全局中斷再設(shè)觸發(fā)方式初始化過程中可能誤觸發(fā)while(KEY0)必不可少機械按鍵釋放時同樣存在抖動若不等待釋放松手瞬間又會觸發(fā)一次中斷。實測發(fā)現(xiàn)若去掉延時和等待釋放單次按鍵會導(dǎo)致LED閃爍3~5次。這是因為抖動期間電平在0/1間快速跳變每次下降沿都被CPU捕獲。而“smart200定時中斷濾波”方案更優(yōu)用T0定時器每10ms觸發(fā)一次中斷在該中斷中讀取所有按鍵狀態(tài)并做狀態(tài)機判別徹底隔離抖動影響。3.2 場景二STM32串口空閑中斷——解決幀接收的終極方案針對stm32f103c8t6 hal庫串口中斷接收只收一次的痛點采用空閑中斷DMA方案// 初始化開啟USART1配置DMA接收 __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); huart1.Instance USART1; huart1.Init.BaudRate 115200; huart1.Init.WordLength UART_WORDLENGTH_8B; huart1.Init.StopBits UART_STOPBITS_1; huart1.Init.Parity UART_PARITY_NONE; huart1.Init.Mode UART_MODE_TX_RX; HAL_UART_Init(huart1); // 配置DMA從USART1_RDR傳輸?shù)絩x_buffer長度128 hdma_usart1_rx.Instance DMA1_Channel5; hdma_usart1_rx.Init.Direction DMA_PERIPH_TO_MEMORY; hdma_usart1_rx.Init.MemInc DMA_MINC_ENABLE; hdma_usart1_rx.Init.PeriphDataAlignment DMA_PDATAALIGN_BYTE; hdma_usart1_rx.Init.MemDataAlignment DMA_MDATAALIGN_BYTE; hdma_usart1_rx.Init.Mode DMA_CIRCULAR; // 循環(huán)模式避免溢出 HAL_DMA_Init(hdma_usart1_rx); __HAL_LINKDMA(huart1, hdmarx, hdma_usart1_rx); // 使能空閑中斷 __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); HAL_UART_Receive_DMA(huart1, rx_buffer, RX_BUFFER_SIZE); // 空閑中斷服務(wù)函數(shù) void USART1_IRQHandler(void) { HAL_UART_IRQHandler(huart1); } // HAL庫回調(diào)函數(shù)空閑中斷觸發(fā)時調(diào)用 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if(huart-Instance USART1) { // 計算本次接收長度總緩沖區(qū)長度 - 當(dāng)前DMA剩余字節(jié)數(shù) uint16_t received_len RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart1_rx); // 處理rx_buffer中從last_pos到received_len的數(shù)據(jù) parse_frame(rx_buffer last_pos, received_len - last_pos); last_pos received_len; // 重置DMA計數(shù)器準(zhǔn)備下一次接收 __HAL_DMA_SET_COUNTER(hdma_usart1_rx, RX_BUFFER_SIZE); } }此方案優(yōu)勢在于零CPU占用DMA全程搬運數(shù)據(jù)CPU只在幀結(jié)束時介入精準(zhǔn)幀邊界IDLE中斷保證每次回調(diào)都對應(yīng)完整一幀抗干擾強即使串口線受干擾出現(xiàn)亂碼只要幀頭幀尾校驗正確就不會誤判。對比傳統(tǒng)RXNE中斷后者需在每次接收一個字節(jié)時進入ISR對于115200波特率每秒中斷約11500次嚴(yán)重擠占CPU資源。而空閑中斷平均每幀觸發(fā)一次效率提升兩個數(shù)量級。3.3 場景三PY32F003定時器中斷——國產(chǎn)芯片的NVIC實戰(zhàn)PY32F003作為國產(chǎn)替代熱門其NVIC配置與STM32高度兼容但寄存器命名略有差異。實現(xiàn)1ms定時中斷控制LED呼吸效果// 啟用TIM16時鐘 RCC-APB2ENR | RCC_APB2ENR_TIM16EN; // 配置TIM1616MHz主頻預(yù)分頻8000-1→1kHz自動重載1000-1→1ms TIM16-PSC 7999; // (8000-1) TIM16-ARR 999; // (1000-1) TIM16-CR1 TIM_CR1_CEN; // 啟動計數(shù) // 配置NVICTIM16中斷號為25 NVIC-ISER[0] 1 25; // 使能中斷 NVIC-IP[25] 0x00; // 設(shè)置優(yōu)先級為0 NVIC-ICPR[0] 1 25; // 清除掛起位確保初始狀態(tài) // 開啟更新中斷 TIM16-DIER | TIM_DIER_UIE; // 中斷服務(wù)函數(shù) void TIM16_BRK_UP_TRG_COM_IRQHandler(void) { if(TIM16-SR TIM_SR_UIF) { static uint16_t cnt 0; cnt; if(cnt 1000) cnt 0; // PWM占空比cnt從0到1000線性變化 TIM1-CCR1 cnt; TIM16-SR ~TIM_SR_UIF; // 手動清中斷標(biāo)志 } }關(guān)鍵差異點中斷號計算PY32F003的TIM16_IRQn25而STM32F0xx為29需查芯片手冊確認(rèn)標(biāo)志位清除PY32F003需手動寫SR ~UIF而部分STM32型號支持寫1清零SR UIF時鐘樹配置PY32F003默認(rèn)HSI8MHz若主頻設(shè)為16MHz需先配置PLL否則TIM時鐘頻率錯誤導(dǎo)致定時不準(zhǔn)。實測發(fā)現(xiàn)若忘記清除UIF標(biāo)志中斷會持續(xù)觸發(fā)LED狂閃失控。這是國產(chǎn)芯片文檔不完善導(dǎo)致的典型坑——必須逐字核對參考手冊的“中斷管理”章節(jié)。4. 中斷配置避坑指南那些讓工程師熬夜調(diào)試的隱藏陷阱4.1 GPIO模式與中斷能力的隱性綁定GPIO的八種工作模式STM32并非都能觸發(fā)中斷。只有以下四種支持外部中斷模式是否支持中斷適用場景GPIO_MODE_IT_RISING?檢測按鍵彈起上升沿GPIO_MODE_IT_FALLING?檢測按鍵按下下降沿GPIO_MODE_IT_RISING_FALLING?檢測雙邊沿如編碼器AB相GPIO_MODE_INPUT?普通輸入需輪詢讀取GPIO_MODE_OUTPUT_PP?推挽輸出無法感知外部電平GPIO_MODE_OUTPUT_OD?開漏輸出同上GPIO_MODE_ANALOG?模擬輸入關(guān)閉數(shù)字電路GPIO_MODE_AF_PP??復(fù)用推挽僅當(dāng)復(fù)用功能支持中斷時可用如USART_RX常見錯誤將按鍵接在PA9USART1_TX配置為GPIO_MODE_AF_PP期望TX引腳能觸發(fā)中斷——這是不可能的因為TX是輸出功能其輸入緩沖器被禁用。正確做法是改用PA10USART1_RX并設(shè)為GPIO_MODE_IT_FALLING。4.2 中斷優(yōu)先級嵌套的數(shù)學(xué)陷阱NVIC支持16級可編程優(yōu)先級4位搶占優(yōu)先級4位子優(yōu)先級。但優(yōu)先級數(shù)值越小權(quán)限越高。例如設(shè)置EXTI0_IRQn優(yōu)先級為0x01二進制0001TIM2_IRQn為0x020010當(dāng)EXTI0中斷執(zhí)行中TIM2中斷到來因0x01 0x02TIM2會被掛起等待EXTI0返回若反過來設(shè)EXTI0為0x02、TIM2為0x01則TIM2會打斷EXTI0形成嵌套。致命陷阱在于若兩個中斷設(shè)為相同搶占優(yōu)先級子優(yōu)先級高的會先響應(yīng)但不會嵌套。比如EXTI0和USART1都設(shè)為0x01當(dāng)它們同時觸發(fā)子優(yōu)先級高的先執(zhí)行另一個掛起等待——這看似合理但若在EXTI0 ISR中調(diào)用了HAL_UART_Transmit()而該函數(shù)內(nèi)部又依賴USART1中斷就會因優(yōu)先級相同導(dǎo)致死鎖。解決方案將通信類中斷USART、SPI設(shè)為高搶占優(yōu)先級如0x00將按鍵、ADC等設(shè)為低優(yōu)先級如0x0F。4.3 上電后外設(shè)邏輯的時序雷區(qū)很多項目上電后功能異常根源在于外設(shè)初始化順序。以“按鍵中斷串口打印”為例錯誤順序HAL_UART_Init(huart1); // 先初始化串口 HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // 再初始化GPIO問題在于串口初始化會配置PA9/PA10為復(fù)用功能若此時PA0按鍵尚未配置為輸入模式其默認(rèn)狀態(tài)可能是浮空導(dǎo)致EXTI0線路電平不穩(wěn)定上電瞬間誤觸發(fā)中斷。正確順序應(yīng)為先配置所有GPIO為安全狀態(tài)如輸入浮空、輸出低電平再初始化外設(shè)UART、TIM、ADC等最后使能中斷HAL_NVIC_EnableIRQ()。此外部分芯片如STC單片機上電時GPIO默認(rèn)為高阻態(tài)需在main()開頭立即配置模式否則前10ms內(nèi)引腳電平隨機可能觸發(fā)意外中斷。4.4 中斷服務(wù)函數(shù)的四大禁忌所有中斷服務(wù)函數(shù)ISR必須遵守鐵律禁止調(diào)用printf等阻塞函數(shù)printf內(nèi)部使用串口發(fā)送需等待TXE標(biāo)志而串口發(fā)送完成中斷可能被當(dāng)前ISR屏蔽導(dǎo)致死鎖禁止使用局部靜態(tài)變量static int cnt 0; cnt;在多中斷環(huán)境下cnt可能被不同ISR并發(fā)修改需加臨界區(qū)保護禁止長延時for(i0;i10000;i)會阻塞主程序超10ms實時系統(tǒng)直接崩潰禁止調(diào)用HAL_Delay()該函數(shù)基于SysTick而SysTick本身也是中斷嵌套調(diào)用必死。正確做法ISR中只做最簡操作——置位標(biāo)志位、寫入環(huán)形緩沖區(qū)、更新計數(shù)器。復(fù)雜處理交給主循環(huán)volatile uint8_t uart_rx_flag 0; uint8_t rx_buffer[64]; uint16_t rx_head 0, rx_tail 0; void USART1_IRQHandler(void) { uint8_t data USART1-DR; // 清RXNE標(biāo)志并讀數(shù)據(jù) rx_buffer[rx_head] data; rx_head % sizeof(rx_buffer); uart_rx_flag 1; // 僅置標(biāo)志 } int main() { while(1) { if(uart_rx_flag) { uart_rx_flag 0; process_uart_data(); // 在主循環(huán)中處理可調(diào)用printf } } }5. 中斷調(diào)試實戰(zhàn)從現(xiàn)象反推故障鏈路5.1 常見問題速查表現(xiàn)象可能原因排查步驟按鍵按下無反應(yīng)① GPIO時鐘未使能② EXTI線未映射③ NVIC未使能④ 按鍵硬件斷路用萬用表測按鍵兩端電壓用邏輯分析儀看EXTI線電平變化檢查RCC-APB2ENR寄存器值中斷反復(fù)觸發(fā)① 未清中斷標(biāo)志位② 按鍵抖動未濾波③ GPIO模式錯誤如設(shè)為推挽在ISR開頭加__NOP()用調(diào)試器單步執(zhí)行示波器觀察引腳波形查GPIOx_MODER寄存器主程序卡死① ISR中調(diào)用阻塞函數(shù)② 中斷優(yōu)先級配置錯誤導(dǎo)致死鎖③ 堆棧溢出查看SP寄存器值是否接近棧頂用__get_SP()監(jiān)控棧指針關(guān)閉所有中斷逐步啟用排查串口接收丟數(shù)據(jù)① RXNE中斷未及時清標(biāo)志② 主循環(huán)處理速度慢于接收速率③ 未啟用空閑中斷用邏輯分析儀測RX線上數(shù)據(jù)間隔增大DMA緩沖區(qū)改用IDLE中斷定時器中斷不準(zhǔn)① PSC/ARR計算錯誤② 時鐘源配置錯誤如誤用LSI③ 中斷服務(wù)函數(shù)執(zhí)行時間過長用示波器測TIMx_CHy輸出波形查RCC-CFGR寄存器測量ISR執(zhí)行周期5.2 邏輯分析儀實操捕捉中斷觸發(fā)全過程以調(diào)試“PY32F003按鍵中斷”為例接線方案CH0PA0引腳按鍵信號CH1PA1引腳在ISR開頭置高結(jié)尾置低標(biāo)記ISR執(zhí)行區(qū)間CH2LED引腳驗證功能是否生效捕獲波形后重點觀察PA0下降沿是否干凈有無抖動PA1高電平寬度是否≤10μsISR執(zhí)行時間LED翻轉(zhuǎn)是否與PA1高電平同步連續(xù)按鍵時PA1脈沖間隔是否穩(wěn)定驗證消抖效果。若發(fā)現(xiàn)PA1高電平持續(xù)2ms說明ISR中存在隱式延時如未優(yōu)化的字符串處理若PA0下降沿后PA1無響應(yīng)說明硬件信號未到達NVIC需檢查EXTI映射配置。5.3 經(jīng)驗技巧三招快速定位NVIC配置錯誤向量表校驗法在調(diào)試器中查看內(nèi)存0x00000000處的前32個字128字節(jié)確認(rèn)第7個字0x00000018是否為你的ISR地址。若為0x00000000說明向量表未正確加載NVIC寄存器快照法在main()中添加printf(ISER0: 0x%08X\r\n, NVIC-ISER[0]); // 應(yīng)含對應(yīng)中斷位 printf(IPR6: 0x%08X\r\n, NVIC-IP[6]); // EXTI0優(yōu)先級應(yīng)非0 printf(ICPR0: 0x%08X\r\n, NVIC-ICPR[0]); // 應(yīng)為0中斷掛起診斷法若懷疑中斷被屏蔽讀取NVIC-IABR[0]中斷活躍位寄存器若對應(yīng)位為1但ISR未執(zhí)行說明該中斷正在執(zhí)行中可能死循環(huán)若為0則未觸發(fā)。最后分享個小技巧在Keil MDK中右鍵點擊函數(shù)名→“Go to Definition”可直接跳轉(zhuǎn)到startup_stm32f10x_md.s中的中斷向量表定義對照匯編代碼確認(rèn)你的函數(shù)是否被正確注冊。這比翻手冊快十倍。我在實際項目中曾遇到一個詭異問題STM32F103的EXTI9_5中斷對應(yīng)PA5~PA9始終不觸發(fā)查遍寄存器全正常。最終發(fā)現(xiàn)是PCB上PA5走線過長未加下拉電阻上電時引腳電平浮動導(dǎo)致EXTI線路處于不確定態(tài)。加10kΩ下拉電阻后立即正常——硬件設(shè)計細(xì)節(jié)往往比代碼更致命。