公司产品要求,需要做一个能远程升级程序的功能,找了很多例程,大多都是需要按键来完成操作的,而我需要的是通过串口发送指令来完成,于是东拼西凑最后还是用了四天的时间勉强做出来
整个功能需要的程序是两个部分。一个是IAP程序,一个是APP程序。对于IAP程序和APP原理方面的内容就不再过多赘述。直接从操作开始吧。
IAP程序
写IAP程序之前首先得配置程序的起始地址和大小。这里根据个人情况而定,我这里单片机flash大小是512k,所以IAP程序选择分配的大小是64k。
点击魔术棒,然后在选择target,设置起始地址(start)和大小(size),IAP程序起始地址都是从0x8000000开始的,大小就是0x10000也就是64k啦。
在这里我参考了原子哥的源码和一位大神的分享,原子的资料一搜一大堆,这里就仅贴出大佬的链接
stm32 IAP 程序编写心得
有了前车之鉴,做起来也稍显得心应手,这里的flash操作的函数和IAP功能函数都是拿来主义了,原子IAP例程里拿来就可以用。主要工作还是针对main函数,这里直接贴出源码:
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "stmflash.h"
#include "iap.h"
#define ADDR_CodeWriteFlag 0X08070000 //设置跳转标志位保存地址
#define ADDR_JumpToIAPFlag 0X08070001
int main(void)
{
u16 IAPFlagBuf[2];
u16 RxDataCount=0; //串口接收到的数据计数
u16 RxDataLength=0; //串口接收到的数据长度
u16 AppCodeLength = 0; //接收到的app代码长度
u8 RxCmdFlag = 0;
u8 AppRunFlag = 0; //应用程序运行标志
u16 JumpToAPPFlag; //跳转至APP程序标志位
u16 JumpToIAPFlag; //跳转回IAP标志位
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
JumpToAPPFlag = STMFLASH_ReadHalfWord(ADDR_CodeWriteFlag); //读取APP写入标志位,判断是否已有程序
JumpToIAPFlag = STMFLASH_ReadHalfWord(ADDR_JumpToIAPFlag);
uart_init(9600); //串口初始化为9600
delay_init(); //延时初始化
IAPFlagBuf[0] = 0x11;
IAPFlagBuf[1] = 0x00;
printf("提示:输入send发送bin文件!\r\n");
while(1)
{
if(JumpToAPPFlag != 0x11) //判断是否已有APP程序,如果已有,跳转至APP程序运行
{
if(JumpToIAPFlag == 0x11) //判断是否从APP程序跳转回来,如果是擦除已有APP程序
{
JumpToIAPFlag = 0x00;
}
if(USART_RX_CNT) //如果有数据进来
{
if(RxDataCount == USART_RX_CNT) //串口没有再收到新数据
{
RxDataLength = USART_RX_CNT;
if(RxCmdFlag == 0 && RxDataLength == 4) //接收到IAP指令
{
if(USART_RX_BUF[0] == 's' && USART_RX_BUF[1] == 'e' && USART_RX_BUF[2] == 'n' && USART_RX_BUF[3] == 'd')//判断是否为IAP指令
{
RxCmdFlag = 1; //接收到更新APP代码指令,标志位置1
RxDataLength = 0; //清空指令长度,防止影响后面计算APP代码大小
printf("准备接收app程序,请添加bin文件!\r\n"); //准备好接收bin文件,等待用户添加
}
else
{
CodeUpdateFlag = 0;
AppCodeLength = 0;
printf("指令错误!\r\n"); //未接收到IAP更新指令,其他任何串口发送数据都认为指令错误
}
}
else if(RxCmdFlag == 1 && RxDataLength > 10)//接收APP程序
{
CodeUpdateFlag = 1; //代码更新标志位置位,用于应用程序代码接收完成后写FLASH
RxCmdFlag = 0;
AppCodeLength = USART_RX_CNT;
printf("APP程序接收完成!\r\n");
printf("程序大小:%dBytes\r\n",AppCodeLength);
}
else
{
RxDataLength = 0;
printf("文件或指令错误!\r\n"); //如果代码大小不足10Bytes,认为没有正确添加bin文件
}
RxDataCount = 0;
USART_RX_CNT = 0;
}
else
{
RxDataCount = USART_RX_CNT;
}
}
delay_ms(10); //给以串口中断的时间,判断是否接收完成
if(CodeUpdateFlag) //代码更新标志位置位
{
CodeUpdateFlag = 0;
if(AppCodeLength)
{
printf("程序更新中...\r\n");
if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000) //判断代码合法性
{
printf("正在下载程序!\r\n");
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,AppCodeLength); //新代码写入FLASH
AppRunFlag = 1;
}
else
{
printf("程序更新失败,请检查bin文件是否正确!\r\n");
printf("跳转到原有应用程序!\r\n");
iap_load_app(FLASH_APP1_ADDR); //执行FLASH APP代码
}
}
else
{
printf("没有程序可以更新!\r\n");
}
}
if(AppRunFlag) //App运行标志置位
{
printf("开始运行程序!\r\n");
delay_ms(10);
if(((*(vu32*)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000) //判断代码合法性
{
AppRunFlag = 0;
STMFLASH_Write(ADDR_CodeWriteFlag,IAPFlagBuf,2); //写入APP代码标志
//RCC_DeInit(); //关闭外设
//__disable_irq();
iap_load_app(FLASH_APP1_ADDR); //执行FLASH APP代码
}
else
{
printf("应用程序错误!\r\n");
}
}
}
else
{
printf("已有一个应用程序!\r\n");
printf("开始运行程序!\r\n");
delay_ms(10);
iap_load_app(FLASH_APP1_ADDR); //执行FLASH APP代码
}
}
}
-
main函数中,选择一块不使用的flash区域来保存跳转标志位。
我选择的是0x8070000开始的区域,整个flash大小是512k也就是0x8080000,所以最后我又留了64k的大小来存放,所以留给APP程序的就是0x08010000到0x08070000的地址,也是就384k大小空间,记住这个0x08010000到0x08070000,后面APP程序要考。 -
每次进入main函数都要先读这个地址的值,若等于0x11则直接跳转到APP,因为在跳转APP之前是要对这个地址进行写0x11的,所以跳转到APP后这个地址是0x11,开机启动会自动进入APP。
到此似乎思路就清晰了,原子通过按键来操作接收,下载。我这里通过指令来接收下载,其他的判断栈顶地址的合法性,看两遍代码也能明白了。
APP程序
接下来就是APP程序的操作部分了。
要从IAP跳转到APP,APP的程序也需要修改。首先就是程序的起始地址和大小。
前面的IAP从0x8000000到0x8010000,所以我们的APP程序只能从0x8010000开始,而0x8070000到0x8080000又要保存跳转标志位,那么APP的地址就只能是0x8010000到0x8070000了,既然这样,那就拉满吧,所以大小设置我选择0x60000。
然后就是输出bin文件了,要用串口传输不能用hex文件,所以在USER界面
选择如图所示操作,方框里填的是E:\keil5\ARM\ARMCC\bin\fromelf.exe --bin -o …\output\Project.bin …\output\Project.axf 注意有空格,当然这个也得根据自己的文件位置更改。
下一步就是更改偏移地址了,这里只需要在主函数里加
SCB->VTOR=FLASH_BASE|0x10000; 即可,我这里偏移0x10000
到此就能编译生成bin文件,通过IAP程序,使用串口来完成升级了。
但是后续要升级怎么办,我们还得从APP跳转到IAP来。所以在APP程序中还需要接收指令来跳转到IAP去。
这里Receive_Data_Point3是接收到的字节长度,我设置的跳转指令是APPTOIAP!共9位。
然后就是跳转前还需将存放标志位得地址写入0x00,前面IAP中会对该地址得值进行判断是否是0x11,是的话就又跳回来了。然后有大佬踩坑后,得知跳转到IAP直接用NVIC_SystemReset(); 即可。
到此就基本完成了。
第一次写博客,也是为了记录和学习。语言不通顺还请多谅解,如有错误的地方也请多加指正。同时有疑问的同学也可以评论留言,欢迎讨论交流。文章来源:https://www.toymoban.com/news/detail-686537.html
可能出现的问题解决方法:
在后续的测试中发现,串口接收bin文件时还没接收完就进入到了写入flash的动作,导致有很大的概率程序升级失败。分析了半天原因可能是,接收缓存在10ms内没收到数据就默认接收完毕进入写入跳转了,我使用的9600波特率,在将下图中的延时增加到100ms后,没有再出现问题了。文章来源地址https://www.toymoban.com/news/detail-686537.html
到了这里,关于STM32F1 IAP在线升级功能实现(使用串口)及心得的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!