本帖最后由 DKENNY 于 2023-12-24 17:32 编辑
承接上文,这篇主要对比一下APM32系列中实现IAP固件升级的3种方式。
上节阐述了VTOR寄存器的作用,IAP固件升级也是与VTOR寄存器有关的,下面具体介绍一下这3种方式,主要是当APP产生中断时,其对应的中断向量表的查找方式。
1. 有VTOR寄存器
在有VTOR寄存器的情况下,我们直接设置VTOR的值,直接指向APP的中断向量表的入口地址。
在APM32F1(CORTEX-M3) 中就支持中断向量重定向,因为它有VTOR寄存器,可以重新设置中断向量在FLASH中的地址,这个功能使IAP实现变得非常完美。
如下APP的main.c代码。 int main(void)
{
SCB->VTOR = FMC_BASE | 0x4000;
SysTick_Config(SystemCoreClock / 1000);
APM_MINI_LEDInit(LED2);
while (1)
{
Delay();
APM_MINI_LEDToggle(LED2);
}
}
如下的System_Init()函数,也成功设置了中断向量表的偏移量,如果没有VTOR寄存器,下面的代码是不起作用的。 #ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
#else
SCB->VTOR = FMC_BASE | VECT_TAB_OFFSET;
#endif
若APP产生了中断,其对应的中断执行流程如下。
因为APP有自己的中断向量表,所以当APP产生中断时,会直接从VTOR所设置的地址查找中断向量表,执行对应的中断服务函数。
如下是KEIL的配置,只需设置APP的FLASH的起始地址即可。
2. 无VTOR寄存器 有些芯片内核没有VTOR寄存器,即使这样,仍然有两种方式可以进行中断的相关处理,一种是中断向量表重映射,另一种是中断服务函数重定位。
2.1 中断向量表重映射 由于没有VTOR寄存器,所以我们可以在APP代码中,先将APP的中断向量表从FLASH中复制到RAM里,再将RAM区映射到vector table区域。
以下是中断向量表重映射的相关寄存器。 (1)当MEM_MODE=00/10时,MainFlash映射到地址0x00000000,即地址0x08000000映射到0x00000000. (2)当MEM_MODE=01时,SystemFlash映射到地址0x00000000,也就是芯片自带的Bootloader代码部分会映射到地址0x00000000,即0x1FFFC800映射到地址0x00000000. (3)当MEM_MODE=11时,SRAM映射到地址0x00000000,也就是内存地址0x20000000映射到地址0x00000000.
那么,中断向量表的固定地址是多少呢?在ARM官网上我们找到如下信息:
也就是说,在没有VTOR寄存器时,中断向量表的地址固定在地址0x00000000上。 在这个过程中,中断的调用过程如下: ->产生中断 ->CPU固定到地址0x00000000上找中断入口函数,由于映射关系,实际上是在从映射地址上寻找,即0x20000000。 ->找到并执行中断例程
若使用这种方式进行IAP的固件升级,则必须在APP中将中断向量表拷贝到SRAM,并将系统重映射到SRAM,这样才能使中断恢复正常工作。 如下为实现IAP跳转的代码: /*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Jump to user application
*
* @param Application : APP1 or APP2
*
* @retval None
*
* @note
*/
void Jump_to_App(uint8_t Application)
{
uint32_t address;
/* Lock the Program memory */
FMC_Lock();
if(Application == APP1)
{
address = USER_APP1_START_ADDRESS;
}
else
{
address = USER_APP2_START_ADDRESS;
}
/* Jump to user application */
JumpAddress = *(__IO uint32_t *) (address + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t *) address);
/* Jump to application */
Jump_To_Application();
}
其具体的中断服务函数的实现流程如下图所示:
当APP产生中断时,系统会选择根据MEM的映射方式,去0x00000000查找中断向量表,执行其对应的中断服务函数。
如下为KEIL的相关配置:
为什么设置IRAM1的Start地址为0x200000C0? RAM区是用来存放程序运行时的一些变量,设置RAM区地址从0x200000C0,是因为0x20000000处存放了中断向量表,这是为了防止中断向量表被变量重新覆写。
2.2中断服务函数重定位 这种方式下,它并没有将中断向量表重映射,而是将中断服务函数进行了重定位,直接跳转到对应的中断服务函数。 在IAP程序中定义APP_interrupt_vector_addr(存储在0x20000000)变量,用于处理IAP中断函数,以响应正确的中断,该变量是一个标志位。 各个工程均需初始化变量FLASH_interrupt_vector(存储在0x20000004)变量,用于指向当前系统所需的中断服务函数,该变量是一个标志位。
如下为IAP的中断服务函数,目的是为了判断当前是哪个APP发生了中断,跳转到对应APP的中断服务函数.
同时在APP1中,更改FLASH_interrupt_vector,设置为APP1_INTERRUPT_VECTOR,代表指向APP1的中断向量表。
具体的执行流程图如下:
当APP产生中断时,其具体的执行流程如下:
在这种情况下,当APP产生中断时,会去查找IAP的中断向量表,在IAP的中断服务函数里直接跳转到APP的中断服务函数。
如下为KEIL的相关配置。
为什么设置IRAM1的start值为0x20000008 ? 因为在0x20000000处存放了两个常量,常量类型为uint32_t,故一个变量占4个字节,两个常量占8个字节,这两个常量在程序运行时是不能被轻易修改的。 程序运行时,会在RAM区生成一些数据,为了防止这两个常量被这些数据覆盖,从而影响后续的中断响应及中断服务函数的跳转。
3. 相关例程 附件中含上述3种情况的例程,有需要的可查看。
以上,就是本次分享的内容了,如有问题,欢迎讨论。
|
赞