痞子衡嵌入式:嵌入式Cortex-M中斷向量表原理及其重定向方法



  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是Cortex-M中斷向量表原理及其重定向方法

  接着前文 《嵌入式Cortex-M裸機環境下臨界區保護的三種實現》 繼續聊,嵌入式代碼設計里有時候一些特殊操作(比如 XIP 下 Flash 擦寫、低功耗模式切換)不能被隨意打斷,或者一些共享數據區不能被無序訪問(A 任務正在讀,B 任務卻要寫),這時候我們可以利用系統全局中斷開關控制來實現所謂的臨界區保護。

  但有些場景下開關系統全局中斷這種方法並不總是很湊效,比如 XIP 下 Flash 擦寫這種情況,如果項目里還有一個后台定時器(比如SysTick)在實時運行,擦除 Flash 期間(這個時間可能會很長)我們直接關閉系統全局中斷,會導致定時器中斷無法響應,系統計時會出偏差,對於這種情況,我們顯然不能關閉系統全局中斷。

  為了在 Flash 擦寫期間系統還能夠及時響應定時器中斷(執行中斷響應函數),我們需要將定時器中斷響應函數及其相關代碼像 Flash IAP 操作代碼一樣都鏈接到 RAM 里執行,此外還需要將中斷向量表也重定向到 RAM 里才行。今天痞子衡就來聊一聊重定向中斷向量表的方法:

一、Cortex-M中斷向量表簡介

  熟悉 ARM Cortex-M 處理器的朋友應該都對下面這張表有所了解,這就是中斷向量表,表中每個向量大小都是 4 字節,除了第 0 個向量外,其余向量都是函數地址,這個表集中保存了系統全部的中斷處理函數(xxxIRQHandler)地址。

  對於內嵌 Flash 的 MCU 來說,初始中斷向量表一般會被要求固定鏈接到 Flash 起始地址處,因為系統啟動總是從 Flash 起始地址獲取第 0(初始棧)、1個向量(初始PC,復位函數ResetHandler)來開始應用程序代碼的執行。對於一些包含 BootROM 或者沒有內部 Flash 的 MCU,初始中斷向量表也許可以放到 Flash 中的其他地址處,這要取決於具體芯片設計。

  當應用程序執行起來后,如果發生了中斷,系統會根據發出請求的外設中斷號來中斷向量表里找到對應的外設中斷響應函數並去執行。Cortex-M 內核(除了CM0)模塊 SCB 里有個專門的 VTOR 寄存器用來控制中斷向量表首地址(注意,地址需要 128 字節對齊),程序運行起來后用戶可以配置 SCB->VTOR 寄存器來重設中斷向量表地址。

二、重定向中斷向量表的方法

  現在我們以恩智浦 i.MXRT1170 型號為例介紹重定向中斷向量表的方法,在 \SDK_2.9.1_MIMXRT1170-EVK\boards\evkmimxrt1170\demo_apps\led_blinky\cm7\iar 工程上示例。

2.1 與中斷向量表相關的文件

  這個 led_blinky 工程里跟中斷向量表有關的一共兩個文件,一是 startup_MIMXRT1176_cm7.s 啟動文件,這里面存放了中斷向量表實體定義,以及復位函數 ResetHandler(),從復位函數里你可以看到上來就先重置了一遍 SCB->VTOR 寄存器。

        THUMB

        PUBWEAK Reset_Handler
        SECTION .text:CODE:REORDER:NOROOT(2)
Reset_Handler
        CPSID   I               ; Mask interrupts
        LDR     R0, =0xE000ED08      ; 即 SCB->VTOR
        LDR     R1, =__vector_table  ; section .intvec 段首地址
        STR     R1, [R0]
        LDR     R2, [R1]
        MSR     MSP, R2
        LDR     R0, =SystemInit
        BLX     R0
        CPSIE   I               ; Unmask interrupts
        LDR     R0, =__iar_program_start
        BX      R0

  復位函數里用到的 __vector_table 值取決於 MIMXRT1176xxxxx_cm7_flexspi_nor.icf 鏈接文件里如下語句設置。由於 i.MXRT1170 沒有內部 Flash,分配給外部 NOR Flash (掛在 FlexSPI1 外設上)的系統映射起始地址是 0x30000000,而 0x30002000 是 BootROM 能支持的應用程序初始中斷向量表地址之一(在 IVT 啟動頭里指示)。

