W5500自动固件更新


关键词:W5500自动固件更新  W5500  WIZnet


常见的嵌入式设备的固件更新有两种方式:上位机工具更新和HTTP嵌入式网页更新。但两种方式都无法批量更新,都需要用户手动操作,如果用户有大量模块需要更新固件,而且很多厂商的固件因为保密的问题是不开放给客户的。下面介绍一种嵌入式设备批量实现固件更新的方法----通过云平台服务器自动更新。 只需要有一台云平台,一些HTML/PHP和数据库方面的知识,然后再在模块里植入HTTP客户端固件更新的相关代码。之后用户只需要将模块联网,固件更新就能自动完成。

本次自动固件更新分为服务器端(云平台)和客户端(设备)。服务器和模块之间的信息交流需要制订一些私有协议去完成具体的操作。就好比2个人说好了是用普通话交流,但是还需要提前制订好交流的内容。 本次自动固件更新操作时,云平台已经制定好了规则,因此我们只需要遵循该规则,就能够保证模块和云平台之间畅通地进行交流。下面介绍一下云平台和模块之间制定好的规则.如图1所示。


图1:自动更新固件规则——云平台端

以上介绍了服务器端制定好的规则后,客户端(即模块)只需要遵循规则并且按照规则来设计程序即可。客户端所做的工作分为“准备工作”、“程序设计”和“代码实现”三个部分。


1.准备工作

为了实现“下载并更新固件”,我们先要做一些准备工作——把MCU中的Flash划分为三个区域分别为BOOT区、APP区和Backup区。如图2所示。


图2:内存分配空间
了解了空间分配之后,我们再来看一下我们这个演示中各部分的主要功能:

a.BOOT区:

b.APP区:

