久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2580|回復: 0
打印 上一主題 下一主題
收起左側

基于boot+雙app區間的方案

[復制鏈接]
跳轉到指定樓層
樓主
ID:151861 發表于 2022-8-22 16:13 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
# 基于boot+雙app區間的方案

前言
                  在普通的項目中,MCU的內存稍微大一點的項目,在項目需要留出升級功能的時候,都會選擇boot+app+app緩存區這種方案,這樣讓MCU內存小的情況下也能使用boot+app+app緩存區(外部Flash)等任何存儲方式,原理簡單可行也易于移植,但此方案有個缺點是在正常接收完app固件后還需要復位到boot中將接收到的緩存固件重新復制到app區,這使得整個升級方案耗時比較長時間,并且上位機或者手機app升級控制臺不好設計(需要為每一個不同大小的升級固件設置一個倒計時時間),此文將采用boot+雙app區間方案,不再需要進入boot中去復制緩存固件到app區,并且固件經過更深入的設計可以實現固件秒回滾的功能。

一、分區
將MCU Flash分區,分成boot區、appC區、appD區三個區間,其中boot區大小為2K左右,本文設置為2K足夠了(Program Size: Code=1868 RO-data=336 RW-data=24 ZI-data=2000),appC和appD占用剩余的Flash,如果Flash有其他功能使用時相應減除掉,本文采用STM32F103CBT6,容量為128K,除去2Kboot和2K升級配置參數剩余124K,預留4K用做其他,appC/appD分區60K大小,

二、boot設計
    1 boot功能負責根據2K升級配置參數來啟動相應的分區app,升級配置參數結構體包含:
    typedef struct _APP_FIRMWARE_{
  uint32_t Flag;//標志
  uint32_t State;//當前升級狀態
  uint32_t Packet;//包數
  uint32_t Version;//版本號
  uint32_t FileSize;//文件大小
        uint32_t CheckSumC;//校驗和
  uint32_t CheckSumD;//校驗和
  uint8_t BuilTime[4];//文件編譯時間
  uint8_t FileName[32];//文件名稱
       
        uint32_t PartitionNumber;//分區號
       
        uint32_t FlashAddrBase;//Flash首地址
  uint32_t BootAddrBase;//Boot首地址
  uint32_t AppAddrBase;//App首地址
  uint32_t AppBakAddrBase;//AppBak首地址

  uint32_t BootSize;//Boot空間大小
  uint32_t AppSize;//App空間大小
  uint32_t AppBakSize;//AppBak空間大小

        uint32_t RunState;//當前運行狀態
       
  uint32_t ConfigAddrBase;//Config首地址
  uint32_t ConfigSize;//Config空間大小
}APP_FIRMWARE;

2 其中uint32_t PartitionNumber;//分區號即為當前運行的分區號,boot只需要讀取Flash內容后再判斷此變量即可直接啟動分區app固件。

boot啟動函數如下,在main函數中調用即可:

void Bootstrap(void)
{
        uint32_t appAddress = AppFirmUnion.AppFirmware.AppAddrBase;
        uint8_t  RunState = 0;
       
        LoadFirmwareParameter();
       
        for(;;)
        {
          if(AppFirmUnion.AppFirmware.PartitionNumber == UPGRADE_NUMBER_C)
                {
                        appAddress = AppFirmUnion.AppFirmware.AppAddrBase;
                        RunState |= 0x01 << 0;
                }
                if(AppFirmUnion.AppFirmware.PartitionNumber == UPGRADE_NUMBER_D)
                {
                        appAddress = AppFirmUnion.AppFirmware.AppBakAddrBase;
                        RunState |= 0x01 << 1;
                }
                if(SetupApp(appAddress))
                {
                        //兩個都運行錯誤
                        if((RunState & (0x01 << 2)) && (RunState & (0x01 << 3)))
                        {
                          while(1);
                        }
                        //A運行錯誤,調到B
                        if(RunState & (0x01 << 0))
                        {
                          RunState |= 0x01 << 2;
                                AppFirmUnion.AppFirmware.PartitionNumber = UPGRADE_NUMBER_C;
                        }
                        //B運行錯誤,調到A
                        if(RunState & (0x01 << 1))
                        {
                          RunState |= 0x01 << 3;
                                AppFirmUnion.AppFirmware.PartitionNumber = UPGRADE_NUMBER_D;
                        }
                }
        }
}

3 啟動分區app函數如下:
#define __IO    volatile

pFunction Jump_To_Application;
uint32_t JumpAddress;


uint8_t SetupApp(uint32_t appAddress)
{
  if(((*(__IO uint32_t *)(0x08000000 + appAddress+4))&0xFF000000) == 0x08000000)//判斷是否為0X08XXXXXX.
        {
                JumpAddress = *(__IO uint32_t*) (0x08000000 + appAddress + 4);         //===Jump to user application
                Jump_To_Application = (pFunction) JumpAddress;
                Mcu_Misc_Set_MSP(*(__IO uint32_t*)(0x08000000 + appAddress));                  //===Initialize user application's Stack Pointer        
                __disable_irq();                      //關閉中斷                               
               
                if (appAddress == CHIP_APP_BAK_FLASH_ADDRESS*1024)
                {
                  Mcu_Misc_SetVTOR(CHIP_APP_BAK_FLASH_ADDRESS*1024);
                }
                else
                {
                  Mcu_Misc_SetVTOR(CHIP_APP_FLASH_ADDRESS*1024);
                }
                Jump_To_Application();                                                //===Jump to application
        }
       
        return 1;
}

