基于GPRS网络通信的STM32程序IAP升级应用

STM32的IAP升级是一个比较重要和常规的功能,特别是在物联网产品的应用上尤为重要,能够非常方便地完成应用程序的更新和升级。正点原子的开发板都有例程可以参考,基本的IAP功能能够实现。

本次IAP升级的应用的场景恰好就是物联网终端产品的应用,最终实现上位机平台通过GPRS网络方式完成终端应用程序的远程升级。物联网终端使用的是STM32F103RC,256Kflash和48KRAM,并有64M的外扩FLASH芯片W25Q64,GPRS通讯模块使用芯讯通SIM800C。应用程序(app)大小约45k,使用FreeRTOS操作系统。基本设计思路是:

将256K的FLASH分为三个部分:

第一个部分64K(0x8000000~0x8010000),用于bootLoader程序存放位置;

第二个部分128K(0x8010000~0x8030000),用于正常app程序存放位置;

第三个部分64K(0x8030000~0x8040000),用于初版app程序存放位置。

正常情况下设备始终运行在第二个部分正常app程序,当上位机平台有升级请求时,会下发远程升级基本信息指令到物联网终端,基本信息指令内容包含升级bin文件的URL地址,升级bin文件MD5校验值。终端收到远程升级指令后,将升级基本信息和升级标志写入FLASH中,执行复位重启,程序复位后首先进入第一个部分BootLoader程序中,读取到升级标志后立即进入到IAP升级任务。

IAP升级任务主要工作是:读取FLASH中URL地址和MD5校验值,然后配置GPRS相关功能进行bin文件下载,最后进行MD5校验,成功之后写入正常app程序区完成升级,若升级失败则程序跳转到初版app程序执行,以防止设备变成砖头。

整个功能设计之中的重点是如何保证升级的可靠性和安全性。那么如何能保证安全可靠呢?首先想到的就是bin文件接收到的内容必须要校验,对于这个45K大小的文件最后选择了MD5校验的方式,关于MD5计算也是第一次使用,在了解其原理和方法之后发现并不容易实现。首先上位机计算一个45K大小文件的MD5值是比较容易的,但针对单片机的内容和空间却是存在很大问题的,只能分段计算MD5。所以在读取bin文件数据时,使用的方式是每次读取1K内容,然后计算MD5中间值,然后把每次读取的1K数据内容存储到外扩FLASH中,读完所有数据之后再校验总的MD5值。

bin文件的下载使用了GPRS模块的http访问网页的方式,避免了实时数据交互的繁琐。

SIM800C HTTP访问网页数据AT指令流程:

(1)AT+HTTPINIT:初始化HTTP。

正常 返回OK

(2)AT+HTTPPARA="CID","1":设置承载上下文标识,和上面一致。

正常返回OK

(3)AT+HTTPPARA="URL","www.baidu.com/img/baidu_logo.gif":设置你要访问的那个网站,要加上“http://”协议

正常返回OK

(4)AT+HTTPACTION=0:激活HTTP请求,0表示get方式、1表示post方式、2表示head。

这个时候要耐心等,在OK之后会返回HTTP的状态。


(5)AT+HTTPREAD=1,1489:读取HTTP相应数据。1表示从第一个字节开始读,读1489个字节。

(6)AT+HTTPTERM:访问完了,终止HTTP服务。

返回OK

(7)AT+SAPBR=0,1:最后关闭承载。


最后在完成bin文件的接收以及写入FLASH之后,发现bootloader跳转到app程序之后并不能运行。检查了app程序的中断向量地址设置和bin文件生成配置都没有问题:

最后在网上看到一些资料说是,freeRTOS系统程序的IAP升级并不能完全像裸机一样实现!主要问题出在iap_load_app这个函数这里,这里完全是参照正点原子IAP例程编写的,需要修改!由于使用了RTOS,跳转之前必须关闭掉中断,清除中断相关寄存器,并且要关闭系统滴答定时器,程序才能成功跳转。重写iap_load_app函数如下:

void iap_load_app(uint32_t app_address)

{

typedef void (*_func)(void);

//__disable_irq();

__set_FAULTMASK(1);

/* MCU peripherals re-initial. */

{

GPIO_InitTypeDef GPIO_InitStruct;

  GPIO_InitStruct.Pin = ~(GPIO_PIN_13|GPIO_PIN_14);

  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;//输入

  GPIO_InitStruct.Pull = GPIO_NOPULL;//

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_All;

  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

/* reset systick */

SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

/* disable all peripherals clock. */

RCC->AHBENR = 0;

RCC->APB1ENR = 0;

RCC->APB2ENR = 0; /* Switch to default cpu clock. */

RCC->CFGR = 0;

} /* MCU peripherals re-initial. */

/* Disable MPU */

#if (__MPU_PRESENT == 1U)

HAL_MPU_Disable();// MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;

#endif

/* disable and clean up all interrupts. */

{

int i;

for(i = 0; i < 8; i++)

{

/* disable interrupts. */

NVIC->ICER[i] = 0xFFFFFFFF; /* clean up interrupts flags. */

NVIC->ICPR[i] = 0xFFFFFFFF;

}

} /* Set new vector table pointer */

SCB->VTOR = app_address; /* reset register values */

__set_BASEPRI(0);

__set_FAULTMASK(0); /* set up MSP and switch to it */

__set_MSP(*(uint32_t*)app_address);

__set_PSP(*(uint32_t*)app_address);

__set_CONTROL(0); /* ensure what we have done could take effect */

__ISB();

//__disable_irq(); /* never return */

__set_FAULTMASK(1);

((_func)(*(uint32_t*)(app_address + 4)))();

}

你可能感兴趣的:(基于GPRS网络通信的STM32程序IAP升级应用)