c.Backup区:

  • 从云平台接收并存储新固件。

  • 2.程序流程设计

    我们完成了对Flash的分区规划以及一些准备工作后,就要设计我们程序的流程,在这里我们把程序分为APP程序和BOOT程序。APP程序完成配置网络参数、实现正常网络连接以及固件版本的比对和下载,然后设备从BOOT启动运行;BOOT程序完成清空APP区,把Backup的固件复制到APP区,清空Backup,然后设备从APP启动运行。 且每次上电都会从BOOT区引导,若判断上层APP区载入程序是否成功,成功则直接从BOOT区跳转到APP区,正常运行主程序。APP和BOOT的程序流程图如图3、图4所示。


    图3:APP程序流程图

    图4:BOOT程序流程图

    3.代码实现

    我们APP区的函数主要做的就是下载固件,在程序里我们是通过W5500_version()和W5500_update()两个函数来实现的。首先我们要为嵌入式设备分配一个Socket W5500_UPDATE,这个Socket初始状态为SOCK_CLOSED,我们通过调用函数socket(W5500_UPDATE, Sn_MR_TCP,30000,Sn_MR_ND),打开Socket,Socket状态改变为SOCK_INIT,打开Socket后调用connect(W5500_UPDATE,server_ip ,server_port)连接服务器,server_ip和server_port分别为服务器的IP地址和端口号,W5500_version()中首先向服务器发送HTTP POST请求并接收服务器所响应的固件版本信息,通过比对整型数据的大小来验证当前的版本号与服务器上的版本号是否相同,如果当前版本号小于服务器上的版本号就置位更新标志位,进入W5500_update()函数; W5500_update()函数主要进行固件的下载,把固件长度写入EEPROM中,Socket状态变为SOCK_ESTABLISHED,在此状态下用for循环把固件写入到Backup,更新下载标志位,程序跳转到BOOT区。BOOT主要是通过判断写入EEPROM中的固件长度是否为0, 从而判断Backup区是否有新的固件,若有新固件,则将Backup的新固件复制到APP区,完成以后清空Backup,引导代码跳转到APP区。

    BOOT程序代码如下:
    		int main(void)
    		{
    		  uint16 i;
    		  uint32 len;
    		  
    		  Systick_Init(72);
    		  GPIO_Configuration();
    		  NVIC_Configuration(); 
    		  USART1_115200();
    		  WIZ_SPI_Init();
    		  at24c16_init();
    		  reset_w5500();
    		  get_config();
    		  
    		  len = at24c16_read(210);
    		  len = (len<<8)+at24c16_read(211);
    		  len = (len<<8)+at24c16_read(212);
    		  len = (len<<8)+at24c16_read(213);                     
    		  if(len!=0)
    		  {
    		  	ConfigMsg.state=FW_RXD_SERVER;		
    		  }
    		  else
    		  {
    		  	ConfigMsg.state=FW_APP_STATE;
    		  }
    		  if(is_first_run()==1)
    		  {
    			memcpy(ConfigMsg.mac,DEFAULT_MAC,6);
    			sprintf((char*)ConfigMsg.device_id,"20181208-%02x%02x%02x",ConfigMsg.mac[3],ConfigMsg.mac[4],ConfigMsg.mac[5]);
    			set_default();            
    			write_config_to_eeprom(); 
    		  }
    		  switch (ConfigMsg.state)
    		  {
    			case FW_APP_STATE:
    			#if 1
    				if(is_app_inside()==1)
    				{ 
    					for(i=0;i<10000;i++){};
    					reboot_app();        
    				}
    				else
    				{}
    			#endif
    				break;
    			case FW_RXD_HTTP:
    			case FW_RXD_SERVER: 
    				ConfigMsg.fw_len = len;
    			#if 1
    				if(copy_app(ConfigMsg.fw_len, ConfigMsg.fw_checksum)==TRUE)
    				{
    					ConfigMsg.fw_checksum = 0;
    					ConfigMsg.fw_len = 0;
    					ConfigMsg.state=FW_APP_STATE;
    					len=0;
    					write_config_to_eeprom();     
    					at24c16_write(210,0);
    					at24c16_write(211,0);
    					at24c16_write(212,0);
    					at24c16_write(213,0);	                
    					reboot_app();
    				}
    				else
    				{}
    			#endif
    				break;
    			case EEPROM_CRC_ERROR:
    				printf("CRC error!\r\n");	
    		   		break;		
    			default:
    				break;
    			}
    			set_network();
    		  	while (1)
    			{ 
    			   IWDG_ReloadCounter();       
    			   if(reboot_flag==1)reboot(); 
    			}
    		}
    		
    APP程序代码如下:
    		int main(void)
           {
    			Systick_Init(72);												//时钟输出
        		GPIO_Configuration();        									//中断控制器
      			NVIC_Configuration();
    			at24c16_init();													//初始化EEPROM
      			WIZ_SPI_Init();													//初始化SPI
    			USART1_115200();												
    			reset_w5500();
    			set_default();
    			set_network();	
    			write_config_to_eeprom();
    	
    			if(is_first_run()==1)
    			{
    				write_cpuID();	
    			}
    			printf("The fw version is V1.0\r\n");
    	
      			while (1)
      			{
    				if(update_flag == 0)
    				{
    					w5500_version();    									//新旧版本比对  
    				}
    				if(update_flag == 1)   
    				{						
    					w5500_update();		     								//新固件的下载
    				}						
    				if(download_flag == 1)	  
    				{
    					reboot(); 
    				}
      			}	
    		}
    		

    功能演示

    1. 打开物联网设备固件管理云平台,注册并登录云平台账号,创建产品(创建完产品会生成一个对应的产品API-Key);
    2. 烧录boot,然后将app程序device.h中HTTP POST请求中的key对应的值替换成该产品API-Key,编译后烧录app;
    3. 打开串口工具,复位W5500EVB开发板,串口打印信息:“The fw version is V1.0”;
    4. 将app程序device.h中的版本号FW_VER_HIGH、FW_VER_LOW升高至V2.0,在main.c中修改printf打印信息为:“The fw version is V2.0”, 然后将编译产生的新固件上传至云平台该产品下,并输入版本号为V2.0;
    5. 按下复位,开始固件更新,串口打印信息:“The fw version is V2.0”,固件更新完成,如图5所示。

    图5:自动固件更新
    例程下载:【W5500EVB自动固件更新】
    编译环境:keil V5.11
    云平台网址:http://ibinhub.com
    硬件要求:W5500EVB开发板