机智云 MCU OTA可以对MCU程序进行无线远程升级。

STM32 OTA升级全攻略:从原理到代码实现

Bootloader关键代码实现

以下是基于STM32F4的Bootloader核心代码(使用HAL库):

c 复制代码
#include "stm32f4xx_hal.h"
#include "string.h"

// 分区地址定义(根据实际芯片调整)
#define BOOT_ADDR     0x08000000
#define APP1_ADDR     0x08008000
#define APP2_ADDR     0x08028000
#define PARAM_ADDR    0x08048000
#define APP_SIZE      0x20000  // 128KB(App1和App2大小)

// 参数区结构体(存放OTA状态)
typedef struct {
    uint16_t ota_flag;       // OTA标志:0xAA55表示需要升级
    uint32_t app1_crc;       // App1区固件CRC
    uint32_t app2_crc;       // App2区固件CRC
    uint8_t  app1_version[8];// App1版本号
    uint8_t  app2_version[8];// App2版本号
} ParamTypeDef;

ParamTypeDef param;

// 读取参数区数据
void param_read(ParamTypeDef *p) {
    memcpy(p, (void*)PARAM_ADDR, sizeof(ParamTypeDef));
}

// 计算CRC32(用于固件校验)
uint32_t crc32_calc(uint32_t start_addr, uint32_t size) {
    uint32_t crc = 0xFFFFFFFF;
    uint8_t *data = (uint8_t*)start_addr;
    for (uint32_t i = 0; i < size; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            crc = (crc >> 1) ^ ((crc & 1) ? 0xEDB88320 : 0);
        }
    }
    return ~crc;
}

// 擦除Flash扇区(需根据芯片扇区表调整)
void flash_erase(uint32_t addr, uint32_t size) {
    HAL_FLASH_Unlock();
    FLASH_EraseInitTypeDef erase;
    erase.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase.Sector = get_sector(addr);  // 自定义函数:根据地址获取扇区号
    erase.NbSectors = get_sector_count(addr, size);  // 计算需要擦除的扇区数
    erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;
    uint32_t page_error;
    HAL_FLASHEx_Erase(&erase, &page_error);
    HAL_FLASH_Lock();
}

// 写入数据到Flash
void flash_write(uint32_t addr, uint8_t *data, uint32_t len) {
    HAL_FLASH_Unlock();
    for (uint32_t i = 0; i < len; i += 4) {
        uint32_t word = *(uint32_t*)(data + i);
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, word);
    }
    HAL_FLASH_Lock();
}

// 跳转到App程序
void jump_to_app(uint32_t app_addr) {
    // 检查App地址是否合法(栈顶地址需在RAM范围内)
    uint32_t stack_top = *(volatile uint32_t*)app_addr;
    if (stack_top < 0x20000000 || stack_top > 0x20000000 + RAM_SIZE) {
        return;
    }
    // 关闭中断,避免干扰App
    __disable_irq();
    // 跳转到App的复位向量(app_addr + 4是复位函数地址)
    void (*app_reset_handler)(void) = (void*)*(volatile uint32_t*)(app_addr + 4);
    // 设置主栈指针(MSP)为App的栈顶
    __set_MSP(stack_top);
    // 跳转执行
    app_reset_handler();
}