define symbol m_interrupts_start       = 0x30002000;
define symbol m_interrupts_end         = 0x300023FF;

define exported symbol __VECTOR_TABLE          = m_interrupts_start;

place at address mem: m_interrupts_start    { readonly section .intvec };

  編譯工程后在對應生成的 iled_blinky_cm7.map 映射文件里可以找到初始中斷向量表最終鏈接地址。為了便於后續分析問題,我們將定時器中斷響應函數地址也一並列出來:

*******************************************************************************
*** PLACEMENT SUMMARY
***

"A0":  place at address 0x3000'2000 { ro section .intvec };

  Section              Kind         Address    Size  Object
  -------              ----         -------    ----  ------
"A0":                                         0x400
  .intvec              ro code  0x3000'2000   0x400  startup_MIMXRT1176_cm7.o [1]
                              - 0x3000'2400   0x400

*******************************************************************************
*** ENTRY LIST
***

Entry                       Address   Size  Type      Object
  -----                       -------   ----  ----      ------
SysTick_Handler         0x3000'5767   0x10  Code  Gb  led_blinky.o [1]
__VECTOR_TABLE {Abs}    0x3000'2000         Data  Gb  <internal module>
__Vectors               0x3000'2000          --   Gb  startup_MIMXRT1176_cm7.o [1]
__Vectors_End           0x3000'2400         Data  Gb  startup_MIMXRT1176_cm7.o [1]
__Vectors_Size {Abs}          0x400          --   Gb  startup_MIMXRT1176_cm7.o [1]
__vector_table          0x3000'2000         Data  Gb  startup_MIMXRT1176_cm7.o [1]

2.2 中斷重定向函數示例

  定時器中斷響應函數 SysTick_Handler() 鏈接在 Flash 里顯然是不行的,我們利用 IDE 特性(對於IAR,是 __ramfunc 修飾符)將其鏈接到 RAM 里(MIMXRT1176xxxxx_cm7_flexspi_nor.icf 里定義了 TEXT2_region: 0x0 - 0x3FFFF 空間存放 section .textrw 段), 重新編譯工程,查看映射文件可以看到新分配的地址是 0x1。

__ramfunc void SysTick_Handler(void)
{
    if (g_systickCounter != 0U)
    {
        g_systickCounter--;
    }
}
*******************************************************************************
*** ENTRY LIST
***

Entry                       Address   Size  Type      Object
  -----                       -------   ----  ----      ------
SysTick_Handler                 0x1   0x14  Code  Gb  led_blinky.o [1]

  現在我們嘗試在代碼里純手工搬移中斷向量表,找一塊空閑的 RAM 區域(比如 0x20000000 - 0x200003FF),將中斷向量表內容直接手工拷貝過去即可,示例代碼如下。主函數里一開始就調用一下這個 relocate_vector_table() 函數即可,修改后的工程下載進板卡運行一切正常,表明中斷向量表重定向操作成功了。

extern uint32_t __VECTOR_TABLE[];

void relocate_vector_table(void)
{
    __disable_irq();
    // 將 0x30002000 處的初始中斷向量表拷貝到新地址 0x20000000
    memcpy((void *)0x20000000, (void *)__VECTOR_TABLE, 0x400);
    // 將 VTOR 指向 0x20000000
    SCB->VTOR = 0x20000000;
    __enable_irq();
}

int main(void)
{
    relocate_vector_table();

    // 其余代碼
}

  至此,Cortex-M中斷向量表原理及其重定向方法痞子衡便介紹完畢了,掌聲在哪里~~~

歡迎訂閱

文章會同時發布到我的 博客園主頁CSDN主頁知乎主頁微信公眾號 平台上。

微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM