IAP,即在应用编程。不少单片机都支持这个功能,STM32也不例外。在以前的FLASH模拟EEPROM实验里面,咱们学习了STM32的FLASH自编程,本章咱们将结合FLASH自编程的知识,经过STM32的串口实现一个简单的IAP功能本章分为以下几个部:编程
53.1 IAP简介app
53.2 硬件设计函数
53.3 软件设计工具
53.4 下载验证
post
IAP(In Application Programming)即在应用编程,IAP是用户本身的程序在运行过程当中对User Flash的部分区域进行烧写,目的是为了在产品发布后能够方便地经过预留的通讯口对产品中的固件程序进行更新升级。 一般实现IAP功能时,即用户程序运行中做自身的更新操做,须要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操做,而只是经过某种通讯方式(如USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在User Flash中,当芯片上电后,首先是第一个项目代码开始运行,它做以下操做:学习
1)检查是否须要对第二部分代码进行更新字体
2)若是不须要更新则转到4)ui
3)执行更新操做设计
4)跳转到第二部分代码执行指针
第一部分代码必须经过其它手段,如JTAG或ISP烧入;第二部分代码可使用第一部分代码IAP功能烧入,也能够和第一部分代码一块儿烧入,之后须要程序更新是再经过第一部分IAP代码更新。
咱们将第一个项目代码称之为Bootloader程序,第二个项目代码称之为APP程序,他们存放在STM32 FLASH的不一样地址范围,通常从最低地址区开始存放Bootloader,紧跟其后的就是APP程序(注意,若是FLASH容量足够,是能够设计不少APP程序的,本章咱们只讨论一个APP程序的状况)。这样咱们就是要实现2个程序:Bootloader和APP。
STM32的APP程序不只能够放到FLASH里面运行,也能够放到SRAM里面运行,本章,咱们将制做两个APP,一个用于FLASH运行,一个用于SRAM运行。
咱们先来看看STM32正常的程序运行流程,如图53.1.1所示:
STM32的内部闪存(FLASH)地址起始于0x08000000,通常状况下,程序文件就今后地址开始写入。此外STM32是基于Cortex-M3内核的微控制器,其内部经过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是0x08000004,当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。
在图53.1.1中,STM32在复位后,先从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完以后,会跳转到咱们的main函数,如图标号②所示;而咱们的main函数通常都是一个死循环,在main函数执行过程当中,若是收到中断请求(发生重中断),此时STM32强制将PC指针指回中断向量表处,如图标号③所示;而后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序之后,程序再次返回main函数执行,如图标号⑤所示。
当加入IAP程序以后,程序运行流程如图53.1.2所示:
在图53.1.2所示流程中,STM32复位后,仍是从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序以后跳转到IAP的main函数,如图标号①所示,此部分同图53.1.1同样;在执行完IAP之后(即将新的APP代码写入STM32的FLASH,灰底部分。新程序的复位中断向量起始地址为0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,如图标号②和③所示,一样main函数为一个死循环,而且注意到此时STM32的FLASH,在不一样位置上,共有两个中断向量表。
在main函数执行过程当中,若是CPU获得一个中断请求,PC指针仍强制跳转到地址0X08000004中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据咱们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回main函数继续运行,如图标号⑥所示。
经过以上两个过程的分析,咱们知道IAP程序必须知足两个要求:
1) 新程序必须在IAP程序以后的某个偏移量为x的地址开始;
2) 必须将新程序的中断向量表相应的移动,移动的偏移量为x;
本章,咱们有2个APP程序,一个为FLASH的APP,另一个位SRAM的APP,图53.1.2虽然是针对FLASH APP来讲的,可是在SRAM里面运行的过程和FLASH基本一致,只是须要设置向量表的地址为SRAM的地址。
1.APP程序起始地址设置方法
随便打开一个以前的实例工程,点击Options for TargetàTarget选项卡,如图53.1.3所示:
默认的条件下,图中IROM1的起始地址(Start)通常为0X08000000,大小(Size)为0X80000,即从0X08000000开始的512K空间为咱们的程序存储区。而图中,咱们设置起始地址(Start)为0X08005000,即偏移量为0X5000(20K字节),于是,留给APP用的FLASH空间(Size)只有0X80000-0X5000=0X7B000(480K字节)大小了。设置好Start和Szie,就完成APP程序的起始地址设置。
这里的20K字节,须要你们根据Bootloader程序大小进行选择,好比咱们本章的Bootloader程序为18K左右,理论上咱们只须要确保APP起始地址在Bootloader以后,而且偏移量为0X200的倍数便可(相关知识,请参考:http://www.openedv.com/posts/list/392.htm)。这里咱们选择20K(0X5000)字节,留了一些余量,方便Bootloader之后的升级修改。
这是针对FLASH APP的起始地址设置,若是是SRAM APP,那么起始地址设置如图53.1.4所示:
这里咱们将IROM1的起始地址(Start)定义为:0X20001000,大小为0XA000(40K字节),即从地址0X20000000偏移0X1000开始,存放APP代码。由于整个STM32F103ZET6的SRAM大小为64K字节,因此IRAM1(SRAM)的起始地址变为0X2000B000,大小只有0X5000(20K字节)。这样,整个STM32F103ZET6的SRAM分配状况为:最开始的4K给Bootloader程序使用,随后的40K存放APP程序,最后20K,用做APP程序的内存。这个分配关系你们能够根据本身的实际状况修改,不必定和咱们这里的设置如出一辙,不过也须要注意,保证偏移量为0X200的倍数(咱们这里为0X1000)。
2.中断向量表的偏移量设置方法
此步,咱们经过修改sys.c里面的MYRCC_DeInit函数实现,该函数代码以下:
01 |
void MYRCC_DeInit( void ) |
02 |
03 |
{ |
04 |
05 |
RCC->APB1RSTR = 0x00000000; //复位结束 |
06 |
07 |
RCC->APB2RSTR = 0x00000000; |
08 |
09 |
RCC->AHBENR = 0x00000014; //睡眠模式闪存和SRAM时钟使能.其余关闭. |
10 |
11 |
RCC->APB2ENR = 0x00000000; //外设时钟关闭. |
12 |
13 |
RCC->APB1ENR = 0x00000000; |
14 |
15 |
RCC->CR |= 0x00000001; //使能内部高速时钟HSION |
16 |
17 |
RCC->CFGR &= 0xF8FF0000; |
18 |
19 |
//复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0] |
20 |
21 |
RCC->CR &= 0xFEF6FFFF; //复位HSEON,CSSON,PLLON |
22 |
23 |
RCC->CR &= 0xFFFBFFFF; //复位HSEBYP |
24 |
25 |
RCC->CFGR &= 0xFF80FFFF; //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE |
26 |
27 |
RCC->CIR = 0x00000000; //关闭全部中断 |
28 |
29 |
//配置向量表 |
30 |
31 |
#ifdef VECT_TAB_RAM |
32 |
33 |
MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); |
34 |
35 |
#else |
36 |
37 |
MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH,0); |
38 |
39 |
#endif |
40 |
41 |
} |
该函数咱们只须要修改最后两行代码,默认的状况下VECT_TAB_RAM是没有定义的,因此执行:MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH,0);这是正常状况的向量表偏移量(为0),本章,咱们修改这句代码为:MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH,0X5000);偏移量为0X5000。其中NVIC_VectTab_RAM和NVIC_VectTab_FLASH是在stm32f10x_nvic.h里面定义的两个宏定义,表明RAM和FLASH的起始地址。
以上是FLASH APP的状况,当使用SRAM APP的时候,咱们须要定义VECT_TAB_RAM,点击Options for TargetàC/C++选项卡,在Preprocessor Symblols栏定义:VECT_TAB_RAM,如图54.1.5所示:
经过这个设置,咱们定义VECT_TAB_RAM,故在执行MYRCC_DeInit函数的时候,会执行MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); 这里的0X0是默认的设置,本章咱们修改此句代码为:MY_NVIC_SetVectorTable(NVIC_VectTab_SRAM,0X1000);即设置偏移量为0X1000。
这样,咱们就完成了中断向量表偏移量的设置。
经过以上两个步骤的设置,咱们就能够生成APP程序了,只要APP程序的FLASH和SRAM大小不超过咱们的设置便可。不过MDK默认生成的文件是.hex文件,并不方便咱们用做IAP更新,咱们但愿生成的文件是.bin文件,这样能够方便进行IAP升级(至于为何,请你们自行百度HEX和BIN文件的区别!)。这里咱们经过MDK自带的格式转换工具fromelf.exe,来实现.axf文件到.bin文件的转换。该工具在MDK的安装目录\ARM\BIN40文件夹里面。
fromelf.exe转换工具的语法格式为:fromelf [options] input_file。其中options有不少选项能够设置,详细使用请参考光盘《mdk如何生成bin文件.pdf》.
本章,咱们经过在MDK点击Options for TargetàUser选项卡,在Run User Programs After Build/Rebuild 栏,勾选Run#1和DOS16,并写入:D:\Keil3.80a\ARM\BIN40\fromelf.exe --bin -o ..\OBJ\TEST.bin ..\OBJ\TEST.axf ,如图53.1.6所示:
经过这一步设置,咱们就能够在MDK编译成功以后,调用fromelf.exe(注意,个人MDK是安装在D:\Keil3.80A文件夹下,若是你是安装在其余目录,请根据你本身的目录修改fromelf.exe的路径),根据当前工程的TEST.axf,生成一个TEST.bin的文件。并存放在axf文件相同的目录下,即工程的OBJ文件夹里面。在获得.bin文件以后,咱们只须要将这个bin文件传送给单片机,便可执行IAP升级。
最后再来看看APP程序的生成步骤:
1) 设置APP程序的起始地址和存储空间大小
对于在FLASH里面运行的APP程序,咱们只须要设置APP程序的起始地址,和存储空间大小便可。而对于在SRAM里面运行的APP程序,咱们还须要设置SRAM的起始地址和大小。不管哪一种APP程序,都须要确保APP程序的大小和所占SRAM大小不超过咱们的设置范围。
2) 设置中断向量表偏移量
此步,经过在MYRCC_DeInit函数里面调用MY_NVIC_SetVectorTable函数,实现对中断向量表偏移量的设置。这个偏移量的大小,其实就等于程序起始地址相对于0X08000000或者0X20000000的偏移。对于SRAM APP程序,咱们还须要在C/C++选项卡定义VECT_TAB_RAM,以申明中断向量表是在SRAM里面。
3) 设置编译后运行fromelf.exe,生成.bin文件.
经过在User选项卡,设置编译后调用fromelf.exe,根据.axf文件生成.bin文件,用于IAP更新。
以上3个步骤,咱们就能够获得一个.bin的APP程序,经过Bootlader程序便可实现更新。
本章实验(Bootloader部分)功能简介:开机的时候先显示提示信息,而后等待串口输入接收APP程序(无校验,一次性接收),在串口接收到APP程序以后,便可执行IAP。若是是SRAM APP,经过按下KEY0便可执行这个收到的SRAM APP程序。若是是FLASH APP,则须要先按下WK_UP按键,将串口接收到的APP程序存放到STM32的FLASH,以后再按KEY2既能够执行这个FLASH APP程序。经过KEY1按键,能够手动清除串口接收到的APP程序。DS0用于指示程序运行状态。
本实验用到的资源以下:
1) 指示灯DS0
2) 四个按键(KEY0/KEY1/KEY2/WK_UP)
3) 串口
4) TFTLCD模块
这些用到的硬件,咱们在以前都已经介绍过,这里就再也不介绍了。
本章,咱们总共须要3个程序:1,Bootloader;2,FLASH APP;3)SRAM APP;其中,咱们选择以前作过的RTC实验(在第二十章介绍)来作为FLASH APP程序(起始地址为0X08005000),选择触摸屏实验(在第三十一章介绍)来作SRAM APP程序(起始地址为0X20001000)。Bootloader则是经过TFTLCD显示实验(在第十八章介绍)修改得来。本章,关于SRAM APP和FLASH APP的生成比较简单,咱们就不细说,请你们结合光盘源码,以及53.1节的介绍,自行理解。本章软件设计仅针对Bootloader程序。
复制第十八章的工程(即实验13),做为本章的工程模版(命名为:IAP Bootloader V1.0),并复制第三十九章实验(FLASH模拟EEPROM实验)的STMFLASH文件夹到本工程的HARDWARE文件夹下,打开本实验工程,并将STMFLASH文件夹内的stmflash.c加入到HARDWARE组下,同时将STMFLASH加入头文件包含路径。
在HARDWARE文件夹所在的文件夹下新建一个IAP的文件夹,并在该文件夹下新建iap.c和iap.h两个文件。而后在工程里面新建一个IAP的组,将iap.c加入到该组下面。最后,将IAP文件夹加入头文件包含路径。
打开iap.c,输入以下代码:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "stmflash.h"
#include "iap.h"
iapfun jump2app;
u16 iapbuf[1024];
//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
u16 t;
u16 i=0;
u16 temp;
u32 fwaddr=appxaddr;//当前写入的地址
u8 *dfu=appbuf;
for(t=0;t<appsize;t+=2)
{
temp=(u16)dfu[1]<<8;
temp+=(u16)dfu[0];
dfu+=2;//偏移2个字节
iapbuf[i++]=temp;
if(i==1024)
{
i=0;
STMFLASH_Write(fwaddr,iapbuf,1024);
fwaddr+=2048;//偏移2048 16=2*8.因此要乘以2.
}
}
if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.
}
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4);
//用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr);
//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}
该文件总共只有2个函数,其中,iap_write_appbin函数用于将存放在串口接收buf里面的APP程序写入到FLASH。iap_load_app函数,则用于跳转到APP程序运行,其参数appxaddr为APP程序的起始地址,程序先判断栈顶地址是否合法,在获得合法的栈顶地址后,经过MSR_MSP函数(该函数在sys.c文件)设置栈顶地址,最后经过一个虚拟的函数(jump2app)跳转到APP程序执行代码,实现IAPàAPP的跳转。
保存iap.c,打开iap.h输入以下代码:
#ifndef __IAP_H__
#define __IAP_H__
#include "sys.h"
typedef void (*iapfun)(void); //定义一个函数类型的参数.
#define FLASH_APP1_ADDR 0x08005000
//第一个应用程序起始地址(存放在FLASH)
//保留0X08000000~0X08004FFF的空间为Bootloader使用
void iap_load_app(u32 appxaddr); //跳转到APP程序执行
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen); //在指定地址开始,写入bin
#endif
这部分代码比较简单,保存iap.h。本章,咱们是经过串口接收APP程序的,咱们将usart.c和usart.h作了稍微修改,在usart.h中,咱们定义USART_REC_LEN为55K字节,也就是串口最大一次能够接收55K字节的数据,这也是本Bootloader程序所能接收的最大APP程序大小。而后新增一个USART_RX_CNT的变量,用于记录接收到的文件大小,而USART_RX_STA再也不使用。在usart.c里面,咱们修改USART1_IRQHandler部分代码以下:
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));
//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
u16 USART_RX_CNT=0; //接收的字节数
void USART1_IRQHandler(void)
{
u8 res;
#ifdef OS_CRITICAL_METHOD
//若是OS_CRITICAL_METHOD定义了,说明使用ucosII了.
OSIntEnter();
#endif
if(USART1->SR&(1<<5))//接收到数据
{
res=USART1->DR;
if(USART_RX_CNT<USART_REC_LEN)
{
USART_RX_BUF[USART_RX_CNT]=res;
USART_RX_CNT++;
}
}
#ifdef OS_CRITICAL_METHOD
//若是OS_CRITICAL_METHOD定义了,说明使用ucosII了.
OSIntExit();
#endif
}
这里,咱们指定USART_RX_BUF的地址是从0X20001000开始,该地址也就是SRAM APP程序的起始地址!而后在USART1_IRQHandler函数里面,将串口发送过来的数据,所有接收到USART_RX_BUF,并经过USART_RX_CNT计数。代码比较简单,咱们就很少说了。
改完usart.c和usart.h以后,咱们在test.c修改main函数以下:
int main(void)
{
u8 t;
u8 key;
u16 oldcount=0; //老的串口接收数据值
u16 applenth=0; //接收到的app代码长度
u8 clearflag=0;
Stm32_Clock_Init(9); //系统时钟设置
uart_init(72,256000); //串口初始化为256000
delay_init(72); //延时初始化
LED_Init(); //初始化与LED链接的硬件接口
LCD_Init(); //初始化LCD
KEY_Init(); //按键初始化
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(60,50,200,16,16,"Warship STM32");
LCD_ShowString(60,70,200,16,16,"IAP TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2012/9/24");
LCD_ShowString(60,130,200,16,16,"WK_UP:Copy APP2FLASH");
LCD_ShowString(60,150,200,16,16,"KEY1:Erase SRAM APP");
LCD_ShowString(60,170,200,16,16,"KEY0:Run SRAM APP");
LCD_ShowString(60,190,200,16,16,"KEY2:Run FLASH APP");
POINT_COLOR=BLUE;
//显示提示信息
POINT_COLOR=BLUE;//设置字体为蓝色
while(1)
{
if(USART_RX_CNT)
{
if(oldcount==USART_RX_CNT)
//新周期内,没有收到任何数据,认为本次数据接收完成.
{
applenth=USART_RX_CNT;
oldcount=0;
USART_RX_CNT=0;
printf("用户程序接收完成!\r\n");
printf("代码长度:%dBytes\r\n",applenth);
}else oldcount=USART_RX_CNT;
}
t++; delay_ms(10);
if(t==30)
{
LED0=!LED0; t=0;
if(clearflag)
{
clearflag--;
if(clearflag==0)LCD_Fill(60,210,240,210+16,WHITE);//清除显示
}
}
key=KEY_Scan(0);
if(key==KEY_UP)
{
if(applenth)
{
printf("开始更新固件...\r\n");
LCD_ShowString(60,210,200,16,16,"Copying APP2FLASH...");
if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)
//判断是否为0X08XXXXXX.
{
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,
applenth); //更新FLASH代码
LCD_ShowString(60,210,200,16,16,"Copy APP Successed!!");
printf("固件更新完成!\r\n");
}else
{
LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP! ");
printf("非FLASH应用程序!\r\n");
}
}else
{
printf("没有能够更新的固件!\r\n");
LCD_ShowString(60,210,200,16,16,"No APP!");
}
clearflag=7;//标志更新了显示,而且设置7*300ms后清除显示
}
if(key==KEY_DOWN)
{
if(applenth)
{
printf("固件清除完成!\r\n");
LCD_ShowString(60,210,200,16,16,"APP Erase Successed!");
applenth=0;
}else
{
printf("没有能够清除的固件!\r\n");
LCD_ShowString(60,210,200,16,16,"No APP!");
}
clearflag=7;//标志更新了显示,而且设置7*300ms后清除显示
}
if(key==KEY_LEFT)
{
printf("开始执行FLASH用户代码!!\r\n");
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)
//判断是否为0X08XXXXXX.
{
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}else
{
printf("非FLASH应用程序,没法执行!\r\n");
LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!");
}
clearflag=7;//标志更新了显示,而且设置7*300ms后清除显示
}
if(key==KEY_RIGHT)
{
printf("开始执行SRAM用户代码!!\r\n");
if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)
//判断是否为0X20XXXXXX.
{
iap_load_app(0X20001000);//SRAM地址
}else
{
printf("非SRAM应用程序,没法执行!\r\n");
LCD_ShowString(60,210,200,16,16,"Illegal SRAM APP!");
}
clearflag=7;//标志更新了显示,而且设置7*300ms后清除显示
}
}
}
该段代码,实现了串口数据处理,以及IAP更新和跳转等各项操做。Bootloader程序就设计完成了,可是通常要求bootloader程序越小越好(给APP省空间嘛),因此,本章咱们把一些不须要用到的.c文件所有去掉,最后获得工程截图如图53.3.1所示:
从上图能够看出,虽然去掉了一些不用的.c文件,可是Bootloader大小仍是有18K左右,比较大,主要缘由是液晶驱动和printf占用了比较多的flash,若是你们想进一步删减,能够去掉LCD显示和printf等,不过咱们在本章为了演示效果,因此保留了这些代码。
至此,本实验的软件设计部分结束。
FLASH APP和SRAM APP两部分代码,根据53.1节的介绍,你们自行修改都比较简单,咱们这里就不介绍了,不过要提醒你们:FLASH APP的起始地址必须是0X08005000,而SRAM APP的起始地址必须是0X20001000。
在代码编译成功以后,咱们下载代码到ALIENTEK战舰STM32开发板上,获得,如图53.4.1所示:
此时,咱们能够经过串口,发送FLASH APP或者SRAM APP到战舰STM32开发板,如图53.4.2所示:
先用串口调试助手的打开文件按钮(如图标号1所示),找到APP程序生成的.bin文件,而后设置波特率为256000(为了提升速度,Bootloader程序将波特率被设置为256000了),最后点击发送文件(图中标号3所示),将.bin文件发送给战舰STM32开发板。
在收到APP程序以后,咱们就能够经过KEY0/KEY2运行这个APP程序了(若是是FLASH APP,则先须要经过WK_UP将其存入对应FLASH区域)。