<笔记>STM32&4G模组实现OTA升级
前言
这是一个STM32&4G模组配合实现的OTA升级教程。硬件平台是小熊派的开发板+自己其他项目上的4G模组(型号为:SIMCOM7600CE)。软件是在STM32里面编写裸机程序,使用了状态机编程,主业务是用AT指令连接MQTT平台,OTA升业务是用HTTP下载将要升级的BIN文件。
Drawn By:67373UPUP
第一步OTA升级的大概思路
前提条件,我使用的是STM32L431RCTC这个芯片。这个芯片的内部Flash是256K。那我们将256K分为3块。
第一部分: Boot_Loader 起始地址: 0x0800F000 大小为:60K 占30页第二部分:Application_1 起始地址: 0x0800F000 大小为:90K 占45页第三部分:Application_2 起始地址: 0x08025800 大小为:90K 占45页
OTA升级的程序包含三部分,分别命名为BOOT,APP1,APP2。(名字自己随便取,本例程我是为了方便理解这样子取的,这三部分的程序本质上还是嵌入式程序,和我们平常编写的程序没有什么区别)
( 1 ) BOOT:主要实现的功能是开机检查是否有升级的标志位。
如果没有升级的标志位,那就把程序的指针跳转到APP1的地址,正常启动APP1的业务逻辑。
如果 有升级的标志位,BOOT会先把APP1占据的内部Flash清空,然后将APP2的内容搬运到APP1内,搬运完成后清除升级的标志位以及重启单片机,重启后BOOT先运行,检测到没有升级的标志位。那就把程序的指针跳转到APP1的地址,正常启动APP1的业务逻辑。
( 2 )APP1里存的是主业务逻辑的程序。(如果没有升级的标志位,每次BOOT启动后下一个就会运行APP1)
( 3 )APP2里存的是将要升级的业务逻辑的程序。(APP2内只负责存储将要升级的BIN文件,程序永远不会跳转到这里)
第二步OTA本地升级测试
BOOT的程序大致分为以下几块
1,内部Flash的读写。(内部Flash的读写的不展开了,具体参考源码)
2,搬运APP的BIN文件到APP1的位置。
/**
* @bieaf 进行程序的覆盖
* @detail 1.擦除目的地址
* @detail 2.源地址的代码拷贝到目的地址
* @detail 3.擦除源地址
*
* @param 搬运的源地址
* @param 搬运的目的地址
* @return 搬运的程序大小
*/
void MoveCode(unsigned int src_addr, unsigned int des_addr, unsigned int byte_size)
{
/*1.擦除目的地址*/
printf("> Start erase des flash......\r\n");
Erase_page(des_addr, (byte_size/PageSize));
printf("> Erase des flash sucessfully......\r\n");
/*2.开始拷贝*/
uint8_t temp[1024];
printf("> Start copy......\r\n");
for(int i = 0; i < byte_size/1024; i++)
{
ReadFlash((src_addr + i*1024), temp, 1024);
WriteFlash((des_addr + i*1024), temp, 1024);
}
printf("> Copy sucessfully......\r\n");
/*3.擦除源地址*/
printf("> Start erase src flash......\r\n");
Erase_page(src_addr, (byte_size/PageSize));
printf("> Erase src flash sucessfully......\r\n");
}
3,开机判断启动方式
/**
* @bieaf 进行BootLoader的启动
* @return none
*/
void Start_BootLoader(void)
{
printf("\r\n");
printf("***********************************\r\n");
printf("* *\r\n");
printf("* BootLoader *\r\n");
printf("* *\r\n");
printf("***********************************\r\n");
printf("> Choose a startup method......\r\n");
switch(Read_Start_Mode()) //读取是否启动应用程序
{
case Startup_Normal: //正常启动
{
printf("> Normal start......\r\n");
break;
}
case Startup_Update: //升级再启动
{
printf("> Start update......\r\n");
MoveCode(Application_2_Addr, Application_1_Addr, Application_Size);
printf("> Update sucessfully......\r\n");
break;
}
default: //启动失败
{
printf("> Error:%X!!!......\r\n", Read_Start_Mode());
return;
}
}
/* 跳转到应用程序 */
// __disable_irq() ; //很重要!经测试STM32F4必要! 貌似F105也需要 L431 裸机 却不需要 RTOS需要
printf("> Start up......\r\n\r\n");
IAP_ExecuteApp(Application_1_Addr);
}
4,跳转运行程序到APP1的地址。
/** @bieaf 采用汇编设置栈的值
* @return none 返回值
*/
__asm void MSR_MSP (uint32_t ulAddr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
/** @bieaf 程序跳转函数
* @return none 返回值
*/
typedef void (*Jump_Fun)(void);
void IAP_ExecuteApp (uint32_t App_Addr)
{
Jump_Fun JumpToApp;
if (((*(__IO uint32_t *)App_Addr) & 0x2FFE0000) == 0x20000000) //检查栈顶地址是否合法.
{
JumpToApp = (Jump_Fun) * (__IO uint32_t *)(App_Addr + 4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP( * (__IO uint32_t *) App_Addr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
JumpToApp(); //跳转到APP
}else{
printf("There is None APP to jump,ERROR!!!\r\n");
}
}
5,设置BOOT程序的ROM地址以及大小
6,用ST-Link下载时设置为全擦除内部Flash
APP1的程序
1,需要在main主程序的第一行更改中断向量表地址到0x800F000U。
2,将升级的标志位设为0xAA。
3,在升级标志位设为0xAA之前硬延迟10S,这一步的目的是有充足的时间将APP2的BIN文件刷进板子里。(在实际的项目中不会有这个东西)
4,将LED的闪烁配置为1s闪烁一次。(这是测试程序,故只配置了一个闪烁的LED灯来关注实验现象)
5,设置APP1程序的ROM地址以及大小
6,用ST-Link下载时不要设置为全擦除内部Flash
APP2的程序
1,需要在main主程序的第一行更改中断向量表地址到0x800F000U。
2,需要将User的一下内容配置一下,这一步的目的是生成BIN文件。(别死搬硬抄,结合自己的工程微调一下)
C:\Program Files\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin --output .\APP\APP.bin .\APP*.axf
然后每次编译后就可以在自己电脑的工程文件夹里找到app.bin文件。
3,将LED的闪烁配置为50ms闪烁一次。(这是测试程序,故只配置了一个闪烁的LED灯来关注实验现象)
4,设置APP2程序的ROM地址以及大小
测试步骤
1,步骤1:先用ST-link将BOOT的程序刷进板子内。
2,步骤2:再用ST-link将APP1的程序双进板子内。
3,步骤3:将APP2的BIN文件通过工具STM32CubeProgrammer刷进板子内。(经测试这一步以后程序会陷入假死机状态,原因未明,知道的大神可以给我留言解释下,谢谢了)
4,复位开发板,,经过十秒后开发板的LED灯会满闪。同时根据程序也可知道升级标志位已经写好了。
5,复位开发板,就能发现LED灯快闪了。表示升级完成。
第三步OTA升级的完全体
这一步的主要工作就算是把4G的联网驱动完善。我们需要一个平台来供我们下载BIN文件,(平台需要找软件开发,我不会。我用的是公司的所以我不可能公开出来的)
1,首先我们先写联网驱动。(采用有限的状态机编程,状态机是参考的正道老师的教程)
以下是完整联网+升级部分的代码,每次开机就会往我的平台(47.98.183.97:1884)上发送模组的信号值。
/*设置CAT1状态的枚举变量*/
typedef enum
{
CAT1_IDIE = 0,
CAT1_SEND,
CAT1_WAIT,
CAT1_ACCESS
} teCAT1_TaskStatus;
/*设置AT指令集的枚举变量*/
typedef enum
{
//AT_RST,
AT,
ATE0,
AT_CGSN,
AT_CCID,
AT_CEREG,
AT_CGATT,
AT_CSQ_1,
AT_CMQTTSTART,
AT_CMQTTACCQ,
AT_CMQTTCONNECT,
AT_CSQ_2,
AT_CMQTTTOPIC,
AT_TOPIC,
AT_CMQTTPAYLOAD,
AT_MESSAGE,
AT_CMQTTPUB,
/*HTTP相关指令*/
AT_HTTPINIT_1,
AT_HTTPPARA_VERSION,
AT_HTTPACTION_1,
AT_HTTPREAD_1,
AT_HTTPTERM_1,
AT_HTTPINIT_2,
AT_HTTPPARA_DATA,
AT_HTTPACTION_2,
AT_HTTPREAD_2,
AT_HTTPTERM_2,
} teATCmdNum;
/*设置模组状态的枚举变量*/
typedef enum
{
SUCCESS_REC = 0,
TIME_OUT,
NO_REC
} teATStatus;
/*设置AT指令集的结构体*/
typedef struct
{
char *ATSendStr;
char *ATRecStr;
uint16_t TimeOut;
teATStatus ATStatus;
uint8_t RtyNum;
} tsATCmds;
/*入网AT指令集*/
tsATCmds ATCmds[] =
{
/* 下面是关于连接MQTT的指令集*/
//{"AT+CRESET\r\n","PB DONE",5000,NO_REC,3},
{"AT\r\n","OK",1000,NO_REC,3}, //AT指令测试
{"ATE0\r\n","OK",1000,NO_REC,3}, //关闭回显
{"AT+CGSN\r\n","OK",1000,NO_REC,3}, //查询Imei
{"AT+CCID\r\n","OK",1000,NO_REC,3}, //查询ICCID
{"AT+CEREG?\r\n","OK",1000,NO_REC,100}, //查询当前GPRS注册状态
{"AT+CGATT?\r\n","OK",1000,NO_REC,3}, //查询当前GPRS附着状态
{"AT+CSQ\r\n","OK",1000,NO_REC,3}, //查询信号值
{"AT+CMQTTSTART\r\n","+CMQTTSTART: 0",2000,NO_REC,3}, //开启MQTT连接
{"AT+CMQTTACCQ=0,\"clientid01\"\r\n","OK",2000,NO_REC,3}, //注册MQTT的ClientID
{"AT+CMQTTCONNECT=0,\"tcp://47.98.183.97:1884\",120,1,\"admin\",\"public\"\r\n","+CMQTTCONNECT: 0,0",3000,NO_REC,3}, //设置IP port username password keepalive
{"AT+CSQ\r\n","OK",1000,NO_REC,3}, //查询CSQ
{"AT+CMQTTTOPIC=0,",">",2000,NO_REC,3}, //设置topic
{topic_buffer,"OK",3000,NO_REC,3}, //topic内容
{"AT+CMQTTPAYLOAD=0,",">",2000,NO_REC,3}, //设置发送报文
{message_buffer,"OK",3000,NO_REC,5}, //msg内容
{"AT+CMQTTPUB=0,1,60\r\n","+CMQTTPUB: 0,0",3000,NO_REC,5}, //发送报文
/* 下面是关于查询版本号的AT指令集*/
{"AT+HTTPINIT\r\n","OK",1000,NO_REC,3}, //开启HTTP服务
{"AT+HTTPPARA=\"URL\",\"http://47.98.248.24:8888/getVersion?device_id=123456\"\r\n","OK",1000,NO_REC,3}, //设置HTTP参数
{"AT+HTTPACTION=0\r\n","+HTTP_PEER_CLOSED",1000,NO_REC,3}, //操作HTTP方法
{"AT+HTTPREAD=0,100\r\n","+HTTPREAD:0",1000,NO_REC,3}, //读取HTTP服务回复
{"AT+HTTPTERM\r\n","OK",1000,NO_REC,3}, //关闭HTTP服务
/* 下面是关于下载BIN文件的AT指令集*/
{"AT+HTTPINIT\r\n","OK",1000,NO_REC,3}, //开启HTTP服务
{"AT+HTTPPARA=\"URL\",\"http://47.98.248.24:8888/download1?device_id=123456&version=3.0.0\"\r\n","OK",3000,NO_REC,3}, //设置HTTP参数
{"AT+HTTPACTION=0\r\n","+HTTPACTION:",3000,NO_REC,3}, //操作HTTP方法
{"AT+HTTPREAD=0,1024\r\n","+HTTPREAD:",3000,NO_REC,3}, //读取HTTP服务回复
{"AT+HTTPTERM\r\n","OK",1000,NO_REC,3}, //关闭HTTP服务
};
/* AT指令发送处理逻辑 */
void ATSend(teATCmdNum ATCmdNum)
{
//清空接收缓存区
memset(Lpuart1type.Lpuart1RecBuff,0,LPUART1_REC_SIZE);
ATCmds[ATCmdNum].ATStatus = NO_REC;
ATRecCmdNum = ATCmdNum;
printf("\r\n当前命令码:%d\r\n",ATCmdNum);
/* 设置topic的长度 */
if(ATCmdNum == AT_CMQTTTOPIC)
{
memset(topic_buffer,0x00,sizeof(topic_buffer));
sprintf(topic_buffer,"dev/%s",Imei_buffer);
sprintf(send_buffer,"%s%d\r\n",ATCmds[ATCmdNum].ATSendStr,strlen(topic_buffer));
HAL_UART_Transmit(&hlpuart1,(uint8_t*)send_buffer,strlen(send_buffer),0xFF);
printf("send_buffer:%s\r\n",send_buffer);
memset(send_buffer,0x00,strlen(send_buffer));
}
/* 设置msg的长度 */
else if(ATCmdNum == AT_CMQTTPAYLOAD)
{
memset(message_buffer,0x00,sizeof(message_buffer));
sprintf(message_buffer,"{\"CSQ\":%s}",CSQ_buffer);
sprintf(send_buffer,"%s%d\r\n",ATCmds[ATCmdNum].ATSendStr,strlen(message_buffer));
HAL_UART_Transmit(&hlpuart1,(uint8_t*)send_buffer,strlen(send_buffer),0xFF);
printf("send_buffer:%s",send_buffer);
memset(send_buffer,0x00,sizeof(send_buffer));
}
else
{
HAL_UART_Transmit(&hlpuart1,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),0xff);
printf("send:%s",ATCmds[ATCmdNum].ATSendStr);
}
//打开超时定时器
SetTime(&TimeCAT1,ATCmds[ATCmdNum].TimeOut);
}
/* AT指令接收处理逻辑 */
void ATRec(void)
{
if(Lpuart1type.Lpuart1RecFlag)
{
if(strstr((const char*)Lpuart1type.Lpuart1RecBuff,ATCmds[ATRecCmdNum].ATRecStr) != NULL)
{
ATCmds[ATRecCmdNum].ATStatus = SUCCESS_REC;
}
printf("收到数据:%s",Lpuart1type.Lpuart1RecBuff);
Lpuart1type.Lpuart1RecFlag = 0;
Lpuart1type.Lpuart1RecLen = 0;
}
}
/* 初始化CAT1 */
void CAT1_Init(void)
{
Start_4G();
CAT1_TaskStatus = CAT1_SEND;
ATCurrentCmdNum = AT;
ATNextCmdNum = ATE0;
}
/* AT指令交互逻辑 */
void CAT1_Task(void)
{
while(1)
{
switch(CAT1_TaskStatus)
{
case CAT1_IDIE: //空闲态
return;
case CAT1_SEND:
if(ATCurrentCmdNum !=ATNextCmdNum)
{
CurrentRty = ATCmds[ATCurrentCmdNum].RtyNum;
}
ATSend(ATCurrentCmdNum);
CAT1_TaskStatus = CAT1_WAIT;
return;
case CAT1_WAIT: //等待态,等待CAT1返回的信息
ATRec(); //调用接收函数
if(ATCmds[ATRecCmdNum].ATStatus == SUCCESS_REC)
{
printf("当前码:%d\r\n",ATCurrentCmdNum);
if(ATCurrentCmdNum == AT_CGSN) //如果AT指令为查询IMEI
{
ATCurrentCmdNum += 1;
ATNextCmdNum = ATCurrentCmdNum+1;
CAT1_TaskStatus = CAT1_SEND;
memset (CSQ_buffer,0x00,sizeof(CSQ_buffer));
Find_string((char*)Lpuart1type.Lpuart1RecBuff, "\r\n", "\r\n", Imei_buffer);
printf("Imei_buffer=%s\r\n",Imei_buffer);
break;
}
else if(ATCurrentCmdNum == AT_CSQ_2) //如果AT指令为查询CSQ
{
ATCurrentCmdNum += 1;
ATNextCmdNum = ATCurrentCmdNum+1;
CAT1_TaskStatus = CAT1_SEND;
memset(CSQ_buffer,0x00,sizeof(CSQ_buffer));
Find_string((char*)Lpuart1type.Lpuart1RecBuff, " ", ",", CSQ_buffer);
printf("CSQ_buffer=%s\r\n",CSQ_buffer);
break;
}
else if(ATCurrentCmdNum == AT_HTTPREAD_1) //如果AT指令为查询版本号
{
ATCurrentCmdNum += 1;
ATNextCmdNum = ATCurrentCmdNum+1;
CAT1_TaskStatus = CAT1_SEND;
memset(Version_buffer,0x00,sizeof(Version_buffer));
Find_string((char*)Lpuart1type.Lpuart1RecBuff, "{", "}", Version_buffer);
printf("Version_buffer=%s\r\n",Version_buffer);
if(strcmp(Version_buffer,"\"version\":\"3.0.0\"") == 0)
{
printf("硬件版本和云端版本一致,无需升级!\r\n");
}
else
{
Update_Task();
}
break;
}
else if(ATCurrentCmdNum == AT_HTTPACTION_2) //如果AT指令为读取BIN文件长度
{
ATCurrentCmdNum += 1;
ATNextCmdNum = ATCurrentCmdNum+1;
CAT1_TaskStatus = CAT1_SEND;
memset (Bin_len,0x00,sizeof(Bin_len));
Find_string((char*)Lpuart1type.Lpuart1RecBuff, "200,", "\r\n", Bin_len);
printf("Bin_len=%s\r\n",Bin_len);
break;
}
else if(ATCurrentCmdNum == AT_HTTPREAD_2) //如果AT指令为下载BIN文件
{
int len = 0;
long compare_len = 0;
memset(Msg_Len,0x00,sizeof(Msg_Len));
Find_string((char*)Lpuart1type.Lpuart1RecBuff, "DATA,", "\r\n", Msg_Len);
compare_len = atoi(Msg_Len);
printf("Find_Buf:%lu\r\n",compare_len);
if(Erase_flag == 1) //仅仅开始是擦除flash一次
{
Erase_flag = 0;
Erase_page(Application_2_Addr, 45); //擦除45页 90K
}
len = strstr((char *)Lpuart1type.Lpuart1RecBuff, Msg_Len) - (char*)Lpuart1type.Lpuart1RecBuff + strlen(Msg_Len) + 2;
printf("offset address is: %d\r\n",len);
if(compare_len == 1024)
{
memset (Bin_len,0x00,sizeof(Bin_len));
for(long b = 0; b < 1024; b++)
{
Bin_buffer[b] = Lpuart1type.Lpuart1RecBuff[len+b];
}
/* 接下来将固件写进flash内 */
printf("烧录第%d包...................\r\n",addr_count);
WriteFlash((Application_2_Addr+(addr_count)*1024), (uint8_t *)(&Bin_buffer[0]), 1024);
addr_count++;
ATNextCmdNum = ATCurrentCmdNum;
CAT1_TaskStatus = CAT1_SEND;
}
else if(compare_len < 1024)
{
memset (Bin_len,0x00,sizeof(Bin_len));
for(int b = 0; b < 1024; b++)
{
Bin_buffer[b] = Lpuart1type.Lpuart1RecBuff[len+b];
}
/* 接下来将固件写进flash内 */
printf("烧录第%d包...................\r\n",addr_count);
WriteFlash((Application_2_Addr+(addr_count)*1024), (uint8_t *)(&Bin_buffer[0]), compare_len);
addr_count = 0;
Erase_flag =1;
reboot_flag = 1;
ATCurrentCmdNum += 1;
ATNextCmdNum = ATCurrentCmdNum+1;
CAT1_TaskStatus = CAT1_SEND;
}
break;
}
else if(ATCurrentCmdNum == AT_CMQTTPUB) //表示发送msg成功
{
CAT1_TaskStatus = CAT1_ACCESS;
break;
}
else if(ATCurrentCmdNum == AT_HTTPTERM_1) //表示查询版本号成功
{
CAT1_TaskStatus = CAT1_ACCESS;
break;
}
else if(ATCurrentCmdNum == AT_HTTPTERM_2) //表示下载BIN文件成功
{
CAT1_TaskStatus = CAT1_ACCESS;
break;
}
else
{
ATCurrentCmdNum += 1;
ATNextCmdNum = ATCurrentCmdNum+1;
CAT1_TaskStatus = CAT1_SEND;
break;
}
}
else if(CompareTime(&TimeCAT1))//表示发送超时
{
printf("TimeOut:%s\r\n",Lpuart1type.Lpuart1RecBuff);
ATCmds[ATRecCmdNum].ATStatus = TIME_OUT;
if(CurrentRty > 0)
{
CurrentRty--;
ATNextCmdNum = ATCurrentCmdNum;
CAT1_TaskStatus = CAT1_SEND;
break;
}
else
{
CAT1_Init();
return;
}
}
return;
case CAT1_ACCESS: //成功态
CAT1_TaskStatus = CAT1_IDIE;
break;
default:
return;
}
}
}
设置按键1和按键2 分别是升级和查询版本号。
/* 外部中断触发 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* 判断哪个引脚触发了中断 */
switch(GPIO_Pin)
{
case GPIO_PIN_2:
ExitFlag = 1;
break;
case GPIO_PIN_3:
ExitFlag = 2;
break;
default:
break;
}
}
void KEY_Task(void)
{
if(ExitFlag == 1)
{
ExitFlag = 0;
Update_Task();
}
else if(ExitFlag == 2)
{
ExitFlag = 0;
Get_Version();
}
}
下面是升级部分的具体代码以及流程图如下。
else if(ATCurrentCmdNum == AT_HTTPREAD_2) //如果AT指令为下载BIN文件
{
int len = 0;
long compare_len = 0;
memset(Msg_Len,0x00,sizeof(Msg_Len));
Find_string((char*)Lpuart1type.Lpuart1RecBuff, "DATA,", "\r\n", Msg_Len);
compare_len = atoi(Msg_Len);
printf("Find_Buf:%lu\r\n",compare_len);
if(Erase_flag == 1) //仅仅开始是擦除flash一次
{
Erase_flag = 0;
Erase_page(Application_2_Addr, 45); //擦除45页 90K
}
len = strstr((char *)Lpuart1type.Lpuart1RecBuff, Msg_Len) - (char*)Lpuart1type.Lpuart1RecBuff + strlen(Msg_Len) + 2;
printf("offset address is: %d\r\n",len);
if(compare_len == 1024)
{
memset (Bin_len,0x00,sizeof(Bin_len));
for(long b = 0; b < 1024; b++)
{
Bin_buffer[b] = Lpuart1type.Lpuart1RecBuff[len+b];
}
/* 接下来将固件写进flash内 */
printf("烧录第%d包...................\r\n",addr_count);
WriteFlash((Application_2_Addr+(addr_count)*1024), (uint8_t *)(&Bin_buffer[0]), 1024);
addr_count++;
ATNextCmdNum = ATCurrentCmdNum;
CAT1_TaskStatus = CAT1_SEND;
}
else if(compare_len < 1024)
{
memset (Bin_len,0x00,sizeof(Bin_len));
for(int b = 0; b < 1024; b++)
{
Bin_buffer[b] = Lpuart1type.Lpuart1RecBuff[len+b];
}
/* 接下来将固件写进flash内 */
printf("烧录第%d包...................\r\n",addr_count);
WriteFlash((Application_2_Addr+(addr_count)*1024), (uint8_t *)(&Bin_buffer[0]), compare_len);
addr_count = 0;
Erase_flag =1;
reboot_flag = 1;
ATCurrentCmdNum += 1;
ATNextCmdNum = ATCurrentCmdNum+1;
CAT1_TaskStatus = CAT1_SEND;
}
}
测试步骤
步骤1:通过ST-Link将BOOT刷进单片机
步骤2:通过ST-Link将APP1刷进单片机,刷完程序,单片机会自动重启并上报CSQ的值,如下图所示。
步骤3:复制一份APP1的代码,重命名为APP2,将上报信号的CSQ改成BSQ,如下所示。
sprintf(message_buffer,"{\"BSQ\":%s}",CSQ_buffer);
步骤4:将APP2上传到OTA平台上
步骤4:按下按键2,查询版本号是否一致
步骤5:按下按键1,升级程序。通过串口信息判断是否升级完成,如果打印以下信息就表示升级完成了。(通过4G模组下载BIN文件时显示下载数据有乱码是正常现象,因为BIN文件时二进制。)
[16:58:39.108]收←◆
当前命令码:21
send:AT+HTTPINIT
DMA_len:6
收到数据:
OK
当前码:21
当前命令码:22
send:AT+HTTPPARA="URL","http://47.98.248.24:8888/download1?device_id=123456&version=3.0.0"
收到数据:
OK
当前码:22
当前命令码:23
send:AT+HTTPACTION=0
DMA_len:6
收到数据:
OK
[16:58:39.671]收←◆DMA_len:28
收到数据:
+HTTPACTION: 0,200,15364
当前码:23
Bin_len=15364
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:39.776]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
x当前码:24
Find_Buf:1024
[16:58:41.432]收←◆erase success
offset address is: 30
烧录第0包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:41.548]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
??当前码:24
Find_Buf:1024
offset address is: 30
烧录第1包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:41.670]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
0?齥Kw?3wF当前码:24
Find_Buf:1024
offset address is: 30
烧录第2包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:41.793]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
`)?)?hA魛Q`h!魛Q`hA魛a`当前码:24
Find_Buf:1024
offset address is: 30
烧录第3包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:41.918]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
旞%`当前码:24
Find_Buf:1024
offset address is: 30
烧录第4包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:42.040]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
朇_陥(当前码:24
Find_Buf:1024
offset address is: 30
烧录第5包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:42.162]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
€1砒?砒惖鼻奥耄?4
Find_Buf:1024
offset address is: 30
烧录第6包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:42.285]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
醝 ?@@? h`???当前码:24
Find_Buf:1024
offset address is: 30
烧录第7包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:42.409]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
#?h€ ?h@饊p(`鑘@饊p鑐??F??鼻奥耄?4
Find_Buf:1024
offset address is: 30
烧录第8包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:42.533]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
a F??hi蒀I 粤h蒀I詏餈a F当前码:24
Find_Buf:1024
offset address is: 30
烧录第9包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
收到数据:
OK
[16:58:42.658]收←◆DMA_len:1063
收到数据:
+HTTPREAD: DATA,1024
觖8? 鄃匄pP `g p絼鴓P hhA魛q` h乭A?乣 h乭A餈乣当前码:24
Find_Buf:1024
offset address is: 24
烧录第10包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:42.784]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
鄅ih荔@圔??(?(?(??h??hI当前码:24
Find_Buf:1024
offset address is: 30
烧录第11包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:42.910]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
奰慀$I?hJhl"魛CJ`l滨€?hJhCl"衾CJ`慀$ ?hJh€l"舻鼻奥耄?4
Find_Buf:1024
offset address is: 30
烧录第12包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:43.038]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
h纈6甑鼻奥耄?4
Find_Buf:1024
offset address is: 30
烧录第13包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
[16:58:43.161]收←◆DMA_len:1069
收到数据:
OK
+HTTPREAD: DATA,1024
当前码:24
Find_Buf:1024
offset address is: 30
烧录第14包...................
当前命令码:24
send:AT+HTTPREAD=0,1024
DMA_len:31
收到数据:
OK
+HTTPREAD: DATA,4
=当前码:24
Find_Buf:4
offset address is: 27
烧录第15包...................
当前命令码:25
send:AT+HTTPTERM
***********************************
* *
* BootLoader *
* *
***********************************
> Choose a startup method......
> Start update......
> Start erase des flash......
Bank_1
识别的初始页数:30 共删除45页
[16:58:44.228]收←◆Erase sucessfully!
> Erase des flash sucessfully......
> Start copy......
[16:58:45.277]收←◆> Copy sucessfully......
> Start erase src flash......
Bank_1
识别的初始页数:75 共删除45页
[16:58:46.275]收←◆Erase sucessfully!
> Erase src flash sucessfully......
> Update sucessfully......
> Start up......
system init is complect!
[16:58:46.791]收←◆wait 14s for 4G power on !
6,升级成功需要手动断电重启。(因为程序里没写重启4G模组部分的驱动。如果不重启单片机和4G交互数据会出错。后期读者可以在开机时加上复位的AT指令来解决。)
7,重启后等待4G模组组网。然后就会发现报的数据变成了BSQ,如下图所示,表示升级成功。
注意事项
这只是个OTA升级的demo版程序。可以借鉴,但不要拿来主义。就不能重启这一个问题在项目上就是致命的。我写这个笔记的主要目的还是记录下OTA升级的一套完整流程,以防后期自己忘记了。