我用的芯片是STM32L152RET6
IAP(在程序中升级)一般是需要写一个Bootloader引导程序,对程序进行引导。但因项目需要,不能单独烧写bootloader程序,所以采用在程序中升级,通过读写Flash,跳转的方法,省去了单独烧写bootloader。(其实只是把Bootloader加入app中,在程序中去引导)
IAP在线升级流程图:思路
APP1:
print(USART1, " APP1 is running! ");
while (1)
{
if (UP_flag==1)
{
iap_start(0x8040000); //写入Flash memory Bank 2
}
LL_mDelay(5000);
}
串口1输出:APP1 is runing! main函数直接进入大循环,等待升级指令,当指令下发,进入串口中断,标志位置1
void USART1_IRQHandler(void)
{
uint8_t res;
if(LL_USART_IsActiveFlag_RXNE(USART1))
{
if(LL_USART_IsActiveFlag_RXNE(USART1))
{
if(U_RX_CNT<U_RECC_LEN)
{
res=LL_USART_ReceiveData8(USART1);
U_RX_BUF[U_RX_CNT]=res;
U_RX_CNT++;
}
}
}
if(U_RX_BUF[0]==0x04) //0x04表示升级指令
{
UP_flag = 1; //标志位置1
}
}
标志位置1后进入iap_start()函数,函数参数为要写入Flash地址即程序跳转执行地址。在这个函数里等待串口中断接收APP2升级程序文件,接收完成后写入Flash Bank 2,并将程序跳转到Flash Bank 2执行。
void iap_start(int FLASH_APP_ADDR)
{
u16 oldcount=0; //原串口接收数据值
u16 applenth=0; //接收到的APP代码长度
u16 page_num = 0;
print(USART1,"iapstart\r\n");
USART1_CLR_RecvBuf();
U_RX_CNT=0;
print(USART1,"等待接收完成\r\n"); //从这里进入大循环,等待串口中断接收待升级的APP2数据文件
while (1)
{
LL_mDelay(100);
if(U_RX_CNT)
{
if(oldcount==U_RX_CNT)//新周期内,没有收到任何数据,认为本次数据就收完成
{
applenth=U_RX_CNT;
oldcount=0;
U_RX_CNT=0;
print(USART1,"用户程序接收完成\r\n");
print(USART1,"开始更新固件库...\r\n");
/**************************擦除FLASH**************************/
HAL_FLASH_Unlock(); //解锁FLASH
page_num = applenth/256 + 1; //计算需要擦除的页数,L152RE每页256字节
FLASH_EraseInitTypeDef f;
f.TypeErase = FLASH_TYPEERASE_PAGES; // 擦除页
f.PageAddress = FLASH_APP_ADDR; //擦除起始地址
f.NbPages = page_num; // 擦除的页数
uint32_t PageError = 0; // 接收错误返回值
HAL_FLASHEx_Erase(&f, &PageError); //擦除FLASH
HAL_FLASH_Lock(); //锁Flash
print(USART1,"等待更新...\r\n");
iap_write_appbin(FLASH_APP_ADDR,U_RX_BUF,applenth);
print(USART1,"更新固件完成...\r\n");
if(((*(volatile u32*)(FLASH_APP_ADDR+4))&0xFF000000)==0x08000000) //判断是否为0x08xxxxxxx,程序开始地址(复位地址)
{
iap_load_app(FLASH_APP_ADDR); //跳转到Flash Bank 2,执行APP2
}else
{
print(USART1,"非Flash应用程序,无法执行!\r\n");
}
}else oldcount=U_RX_CNT;
}
}
}
下发升级指令: 等待接收
发送APP2 bin文件:
bin文件传输完成后等待更新,并跳转执行APP2,从串口助手可以看到,程序已经跳转执行APP2了
现在我把掉电源,重新上电,看看是执行的哪个程序。 升级成功!
APP2:
在APP2程序中需要将Flash Bank 2 读出并写入Flash Bank 1中。这样子,在芯片掉电以后就会执行APP2,而不是APP1,升级成功 (我程序里规定的是读30K,够用了)
for(ii=0;ii<30720;ii++)
{
teemp[ii]=Flash_read_data(adderrr+ii);
}
writeFlashTest(0x8000000,(u8*)teemp,7680);
print(USART1, " copy ok2! "); //调试语句,表示已经将Flash Bank 2 读出并写入了Flash Bank 1
另外APP2生成bin文件时,工程配置的起始地址需要改动
上面是APP1起始地址为:0x800 0000 下面是APP2起始地址:0x804 0000
有问题欢迎留言讨论哈,表达能力欠缺,有不清楚的可以问我