日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

电子产品如何使用IAP方式升级程序

發布時間:2023/11/27 生活经验 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 电子产品如何使用IAP方式升级程序 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

1、ICP、ISP和IAP的概念

2、IAP升級程序的原理

3、IAP升級程序的流程

4、IAR環境下IAP的實現

4.1、BootLoader程序設計

4.2、User Application程序設計

4.3、IAR地址配置及文件輸出

5、拓展:解析HEX文件


1、ICP、ISP和IAP的概念

在項目開發過程中通常使用SWD、JTAG等工具進行程序燒錄和仿真,若產品節點較少還是比較方便,但是當設備節點量產時,就需要使用IAP的方式進行程序燒錄。

簡單說明幾個概念ICP、ISP和IAP。

ICP In-circuit programmer

ICP:在電路編程,MCU內部不需要有程序,上電就能夠對程序存儲區域進行編程,例如平時使用JTAG、SWD等方式。

ISP?In-system?programer

ISP:在系統編程,通過MCU專用的串行編程接口進行編程,MCU需要具有運行的外部條件,例如有晶振等。

例如STM32通過設置BOOT引腳設置對應啟動模式,然后通過串口等對內部Flash進行升級,可以說這種方式就是廠家在芯片內部固化了一個BootLoader程序。

IAP In-application?programer?

IAP:在應用編程,開發者設計BootLoader程序,通過串口、CAN、以太網等通信方式實現程序升級。

2、IAP升級程序的原理

通常一塊MCU芯片的Code(代碼)區內只有一個用戶程序,而IAP方案則是將代碼劃分為兩部分,兩部分區域各存放一個程序,一個為BootLoader(引導加載程序)另一個為User Application(用戶應用程序)。

BootLoader出廠時就固定下來了,在需要變更User Application時只需要通過觸發BootLoader對User Application的擦除和重新寫入即可完成用戶應用的更換。

程序執行初始化后首先會進入BootLoader,在BootLoader里面檢測條件是否被觸發(可通過按鍵是否被按下、串口是否接收到特定的數據、U盤是否插入等),如果有則進行對User Application進行擦除和重新寫入操作新程序,如果沒有則直接跳轉到BootLoader執行User Application。

3、IAP升級程序的流程

假設設備僅有User Application,以STM32F103ZET6為例,其啟動方式有三種:內置FLASH啟動、內置SRAM啟動、系統存儲器ROM啟動。通過BOOT0和BOOT1引腳的設置可以選擇從哪中方式啟動,這里選擇內置的FLASH啟動,STM32F103ZET6?FLASH的地址為0x08000000—0x0807FFFF,共512KB。

通常STM32發生中斷的過程為以下五步:

1、發生中斷(中斷請求);

2、到中斷向量表查找中斷函數入口地址;

3、跳轉到中斷函數;

4、執行中斷函數;

5、中斷返回。

也就是說,STM32的內置的Flash中有一個中斷向量表來存放各個中斷服務函數的入口地址,內置Flash的分配情況如下圖所示:

所以當只有一個程序的情況下(僅有User Applicatio時),程序執行的走向如下所示:

解析上圖:

STM32F103ZET6有一個中斷向量表,這個中斷向量表存放在代碼開始部分的后4個字節處(即0x08000004),代碼開始的4個字節存放的是堆棧棧頂的地址,當發生中斷后程序通過查找該表得到相應的中斷服務程序入口地址,然后再跳到相應的中斷服務程序中執行。

設備上電后從0x08000004處取出復位中斷向量的地址,然后跳轉到復位中斷程序的入口(標號①所示),執行結束后跳轉到main函數中(標號②所示)。在執行main函數的過程中發生中斷,則STM32強制將PC指針指回中斷向量表處(標號③所示),從中斷向量表中找到相應的中斷函數入口地址,跳轉到相應的中斷服務函數(標號④所示),執行完中斷函數后再返回到main函數中來(標號⑤所示)。

下面要講正題了。

若將STM32F103ZET6在內置的Flash里面添加User Application和BootLoader程序,則Flash分配情況大致如下圖所示:

此時,User Application和BootLoader程序各有一個中斷向量表,假設BootLoader程序占用的空間為N+M字節,則程序的走向應該如下圖所示:

解析上圖:

設備上電初始程序依然從0x08000004處取出復位中斷向量地址,執行復位中斷函數后跳轉到IAP的main(標號①所示),在IAP的main函數執行完成后(在BootLoader里面檢測條件是否被觸發(可通過按鍵是否被按下、串口是否接收到特定的數據、U盤是否插入等),如果有則進行對User Application進行擦除和重新寫入操作新程序,如果沒有則直接跳轉到BootLoader執行User Application)強制跳轉到0x08000004+N+M處(標號②所示),最后跳轉到新的main函數中來(標號③所示),當發生中斷請求后,程序跳轉到新的中斷向量表中取出新的中斷函數入口地址,再跳轉到新的中斷服務函數中執行(標號④⑤所示),執行完中斷函數后再返回到main函數中來(標號⑥所示)。

