STM32F7 IAP在线刷写简述 (STM32CUBE+freeRTOS)
硬件平台:STM32F767ZGT6,这个差别不大,用了STM32CUBE+freeRTOS,这个也没啥太大差别。
软件平台:KEIL5
使用库:使用的CUBE的HAL库
1.理论知识
1)IAP方法
IAP一般指的就是不使用官方下载器(如STLINK/ULINK/JLINK等),而是借助RS232、RS485、CAN、以太网、USB等等接口来实现程序的编写,具体采用哪种接口对于IAP过程来说没什么区别。
IAP程序一般包括bootloader程序(这个轻易不改的)和app程序(一般改这个),而下载逻辑有两种:
一般新程序的接收代码都是放到APP中,APP接收验证完毕后把新程序存到新程序缓存flash区域,然后软件复位,然后再由boot程序把新程序数据提取到RAM,然后覆盖到正常程序的FALSH区域,然后跳转。。
但是也可以由BOOT直接接收并存储新程序,流程是:APP收到新程序下载申请后写个FLASH标志然后软件复位到boot,boot查询到FALSH更新标志位后发出允许发送程序的握手指令,然后开始接收数据到RAM,接收完毕并校验后直接覆盖到正常程序的FALSH区域,然后跳转。。
两种方式差异如下:
BOOT接收 | APP接收 | |
RAM占用 | 不占用APP中RAM | 需要定义RAM来接收APP数据 |
FLASH占用 | FLASH不需要缓存程序区,节省FLASH | 需要把FLASH分为3部分:boot+正常APP区+缓存APP区 |
允许APP大小 | 原则上等于整个FLASH区域减去boot大小 | 小于整个FLASH的一半 |
接收新程序数据时其他程序能否正常执行 | 不可以 | 可以,适合通信线路不稳定、低速等需要大量传输时间的场景 |
boot程序大小 | 稍大,包含通讯初始化 | 很小,可以只用到FLASH+时钟等,大概十几K以内 |
整个流程复杂度 | 简单,只需要RAM读取,然后写入FLASH | 稍复杂,先APP的RAM读取并写入备份FLASH,然后BOOT再RAM读取并写入正常程序FLASH |
这两种方法一般APP接收用的比较多。实际编程差别不大。
2)程序格式
我们一般编译出来的是hex,但flash中存储的其实是跟bin一样的,因此我们需要把hex转bin,这有两种方式:
①用keil直接生成bin,上位机读取bin直接发送就可以
keil集成了转bin的功能,我们直接用就可以
红框位置填写: $K\ARM\ARMCC\bin\fromelf.exe --bin --output=@L.bin !L
并把前面打钩
这样每次编译程序会在工程文件目录生成bin,如:
②用上位机读取hex然后自己转bin
具体方法就不说了,这主要关系到上位机的知识了
2.主要代码及设置方法:
1)FLASH分配
bootloader一般都是从默认FLASH首地址,即0x0800 0000,下图为参考手册截图,预留多大需要自己计算下,如果是使用APP进行数据接收,那么boot不需要太多外设,一般也就十几KB,可以给分sector 0
如果使用App接收,那么需要分为正常APP区域和缓存APP区域,比如正常APP用sector4+5,即0x0802 0000--0x0807 FFFF,缓存APP用sector4+6+7 。
而如果使用boot接收,就不需要缓存APP了。
2)地址偏移
APP程序想正常执行需要设置地址偏移,程序很简单,就是main最开始用这句话:
SCB->VTOR = FLASH_BASE | 0x20000;//设置偏移量,即0x0802 0000
另外需要KEIL设置flash地址:
3)APP定义一个RAM,用来存储通讯外设中断接收的数据
#define BOOT_REC_LEN 256*1024//定义最大接收字节数
uint8_t BOOT_RX_BUF[BOOT_REC_LEN] __attribute__ ((at(0X20021000)));//接收缓冲
4)设立握手信号和逻辑
一般使用握手信号来管理通讯两端的节奏和时序,以下以app接收为例
①.上位机向mcu发送下载申请
②mcu反馈允许发送数据
③上位机开始下载程序,格式要约定好
④mcu接收数据并存到ram
⑤上位机发送数据发送完成的信号,并包含crc等
⑥mcu开始对ram中的数据进行校验,如果正确则反馈一下
⑦mcu把ram中的数据保存到缓存flash
⑧mcu软复位到boot
⑨boot查案缓存flash有无数据,如有则保存到正常app的flash区域,然后清缓存并跳转到正常app,如果缓存flash中无数据,则直接跳转到正常app。
5)app中接收新程序到RAM
这个得看具体用什么外设了,一般用中断,直接接收放到BOOT_RX_BUF中就可以
6)app中把RAM中新程序数据保存到flash
if(((*(vu32*)(0x20021000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_write_appbin(FLASH_APP1_ADDR,BOOT_RX_BUF,applenth);//更新FLASH代码
printf("app 固件保存完成!\r\n");
}else
{
printf("非FLASH应用程序!\r\n");
}
#define FLASH_APP1_ADDR 0x08080000
第一句为查询ram中的数据是否是程序的格式,0x20021000即为定义的RAM的地址
其中用到的函数定义如下:
//从指定地址开始写入指定长度的数据
//特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数
// 写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
// 写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
// 没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
//该函数对OTP区域也有效!可以用来写OTP区!
//OTP区域地址范围:0X1FF0F000~0X1FF0F41F
//WriteAddr:起始地址(此地址必须为4的倍数!!)
//pBuffer:数据指针
//NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
void STMFLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite)
{ FLASH_EraseInitTypeDef FlashEraseInit;HAL_StatusTypeDef FlashStatus=HAL_OK;uint32_t SectorError=0;uint32_t addrx=0;uint32_t endaddr=0; if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址HAL_FLASH_Unlock(); //解锁 addrx=WriteAddr; //写入的起始地址endaddr=WriteAddr+NumToWrite*4; //写入的结束地址if(addrx<0X1FF00000){while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除){if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区{ FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型,扇区擦除 FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx); //要擦除的扇区FlashEraseInit.NbSectors=1; //一次只擦除一个扇区FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围,VCC=2.7~3.6V之间!!if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK) {break;//发生错误了 }SCB_CleanInvalidateDCache(); //清除无效的D-Cache}else addrx+=4;FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成}}FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成if(FlashStatus==HAL_OK){while(WriteAddr<endaddr)//写数据{if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据{ break; //写入异常}WriteAddr+=4;pBuffer++;} }HAL_FLASH_Lock(); //上锁
} //appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节).
void iap_write_appbin(uint32_t appxaddr,uint8_t *appbuf,uint32_t appsize)
{uint32_t t;uint16_t i=0;uint32_t temp;uint32_t fwaddr=appxaddr;//当前写入的地址uint8_t *dfu=appbuf;for(t=0;t<appsize;t+=4){ temp=(uint32_t)dfu[3]<<24; temp|=(uint32_t)dfu[2]<<16; temp|=(uint32_t)dfu[1]<<8;temp|=(uint32_t)dfu[0]; dfu+=4;//偏移4个字节iapbuf[i++]=temp; if(i==512){i=0; STMFLASH_Write(fwaddr,iapbuf,512);fwaddr+=2048;//偏移2048 512*4=2048}} if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.
}
7)app复位跳转到boot
HAL_NVIC_SystemReset();
8)boot查看缓存程序flash区域是否有程序,如有则存到RAM,然后存到正常程序flash
其中BOOT_RX_BUF跟app中定义的一样
if(((*(vu32*)(0x08080000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.{ for(i=0;i<USART_REC_LEN;i++){BOOT_RX_BUF[i] = *(__IO uint32_t *)(0x08080000+i);}iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,USART_REC_LEN);//更新FLASH代码 printf("boot固件更新完成!\r\n");for(i=0;i<USART_REC_LEN;i++){USART_RX_BUF[i]=0;}STMFLASH_Write(0x08080000,(u32*)USART_RX_BUF,USART_REC_LEN/4);//清空缓存flashprintf("boot固件缓存清除完成!\r\n"); }
9)boot跳转到APP
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.{ printf("boot开始执行FLASH用户代码!!\r\n");iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码}else {printf("boot非FLASH应用程序,无法执行!\r\n"); }
iapfun jump2app;
u32 iapbuf[512]; //2K字节缓存 //THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI
__asm void WFI_SET(void)
{WFI;
}
//关闭所有中断(但是不包括fault和NMI中断)
__asm void INTX_DISABLE(void)
{CPSID IBX LR
}
//开启所有中断
__asm void INTX_ENABLE(void)
{CPSIE IBX LR
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{MSR MSP, r0 //set Main Stack valueBX r14
}//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{ if(((*(vu32*)appxaddr)&0x2FF00000)==0x20000000) //检查栈顶地址是否合法.{ HAL_RCC_DeInit();//__set_FAULTMASK(1);INTX_DISABLE();jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址) MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)jump2app(); //跳转到APP.}
}
typedef void (*iapfun)(void); //定义一个函数类型的参数.
#define FLASH_APP1_ADDR 0x08020000 //第一个应用程序起始地址(存放在FLASH)
3 优化流程
1)app中可以在接收并保存完新app数据后写一个状态来告诉boot可以更新了,或者boot更新前计算一下crc,但这样同样需要保存APP中计算的CRC。这样可以防止app数据写了一半后异常断电导致的死机现象。
2)。。。
4 注意事项
①.boot如果有中断,那么在跳转到app前必须关闭中断,否则跳转到app后还按照boot的向量表来执行就跑飞了
②.boot跳转到app前最好HAL_RCC_DeInit();应该是防止boot和root设置不一样导致的死机
③.如果app使用了freeRTOS,那么最好不要在freeRTOS初始化之前使用HAL_Delay,否则会卡死,测试发现app中freeRTOS初始化之前定时器中断无法进入,而HAL_Delay是依托定时器的,所以卡死,而如果不用app的话HAL_Delay可以正常使用,具体原因不知道,请知道具体原因的朋友告诉我下。
④.boot接收新程序数据的流程差不多,使用的代码也差不多,就不多说了
由于程序集成在项目里了,就不上传代码了。上面这些应该足够了
另外我使用kvasr进行的can刷写测试的。自己写了个简单的上位机软件,截图供大家理解流程:
STM32F7 IAP在线刷写简述 (STM32CUBE+freeRTOS)相关推荐
- CRC校验原理及STM32 IAP在线升级程序
CRC校验原理: 什么是CRC校验? CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定.循环冗余检查(CRC)是一种数据传输检错功能,对数据 ...
- STM32F429实现USB通过IAP在线升级
1.目标 1.实现STM32对U盘文件的读取. 2.实现STM32拓展外部SDRAM. 3.实现STM32拓展外部Flash. 4.实现内存管理. 5.实现Fatfs文件系统,读写U盘和外部Flash ...
- 基于Ymodem协议的stm32f405rgt6+CubeMx+IAP在线升级
基于Ymodem协议的stm32f405rgt6+CubeMx+IAP在线升级 目录 一.CubeMX的配置 1.IAP 2.APP 二.移植Ymodem官方代码 1.文件移植 2.MDK文件.路径添 ...
- STM32 IAP 在线升级原理全解析
点击左上角的"关注",定期更新 STM32 最新资讯,总有你想要的信息! STM32 IAP 在线升级原理全解析 1. 什么是 IAP? IAP(In-Application ...
- 51单片机IAP在线升级
51单片机IAP在线升级 爱矽半导体E85F3325单片机IAP在线升级教程,此处可查看更新及demo下载 文章目录 前言 一.ROM资源 二.KEIL有关知识 1.BL51连接器: 2.LX51连接 ...
- LPC2478(22)IAP在线升级
目录 1.开发环境 2.特性 3.IAR编译器的相关文件 3.1.icf文件 3.2.IcfEditorFile文件内容 3.3.ddf文件 3.4.board文件 4.IAP相关 4.1.软件复位 ...
- 【IAP】IAP在线升级流程
IAP,全称是"In-Application-Programming",中文解释为"在程序中编程".不同于ISP通过设置MCU内部的BootLoader程序引导 ...
- Stm 32 IAP 在线 升级IAP 的 操作
(扩展-IAP主要用于产品出厂后应用程序的更新作用,考虑到出厂时要先烧写IAP 再烧写APP应用程序要烧写2次增加工人劳动力基础上写了"STM32 IAP+APP ==>双剑合一&q ...
- STM32的IAP在线升级的源码中的地址解读
1.ApplicationAddress的内容含义 if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x200 ...
最新文章
- 亿级流量系统架构之如何设计承载百亿流量的高性能架构【石杉的架构笔记】...
- 吉大c 语言程序设计奥鹏作业,吉大19秋学期《C语言程序设计》在线作业一【满分答案】...
- Java设计模式-观察者模式(订阅发布模式)
- 【Linux】一步一步学Linux——visudo命令(104)
- 国家部委对4G调研:未定给中电信联通发放牌照
- Linux常用命令汇总 - Linux Shell Cheat Sheet
- 测试与开发的冲突举例
- Optimus双显卡笔记本上用MediaCoder转换iPhone/iPod 4视频
- android javacv,【首发】AndroidStudio配置JavaCV环境
- 判断一个数是否为质数(素数)的4种方法
- root 红米note5_小米红米Note 5(不要降级刷低版本)手机完美获取root教程,最强root工具,亲测可用!...
- NoteExpress中遇到的坑
- 角度前方交会点坐标计算完整步骤
- 关于弱酸性次氯酸水,你需要了解更多!
- LeetCode-1225. 报告系统状态的连续日期(困难)
- 【C++】Lambda 表达式详解
- 2021-2027全球与中国物联网网关设备市场现状及未来发展趋势
- unsigned详讲(干货满满)
- BenchmarkSQL配置参数介绍
- 【洛谷P1486】郁闷的出纳员【树状数组】