机智云 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升级方案:从原理到实现的全流程解析

相关推荐
清风66666620 分钟前
基于单片机与DAC0832的双路波形信号发生系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
azwsm2 小时前
电路元器件和GPIO控制器
单片机·嵌入式硬件
kebidaixu5 小时前
FreeRTOS 移植到 STM32F407VETX 记录(一)
stm32·单片机·嵌入式硬件
CSDN官方博客5 小时前
「谁说嵌入式只是调包和焊板子?」—— 2026嵌入式全栈技术征锋令
嵌入式硬件·物联网·embedding
点灯小铭6 小时前
基于单片机的数码管定时插座设计与定时开关功能实现
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
云栖梦泽6 小时前
玩转RK3506SDK
linux·嵌入式硬件
数智工坊8 小时前
机器人四大主控板系统分层选型指南:树莓派、ESP32、STM32与Arduino的能力边界与实战定位
stm32·嵌入式硬件·机器人
进击的小头8 小时前
第8篇:IGBT 从零到精通:核心原理、关键参数、选型指南与工业级应用要点
经验分享·嵌入式硬件·学习
点灯小铭9 小时前
基于单片机的多模式智能洗衣机设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
项目題供诗9 小时前
STM32-AD单通道&AD多通道(十九)
stm32·单片机·嵌入式硬件