電子產(chǎn)品如何使用IAP方式升級程序

      網(wǎng)友投稿 894 2025-04-03

      目錄


      1、ICP、ISP和IAP的概念

      2、IAP升級程序的原理

      3、IAP升級程序的流程

      4、IAR環(huán)境下IAP的實(shí)現(xiàn)

      4.1、BootLoader程序設(shè)計(jì)

      4.2、User Application程序設(shè)計(jì)

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

      5、拓展:解析HEX文件

      1、ICP、ISP和IAP的概念

      在項(xiàng)目開發(fā)過程中通常使用SWD、JTAG等工具進(jìn)行程序燒錄和仿真,若產(chǎn)品節(jié)點(diǎn)較少還是比較方便,但是當(dāng)設(shè)備節(jié)點(diǎn)量產(chǎn)時(shí),就需要使用IAP的方式進(jìn)行程序燒錄。

      簡單說明幾個(gè)概念I(lǐng)CP、ISP和IAP。

      ICP In-circuit programmer

      ICP:在電路編程,MCU內(nèi)部不需要有程序,上電就能夠?qū)Τ绦虼鎯^(qū)域進(jìn)行編程,例如平時(shí)使用JTAG、SWD等方式。

      ISP?In-system?programer

      ISP:在系統(tǒng)編程,通過MCU專用的串行編程接口進(jìn)行編程,MCU需要具有運(yùn)行的外部條件,例如有晶振等。

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

      IAP In-application?programer

      IAP:在應(yīng)用編程,開發(fā)者設(shè)計(jì)BootLoader程序,通過串口、CAN、以太網(wǎng)等通信方式實(shí)現(xiàn)程序升級。

      2、IAP升級程序的原理

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

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

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

      3、IAP升級程序的流程

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

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

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

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

      3、跳轉(zhuǎn)到中斷函數(shù);

      4、執(zhí)行中斷函數(shù);

      5、中斷返回。

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

      所以當(dāng)只有一個(gè)程序的情況下(僅有User Applicatio時(shí)),程序執(zhí)行的走向如下所示:

      解析上圖:

      STM32F103ZET6有一個(gè)中斷向量表,這個(gè)中斷向量表存放在代碼開始部分的后4個(gè)字節(jié)處(即0x08000004),代碼開始的4個(gè)字節(jié)存放的是堆棧棧頂?shù)牡刂罚?dāng)發(fā)生中斷后程序通過查找該表得到相應(yīng)的中斷服務(wù)程序入口地址,然后再跳到相應(yīng)的中斷服務(wù)程序中執(zhí)行。

      設(shè)備上電后從0x08000004處取出復(fù)位中斷向量的地址,然后跳轉(zhuǎn)到復(fù)位中斷程序的入口(標(biāo)號①所示),執(zhí)行結(jié)束后跳轉(zhuǎn)到main函數(shù)中(標(biāo)號②所示)。在執(zhí)行main函數(shù)的過程中發(fā)生中斷,則STM32強(qiáng)制將PC指針指回中斷向量表處(標(biāo)號③所示),從中斷向量表中找到相應(yīng)的中斷函數(shù)入口地址,跳轉(zhuǎn)到相應(yīng)的中斷服務(wù)函數(shù)(標(biāo)號④所示),執(zhí)行完中斷函數(shù)后再返回到main函數(shù)中來(標(biāo)號⑤所示)。

      下面要講正題了。

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

      此時(shí),User Application和BootLoader程序各有一個(gè)中斷向量表,假設(shè)BootLoader程序占用的空間為N+M字節(jié),則程序的走向應(yīng)該如下圖所示:

      解析上圖:

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

      4、IAR環(huán)境下IAP的實(shí)現(xiàn)

      以IAR環(huán)境為例,簡單講述IAP的實(shí)現(xiàn)步驟。這里MCU以華大HC32L130為例,因?yàn)槭褂玫腗CU不同,所以實(shí)現(xiàn)的細(xì)節(jié)也不一致,但是基本上官方都會提供Demo例程。

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

      4.1、BootLoader程序設(shè)計(jì)

      第1步:設(shè)計(jì)總體架構(gòu),包含三個(gè)功能函數(shù):檢測BootLoader標(biāo)志程序、IAP配置程序和IAP燒錄功能程序。

      /**

      *******************************************************************************

      ** \brief IAP 主函數(shù)

      **

      ** \param None

      **

      ** \retval int32_t Return value, if needed

      **

      ******************************************************************************/

      int32_t main(void)

      {

      IAP_UpdateCheck();

      IAP_Init();

      IAP_Main();

      }

      第2步:檢查BootPara標(biāo)記區(qū)數(shù)據(jù)值,判斷是否需要升級APP程序,若需要升級則才會執(zhí)行IAP_Init()和IAP_Main()函數(shù),否則會直接跳轉(zhuǎn)到User Application程序。

      /**

      *******************************************************************************

      ** \brief 檢查BootPara標(biāo)記區(qū)數(shù)據(jù)值,判斷是否需要升級APP程序.

      **

      ** \param None

      **

      ** \retval None

      **

      ******************************************************************************/

      void IAP_UpdateCheck(void)

      {

      uint32_t u32AppFlag;

      u32AppFlag = *(__IO uint32_t *)BOOT_PARA_ADDRESS; //讀出BootLoader para區(qū)標(biāo)記值

      if (APP_FLAG != u32AppFlag) //如果標(biāo)記值不等于APP_FLAG,表示不需要升級APP程序

      {

      IAP_JumpToApp(APP_ADDRESS); //則直接跳轉(zhuǎn)至APP

      }

      }

      第3步:IAP_Init()函數(shù)的實(shí)現(xiàn),主要包括外圍模塊初始化和IAP通信協(xié)議標(biāo)志初始化。

      /**

      *******************************************************************************

      ** \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文件中相關(guān)變量參數(shù)初始化

      **

      ** \param [out] None

      ** \param [in] None

      **

      ** \retval None

      **

      ******************************************************************************/

      void Modem_RamInit(void)

      {

      uint32_t i;

      enFrameRecvStatus = FRAME_RECV_IDLE_STATUS; //幀狀態(tài)初始化為空閑狀態(tài)

      for (i=0; i

      {

      u8FrameData[i] = 0; //幀數(shù)據(jù)緩存初始化為零

      }

      u32FrameDataIndex = 0; //幀緩存數(shù)組索引值初始化為零

      }

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

      /**

      *******************************************************************************

      ** \brief IAP APP程序升級主函數(shù).

      **

      ** \param None

      **

      ** \retval None

      **

      ******************************************************************************/

      void IAP_Main(void)

      {

      en_result_t enRet;

      while (1)

      {

      enRet = Modem_Process(); //APP程序更新處理

      if (Ok == enRet)

      {

      IAP_ResetConfig(); //復(fù)位所有外設(shè)模塊

      if (Error == IAP_JumpToApp(APP_ADDRESS)) //如果跳轉(zhuǎn)失敗

      {

      while(1);

      }

      }

      }

      }

      /**

      *******************************************************************************

      ** \brief 上位機(jī)數(shù)據(jù)幀解析及處理

      **

      ** \param [in] None

      **

      ** \retval Ok APP程序升級完成,并接受到跳轉(zhuǎn)至APP命令

      ** \retval OperationInProgress 數(shù)據(jù)處理中

      ** \retval Error 通訊錯(cuò)誤

      **

      ******************************************************************************/

      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) //有數(shù)據(jù)幀待處理, enFrameRecvStatus值在串口中斷中調(diào)整

      {

      u8Cmd = u8FrameData[PACKET_CMD_INDEX]; //獲取幀指令碼

      if (PACKET_CMD_TYPE_DATA == u8FrameData[PACKET_TYPE_INDEX]) //如果是數(shù)據(jù)指令

      {

      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))) //如果地址值在有效范圍內(nèi)

      {

      u8FlashAddrValid = 1u; //標(biāo)記地址有效

      }

      }

      switch (u8Cmd) //根據(jù)指令碼跳轉(zhuǎn)執(zhí)行

      {

      case PACKET_CMD_HANDSHAKE : //握手幀 指令碼

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //返回狀態(tài)為:正確

      Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀給上位機(jī)

      break;

      case PACKET_CMD_ERASE_FLASH : //擦除flash 指令碼

      if ((u32FlashAddr % FLASH_SECTOR_SIZE) != 0) //如果擦除地址不是頁首地址

      {

      u8FlashAddrValid = 0u; //標(biāo)記地址無效

      }

      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); //計(jì)算需擦除多少頁

      for (u8Cnt=0; u8Cnt

      {

      u8Ret = Flash_EraseSector(u32FlashAddr + (u8Cnt * FLASH_SECTOR_SIZE));

      if (Ok != u8Ret) //如果擦除失敗,反饋上位機(jī)錯(cuò)誤代碼

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR;

      break;

      }

      }

      if (Ok == u8Ret) //如果全部擦除成功,反饋上位機(jī)成功

      {

      電子產(chǎn)品如何使用IAP方式升級程序

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;

      }else //如果擦除失敗,反饋上位機(jī)錯(cuò)誤超時(shí)標(biāo)志

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_TIMEOUT;

      }

      }

      else //地址無效,反饋上位機(jī)地址錯(cuò)誤

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR;

      }

      Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī)

      break;

      case PACKET_CMD_APP_DOWNLOAD : //數(shù)據(jù)下載 指令碼

      if (1u == u8FlashAddrValid) //如果地址有效

      {

      u16DataLength = u8FrameData[FRAME_LENGTH_INDEX] + (u8FrameData[FRAME_LENGTH_INDEX + 1] << 8)

      - PACKET_INSTRUCT_SEGMENT_SIZE; //獲取數(shù)據(jù)包中的數(shù)據(jù)長度(不包含指令碼指令類型等等)

      if (u16DataLength > PACKET_DATA_SEGMENT_SIZE) //如果數(shù)據(jù)長度大于最大長度

      {

      u16DataLength = PACKET_DATA_SEGMENT_SIZE; //設(shè)置數(shù)據(jù)最大值

      }

      u8Ret = Flash_WriteBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u16DataLength); //把所有數(shù)據(jù)寫入flash

      if (Ok != u8Ret) //如果寫數(shù)據(jù)失敗

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR; //反饋上位機(jī)錯(cuò)誤 標(biāo)志

      }

      else //如果寫數(shù)據(jù)成功

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 標(biāo)志

      }

      }

      else //如果地址無效

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反饋上位機(jī)地址錯(cuò)誤

      }

      Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī)

      break;

      case PACKET_CMD_CRC_FLASH : //查詢flash校驗(yàn)值 指令碼

      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); //獲取待校驗(yàn)flash大小

      if ((u32FlashLength + u32FlashAddr) > (FLASH_BASE + FLASH_SIZE)) //如果flash長度超出有效范圍

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_FLASH_SIZE_ERROR; //反饋上位機(jī)flash尺寸錯(cuò)誤

      }else

      {

      u16Ret = Cal_CRC16(((unsigned char *)u32FlashAddr), u32FlashLength);//讀取flash指定區(qū)域的值并計(jì)算crc值

      u8FrameData[PACKET_FLASH_CRC_INDEX] = (uint8_t)u16Ret; //把crc值存儲到應(yīng)答幀

      u8FrameData[PACKET_FLASH_CRC_INDEX+1] = (uint8_t)(u16Ret>>8);

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 標(biāo)志

      }

      }

      else //如果地址無效

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反饋上位機(jī)地址錯(cuò)誤

      }

      Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE+2); //發(fā)送應(yīng)答幀到上位機(jī)

      break;

      case PACKET_CMD_JUMP_TO_APP : //跳轉(zhuǎn)至APP 指令碼

      Flash_EraseSector(BOOT_PARA_ADDRESS); //擦除BOOT parameter 扇區(qū)

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功

      Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī)

      return Ok; //APP更新完成,返回OK,接下來執(zhí)行跳轉(zhuǎn)函數(shù),跳轉(zhuǎn)至APP

      case PACKET_CMD_APP_UPLOAD : //數(shù)據(jù)上傳

      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); //讀取上傳數(shù)據(jù)長度

      if (u32Temp > PACKET_DATA_SEGMENT_SIZE) //如果數(shù)據(jù)長度大于最大值

      {

      u32Temp = PACKET_DATA_SEGMENT_SIZE; //設(shè)置數(shù)據(jù)長度為最大值

      }

      Flash_ReadBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u32Temp); //讀flash數(shù)據(jù)

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 標(biāo)志

      Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE + u32Temp);//發(fā)送應(yīng)答幀到上位機(jī)

      }

      else //如果地址無效

      {

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反饋上位機(jī)地址錯(cuò)誤 標(biāo)志

      Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī)

      }

      break;

      case PACKET_CMD_START_UPDATE : //啟動APP更新(此指令正常在APP程序中調(diào)用)

      u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 標(biāo)志

      Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī)

      break;

      }

      enFrameRecvStatus = FRAME_RECV_IDLE_STATUS; //幀數(shù)據(jù)處理完成,幀接收狀態(tài)恢復(fù)到空閑狀態(tài)

      }

      return OperationInProgress; //返回,APP更新中。。。

      }

      4.2、User Application程序設(shè)計(jì)

      在本示例User Application中,觸發(fā)BootLoader更新程序的標(biāo)志在串口接收中實(shí)現(xiàn)。

      //UART0中斷函數(shù)

      void Uart0_IRQHandler(void)

      {

      if(Uart_GetStatus(M0P_UART0, UartRC)) //UART0數(shù)據(jù)接收

      {

      Uart_ClrStatus(M0P_UART0, UartRC); //清中斷狀態(tài)位

      u8RxData[u8RxCnt] = Uart_ReceiveData(M0P_UART0); //接收數(shù)據(jù)字節(jié)

      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]); //查詢方式發(fā)送數(shù)據(jù)

      }

      //boot para區(qū)域?qū)憳?biāo)記值,通知BootLoader要更新程序了

      Flash_SectorErase(0xF00);

      Flash_WriteWord(0xF00, 0x12345678);

      NVIC_SystemReset(); //軟件復(fù)位MCU

      }

      }

      }

      if(Uart_GetStatus(M0P_UART0, UartTC)) //UART0數(shù)據(jù)發(fā)送

      {

      Uart_ClrStatus(M0P_UART0, UartTC); //清中斷狀態(tài)位

      }

      }

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

      最后還需要簡答配置下IAR環(huán)境。

      第1步:確定輸出的Linker配置地址,因?yàn)樾枰谶@里程序修改地址。

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

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

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

      5、拓展:解析HEX文件

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

      使用Notepad++打開后會不同含義的數(shù)據(jù)其顏色不同。每行數(shù)據(jù)都會有一個(gè)冒號開始,后面的數(shù)據(jù)由:數(shù)據(jù)長度、地址、標(biāo)識符、有效數(shù)據(jù)、校驗(yàn)數(shù)據(jù)等構(gòu)成。以上圖的第一行為例,進(jìn)行解析:

      第1個(gè)字節(jié)10,表示該行具有0x10個(gè)數(shù)據(jù),即16個(gè)字節(jié)的數(shù)據(jù);

      第2、3個(gè)字節(jié)3E00,表示該行的起始地址為0x3E00;

      第4個(gè)字節(jié)00,表示該行記錄的是數(shù)據(jù);

      第5-20個(gè)字節(jié),表示的是有效數(shù)據(jù);

      第21個(gè)字節(jié)EB,表示前面數(shù)據(jù)的校驗(yàn)數(shù)據(jù),校驗(yàn)方法:0x100-前面字節(jié)累加和;

      其中,第4個(gè)字節(jié)具有5種類型:00-05,含義如下:

      單片機(jī)的hex文件以00居多,都用來表示數(shù)據(jù)。hex文件的結(jié)束部分如下圖所示:

      最后一行的01表示文件結(jié)束了,最后的FF表示校驗(yàn)數(shù)據(jù),由0x100-0x01=0xFF得來。

      資源下載:IAR環(huán)境下STM32+IAP方案的實(shí)現(xiàn)

      ARM 單片機(jī)

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:怎么打字進(jìn)去,除了標(biāo)題和副標(biāo)題(標(biāo)題后面的點(diǎn)怎么打出來)
      下一篇:生產(chǎn)管理移動辦公平臺(生產(chǎn)管理移動辦公平臺有哪些)
      相關(guān)文章
      国产亚洲玖玖玖在线观看| 久久亚洲AV成人无码软件| 亚洲日本乱码卡2卡3卡新区| 亚洲精品成人网站在线播放| 亚洲国产香蕉碰碰人人| 黑人精品videos亚洲人| 亚洲av永久无码制服河南实里| 国产成人无码综合亚洲日韩| 亚洲中文字幕在线第六区| 亚洲人精品午夜射精日韩| 亚洲宅男天堂在线观看无病毒| 伊人久久大香线蕉亚洲五月天| 亚洲一级特黄大片无码毛片 | 亚洲综合国产一区二区三区| 国产AV无码专区亚洲AV漫画 | 亚洲色大成网站www永久网站| 亚洲日本va一区二区三区| 亚洲中文字幕无码亚洲成A人片| 亚洲精品无码久久久久YW| 久久久久亚洲AV无码去区首| 在线观看亚洲专区| 亚洲欧洲日产国码高潮αv| 久久久久无码专区亚洲av| 亚洲亚洲人成综合网络| 午夜亚洲www湿好大| 99久久亚洲综合精品成人网| 亚洲成年人电影网站| 久久夜色精品国产噜噜亚洲a| 亚洲精品无码永久在线观看男男| 一本久久综合亚洲鲁鲁五月天| 亚洲日本在线观看视频| 亚洲精品无码久久久久去q| 亚洲午夜精品一区二区| 亚洲国产成人精品电影| 亚洲熟女综合一区二区三区| 国产av无码专区亚洲av毛片搜 | 亚洲资源最新版在线观看| 亚洲欧洲免费无码| 亚洲精品视频在线看| 亚洲国产精品特色大片观看完整版| 亚洲国产精品婷婷久久|