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

相关推荐
Lester_110121 小时前
STM32 高级定时器PWM互补输出模式--如果没有死区,突然关闭PWM有产生瞬间导通的可能吗
stm32·单片机·嵌入式硬件·嵌入式软件
小李独爱秋1 天前
“bootmgr is compressed”错误:根源、笔记本与台式机差异化解决方案深度指南
运维·stm32·单片机·嵌入式硬件·文件系统·电脑故障
梁洪飞1 天前
内核的schedule和SMP多核处理器启动协议
linux·arm开发·嵌入式硬件·arm
进击的小头1 天前
实战案例:51单片机低功耗场景下的简易滤波实现
c语言·单片机·算法·51单片机
宵时待雨1 天前
STM32笔记归纳8:时钟
笔记·stm32·单片机·嵌入式硬件
JJRainbow1 天前
SN75176 芯片设计RS-232 转 RS-485 通信模块设计原理图
stm32·单片机·嵌入式硬件·fpga开发·硬件工程
花月mmc1 天前
CanMV K230 波形识别——整体部署(4)
人工智能·python·嵌入式硬件·深度学习·信号处理
宁静致远20211 天前
STM32模拟IIC读取PCF8563
stm32·单片机·嵌入式硬件
三佛科技-134163842121 天前
宠物洗澡打泡机方案,宠物泡泡机MCU方案开发设计分享
单片机·嵌入式硬件·物联网·智能家居·pcb工艺·宠物
芯岭技术1 天前
低成本315/433M接收芯片 XL420 SOP8封装,支持 1527 等常见 OOK编码
单片机·嵌入式硬件