向量表偏移量寄存器(VTOR)在MultiTimer.c上的使用

前言

中断向量表里的中断跳转地址在编译后就定下来了,那我们写的程序有办法改变吗?答案是肯定的。
本文以一个实例介绍如何实现。下面先介绍理论原理。

向量表

当发生了异常并且要响应它时,CM3 需要定位其处理例程的入口地址。这些入口地址存储在所谓的“(异常)向量表”中。缺省情况下,CM3 认为该表位于零地址处,且各向量占用4 字节,因此每个表项占用4 字节。
这里写图片描述

向量表重定位

因为地址0 处应该存储引导代码,所以它通常是Flash 或者是ROM 器件,并且它们的值不得在运行时改变。然而,为了动态重分发中断,CM3 允许向量表重定位——从其它地
址处开始定位各异常向量。这些地址对应的区域可以是代码区,但也可以是RAM 区。在RAM区就可以修改向量的入口地址了。为了实现这个功能,NVIC 中有一个寄存器,称为“向量表偏移量寄存器”(在地址0xE000_ED08 处),通过修改它的值就能定位向量表。但必须注意的是:向量表的起始地址是有要求的:必须先求出系统中共有多少个向量,再把这个数字向上增大到是2 的整次幂,而起始地址必须对齐到后者的边界上。例如,如果一共有32 个中断,则共有32+16(系统异常)=48 个向量,向上增大到2 的整次幂后值为64,因此地址
地址必须能被64*4=256 整除,从而合法的起始地址可以是:0x0, 0x100, 0x200 等。

如果需要动态地更改向量表,则对于任何器件来说,向量表的起始处都必须包含以下向量:

  • 主堆栈指针(MSP)的初始值
  • 复位向量
  • 列表内容
  • NMI
  • 硬 fault 服务例程

后两者也是必需的,因为有可能在引导过程中发生这两种异常。
可以在SRAM 中开出一块用于存储向量表。然后在引导完成后,就可以启用内存中的向量表,从而实现向量可动态调整的功能。

实例分析:MultiTimer.c中的向量表重定位

MultiTimer.c —>mt_delayMs—>init_multi_timer—>InstallIRQHandler

void InstallIRQHandler(IRQn_Type irq, uint32_t irqHandler)
{
/* Addresses for VECTOR_TABLE and VECTOR_RAM come from the linker file */
#if defined(__CC_ARM)  //__CC_ARM ---KEIL
    extern uint32_t Image$$VECTOR_ROM$$Base[];
    extern uint32_t Image$$VECTOR_RAM$$Base[];
    extern uint32_t Image$$RW_m_data$$Base[];

#define __VECTOR_TABLE Image$$VECTOR_ROM$$Base
#define __VECTOR_RAM Image$$VECTOR_RAM$$Base
#define __RAM_VECTOR_TABLE_SIZE (((uint32_t)Image$$RW_m_data$$Base - (uint32_t)Image$$VECTOR_RAM$$Base))
#elif defined(__ICCARM__) //__ICCARM ---IAR
    extern uint32_t __RAM_VECTOR_TABLE_SIZE[];
    extern uint32_t __VECTOR_TABLE[];
    extern uint32_t __VECTOR_RAM[];
#elif defined(__GNUC__)
    extern uint32_t __VECTOR_TABLE[];
    extern uint32_t __VECTOR_RAM[];
    extern uint32_t __RAM_VECTOR_TABLE_SIZE_BYTES[];
    uint32_t __RAM_VECTOR_TABLE_SIZE = (uint32_t)(__RAM_VECTOR_TABLE_SIZE_BYTES);
#endif /* defined(__CC_ARM) */
    uint32_t n;

    __disable_irq();
    if (SCB->VTOR != (uint32_t)__VECTOR_RAM)
    {
        /* Copy the vector table from ROM to RAM */
        for (n = 0; n < ((uint32_t)__RAM_VECTOR_TABLE_SIZE) / sizeof(uint32_t); n++)
        {
            __VECTOR_RAM[n] = __VECTOR_TABLE[n];
        }
        /* Point the VTOR to the position of vector table */
        SCB->VTOR = (uint32_t)__VECTOR_RAM;
    }

    /* make sure the __VECTOR_RAM is noncachable */
    __VECTOR_RAM[irq + 16] = irqHandler;

    __enable_irq();
}


system_QN908X.c

void SystemInit(void)
{

    SCB->VTOR = (uint32_t)&__Vectors;
}

startup_QN908X.s

__Vectors       EQU   __vector_table


__vector_table
                DCD     sfe(CSTACK)
                DCD     Reset_Handler

                DCD     NMI_Handler
                DCD     HardFault_Handler
                DCD     MemManage_Handler
                DCD     BusFault_Handler
                DCD     UsageFault_Handler       

SCB->VTOR 在SystemInit里被设置成flash中异常地址。SCB->VTOR = (uint32_t)&__Vectors;

    __disable_irq();
    if (SCB->VTOR != (uint32_t)__VECTOR_RAM)
    {
        /* Copy the vector table from ROM to RAM */
        for (n = 0; n < ((uint32_t)__RAM_VECTOR_TABLE_SIZE) / sizeof(uint32_t); n++)
        {
            __VECTOR_RAM[n] = __VECTOR_TABLE[n];
        }
        /* Point the VTOR to the position of vector table */
        SCB->VTOR = (uint32_t)__VECTOR_RAM;
    }

    /* make sure the __VECTOR_RAM is noncachable */
    __VECTOR_RAM[irq + 16] = irqHandler;

    __enable_irq();

所以上面代码的意思就是:
如果现在SCB->VTOR 指向的不是RAM中的中断向量地址,就通过赋值的方法将ROM中的中断向量地址一个一个的赋值到RAM中去,__VECTOR_RAM[n] = __VECTOR_TABLE[n];

然后SCB->VTOR中赋值 已经赋值好的RAM中的中断向量表的首地址,SCB->VTOR = (uint32_t)__VECTOR_RAM;

这样下次异常发生时,就直接跳到RAM中断向量表的首地址处,再匹配对应的中断向量。

总结

中断向量表里的中断跳转地址在编译后就定下来了,SCB->VTOR向量可动态调整就是让我们的程序运行后还能改变向量的跳转地址。方法就是:在RAM重建一个中断向量表,在想改变的位置重新赋值新的跳转地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值