4、IAR環境下IAP的實現

以IAR環境為例,簡單講述IAP的實現步驟。這里MCU以華大HC32L130為例,因為使用的MCU不同,所以實現的細節也不一致,但是基本上官方都會提供Demo例程。

本示例Flash分配情況為:BootLoader地址:0x00000000~0x00000DFF,User Application地址:0x00001000~0x0000FFFF。

4.1、BootLoader程序設計

第1步:設計總體架構,包含三個功能函數:檢測BootLoader標志程序、IAP配置程序和IAP燒錄功能程序。

/*********************************************************************************** \brief  IAP 主函數**** \param  None**** \retval int32_t Return value, if needed********************************************************************************/
int32_t main(void)
{IAP_UpdateCheck();IAP_Init();IAP_Main();
}

第2步:檢查BootPara標記區數據值,判斷是否需要升級APP程序,若需要升級則才會執行IAP_Init()和IAP_Main()函數,否則會直接跳轉到User Application程序。

/*********************************************************************************** \brief  檢查BootPara標記區數據值,判斷是否需要升級APP程序.**** \param  None**** \retval None********************************************************************************/
void IAP_UpdateCheck(void)
{uint32_t u32AppFlag;u32AppFlag = *(__IO uint32_t *)BOOT_PARA_ADDRESS; //讀出BootLoader para區標記值if (APP_FLAG != u32AppFlag)                       //如果標記值不等于APP_FLAG,表示不需要升級APP程序{IAP_JumpToApp(APP_ADDRESS);                   //則直接跳轉至APP}    
}

第3步:IAP_Init()函數的實現,主要包括外圍模塊初始化和IAP通信協議標志初始化。

/*********************************************************************************** \brief  IAP 初始化**** \param  [in] None**** \retval None********************************************************************************/
void IAP_Init(void)
{PreiModule_Init();Modem_RamInit();
}/*********************************************************************************** \brief CPU外圍模塊初始化**** \param [in] None**** \retval None********************************************************************************/
void PreiModule_Init(void)
{HC32_SetSystemClockToRCH22_12MHz();HC32_InitUart();HC32_InitCRC();HC32_InitTIM();HC32_InitFlash(FLASH_CONFIG_FREQ_22_12MHZ);
}/*********************************************************************************** \brief modem文件中相關變量參數初始化**** \param [out] None** \param [in]  None**** \retval None********************************************************************************/
void Modem_RamInit(void)
{    uint32_t i;enFrameRecvStatus = FRAME_RECV_IDLE_STATUS;                         //幀狀態初始化為空閑狀態for (i=0; i<FRAME_MAX_SIZE; i++){u8FrameData[i] = 0;                                             //幀數據緩存初始化為零}u32FrameDataIndex = 0;                                              //幀緩存數組索引值初始化為零
}

第4步:IAP_Main()函數的實現,主要包含對User Application程序更新處理。

