MCU -- OTA (概念)

发布时间 2023-07-26 10:40:21作者: 一条名叫西西的狗

一、A/B面OTA分类

二、A/B面OTA流程

三、A/B面OTA实现

四、A/B面OTA优缺点

 

 

一、A/B面OTA分类

  1. 双系统切换方案:使用两个独立的MCU芯片,每个芯片负责一个面(A面和B面)的固件运行。在升级时,先将待升级的固件下载到未运行的那一面对应的芯片上,并执行切换操作。这种方案需要双份硬件资源,但实现简单可靠;
  2. 双分区切换方案:使用单个MCU芯片,将存储空间划分为两个分区,分别用于存放A面和B面的固件。在升级时,将待升级的固件下载到未运行的分区,并在重启后切换分区。这种方案相对于双系统切换方案节省硬件成本,但需要较高的存储器和固件管理能力;
  3. 双镜像切换方案:使用单个MCU芯片,将存储空间划分为两个独立的镜像,分别用于存放A面和B面的固件。在升级时,将待升级的固件下载到未运行的镜像中,并通过引导程序切换到新的镜像。这种方案可以在固件运行期间完成切换,具有较高的实时性和可靠性;
  4. 双流程切换方案:使用单个MCU芯片,将固件设计为两个独立的流程,分别对应A面和B面。在升级时,将待升级的固件下载到未运行的流程中,并通过切换信号或事件触发切换到新的流程。这种方案适用于需要更细粒度控制和灵活切换的场景;
  5. 双引导程序切换方案:将存储空间划分为两个部分,分别用于存放A面和B面的引导程序。在升级时,将待升级的固件下载到未运行的引导程序中,并通过重启或软复位操作切换到新的引导程序。这种方案具有较低的系统开销和实现难度。

二、A/B面OTA流程

  1. 双系统切换方案
    开始 -> 检查当前运行的系统
          └─> 如果是A面系统,则下载B面固件
          └─> 如果是B面系统,则下载A面固件
    下载固件 -> 校验固件完整性和合法性
             └─> 如果校验失败,则重新下载固件
             └─> 如果校验通过,则保存固件到合适的存储介质
    切换系统 -> 执行系统切换操作
    确认切换 -> 验证切换是否成功
    结束

    在这个流程中,首先检查当前运行的系统是A面还是B面。然后根据当前的系统,下载对应面的固件。下载完成后,对固件进行校验,确保其完整性和合法性。如果校验失败,则重新进行固件下载。校验通过后,将固件保存到适当的存储介质中(例如Flash存储器)。接下来执行系统切换操作,将新固件加载到相应的系统中。最后确认切换是否成功。若切换成功,流程结束;若切换失败,则需要重新进行固件下载和切换操作。

  2. 双分区切换方案
    开始 -> 检查当前运行的分区
          └─> 如果当前运行的是A面分区,则下载B面固件到B面分区
          └─> 如果当前运行的是B面分区,则下载A面固件到A面分区
    下载固件 -> 校验固件完整性和合法性
             └─> 如果校验失败,则重新下载固件
             └─> 如果校验通过,则更新对应分区的固件
    切换分区 -> 设置下一次启动时要运行的分区
    确认切换 -> 验证切换是否成功
    结束

    在这个流程中,首先检查当前运行的分区是A面还是B面。然后根据当前的分区,下载对应面的固件到相应的分区中。下载完成后,对固件进行校验,确保其完整性和合法性。如果校验失败,则重新进行固件下载。校验通过后,更新对应分区的固件。接下来,设置下一次启动时要运行的分区。最后确认切换是否成功。若切换成功,流程结束;若切换失败,则需要重新进行固件下载和切换操作。

  3. 双镜像切换方案
    开始 -> 检查当前运行的镜像
          └─> 如果当前运行的是A面镜像,则下载B面固件到B面镜像
          └─> 如果当前运行的是B面镜像,则下载A面固件到A面镜像
    下载固件 -> 校验固件完整性和合法性
             └─> 如果校验失败,则重新下载固件
             └─> 如果校验通过,则在合适的存储介质中保存固件
    切换镜像 -> 设置下一次启动时要运行的镜像
    确认切换 -> 验证切换是否成功
    结束

    在这个流程中,首先检查当前运行的镜像是A面还是B面。然后根据当前的镜像,下载对应面的固件到相应的镜像中。下载完成后,对固件进行校验,确保其完整性和合法性。如果校验失败,则重新进行固件下载。校验通过后,将固件保存到适当的存储介质中(例如Flash存储器)。接下来,设置下一次启动时要运行的镜像。最后确认切换是否成功。若切换成功,流程结束;若切换失败,则需要重新进行固件下载和切换操作。

  4. 双流程切换方案
    开始 -> 检查当前运行的流程
          └─> 如果当前运行的是流程A,则下载流程B相关的代码和资源
          └─> 如果当前运行的是流程B,则下载流程A相关的代码和资源
    下载代码和资源 -> 校验代码和资源完整性和合法性
                  └─> 如果校验失败,则重新下载代码和资源
                  └─> 如果校验通过,则更新对应流程的代码和资源
    切换流程 -> 设置下一次启动时要运行的流程
    确认切换 -> 验证切换是否成功
    结束

    在这个流程中,首先检查当前运行的流程是A还是B。然后根据当前的流程,下载对应流程的代码和资源。下载完成后,对代码和资源进行校验,确保其完整性和合法性。如果校验失败,则重新进行代码和资源下载。校验通过后,更新对应流程的代码和资源。接下来,设置下一次启动时要运行的流程。最后确认切换是否成功。若切换成功,流程结束;若切换失败,则需要重新进行代码和资源下载和切换操作。

  5. 双引导程序切换方案
    开始 -> 检查当前运行的引导程序
          └─> 如果当前运行的是引导程序A,则下载引导程序B的映像文件
          └─> 如果当前运行的是引导程序B,则下载引导程序A的映像文件
    下载映像文件 -> 校验映像文件完整性和合法性
                 └─> 如果校验失败,则重新下载映像文件
                 └─> 如果校验通过,则在合适的存储介质中保存映像文件
    切换引导程序 -> 设置下一次启动时要运行的引导程序
    确认切换 -> 验证切换是否成功
    结束

    在这个流程中,首先检查当前运行的引导程序是A还是B。然后根据当前的引导程序,下载对应的引导程序的映像文件。下载完成后,对映像文件进行校验,确保其完整性和合法性。如果校验失败,则重新进行映像文件下载。校验通过后,将映像文件保存到适当的存储介质中(例如硬盘或闪存)。接下来,设置下一次启动时要运行的引导程序。最后确认切换是否成功。若切换成功,流程结束;若切换失败,则需要重新进行映像文件下载和切换操作。

