向量表偏移量寄存器(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重建一个中断向量表,在想改变的位置重新赋值新的跳转地址。

根据引用和引用中的内容,CH582开发板中使用的定时器是TIM2,并且推荐使用systick定时器来进行精确的延时。所以,CH582的定时器延时函数的使用步骤如下: 1. 配置定时器和延时函数:首先需要配置定时器(可以是基本定时器,也可以是TIM2)和串口的配置,同时还需要配置延时函数(推荐使用systick定时器)来实现精确的延时功能。 2. 编写程序:在程序中定义两个IO口,将其连接到Trig引脚上,用于触发定时器。然后,通过调用定时器延时函数,设置需要延时的时间,然后执行延时操作。 需要注意的是,具体的定时器延时函数的编写可能会因为不同的开发板和开发环境而有所不同。因此,在使用定时器延时函数之前,建议查阅开发板的相关文档和资料,以了解具体的函数接口和使用方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [CH582使用MultiTimer软件定时器](https://blog.csdn.net/gg1658608470/article/details/128054432)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [STM32单片机 关于超声波模块的学习(1)](https://download.csdn.net/download/weixin_38590520/14032857)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值