STM32 MCU OTA升级办法1

如果mcu升级数据由耳机通过uart传给mcu ,接收完升级bin包,mcu如何校验整体升级bin是否与耳机存储的bin相同,如果不同就不能升级,如果相同就升级

思路

让耳机先通过 UART 发送固件元数据:大小、版本、整体校验(推荐 SHA‑256,次选 CRC32)。

MCU 接收完整 bin 后本地计算同种校验并与耳机提供的校验值比对。

一致则写入升级暂存区并设置 DFU 标记;不一致则丢弃,不升级。

协议建议

帧1:FW_META {magic:"FWUP", size, version, hash_type, hash_value}

帧2...N:FW_DATA 流(size 字节)

结尾:FW_END

实现示例(CRC32)

c 复制代码
#pragma once
#include <stdint.h>
#include <stdbool.h>

typedef struct {
    uint32_t size;
    uint32_t version;
    uint32_t crc32_storage; // 耳机端固件整体 CRC32
} fw_meta_t;

void fw_update_begin(const fw_meta_t *meta);
bool fw_update_feed(const uint8_t *buf, uint32_t len); // 流式喂数据,返回单帧是否成功
bool fw_update_finish(void); // 计算并比对 CRC,一致则写 Flash+标记

// 可选:改用 SHA-256
c 复制代码
#include "fw_update.h"
#include "stm32c0xx_hal.h"
#include <string.h>

#define DFU_MARK_ADDR   (0x1E000U)   // 与 boot 保持一致
#define STAGING_ADDR    (0x10000U)   // 暂存区,boot 会搬到 0x2000
#define FLASH_PAGE_SIZE (0x800U)     // 2KB/页(C0 系列)
#define FLASH_TYPE      FLASH_TYPEPROGRAM_DOUBLEWORD

static fw_meta_t s_meta;
static uint32_t s_crc = 0xFFFFFFFFu;
static uint32_t s_written = 0;
static uint32_t s_flash_addr = STAGING_ADDR;

// 简易 CRC32 (Polynomial 0xEDB88320)
static uint32_t crc32_update(uint32_t crc, const uint8_t *data, uint32_t len) {
    crc = ~crc;
    for (uint32_t i = 0; i < len; ++i) {
        crc ^= data[i];
        for (int j = 0; j < 8; ++j)
            crc = (crc >> 1) ^ (0xEDB88320u & (-(int32_t)(crc & 1)));
    }
    return ~crc;
}

static void flash_erase_staging(void) {
    FLASH_EraseInitTypeDef erase = {0};
    uint32_t page_err = 0;
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
    // 计算 56KB 暂存区需要的页数(与 boot 搬运大小一致)
    erase.TypeErase = FLASH_TYPEERASE_PAGES;
    erase.Page      = STAGING_ADDR / FLASH_PAGE_SIZE;
    erase.NbPages   = (56 * 1024) / FLASH_PAGE_SIZE;
    HAL_FLASHEx_Erase(&erase, &page_err);
    HAL_FLASH_Lock();
}

static bool flash_write_chunk(const uint8_t *buf, uint32_t len) {
    // 以 8 字节对齐写入
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
    uint32_t i = 0;
    while (i + 8 <= len) {
        uint64_t qw;
        memcpy(&qw, buf + i, 8);
        if (HAL_FLASH_Program(FLASH_TYPE, s_flash_addr, qw) != HAL_OK) {
            HAL_FLASH_Lock();
            return false;
        }
        s_flash_addr += 8;
        i += 8;
    }
    // 处理尾部不足 8 字节
    if (i < len) {
        uint8_t tail[8] = {0};
        memcpy(tail, buf + i, len - i);
        uint64_t qw;
        memcpy(&qw, tail, 8);
        if (HAL_FLASH_Program(FLASH_TYPE, s_flash_addr, qw) != HAL_OK) {
            HAL_FLASH_Lock();
            return false;
        }
        s_flash_addr += 8;
    }
    HAL_FLASH_Lock();
    return true;
}

static void write_dfu_mark(void) {
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
    // 擦除 DFU 标记页
    FLASH_EraseInitTypeDef erase = {0};
    uint32_t page_err = 0;
    erase.TypeErase = FLASH_TYPEERASE_PAGES;
    erase.Page      = DFU_MARK_ADDR / FLASH_PAGE_SIZE;
    erase.NbPages   = 1;
    HAL_FLASHEx_Erase(&erase, &page_err);
    // 写入 DFU_/OK__
    const uint32_t DFU_ = ('D' + ('F' << 8) + ('U' << 16) + ('_' << 24));
    const uint32_t OK__ = ('O' + ('K' << 8) + ('_' << 16) + ('_' << 24));
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, DFU_MARK_ADDR,     DFU_);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, DFU_MARK_ADDR + 4, OK__);
    HAL_FLASH_Lock();
}

void fw_update_begin(const fw_meta_t *meta) {
    s_meta = *meta;
    s_crc = 0xFFFFFFFFu;
    s_written = 0;
    s_flash_addr = STAGING_ADDR;
    flash_erase_staging();
}

bool fw_update_feed(const uint8_t *buf, uint32_t len) {
    // 更新 CRC 与写入暂存区
    s_crc = crc32_update(s_crc, buf, len);
    s_written += len;
    return flash_write_chunk(buf, len);
}

bool fw_update_finish(void) {
    if (s_written != s_meta.size) {
        // 大小不符,回滚
        flash_erase_staging();
        return false;
    }
    if (s_crc != s_meta.crc32_storage) {
        // 与耳机存储的整体 CRC32 不一致,不升级
        flash_erase_staging();
        return false;
    }
    // 校验通过:设置 DFU 标记,boot 重启后会搬运到 0x2000
    write_dfu_mark();
    return true;
}

使用流程

UART 收到 FW_META 后调用 fw_update_begin(&meta)。

每帧数据到来调用 fw_update_feed(buf,len)。

收完 size 字节后调用 fw_update_finish();返回 true 则允许升级(可重启进入 boot 搬运)。

若需更强校验

将 crc32_storage 换成 SHA‑256(32 字节),在耳机端与 MCU 端都计算 SHA‑256;比较一致后再升级。

相关推荐
代码游侠2 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
xuxg20055 小时前
4G 模组 AT 命令解析框架课程正式发布
stm32·嵌入式·at命令解析框架
CODECOLLECT6 小时前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen7 小时前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制
全栈游侠9 小时前
STM32F103XX 02-电源与备份寄存器
stm32·单片机·嵌入式硬件
Lsir10110_9 小时前
【Linux】中断 —— 操作系统的运行基石
linux·运维·嵌入式硬件
深圳市九鼎创展科技12 小时前
瑞芯微 RK3399 开发板 X3399 评测:高性能 ARM 平台的多面手
linux·arm开发·人工智能·单片机·嵌入式硬件·边缘计算
辰哥单片机设计12 小时前
STM32项目分享:车辆防盗报警系统
stm32·单片机·嵌入式硬件
風清掦13 小时前
【江科大STM32学习笔记-05】EXTI外部中断11
笔记·stm32·学习
小龙报13 小时前
【51单片机】从 0 到 1 玩转 51 蜂鸣器:分清有源无源,轻松驱动它奏响新年旋律
c语言·数据结构·c++·stm32·单片机·嵌入式硬件·51单片机