原文链接:https://blog.csdn.net/xxdx_admin/article/details/122369753
通常情况下,为了产品后续的升级,程序都会分为两部分Bootloader+App,因为有两个程序,所以需要对中断向量表进行处理,否则,当程序已经跳转到app中运行,当中断产生的时候,响应函数仍然是Bootloader的中断函数,而不是app的
在STM32F103 F3内核上是可以配置寄存器SCB->VTOR设置中断向量表偏移,CH579是M0内核,并不支持这样操作,中断向量表位置固定在(地址0x00000000)位置上
解决思路:
- 将中断向量表重映射到RAM(内存)
- 根据当前运行程序将FLASH中的向量表拷贝到RAM中
解决步骤
- 将地址0x00000000的中断向量表中的全部中断函数都设置为同一个函数,用于映射
- 编写映射函数
- 修改RAM配置信息
- 根据前当前运行的程序拷贝中断向量表到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就是对应定时器的中断函数