int main(void) {
    HAL_Init();
    SystemClock_Config();  // 配置系统时钟(如168MHz)
    uart_init(115200);     // 初始化UART(用于调试或接收固件)

    // 1. 读取参数区
    param_read(¤tparam);

    // 2. 检查是否需要升级(ota_flag == 0xAA55)
    if (param.ota_flag == 0xAA55) {
        // 3. 校验App2区新固件的CRC
        uint32_t calc_crc = crc32_calc(APP2_ADDR, APP_SIZE);
        if (calc_crc == param.app2_crc) {
            // 4. 擦除App1区,将App2区的新固件复制到App1区
            flash_erase(APP1_ADDR, APP_SIZE);
            flash_write(APP1_ADDR, (uint8_t*)APP2_ADDR, APP_SIZE);
            // 5. 更新参数区(清除OTA标志,更新App1版本和CRC)
            param.ota_flag = 0x0000;
            memcpy(param.app1_version, param.app2_version, 8);
            param.app1_crc = param.app2_crc;
            flash_erase(PARAM_ADDR, sizeof(ParamTypeDef));
            flash_write(PARAM_ADDR, (uint8_t*)¤tparam, sizeof(ParamTypeDef));
        } else {
            // 校验失败,保留旧固件
            param.ota_flag = 0x0000;
            flash_write(PARAM_ADDR, (uint8_t*)¤tparam, sizeof(ParamTypeDef));
        }
    }

    // 6. 跳转到App1区运行
    jump_to_app(APP1_ADDR);

    // 若跳转失败,进入死循环
    while (1) {}
}

App关键代码示例(接收固件并设置标志)

c 复制代码
// App中接收新固件并写入App2区
void ota_receive_firmware() {
    uint8_t firmware_buffer[1024];  // 接收缓冲区
    uint32_t firmware_size = 0;     // 已接收固件大小
    uint32_t app2_addr = APP2_ADDR;

    // 1. 擦除App2区
    flash_erase(APP2_ADDR, APP_SIZE);

    // 2. 循环接收固件数据(通过UART/ETH等)
    while (firmware_size < APP_SIZE) {
        uint16_t recv_len = uart_receive(firmware_buffer, 1024);  // 实际项目需实现超时处理
        if (recv_len == 0) break;
        // 写入Flash
        flash_write(app2_addr, firmware_buffer, recv_len);
        app2_addr += recv_len;
        firmware_size += recv_len;
    }

    // 3. 计算新固件CRC并更新参数区
    ParamTypeDef param;
    param_read(¤tparam);
    param.app2_crc = crc32_calc(APP2_ADDR, firmware_size);
    memcpy(param.app2_version, "V2.0.0", 8);  // 新固件版本号
    param.ota_flag = 0xAA55;  // 设置OTA标志
    flash_erase(PARAM_ADDR, sizeof(ParamTypeDef));
    flash_write(PARAM_ADDR, (uint8_t*)¤tparam, sizeof(ParamTypeDef));

    // 4. 重启系统,触发Bootloader升级
    NVIC_SystemReset();
}

MCU OTA可以对MCU程序进行无线远程升级

机智云

STM32 OTA升级方案:从原理到实现的全流程解析

相关推荐
A9better3 小时前
嵌入式开发学习日志52——二值与计数信号量
单片机·嵌入式硬件·学习
日更嵌入式的打工仔5 小时前
(实用向)中断服务程序(ISR)的优化方向
笔记·单片机
想放学的刺客5 小时前
单片机嵌入式试题(第25)嵌入式系统可靠性设计与外设驱动异常处理
stm32·单片机·嵌入式硬件·mcu·物联网
wotaifuzao6 小时前
STM32+FreeRTOS 长期可维护架构设计(事件驱动篇)-- 告别“屎山”代码
c语言·stm32·嵌入式硬件·freertos·状态机·事件驱动·嵌入式架构
淘晶驰AK6 小时前
大学如何自学嵌入式开发?
单片机·嵌入式硬件
yantaohk6 小时前
【2025亲测】中兴B860AV3.2M完美刷机包ATV版本安卓9-解决1G运存BUG,开ADB已ROOT
android·嵌入式硬件·adb·云计算
一路往蓝-Anbo6 小时前
第 1 篇:对象池模式 (Object Pool) —— 裸机下的动态内存革命
jvm·数据库·stm32·单片机·嵌入式硬件·网络协议·tcp/ip
飞凌嵌入式7 小时前
1块集成了4核Cortex-A7高性能CPU、1颗RISC-V MCU、多种高速总线、还兼容树莓派的T153低成本开发板
linux·arm开发·嵌入式硬件·risc-v
大神与小汪7 小时前
STM32WB55蓝牙广播数据
stm32·单片机·嵌入式硬件