步骤一:建立两个工程。①APP程序工程;②BOOT升级工程;这两个工程最终都是要烧录进MCU的。
不过需要注意芯片地址的设置,下图是我所设置的 BOOT升级工程 的需要空间,地址就从起始地址0x08000000开始(这是由芯片手册得到),因为芯片一启动,就是从这个地址开始往下运行。由于升级文件不大,我只用了0x2800大小空间(这里记得要是某个数值的倍数大小,太久忘了,下次用的时候再百度好了,如果有博友知道的话,还请留言评论一下,感谢!)。
下图则是APP程序的预留空间了。
步骤二:开始写BOOT工程
读取标志位(预先再 备份寄存器中写好的,因为配合外接电池的话关电不丢失)
if(标志为BOOT更新)
{
开始更新APP程序,然后跳转进APP代码段
}
else if(标志为直接进入APP)
{
跳转进APP代码段
}
例:
Read_Bkp(1,&flag);//判断是进入BOOT更新,还是直接进入APP
printf("\r\nK2_STM32F103_BOOT!\r\n\r\n");
while(1)
{
if(flag == FLAG_TO_BOOT)
{
ReceiveUsartData();
count++;
if(count == 1000000)//开始进入更新模式的时候,需等待一段时间,防止垃圾数据出现
{
count = 0;
USART2SendRespondToA64(SERIAL_CODE_STM32_UPDATE_PREPAR_BOOT_OK);//向上位机发送准备进入BOOT
// printf("\r\nqwer\r\n");
}
}
else if(flag == FLAG_TO_APP)
{
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}
}
}
上述代码段就是简易的BOOT更新逻辑。
重点来了,我的RAM只有20K,而我的APP程序不止20K。不能一次将其全部缓存成一个数组,于是我只能分批的通过USART接收,然后写入flash。
void ReceiveUsartData(void)
{
u8 CheckSum = 0;
u8 CodeData = 0;
int i;
u32 tempAddr = FLASH_APP1_ADDR;
if(UsartReceiveFlag == 1) //串口接完数据
{
UsartReceiveFlag = 0;
CodeData = USART_Receive[4];
for(i = 0; i < USART_RECEIVE_SIZE; ++i)
{
CheckSum = CheckSum + USART_Receive[i];
}
if((CheckSum == 0) && (USART_Receive[0] == 0x55) && (USART_Receive[1] == 0xaa) && (USART_Receive[2] == 0x01))
{
switch(CodeData)
{
case SERIAL_CODE_SET_STM32_TO_UPDATE:
{
UpdateFlag = 1;//置1之后,USART接收将一直接收.BIN文件
nPageTotal = USART_Receive[5]; //获取总包数
USART2SendRespondToA64(SERIAL_CODE_STM32_UPDATE_PREPAR_OK);//向上位机发送准备开始更新信号
while(1)
{//等待一包数据接收完成
if(UpdateFlag == 1 && NextPageFlag == 1)
{
NextPageFlag = 0;
if((USART_ReCodeData[0] == 0x55) && (USART_ReCodeData[1] == 0xaa) && (USART_ReCodeData[2] == 0x01))
{//判断发送包是否数据准确
nPageCount++;//统计写入次数
STMFLASH_Write(tempAddr, (u16*)(USART_ReCodeData + 5), (sizeof(USART_ReCodeData)/sizeof(USART_ReCodeData[0]) - 6) / 2);
// STMFLASH_Read(tempAddr,(u16*)USART_ReCodeData,(sizeof(USART_ReCodeData)/sizeof(USART_ReCodeData[0]) - 6) / 2);
// for(i = 0;i<((sizeof(USART_ReCodeData)/sizeof(USART_ReCodeData[0]) - 6)); i++)
// {
// if(x%16 == 0)
// {
// printf("\r\n");
// printf("%08x:",y);
// y += 16;
// }
// printf("%.2x ",USART_ReCodeData[i]);
// x++;
// }
// printf("\r\n\r\n");
tempAddr += FLASH_APP_ADDR_OFFSET;
USART2SendRespondToA64(SERIAL_CODE_STM32_UPDATE_NEXT_PACKEG);//向上位机发送本次数据写入成功,准备下一次接收写入
if(nPageCount == nPageTotal)//判断写入次数与总包数是否相同
{
UpdateFlag = 0; //只能升级完成后置此标志
nPageCount = 0;
tempAddr = FLASH_APP1_ADDR;
Write_Bkp(1, FLAG_TO_APP);
USART2SendRespondToA64(SERIAL_CODE_STM32_UPDATE_SUCCESS);
if(((*(vu32*)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000)//判断是否为0X08XXXXXX.
{
printf("\r\nwrite flash successful!\r\n");
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}
}
}
else
{//如果不对,给上位机发送再发一次信号
USART2SendRespondToA64(SERIAL_CODE_STM32_UPDATE_CURRENT_PACKEG);
}
UsartRxCodeCount = 0;
memset(USART_ReCodeData, 0, sizeof(USART_ReCodeData)/sizeof(USART_ReCodeData[0]));
}
}
}break;
default: break;
}
}
UsartRxCount = 0;
memset(USART_Receive, 0, sizeof(USART_Receive)/sizeof(USART_Receive[0]));
}
}
串口这边代码
void USART2_IRQHandler(void)
{
u8 res = 0;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(USART2->SR&(1<<5)) //接收到数据
{
res=USART2->DR;
//更新的.BIN文件太大,而SRAM空间太小(20K)。需要分成多次接收,接一次便写入flash一次,然后清空数组,准备下一次接收。
if(UpdateFlag == 1)//升级程序bin文件接收
{
USART_ReCodeData[UsartRxCodeCount] = res;
UsartRxCodeCount++;
if(UsartRxCodeCount == USART_RECEIVE_CODE_DATA_SIZE)
{
NextPageFlag = 1;
UsartReceiveFlag = 1;
}
}
else//命令接收
{
USART_Receive[UsartRxCount] = res;
UsartRxCount++;
if(UsartRxCount == USART_RECEIVE_SIZE)
{
UsartReceiveFlag = 1;
}
}
}
}
步骤三:对APP工程做一个判断代码,主要是为了在使用过程中,方便直接进行在线更新。
读取标志位(预先再 备份寄存器中写好的,因为配合外接电池的话关电不丢失)
if(标志为BOOT更新)
{
SystemReset(); //系统直接复位
}
else if(标志为直接进入APP)
{
跳转进APP代码段
}
步骤四:就是另外比较重要的一部分了。由C++编写的下载器了,没有这个下载器,那写好编译生成的APP的hex/bin文件就不知道如何通过已经约定好(即ReceiveUsartData()函数)的协议来通过串口发送给MCU了。
详情见百度网盘 路径(我的资源>软件>STM32_Update.rar)百度网盘-免费云盘丨文件共享软件丨超大容量丨存储安全