硬件平台 : STM32f407ZGT6 , 1024K flash, 128K ram
# mtdparts
No. name range size
1 bootloader 0x08000000 ~ 0x08010000 0x10000 (64K)
2 application 0x08010000 ~ 0x08100000 0xF0000 (960K)
```
typedef void (* start_kernel_t)(void);
#define REG32_READ(addr) (*(volatile u32 *)((u32)(addr)))
start_kernel_t start_kernel;
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
void load_kernel(u32 addr)
{
if((REG32_READ(addr) & 0x2FFE0000) == 0x20000000) //检查栈顶地址是否合法.
{
start_kernel = (start_kernel_t)(REG32_READ(addr + 4)); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(REG32_READ(addr)); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
/*
* now, start the application we have
*/
start_kernel(); //
}
}
#ifdef USE_CLI
static void do_load (u32 addr)
{
printf("addr : %08x \r\n", addr);
load_kernel(addr);
}
REGISTER_CMD(
load,
2,
ARG_TYPE_U32,
do_load,
" load the kernel"
);
#endif
```
```
LR_IROM1 0x08010000 0x000F0000 { ; load region size_region
ER_IROM1 0x08010000 0x000F0000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
CMDREG +0
{
.ANY(cmd)
}
RW_IRAM1 0x20000000 0x00020000 { ; RW data
.ANY (+RW +ZI)
}
}
```
keil设置
misc controls :
--keep=strcmd_*
R/O Base : 0x08010000
R/W Base : 0x20000000
IROM1 : 0x08010000 , 0xF0000
After Build/Rebuild : // not neccessary
D:\ProgramFiles\Keil5\ARM\ARMCC\bin\fromelf.exe --bin -o Objects\EVA.bin Objects\EVA.axf
then
void main()
{
...
SCB->VTOR = FLASH_BASE | 0x10000;
...
while(1)
{
...
}
}
# 总结
以上可以实现IAP,但是关于bootloader和application的堆栈没有搞清楚
下面来总结
# bootloader
keil: Program Size: Code=8216 RO-data=960 RW-data=72 ZI-data=3880
在map文件里面:
```
Total RO Size (Code + RO Data) 9176 ( 8.96kB)
Total RW Size (RW Data + ZI Data) 3952 ( 3.86kB)
Total ROM Size (Code + RO Data + RW Data) 9248 ( 9.03kB)
```
flash base = 0x08000000
sram base = 0x20000000
其中 : ROM = Code + RO Data + RW Data = 9284
RAM是多少呢?
RAM = RW-data + ZI-data = 3880 + 72 = 3952(0xF70)
其中ROM存在 flash里面,即基地址0x08000000, 大小0x100000 (1024K)
RAM存在 SRAM里面, 其基地址0x20000000, 大小 0x20000 (128K)
STACK size = 0x400 , 在startup_stm32f40_41xxx.s文件开头指定, 而arm一般栈向下增长,即SP(MSP) = 0xF70
这点可以在keil进debug模式看到MSP的值MSP = 0x20000F70,与上面分析一致。
对于application,由于这里选用与bootloader一样的程序,其map
```
Total RO Size (Code + RO Data) 9248 ( 9.03kB)
Total RW Size (RW Data + ZI Data) 3952 ( 3.86kB)
Total ROM Size (Code + RO Data + RW Data) 9324 ( 9.11kB)
```
SRAM = RW Data + ZI-data = 76 + 3876 = 3952(F70)
也即,启动application后,原来bootloader的栈会完全被application覆盖;这个并没有问题。
#优化栈(重新分配)
No. name stack size
1 bootloader 0x20000000 ~ 0x20008000 0x8000 (32K)
2 application 0x20008000 ~ 0x20020000 0x18000 (96K)
栈的地址和大小分配,需要在keil里面设置,有必要时需要在xxx.sct (即分散存储脚本)
这样的化,将bootloader和application的栈区分开。
```
LR_IROM1 0x08000000 0x00100000 { ; load region size_region
ER_IROM1 0x08000000 0x00100000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
CMDREG +0
{
.ANY(cmd)
}
RW_IRAM1 0x20000000 0x00008000 { ; RW data
.ANY (+RW +ZI)
}
}
```
```
LR_IROM1 0x08010000 0x000F0000 { ; load region size_region
ER_IROM1 0x08010000 0x000F0000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
CMDREG +0
{
.ANY(cmd)
}
RW_IRAM1 0x20008000 0x00018000 { ; RW data
.ANY (+RW +ZI)
}
}
```
ymodem升级
其中一个串口作为调试,用来启动ymodem传输,另一个ymodem使用的串口
调试串口里面输入req命令启动ymodem传输
ymodem端串口接收到C后会发送数据
最后在调试串口执行load 0x08010000,启动app
可以看到新程序启动