手把手教你如何實現自動固件更新-嵌入式篇

博客
※已刊登在“無線電”07月刊上手把手教你如何實現自動固件更新 —— 嵌入式篇 作者:常席正,魏文龍 我們在上期 “手把手教你如何實現自動固件更新——服務器篇”那篇文章中介紹了通過雲服務器更新固件的方法,並着重介紹了服務器端的前期準備以及軟件設計。這次小熊和大家分享下嵌入式端的軟件設計。相對於服務器端的軟件設計,嵌入式軟件設計需要更為嚴謹,因為固件升級出錯的後果會非常嚴重,因為這個功能一般使用在批量的設備上,而固件是控制系統的核心軟件,因此固件更新出錯的話,會造成設備大面積癱瘓。所以嚴重性不言而喻。 本期我們來介紹一下客戶端的具體實現過程,如圖1所示,根據我們的自動固件更新協議,在與更新服務器建立連接後,服務器會要求客戶端進行一系列驗證,嵌入式設備在通過驗證後,更新服務器會告知此嵌入式設備的最新固件信息,嵌入式設備根據這些信息下載並更新固件 圖1 自動固件更新協議 “下載並更新固件”幾個字囊括了我們所要執行的所有步驟。我們將按照以下步驟分別介紹。 1.下載準備--對Flash進行分區 為了實現‘下載並更新固件’我們先要做一些準備工作,我們把MCU的Flash分為三個區分別為BOOT區,APP區和Backup區,如圖2所示 圖2 內存空間分配 了解了空間分配之後,我們再來看一下我們這個演示中各部分的主要功能: BOOT區: 清空APP區,為新APP寫入做準備; 把暫存在Backup區的新版本程序拷貝到APP區; APP區 APP區是應用程序運行區域,實現正常的網絡連接,並更新固件。 配置網絡參數; 在線固件升級; 每次上電都會從Boot區引導,若判斷上層APP區載入程序是否成功,成功則直接從Boot區跳轉到APP區,正常運行主程序。 Backup區 從服務器接收並備份需要更新的新應用程序,也就是固件存儲區域。 備註:由於備區的大小為112K,所以意味着APP的大小最大為112K ; 程序流程設計 我們完成了對閃存的分區規劃後,就要設計我們程序的流程,圖3是程序執行的流程圖。 圖3嵌入式設備固件更新流程圖 每次啟動嵌入式設備,均從首地址開始執行程序: (1)啟動進入BOOT區,若BOOT檢測APP區的不為空,則跳轉到APP區的首地址執行主程序; (2) APP內的代碼主要實現: 配置網絡參數:配置IP地址,MAC地址,建立網絡連接。 遠程更新固件:客戶端向服務器發送固件版本查詢報文條件符合設定則進入步驟(3) (3)當APP將新版本的固件下載完成後,進入步驟(4) (4)跳轉到BOOT區,執行更新操作; (5) BOOT將APP區擦除,並將新APP從備份區寫入到APP區,寫入完畢後擦除備份區的固件; (6)重啟,重新執行程序。 我們將程序主要分為兩個部分,分別為BOOT程序和APP程序.APP程序即是我們的固件下載程序對應流程圖的(1)(2)(3)步,BOOT程序既是固件更新程序對應流程圖的(4)(5)(6)步: APP 程序設計(固件驗證與下載) APP代碼程序初始化網絡配置參數,實現嵌入式設備與服務器的正常連接,下載固件到備區,圖4描述的為嵌入式設備與服務器的通信過程。 圖4服務器-嵌入式設備通信過程示意圖 固件服務器-嵌入式設備的通信過程大致分為三步: 連接:嵌入式設備分配socket並連接到服務器。 通信:連接建立後。服務器在接收到來自嵌入式設備的請求後發送應答。 關閉:請求/應答完成後關閉連接。 我們APP區的函數主要做的就是下載固件,在程序里我們是通過w5500_version()和w5500_update()兩個函數來實現的,w5500_version()用來驗證當前的版本號與服務器上的版本號是否相同,如果當前版本號小於服務器上的版本號就進行更新。 [sourcecode language="c"]void w5500_version(void) { uint8 recv_buffer[2048]; uint8 version[10]; switch (getSn_SR(W5500_UPDATE)) { case SOCK_ESTABLISHED: if (getSn_IR(W5500_UPDATE) & Sn_IR_CON) { setSn_IR(W5500_UPDATE, Sn_IR_CON); } send(W5500_UPDATE,(const uint8 *)postH,sizeof(postH));//發送驗證 Delay_ms(5000); if ((len = getSn_RX_RSR(W5500_UPDATE)) > 0) { len = recv(W5500_UPDATE, (uint8*)recv_buffer, len); //接收數據 if (strstr((char*)recv_buffer,"\"error\"")) { //報文內包含error,就結束函數 printf("upload error\r\n"); return; } printf("%s\r\n",recv_buffer);//打印服務器響應報文 mid((char*)recv_buffer,"\"version\":",",",(char*)version);//可以獲取路徑 /*********讀取版本號************/ if (strncmp(ver_num,version,7)<0) { update_flag=1; mid((char*)recv_buffer,"\"http://W5500.com/fw_update/upload/","\",",(char*)bin_name);//可以獲取路徑 snprintf(post_msg,sizeof(post_msg), "POST /fw_update/upload/%s HTTP/1.1\r\n"\ "Host:w5500.com\r\n"\ "Accept:image/gif,image/x-xbitmap,image/jpeg,image/pjpeg,*/*\r\n"\ "Pragma:no-cache\r\n"\ "Accept-Encoding: gzip,deflate\r\n"\ "Connection:keep-alive\r\n"\ "\r\n",bin_name); printf("The version is %s\r\n",ver_num); } else { printf("The version is %s\r\n",version); printf("The version is…
Read More