/*********************************************************************************** \brief  IAP APP程序升級主函數.**** \param  None**** \retval None********************************************************************************/
void IAP_Main(void)
{en_result_t enRet;while (1){enRet = Modem_Process();                       //APP程序更新處理if (Ok == enRet){IAP_ResetConfig();                         //復位所有外設模塊if (Error == IAP_JumpToApp(APP_ADDRESS))   //如果跳轉失敗{while(1);}}}
}/*********************************************************************************** \brief 上位機數據幀解析及處理**** \param [in] None             **** \retval Ok                          APP程序升級完成,并接受到跳轉至APP命令** \retval OperationInProgress         數據處理中** \retval Error                       通訊錯誤********************************************************************************/
en_result_t Modem_Process(void)
{uint8_t  u8Cmd, u8FlashAddrValid, u8Cnt, u8Ret;uint16_t u16DataLength, u16PageNum, u16Ret;uint32_t u32FlashAddr, u32FlashLength, u32Temp;if (enFrameRecvStatus == FRAME_RECV_PROC_STATUS)                //有數據幀待處理, enFrameRecvStatus值在串口中斷中調整{u8Cmd = u8FrameData[PACKET_CMD_INDEX];                      //獲取幀指令碼if (PACKET_CMD_TYPE_DATA == u8FrameData[PACKET_TYPE_INDEX]) //如果是數據指令{u8FlashAddrValid = 0u;u32FlashAddr = u8FrameData[PACKET_ADDRESS_INDEX] +      //讀取地址值(u8FrameData[PACKET_ADDRESS_INDEX + 1] << 8)  +(u8FrameData[PACKET_ADDRESS_INDEX + 2] << 16) +(u8FrameData[PACKET_ADDRESS_INDEX + 3] << 24);if ((u32FlashAddr >= (FLASH_BASE + BOOT_SIZE)) && (u32FlashAddr < (FLASH_BASE + FLASH_SIZE)))  //如果地址值在有效范圍內{u8FlashAddrValid = 1u;                              //標記地址有效}}switch (u8Cmd)                                              //根據指令碼跳轉執行{case  PACKET_CMD_HANDSHAKE    :                         //握手幀 指令碼u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;   //返回狀態為:正確Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE);   //發送應答幀給上位機break;case  PACKET_CMD_ERASE_FLASH  :                         //擦除flash 指令碼if ((u32FlashAddr % FLASH_SECTOR_SIZE) != 0)        //如果擦除地址不是頁首地址{u8FlashAddrValid = 0u;                          //標記地址無效}if (1u == u8FlashAddrValid)                         //如果地址有效{u32Temp = u8FrameData[PACKET_DATA_INDEX] +      //獲取待擦除flash尺寸(u8FrameData[PACKET_DATA_INDEX + 1] << 8)  +(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +(u8FrameData[PACKET_DATA_INDEX + 3] << 24);u16PageNum = FLASH_PageNumber(u32Temp);          //計算需擦除多少頁for (u8Cnt=0; u8Cnt<u16PageNum; u8Cnt++)         //根據需要擦除指定數量的扇區{u8Ret = Flash_EraseSector(u32FlashAddr + (u8Cnt * FLASH_SECTOR_SIZE));if (Ok != u8Ret)                             //如果擦除失敗,反饋上位機錯誤代碼{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR;break;}}if (Ok == u8Ret)                                 //如果全部擦除成功,反饋上位機成功{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;}else                                            //如果擦除失敗,反饋上位機錯誤超時標志{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_TIMEOUT;}}else                                                 //地址無效,反饋上位機地址錯誤{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR;}Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE);             //發送應答幀到上位機break;case  PACKET_CMD_APP_DOWNLOAD :                          //數據下載 指令碼if (1u == u8FlashAddrValid)                          //如果地址有效{u16DataLength = u8FrameData[FRAME_LENGTH_INDEX] + (u8FrameData[FRAME_LENGTH_INDEX + 1] << 8)- PACKET_INSTRUCT_SEGMENT_SIZE; //獲取數據包中的數據長度(不包含指令碼指令類型等等)if (u16DataLength > PACKET_DATA_SEGMENT_SIZE)    //如果數據長度大于最大長度{u16DataLength = PACKET_DATA_SEGMENT_SIZE;    //設置數據最大值}u8Ret = Flash_WriteBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u16DataLength); //把所有數據寫入flashif (Ok != u8Ret)                                 //如果寫數據失敗       {u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR;                //反饋上位機錯誤 標志}else                                             //如果寫數據成功{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;                   //反饋上位機成功 標志}}else                                                 //如果地址無效{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR;               //反饋上位機地址錯誤}Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE);             //發送應答幀到上位機break;case  PACKET_CMD_CRC_FLASH    :                          //查詢flash校驗值 指令碼if (1u == u8FlashAddrValid)                          //如果地址有效{u32FlashLength = u8FrameData[PACKET_DATA_INDEX] +                 (u8FrameData[PACKET_DATA_INDEX + 1] << 8)  +(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +(u8FrameData[PACKET_DATA_INDEX + 3] << 24);             //獲取待校驗flash大小if ((u32FlashLength + u32FlashAddr) > (FLASH_BASE + FLASH_SIZE))        //如果flash長度超出有效范圍{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_FLASH_SIZE_ERROR;     //反饋上位機flash尺寸錯誤}else{u16Ret = Cal_CRC16(((unsigned char *)u32FlashAddr), u32FlashLength);//讀取flash指定區域的值并計算crc值u8FrameData[PACKET_FLASH_CRC_INDEX] = (uint8_t)u16Ret;              //把crc值存儲到應答幀u8FrameData[PACKET_FLASH_CRC_INDEX+1] = (uint8_t)(u16Ret>>8);u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;                   //反饋上位機成功 標志}}else                                                                        //如果地址無效{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR;               //反饋上位機地址錯誤}Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE+2);           //發送應答幀到上位機break;case  PACKET_CMD_JUMP_TO_APP  :                          //跳轉至APP 指令碼Flash_EraseSector(BOOT_PARA_ADDRESS);                //擦除BOOT parameter 扇區u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;    //反饋上位機成功Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE);             //發送應答幀到上位機return Ok;                                           //APP更新完成,返回OK,接下來執行跳轉函數,跳轉至APPcase  PACKET_CMD_APP_UPLOAD   :                          //數據上傳if (1u == u8FlashAddrValid)                          //如果地址有效{u32Temp = u8FrameData[PACKET_DATA_INDEX] +(u8FrameData[PACKET_DATA_INDEX + 1] << 8)  +(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +(u8FrameData[PACKET_DATA_INDEX + 3] << 24);                   //讀取上傳數據長度if (u32Temp > PACKET_DATA_SEGMENT_SIZE)                                 //如果數據長度大于最大值{u32Temp = PACKET_DATA_SEGMENT_SIZE;                                 //設置數據長度為最大值}Flash_ReadBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u32Temp); //讀flash數據u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;                       //反饋上位機成功 標志Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE + u32Temp);//發送應答幀到上位機}else                                                  //如果地址無效{u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR;               //反饋上位機地址錯誤 標志Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE);         //發送應答幀到上位機}break;case  PACKET_CMD_START_UPDATE :                           //啟動APP更新(此指令正常在APP程序中調用)u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;     //反饋上位機成功 標志Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE);             //發送應答幀到上位機break;}enFrameRecvStatus = FRAME_RECV_IDLE_STATUS;                   //幀數據處理完成,幀接收狀態恢復到空閑狀態}return OperationInProgress;                                       //返回,APP更新中。。。
}

4.2、User Application程序設計

在本示例User Application中,觸發BootLoader更新程序的標志在串口接收中實現。

//UART0中斷函數
void Uart0_IRQHandler(void)
{if(Uart_GetStatus(M0P_UART0, UartRC))         //UART0數據接收{Uart_ClrStatus(M0P_UART0, UartRC);        //清中斷狀態位u8RxData[u8RxCnt] = Uart_ReceiveData(M0P_UART0);   //接收數據字節u8RxCnt++; if(u8RxCnt>=18){u8RxCnt = 0;if ((u8RxData[0]==0x6D)&&(u8RxData[1]==0xAC)&&(u8RxData[6]==0x26)&&(u8RxData[16]==0xA6)&&(u8RxData[17]==0xDA)) //是APP更新幀{for(uint32_t i=0;i<18;i++){Uart_SendDataPoll(M0P_UART0,u8TxData[i]); //查詢方式發送數據}//boot para區域寫標記值,通知BootLoader要更新程序了Flash_SectorErase(0xF00);Flash_WriteWord(0xF00, 0x12345678);NVIC_SystemReset();  //軟件復位MCU}                    }}if(Uart_GetStatus(M0P_UART0, UartTC))         //UART0數據發送{Uart_ClrStatus(M0P_UART0, UartTC);        //清中斷狀態位}}

4.3、IAR地址配置及文件輸出

最后還需要簡答配置下IAR環境。

第1步:確定輸出的Linker配置地址,因為需要在這里程序修改地址。

第2步:找到Linker配置文件,修改BootLoader程序地址:0x00000000~0x00000DFF,User Application程序地址:0x00001000~0x0000FFFF。

第3步:找到User Application程序的配置文件(后綴為.s的文件),添加程序中斷向量偏移長度:0x00001000,和BootLoader程序配置文件相比有兩處不同之處,如下所示:

第4步:將這兩個程序按照ICP方式(SWD、JTAG等)燒錄后,此后就可以使用IAP方式通過串口燒錄HEX文件程序或者BIN文件程序。輸出及燒錄HEX文件程序或者BIN文件程序方式如下圖所示:

5、拓展:解析HEX文件

HEX文件可以通過UltraEdit、Notepad++、記事本等工具打開,用Notepad++打開之后會看到以下數據內容:

使用Notepad++打開后會不同含義的數據其顏色不同。每行數據都會有一個冒號開始,后面的數據由:數據長度、地址、標識符、有效數據、校驗數據等構成。以上圖的第一行為例,進行解析:

第1個字節10,表示該行具有0x10個數據,即16個字節的數據;

第2、3個字節3E00,表示該行的起始地址為0x3E00;

第4個字節00,表示該行記錄的是數據;

第5-20個字節,表示的是有效數據;

第21個字節EB,表示前面數據的校驗數據,校驗方法:0x100-前面字節累加和

其中,第4個字節具有5種類型:00-05,含義如下:

字段含義
00表示后面記錄的是數據
01表示文件結束
02表示擴展段地址
03表示開始段地址
04表示擴展線性地址
05表示開始線性地址

單片機的hex文件以00居多,都用來表示數據。hex文件的結束部分如下圖所示:

最后一行的01表示文件結束了,最后的FF表示校驗數據,由0x100-0x01=0xFF得來。


資源下載:IAR環境下STM32+IAP方案的實現

總結

以上是生活随笔為你收集整理的电子产品如何使用IAP方式升级程序的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。