可以实现单片机升级了,现在是先记录一直东西,因为还没有优化好,唉,出门把钥匙锁家里了。。。。等着开锁的师傅来,看看人家是如何开锁的,学学。。哈哈哈
。。。测试好以后写使用教程吧
参考这个再看这篇文章
https://www.cnblogs.com/yangfengwu/p/6921832.html
整体协议:
APP通过MQTT把信息发给WIFI,WIFI转发给STM32
STM32把信息通过WIFI转发APP
WIFI模块的一个引脚和STM32的复位引脚相连接
该引脚一开始是低电平,WIFI程序运行后该引脚为高电平(STM32开始工作)
注:APP发送给WIFI '切换执行程序' 或者 '擦除所有用户程序' 消息时,WIFI先直接用引脚控制STM32复位重启,
STM32一开始执行IAP程序,IAP程序一开始默认发送:{"datemcu":"updata","status":"switch"} 接着看下文
/*********************IAP程序处理(没有更新状态标志位)************************/
IAP程序一启动STM32发送的消息如下:询问是不是需要切换执行程序
{"datemcu":"updata","status":"switch"} 此协议1S发送一次,总共发3次,超时执行对应的用户程序,如果对应的用户程序可运行
{"datemcu":"updata","status":"run1"} 或者 {"datemcu":"updata","status":"run2"} 该语句放在用户程序里面
WIFI接收到后回复:
不需要切换 切换单片机程序 擦除所有用户程序
{"datemcu":"updata","cmd":"noswitch"} 或 {"datemcu":"updata","cmd":"yesswitch"} 或 {"datemcu":"updata","cmd":"erase"}
第一个:不需要切换
如果对应的用户程序可运行,运行对应的用户程序
第二个:切换单片机程序
STM32接收到以后切换完执行程序的标志位发送:
{"datemcu":"updata","status":"switchok"} 此协议1S发送一次,发4次后复位重启
WIFI接收到后: 复位单片机
第三个:擦除所有用户程序
STM32接收到以后擦除所有用户程序 此协议1S发送一次,发送4次后复位重启
{"datemcu":"updata","status":"eraseok"}
WIFI接收到后: 复位单片机
/*******************************IAP程序实现更新的协议如下********************/
//APP发送获取设备型号
{"datemcu":"updata","cmd":"model"}
//STM32设备回复
{"datemcu":"updata","status":"model","model":"stm32test"}//假设现在的型号是stm32test
二,上位机根据型号http访问更新的信息
实际用域名代替 "型号"
列如:"http://47.92.31.46/hardware/"+stm32test+"/"+"updatainfo.txt"
//APP发送给设备平台的版本号和更新文件的位置,该url由WIFI模块进行记录
{"datemcu":"updata","cmd":"info","version":"1.0.1","url":"http://47.92.31.46/hardware/stm32test"}
//STM32回复是不是和发给它的版号一致,并回复自己的版本号
{"datemcu":"updata","status":"unlike",version:XXXX} 或 {"datemcu":"updata","status":"alike",version:XXXX}
或者 {"datemcu":"updata","status":"unlike","version":"error"} //单片机接收版本出错
//APP发送给设备升级指令
{"datemcu":"updata","cmd":"start"}
//单片机接收到以后,写入需要更新的标志 然后回复
{"datemcu":"updata","status":"start"} 1S 1次 4次,然后复位重启
//WIFI接收到该信息直接用引脚控制STM32复位重启
/**********************IAP程序再次启动,检查到有更新标志*************************/
//单片机发送:{"datemcu":"updata","status":"start","file":"bin1"} 或者 {"datemcu":"updata","status":"start","file":"bin2"}
该指令总共发送两次
WIFI模块第一次接收到此命令,WIFI模块关掉MQTT接收的数据转发给STM32的功能,同时把该信息发给MQTT
在STM32发送第二次这个指令的时候 ,清理一下链表,启动超时定时器,允许串口接收的数据写入Flash
WIFI模块第二次接收到此命令,根据bin1或者bin2访问对应的bin文件,把云端程序通过串口发给STM32
STM32接收完数据 或者 接收出错
第一种:
切换单片机程序的标志位,记录好该执行那一份程序的地址,写入更新完成标志,用新的版本号替换掉旧的版本号,复位重启
复位重启以后,检测到更新完成标志,发送{"datemcu":"updata","status":"finish"} 1S 1次 发送3次 然后清除该标志
第二种: 写入更新失败标志,复位重启
复位重启以后发送{"datemcu":"updata","status":"error"} 1S 1次 发送3次 然后清除该标志
清除更新成功或者失败标志以后,执行最上面的发信息给WIFI模块询问是不是需要切换执行运行的用户程序
{"datemcu":"updata","status":"switch"}
/***********************用户程序********************/
/*
假设用户程序正常运行,用户本身知道单片机是不是正常运行,如果运行不正常,用户可以发送 '切换执行程序' 或者 '擦除所有用户程序'
擦除所有用户程序以后,单片机只运行IAP程序,用户可以重新操作单片机进行更新
*/
//APP发送获取设备型号
{"datemcu":"updata","cmd":"model"}
//STM32设备回复
{"datemcu":"updata","status":"model","model":"stm32test"}//假设现在的型号是stm32test
二,上位机根据型号http访问更新的信息
实际用域名代替 "型号"
列如:"http://47.92.31.46/hardware/"+stm32test+"/"+"updatainfo.txt"
//APP发送给设备平台的版本号和更新文件的位置,该url由WIFI模块进行记录
{"datemcu":"updata","cmd":"info","version":"1.0.1","url":"http://47.92.31.46/hardware/stm32test"}
//WIFI接收到此信息以后去掉后面的url的json部分
//STM32回复是不是和发给它的版号一致,并回复自己的版本号
{"datemcu":"updata","status":"unlike",version:XXXX} 或 {"datemcu":"updata","status":"alike",version:XXXX}
或者 {"datemcu":"updata","status":"unlike","version":"error"} //单片机接收版本出错
//APP发送给设备升级指令
{"datemcu":"updata","cmd":"start"}
//单片机接收到以后,写入需要更新的标志 然后回复
{"datemcu":"updata","status":"start"} 1S 1次 4次,然后复位重启
//WIFI接收到该信息直接用引脚控制STM32复位重启
更新过程中设备发送更新进度--由于更新时间太短...暂不考虑
{"datemcu":"updata","status":"starting","percent":"百分比"} 百分比为数字
注:
由于STM32的各个型号不一致,内存和Flash也不一致
STM32接收统一采用环形队列,边接收边写入
Flash不一致问题(主要是1024和2048),提供两套程序支持
升级程序文件和关于升级的内容(内容里面含有版本号)放在云平台
APP或者wed或者上位机通过http获取关于升级的内容
WIFI通过http获取bin文件,然后通过串口发给单片机
IAP程序中的Flash文件,用户根据自己的程序调整,64KB的一般不需要改动,除非用户需要存储的用户数据很多
首先下载IAP程序,IAP程序不需要对软件做任何配置,直接下载即可
稍微看一下用户程序1的一些主要程序和配置
协议部分请参见最上面的协议
if(StartUpdateFlage) { if(Time2UpdateCnt>=100) { Time2UpdateCnt = 0; printf("%s",UpdateStart); SendDateCnt++; } if(SendDateCnt>=3) { Reset_MCU();//ÖØÆô } } if(Usart1ReadFlage)//´®¿Ú½ÓÊܵ½Êý¾Ý { Usart1ReadFlage = 0; /*************Ô¶³Ì¸üÐÂÏà¹ØStop***************/ if(strstr(Usart1ReadBuff,"{\"datemcu\":\"updata\",\"cmd\":\"model\"}"))//ѯÎÊÉ豸ÐͺŠ{ printf("{\"datemcu\":\"updata\",\"status\":\"model\",\"model\":\"%s\"}",model); } else if(strstr(Usart1ReadBuff,"{\"datemcu\":\"updata\",\"cmd\":\"info\",\"version\""))//APP·¢¹ýÀ´³ÌÐò°æ±¾ { if(Usart1ReadBuff[34]=='v' && Usart1ReadBuff[37]=='s' && Usart1ReadBuff[40]=='n' && Usart1ReadBuff[42]==':' && Usart1ReadBuff[49]=='\"') { Version1[0] = Usart1ReadBuff[44]; Version1[1] = Usart1ReadBuff[45]; Version1[2] = Usart1ReadBuff[46]; Version1[3] = Usart1ReadBuff[47]; Version1[4] = Usart1ReadBuff[48]; SetVersion1(Version1);//ÉèÖÃÔƶ˰汾 if(strstr(Version1,Version2))//°æ±¾Ò»Ñù { printf("{\"datemcu\":\"updata\",\"status\":\"alike\",\"version\":\"%s\"}",Version2); } else { printf("{\"datemcu\":\"updata\",\"status\":\"unlike\",\"version\":\"%s\"}",Version2); } } else//°æ±¾½âÎöʧ°Ü { printf("{\"datemcu\":\"updata\",\"status\":\"unlike\",\"version\":\"error\"}"); } } else if(strstr(Usart1ReadBuff,"{\"datemcu\":\"updata\",\"cmd\":\"start\"}"))//APP·¢¹ýÀ´É豸Éý¼¶Ö¸Áî { SetUpdateFlage();//дÈëÉý¼¶±êÖ¾ printf("%s",UpdateStart); SendDateCnt = 0; Time2UpdateCnt = 0; StartUpdateFlage = 1; } /*************Ô¶³Ì¸üÐÂÏà¹ØStop***************/
告诉IDE,我的程序从8004000开始运行,给我分配5C00大小的Flash = 23KB = 23552个字节
E:\MDK5\ARM\ARMCC\bin\fromelf.exe --bin -o .\Progect\Progect.bin ..\Progect\output\Progect.axf
帮我生成bin文件
然后根据协议把用户程序1,通过串口发给
{"datemcu":"updata","cmd":"model"} {"datemcu":"updata","cmd":"info","version":"0.0.1"} {"datemcu":"updata","cmd":"start"}
现在再把用户程序2更新进去
E:\MDK5\ARM\ARMCC\bin\fromelf.exe --bin -o .\Progect\Progect.bin ..\Progect\output\Progect.axf
切换文件执行:
因为切换文件是WIFI模块先复位单片机,我手动模拟复位单片机
清除程序
OK,单片机这端测试没有问题了,小容量的单片机就是麻烦,还是用的环形队列一边接收一遍写入