boot因為功能簡單,無需太多代碼,一般占用內存會很少,這使得內存使用率極大提高。

4 app設計
    首先上位機發送升級的一些信息(固件的大小,包數,編譯時間、兩個分區的固件校驗碼等,注意此處給的是單個app的信息,合并app時候會說明此處)給MCU,MCU收到后回復請求下一包的命令并帶有當前的運行區間信息,此時上位機可以根據請求的包數和當前運行區間的信息讀取前半部分或者后半部分的數據(固件), app在需要升級的時候將數據(固件)接收后,先判斷當前運行的區間,如果是運行在C區間,則將D區間擦除,然后不斷的寫入D區間,當寫入數據(固件)完成后將接收到的固件校驗和和接收數據計算出來的校驗和比對一樣就激活另一個區間,然后軟復位即可。
......
case PROTOCOL_COMMAND_UPGRADE_START://開始升級
    StartUpgrade(&data[1],&length);

          packets = 0;
                T1Protocol_Obj.length = 6;
    T1Protocol_Obj.data[0] = PROTOCOL_COMMAND_UPGRADE_WRITE;
                T1Protocol_Obj.data[1] = packets >> 0;
    T1Protocol_Obj.data[2] = packets >> 8;
    T1Protocol_Obj.data[3] = packets >> 16;
    T1Protocol_Obj.data[4] = packets >> 24;
                T1Protocol_Obj.data[5] = AppFirmUnion->AppFirmware.PartitionNumber;
    vTaskDelay(200);
                break;
        case PROTOCOL_COMMAND_UPGRADE_WRITE://寫入數據(固件)
                packets  = data[4];packets <<= 8;
                packets |= data[3];packets <<= 8;
                packets |= data[2];packets <<= 8;
                packets |= data[1];
               
    LogE("Source packets:%d\r\n",packets);

    WriteUpgradeData(packets,&data[5],length-5);
                {
                        packets ++;
                        T1Protocol_Obj.length = 6;
                        T1Protocol_Obj.data[1] = packets >> 0;
                        T1Protocol_Obj.data[2] = packets >> 8;
                        T1Protocol_Obj.data[3] = packets >> 16;
                        T1Protocol_Obj.data[4] = packets >> 24;
      T1Protocol_Obj.data[5] = AppFirmUnion->AppFirmware.PartitionNumber;
                }
                Tpackets = packets;
                if(xUpgradeTimerUser == NULL)
                {
                        xUpgradeTimerUser = xTimerCreate(
                                                                                                        "Timer's name",
                                                                                                        2000,
                                                                                                        pdTRUE,
                                                                                                        (void *)0,
                                                                                                        vUpgradeTimerCallback);
                }
                xTimerStart(xUpgradeTimerUser,0);
                ReTpackets = 20;
                break;
case PROTOCOL_COMMAND_UPGRADE_ACTIVATE://激活分區
                T1Protocol_Obj.length = 2;
                T1Protocol_Obj.data[1] = 1;
          
          UpgradeActivate((UPGRADE_NUMBER)data[2]);
          LogE("激活升級分區:%d\r\n",data[2]);
          xTimerStop(xUpgradeTimerUser,0);
          vTaskDelay(2000);
    Mcu_Misc_Soft_Reset();
                break;
......

激活分區函數:
void UpgradeActivate(UPGRADE_NUMBER partition)
{
        AppFirmUnion.AppFirmware.PartitionNumber = partition;
        Drive_Memory_WriteDatas(AppFirmUnion.AppFirmware.ConfigAddrBase,(uint32_t *)&AppFirmUnion.UData,sizeof(APP_FIRMWARE_UNION)/4);//===寫回固件狀態
}

