使用单片机平台:MT031, 32位处理器,最高运行频率72Mhz, 32kb FLASH, 4kb sram。
目标:在该平台上实现bootloader+app的软件结构,bootloader和app分阶段加载,bootloader阶段负责检查需不需要对APP进行固件升级,升级的固件由CAN总线发送过来,然后写入app的固件地址即可。
遇到的问题:该单片机的中断向量表固定在flash的0x0地址,app工程的中断无法响应。
解决思路:bootloader阶段不使用任何中断(reset中断除外),bootloader启动app之前先拷贝app的中断向量表覆盖到bootloader的中断向量表。这样APP阶段使用的中断就能正常响应。
拷贝方式如下:
#define jump_addr FIRMWARE_START_ADDERSS
__align(4) uint32_t * code_buffer;
__align(4) uint8_t code_buf[512] = {0};
void update_rom(void)
{
int i, flag = 0;
code_buffer = (uint32_t *)code_buf;
if (((*(volatile uint32_t *)(jump_addr)) & 0x2FFF0000) != 0x20000000){
return;
}
for (i=1; i<48; i++){
if ((*(volatile uint32_t *)(jump_addr+ i*4)) > 0x8000){
return;
}
}
for (i=8; i<192; i++){
if ((*(volatile uint8_t *)(i)) != (*(volatile uint8_t *)(jump_addr+i)))
{
flag = 1;
}
}
if (flag){
for (i=0; i<48; i++){
code_buffer[i] = *(volatile long *)(jump_addr + i*4);
}
for (; i<128; i++){
code_buffer[i] = *(volatile long *)(0 + i*4);
}
code_buffer[0] = *(volatile long *)(0);
code_buffer[1] = *(volatile long *)(4);
FLASH_FlushCacheCmd(ENABLE); //刷新Cache
FLASH_FlushCacheCmd(DISABLE); //刷新Cache
FLASH_CacheCmd(DISABLE); //关闭Cache
FLASH_FlushCacheCmd(ENABLE); //刷新Cache
FLASH_FlushCacheCmd(DISABLE); //刷新Cache
FLASH_Unlock();
delay_ms(1);
FLASH_EraseSector(0);
FLASH_ProgramData((uint32_t *)code_buffer, 0, 0x200);
FLASH_Lock();
FLASH_CacheCmd(ENABLE); //使能Cache
}
}
解释:
if(flag)前的代码是一些检查动作
1.检查app的运行地址是否是ram。
2.检查app的中断向量表是否指向了不合法的空间,flash 32KB==0x8000
3.检查是否已经拷贝过一次,这样就不用每次启动都拷贝,影响flash的使用寿命
后面是拷贝中断向量表的动作,这里code_buffer是一个flash 的sector大小,拷贝前48个指针,但是第一个和第二个指针不拷贝,因为第一个地址指向栈地址,第二个指针指向reset中断,这两个指针不需要指向APP的地址,其他的46个中断分别指向芯片架构不同的中断,芯片手册可以查到。后面的flash内容不变。然后是写入flash中。
以上就完成了拷贝动作;
然后就可以直接跳转到APP阶段运行了
/*
*Addr;flash addr where do you want to be load
*/
void CAN_BOOT_JumpToApplication(__IO uint32_t Addr)
{
pFunction Jump_To_Application;
__IO uint32_t JumpAddress;
if (((*(__IO uint32_t*)Addr) & 0x2FFE0000 ) == 0x20000000)
{
JumpAddress = *(__IO uint32_t*) (Addr + 4);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*)Addr);
Jump_To_Application();
}
}
注意事项:为了不影响bootloader阶段拷贝动作的正常运行,可以把bootloader的代码分区存放,这里我们把bootloader的flash分为rom1和rom2,rom1是我们刚才拷贝的一个sector的大小512字节,用来存放中断向量表,拷贝flash的代码运行地址要配置成ram。
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x00000000 0x0000200 { ; load region size_region
ER_IROM1 0x00000000 0x0000200 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
}
}
LR_IROM2 0x0000200 0x00001e00 { ; load region size_region
ER_IROM2 0x00000200 0x00001e00 { ; load address = execution address
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00000c00 { ; RW data
.ANY (+RW +ZI)
}
RW_IRAM2 0x20000c00 0x00000400 {
mt031x_flash.o (+RO)
}
}