三、A/B面OTA实现

  1. 双系统切换方案
    // A面MCU代码
    
    // 定义A面固件的功能函数
    void executeFirmwareA() {
        // TODO: 实现A面固件的功能逻辑
    }
    
    int main() {
        // 检测是否需要升级A面固件
        if (isFirmwareUpdateNeededForA()) {
            // 下载待升级的固件到A面MCU
            downloadFirmwareForA();
            
            // 执行固件切换操作,切换到A面固件
            switchToFirmwareA();
        }
        
        // 执行A面固件的功能
        executeFirmwareA();
        
        // 正常运行其他逻辑...
    }
    
    // B面MCU代码
    
    // 定义B面固件的功能函数
    void executeFirmwareB() {
        // TODO: 实现B面固件的功能逻辑
    }
    
    int main() {
        // 检测是否需要升级B面固件
        if (isFirmwareUpdateNeededForB()) {
            // 下载待升级的固件到B面MCU
            downloadFirmwareForB();
            
            // 执行固件切换操作,切换到B面固件
            switchToFirmwareB();
        }
        
        // 执行B面固件的功能
        executeFirmwareB();
        
        // 正常运行其他逻辑...
    }

    在实际应用中,需要根据具体的MCU芯片和开发环境进行相应的配置和编程。代码示例中的"isFirmwareUpdateNeededForA()"、"downloadFirmwareForA()"、"switchToFirmwareA()"、"isFirmwareUpdateNeededForB()"、"downloadFirmwareForB()"、"switchToFirmwareB()"等函数需要根据具体需求实现,用于检测是否需要升级固件、下载固件到MCU芯片并执行固件切换操作。同时,根据实际需求,还可以添加其他必要的功能和逻辑代码。

  2. 双分区切换方案
    // 定义A面固件的功能函数
    void executeFirmwareA() {
        // TODO: 实现A面固件的功能逻辑
    }
    
    // 定义B面固件的功能函数
    void executeFirmwareB() {
        // TODO: 实现B面固件的功能逻辑
    }
    
    int main() {
        // 检测当前运行的固件分区
        int activePartition = getActivePartition();
        
        // 根据当前分区选择执行对应的固件
        if (activePartition == 0) {
            executeFirmwareA();
        } else {
            executeFirmwareB();
        }
        
        // 正常运行其他逻辑...
    }
    
    // 获取当前活动的固件分区
    int getActivePartition() {
        // TODO: 从存储器中读取当前活动的分区标识
        
        // 假设分区标识为0或1
        return readPartitionFlag();
    }

    在实际应用中,需要根据具体的MCU芯片和存储器配置来实现双分区切换方案。代码示例中的"executeFirmwareA()"和"executeFirmwareB()"分别表示A面和B面固件的功能函数,可以根据实际需求进行相应的实现。函数"getActivePartition()"用于获取当前活动的固件分区,可以根据存储器中的分区标识进行判断。

    另外,在固件升级时,需要注意更新非活动分区的固件,并在合适的时机切换活动的固件分区。具体的固件管理、切换和升级逻辑需要根据实际需求进行完善和调试。

  3. 双镜像切换方案
    // 定义镜像A的功能函数
    void executeImageA() {
        // TODO: 实现镜像A的功能逻辑
    }
    
    // 定义镜像B的功能函数
    void executeImageB() {
        // TODO: 实现镜像B的功能逻辑
    }
    
    int main() {
        // 检测当前运行的镜像
        int activeImage = getActiveImage();
        
        // 根据当前镜像选择执行对应的功能
        if (activeImage == 0) {
            executeImageA();
        } else {
            executeImageB();
        }
        
        // 正常运行其他逻辑...
    }
    
    // 获取当前活动的镜像
    int getActiveImage() {
        // TODO: 从存储器中读取当前活动的镜像标识
        
        // 假设镜像标识为0或1
        return readImageFlag();
    }

    在实际应用中,需要根据具体的MCU芯片和存储器配置来实现双镜像切换方案。代码示例中的"executeImageA()"和"executeImageB()"分别表示镜像A和镜像B的功能函数,可以根据实际需求进行相应的实现。函数"getActiveImage()"用于获取当前活动的镜像,可以根据存储器中的镜像标识进行判断。

    同时,在镜像更新时,需要将新镜像加载到存储器中,并在合适的时机切换活动的镜像。具体的镜像管理、切换和更新逻辑需要根据实际需求进行完善和调试。

  4. 双流程切换方案
    // 定义流程A的函数
    void executeProcessA() {
        // TODO: 实现流程A的功能逻辑
    }
    
    // 定义流程B的函数
    void executeProcessB() {
        // TODO: 实现流程B的功能逻辑
    }
    
    int main() {
        // 检测当前运行的流程
        int activeProcess = getActiveProcess();
        
        // 根据当前流程选择执行对应的流程
        if (activeProcess == 0) {
            executeProcessA();
        } else {
            executeProcessB();
        }
        
        // 正常运行其他逻辑...
    }
    
    // 获取当前活动的流程
    int getActiveProcess() {
        // TODO: 从存储器中读取当前活动的流程标识
        
        // 假设流程标识为0或1
        return readProcessFlag();
    }

    在实际应用中,需要根据具体的MCU芯片和存储器配置来实现双流程切换方案。代码示例中的"executeProcessA()"和"executeProcessB()"分别表示流程A和流程B的功能函数,可以根据实际需求进行相应的实现。函数"getActiveProcess()"用于获取当前活动的流程,可以根据存储器中的流程标识进行判断。

    同时,在流程切换时,需要判断当前流程的完成状态,并在合适的时机切换到下一个流程。具体的流程管理和切换逻辑需要根据实际需求进行完善和调试。

  5. 双引导程序切换方案
    // 定义引导程序A的函数
    void executeBootloaderA() {
        // TODO: 实现引导程序A的功能逻辑
    }
    
    // 定义引导程序B的函数
    void executeBootloaderB() {
        // TODO: 实现引导程序B的功能逻辑
    }
    
    int main() {
        // 检测当前运行的引导程序
        int activeBootloader = getActiveBootloader();
        
        // 根据当前引导程序选择执行对应的引导逻辑
        if (activeBootloader == 0) {
            executeBootloaderA();
        } else {
            executeBootloaderB();
        }
        
        // 正常运行其他逻辑...
    }
    
    // 获取当前活动的引导程序
    int getActiveBootloader() {
        // TODO: 从存储器中读取当前活动的引导程序标识
        
        // 假设引导程序标识为0或1
        return readBootloaderFlag();
    }

    在实际应用中,需要根据具体的MCU芯片和存储器配置来实现双引导程序切换方案。代码示例中的"executeBootloaderA()"和"executeBootloaderB()"分别表示引导程序A和引导程序B的功能函数,可以根据实际需求进行相应的实现。函数"getActiveBootloader()"用于获取当前活动的引导程序,可以根据存储器中的引导程序标识进行判断。

    同时,在引导程序切换时,需要根据特定条件或者外部触发来切换到另一个引导程序,例如按下某个特定的按键、通过通信接口接收到特定指令等。具体的引导程序管理和切换逻辑需要根据实际需求进行完善和调试。

四、A/B面OTA优缺点

OTA分类 优点 缺点
双系统切换方案 实现简单可靠,硬件资源独立,故障隔离能力强 需要双份硬件资源,增加了成本和体积
双分区切换方案 硬件成本相对较低,利用单个MCU的存储空间进行分区划分 固件管理复杂,对存储器需求较高
双镜像切换方案 切换过程实时性较好,具备一定的容错能力 存储空间需求较高,需要额外的引导程序来管理镜像切换
双流程切换方案 可以实现较细粒度的控制和灵活的切换 设计和开发难度较高,对MCU的资源要求较高
双引导程序切换方案 系统开销较低,切换效率较高 需要引导程序支持切换操作,对MCU的启动流程有一定要求