转载——CH579(M0内核)中断向量表偏移处理

原文链接:https://blog.csdn.net/xxdx_admin/article/details/122369753

通常情况下,为了产品后续的升级,程序都会分为两部分Bootloader+App,因为有两个程序,所以需要对中断向量表进行处理,否则,当程序已经跳转到app中运行,当中断产生的时候,响应函数仍然是Bootloader的中断函数,而不是app的
在STM32F103 F3内核上是可以配置寄存器SCB->VTOR设置中断向量表偏移,CH579是M0内核,并不支持这样操作,中断向量表位置固定在(地址0x00000000)位置上

解决思路:

  1. 将中断向量表重映射到RAM(内存)
  2. 根据当前运行程序将FLASH中的向量表拷贝到RAM中

解决步骤

  1. 将地址0x00000000的中断向量表中的全部中断函数都设置为同一个函数,用于映射
  2. 编写映射函数
  3. 修改RAM配置信息
  4. 根据前当前运行的程序拷贝中断向量表到RAM

(注意:以下代码都是编写在Bootloader程序中)
第一步:设置映射
这里利用编译器设置函数属性的就可以简单实现

#define ALIAS(x) __attribute__ ((alias (#x)))

void NMI_Handler(void)      ALIAS(interrupt_vector_relay);
void HardFault_Handler(void)ALIAS(interrupt_vector_relay);
void SVC_Handler(void)      ALIAS(interrupt_vector_relay);
void PendSV_Handler(void)   ALIAS(interrupt_vector_relay);
void SysTick_Handler(void)  ALIAS(interrupt_vector_relay);
void TMR0_IRQHandler(void)  ALIAS(interrupt_vector_relay);
void GPIO_IRQHandler(void)  ALIAS(interrupt_vector_relay);
void SLAVE_IRQHandler(void) ALIAS(interrupt_vector_relay);
void SPI0_IRQHandler(void)  ALIAS(interrupt_vector_relay);
void BB_IRQHandler(void)    ALIAS(interrupt_vector_relay);
void LLE_IRQHandler(void)   ALIAS(interrupt_vector_relay);
void USB_IRQHandler(void)   ALIAS(interrupt_vector_relay);
void ETH_IRQHandler(void)   ALIAS(interrupt_vector_relay);
void TMR1_IRQHandler(void)  ALIAS(interrupt_vector_relay);
void TMR2_IRQHandler(void)  ALIAS(interrupt_vector_relay);
void UART0_IRQHandler(void) ALIAS(interrupt_vector_relay);
void UART1_IRQHandler(void) ALIAS(interrupt_vector_relay);
void RTC_IRQHandler(void)   ALIAS(interrupt_vector_relay);
void ADC_IRQHandler(void)   ALIAS(interrupt_vector_relay);
void SPI1_IRQHandler(void)  ALIAS(interrupt_vector_relay);
void LED_IRQHandler(void)   ALIAS(interrupt_vector_relay);
void TMR3_IRQHandler(void)  ALIAS(interrupt_vector_relay);
void UART2_IRQHandler(void) ALIAS(interrupt_vector_relay);
void UART3_IRQHandler(void) ALIAS(interrupt_vector_relay);
void WDT_IRQHandler(void)   ALIAS(interrupt_vector_relay);

这里利用**attribute ((alias ()))**,将全部的中断函数都设置为函数interrupt_vector_relay()的别名,通过这样声明函数,所有的中断函数等于interrupt_vector_relay()这个函数

步骤2:编写映射函数
我们只需要读取寄存器查看当前是发生了哪个中断,然后对应跳转到真正执行动作的函数即可
_vector_table_pointer 这个是RAM地址,这里利用RAM前面的0x100字节作为中断向量表存放位置

//中断向量表位置
#define _vector_table_pointer  0x20000000
__asm void interrupt_vector_relay(void) {
    //IPSR是中断状态寄存器,因为每个中断函数地址是4个字节,因此将读取数值*4(左移两位)得到的就是地址偏移
    mrs	    r0, ipsr;
    //将值左移2位
    lsls    r0, r0, #0x02;
    //从存储器地址取数据到R1
    ldr	    r1, =_vector_table_pointer;
    //计算出偏移
    adds	r1, r1, r0;
    //将该地址的值给r1
    ldr	    r1, [r1];  
    //跳转到相对应的中断
	bx	    r1;
    nop    
}

步骤三:修改RAM信息
因为使用了RAM的一部分存放中断向量表,所以要配置RAM的起始地址,否则这部分会被程序占用使用,导致错误,修改只需要将起始位置向前+0x100即可,当然size也要相应的减去0x100
注意:这个在Bootloader和App中都要修改

步骤四:拷贝向量表到内存
在跳转到APP前,将APP的中断向量表拷贝到RAM里面即可

//APP的地址,根据实际修改
#define APP_BASE       0x00004000  
//拷贝中断向量表
memcpy((void *)(_vector_table_pointer),(void *)(APP_BASE),0x100);

.

补充:app的中断函数编写和使用与正常情况下一致,无需特殊处理
但如果在Bootloader需要用到中断,比如使用定时器中断

 UINT32 TIM_IRQ_ADDR;
 TIM_IRQ_ADDR = (UINT32)DFU_TMR0_IRQHandler;
 memcpy((void *)(_vector_table_pointer + 0x40),&TIM_IRQ_ADDR,4);

Bootloader不能直接编写TMR0_IRQHandler() 因为这个名字已经被占用了, 需要自定义一个函数比如DFU_TMR0_IRQHandler(),然后将该函数的地址拷贝到RAM中的特定位置,这里的0x40就是对应定时器的中断函数

发表评论

邮箱地址不会被公开。 必填项已用*标注