当前位置:首页 > 嵌入式 > 嵌入式客栈
[导读]大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是Cortex-M中断向量表原理及其重定向方法。接着前文《嵌入式Cortex-M裸机环境下临界区保护的三种实现》继续聊,嵌入式代码设计里有时候一些特殊操作(比如XIP下Flash擦写、低功耗模式切换)不能被随意打断,或...

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是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
__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中断向量表原理及其重定向方法痞子衡便介绍完毕了,掌声在哪里~~~


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