寫入數據(固件)函數:
int WriteUpgradeData(uint32_t packets,uint8_t dat[],uint16_t length)
{
        static uint32_t Address=0,CheckSum = 0,LastPacket=0;
        APP_FIRMWARE_UNION *AppFirmUnion = GetAppFirmUnion();

        //LogI("packets:[%d]\r\n",packets);

  if(packets == LastPacket && packets != 0)
        {
                //LogE("[重傳包]\r\n");
                return -1;
        }
        if(packets >= AppFirmUnion->AppFirmware.Packet && packets != 0)
        {
                //LogE("[包大于設定值]\r\n");
                return -2;
        }
        if(packets == 0)
        {
                CheckSum = 0;
                SetUpDataState(UPDATE_STATE_UPGRADE_UPDATAING);

                if(AppFirmUnion->AppFirmware.PartitionNumber == UPGRADE_NUMBER_C)
                {
                        Drive_Memory_ErasePages(AppFirmUnion->AppFirmware.AppBakAddrBase,
                                                                                                                AppFirmUnion->AppFirmware.AppBakSize/DRIVE_MEMORY_SECTOR_SIZE);
                }
                else
                {
                        Drive_Memory_ErasePages(AppFirmUnion->AppFirmware.AppAddrBase,
                                                                                                                AppFirmUnion->AppFirmware.AppSize/DRIVE_MEMORY_SECTOR_SIZE);
                }
        }
        //計算校驗值
        for(uint32_t i = 0;i < length;i ++)
        {
                CheckSum += dat[i];
        }
        //計算包所在的內存地址
        if(AppFirmUnion->AppFirmware.PartitionNumber == UPGRADE_NUMBER_C)
        {
                Address = AppFirmUnion->AppFirmware.AppBakAddrBase + packets * 1024;
        }
        else
        {
                Address = AppFirmUnion->AppFirmware.AppAddrBase + packets * 1024;
        }
        //LogI("Address:[%08x] CheckSum:[%08x]\r\n",Address,CheckSum);
        Drive_Memory_WritePage(Address,(uint32_t *)&dat[0],length/4);       
       
        if(AppFirmUnion->AppFirmware.PartitionNumber == UPGRADE_NUMBER_C)
        {
                if((packets == (AppFirmUnion->AppFirmware.Packet - 1)) &&
                         (AppFirmUnion->AppFirmware.CheckSumD != CheckSum))
                {
                        //LogE("CheckSum Err:%08x %08x\r\n",AppFirmUnion->AppFirmware.CheckSum,CheckSum);
                        SetUpDataState(UPDATE_STATE_UPGRADE_CHECKSUM_ERROR);
                        return -3;
                }
        }
        else
        {
                if((packets == (AppFirmUnion->AppFirmware.Packet - 1)) &&
                         (AppFirmUnion->AppFirmware.CheckSumC != CheckSum))
                {
                        //LogE("CheckSum Err:%08x %08x\r\n",AppFirmUnion->AppFirmware.CheckSum,CheckSum);
                        SetUpDataState(UPDATE_STATE_UPGRADE_CHECKSUM_ERROR);
                        return -3;
                }
        }


        LastPacket = packets;
       
  if(packets == (AppFirmUnion->AppFirmware.Packet - 1))
        {
                //LogA("Upgrade Complete\r\n");
                SetUpDataState(UPDATE_STATE_UPGRADE_OVER);
                return 1;
        }
       
        return 0;
}

    此方案在升級的過程中上位機都是在第一次開始升級作為主動方,其他情況都視為MCU作為主動方,當傳輸數據圖中有中斷傳輸流程時,MCU會不斷的重置定時器,以便定時器超時后會再次主動向上位機請求當前的包數來完成重發機制。
    關于升級的文件:此方案需要將appC和appD合并后制作成雙app升級的bin固件,可以手動將appD放在appC的末尾達到合并的目的或者參考以下附件腳本并如何設置次腳本可參考“基于KEIL 的合并boot.bin&app.bin的腳本文件”一文中介紹的原理來實現自動編譯并合并bin文件。

附件:MergeTool.Bat.zip下載后將名稱重命名為MergeTool.Bat即可


注意:此方案的appC和appD不同點只是啟動的地址和映射的中斷不一樣,其他的地方都是一模一樣。

總結
    此方案不同于一般升級方案,不需要再次到boo中復制文件的等待時間,并且經過進一步的設計可以實現固件回滾,上位機傳輸完成固件后可以及時與新的app通訊并獲取到升級后的信息,給實際用戶體驗到傳輸完成既可查看升級的結果。




MergeTool.Bat.zip

4.38 KB, 下載次數: 22, 下載積分: 黑幣 -5

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏4 分享淘帖 頂1 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产激情在线观看视频 | h视频在线免费看 | 韩国精品一区二区三区 | 91秦先生艺校小琴 | 最新黄色毛片 | 国内精品视频在线观看 | 欧美成视频 | 欧美日韩成人在线 | 亚洲一区二区av | 午夜在线| 亚洲午夜视频在线观看 | 亚洲一一在线 | 国产精品免费在线 | 亚洲成人午夜电影 | 国产欧美日韩精品在线观看 | 亚洲一二视频 | 中文字幕一区二区三区四区五区 | 黄色一级大片在线免费看产 | 在线视频中文字幕 | 一区二区成人 | 亚洲精品一 | 99re视频这里只有精品 | 欧美日韩一 | 亚洲欧美日韩精品久久亚洲区 | 国产一区二| 国产激情91久久精品导航 | 国产精品欧美一区二区 | 日本不卡一区二区三区在线观看 | 午夜精品视频 | 久久大全 | 亚洲精品欧美一区二区三区 | 日韩欧美精品 | 国产一级黄色网 | 久久精品视频在线播放 | 日韩人体视频 | 九九热精品视频 | 国产精品夜间视频香蕉 | 日韩一区二区在线视频 | 国产精品99久久久久久动医院 | 国产欧美一区二区精品忘忧草 | 在线不卡 |