STM32-内部Flash写入原理与应用详解

STM32 内部 Flash 写入原理与应用详解

一、STM32 Flash 存储架构深入解析

1. 物理结构与电气特性

  • NAND vs NOR Flash:STM32 采用 NOR Flash,具有随机访问能力(无需先擦除再写入),但写入速度较慢
  • 存储单元结构
    • 基本存储单元为浮动栅极晶体管 (Floating Gate Transistor)
    • 编程过程通过热电子注入使浮动栅积累电荷(表示逻辑 0)
    • 擦除过程通过隧穿效应释放电荷(恢复逻辑 1)
  • 电压要求
    • 读取:1.8V-3.6V(视具体型号而定)
    • 编程 / 擦除:内部升压电路产生约 12V 高压(无需外部高压源)
  • 温度影响:典型工作温度范围 - 40°C 至 + 125°C,极端温度会影响编程 / 擦除效率

2. 存储器组织方式

  • 块 (block) 与扇区 (sector) 划分
    • 以 STM32F407 为例:
      • 主存储器分为 12 个扇区:
        • 扇区 0-3:16KB / 扇区
        • 扇区 4:64KB
        • 扇区 5-11:128KB / 扇区
      • 系统存储器:用于存储启动代码
      • OTP 区域:一次性可编程区域,512 字节
  • 地址映射表
    • 主存储器起始地址:0x0800 0000
    • 系统存储器起始地址:0x1FFF F000
    • OTP 区域地址:0x1FFF 7800-0x1FFF 7A00

3. 访问权限与保护机制

  • 读保护级别
    • Level 0:无保护
    • Level 1:读保护(可通过擦除解除)
    • Level 2:深度读保护(不可恢复)
  • 写保护机制
    • 扇区写保护:通过选项字节 (Option Bytes) 配置
    • 写保护寄存器 (WRPR):控制哪些扇区可写
  • 编程限制
    • 只能将 1 变为 0,不能直接将 0 变为 1
    • 必须按扇区擦除(将所有位变为 1)后才能重新编程

二、Flash 编程核心原理

1. 编程算法详解

  • 编程电压生成
    • 内部电荷泵电路将 VDD 升压至约 12V
    • 升压时间约 100μs(典型值)
  • 编程时序控制
    • 采用增量脉冲编程算法 (IPP)
    • 典型编程脉冲宽度:20μs-100μs
    • 每次编程后验证数据,未达到目标值则增加脉冲宽度
  • 数据验证机制
    • 每写入一个字后自动读取验证
    • 允许位翻转容差:±1 位(工业级标准)

2. 擦除过程分析

  • 扇区擦除时序
    • 典型擦除时间:10ms-100ms(取决于扇区大小)
    • 擦除电压:约 12V
  • 擦除验证
    • 擦除后所有位应变为 0xFF
    • 若验证失败,自动执行二次擦除

3. Flash 控制器寄存器详解

  • 主要寄存器
    • FLASH_ACR:访问控制寄存器
      • LATENCY 位:配置等待周期数(与 CPU 频率相关)
      • PRFTEN 位:预取缓冲区使能
      • ICEN 位:指令缓存使能
      • DCEN 位:数据缓存使能
    • FLASH_KEYR:密钥寄存器(用于解锁)
    • FLASH_OPTKEYR:选项字节密钥寄存器
    • FLASH_SR:状态寄存器
      • BSY 位:忙标志位
      • PGERR 位:编程错误标志
      • WRPRTERR 位:写保护错误标志
      • EOP 位:操作完成标志
    • FLASH_CR:控制寄存器
      • PG 位:编程使能
      • SER 位:扇区擦除使能
      • MER 位:整片擦除使能
      • SNB [3:0]:扇区选择位
      • STRT 位:启动位
      • LOCK 位:锁定位

三、实际应用方法详解

1. 基于 HAL 库的 Flash 操作示例

 

/* 包含必要的头文件 */
#include "main.h"
#include "flash.h"

/* 定义Flash操作参数 */
#define FLASH_START_ADDR    0x08070000  // 选择第11扇区(128KB)作为数据存储区
#define FLASH_SECTOR        FLASH_SECTOR_11
#define FLASH_DATA_SIZE     sizeof(osc_run_msg_def)

/* 定义数据结构 */
typedef struct {
    uint8_t run_mode;           // 运行模式(0:停止, 1:单触发, 2:连续触发)
    uint8_t trig_type;          // 触发类型(0:上升沿, 1:下降沿, 2:双边沿)
    uint16_t trig_vol_level_ch[2];  // 触发电压阈值(CH1, CH2)
    uint16_t sample_rate;       // 采样率(100k-10M)
    uint16_t record_length;     // 记录长度(100-10000点)
    uint8_t display_mode;       // 显示模式(0:正常, 1:滚动, 2:XY)
    uint8_t reserved[10];       // 保留字节
} osc_run_msg_def;

/* 全局变量 */
osc_run_msg_def osc_run_msg;  // RAM中的数据缓冲区
uint32_t flash_errors = 0;    // Flash操作错误计数

/* 初始化Flash操作 */
void Flash_Init(void)
{
    // 使能Flash时钟
    __HAL_RCC_FLASH_CLK_ENABLE();
    
    // 配置Flash等待周期(根据CPU频率调整)
    // 例如: 168MHz时需配置为5个等待周期
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;
    
    // 从Flash读取配置数据到RAM
    Flash_ReadConfig(&osc_run_msg);
}

/* 解锁Flash */
HAL_StatusTypeDef Flash_Unlock(void)
{
    return HAL_FLASH_Unlock();
}

/* 锁定Flash */
HAL_StatusTypeDef Flash_Lock(void)
{
    return HAL_FLASH_Lock();
}

/* 擦除Flash扇区 */
HAL_StatusTypeDef Flash_EraseSector(uint32_t sector, uint8_t nSectors)
{
    HAL_StatusTypeDef status;
    FLASH_EraseInitTypeDef eraseInit;
    uint32_t sectorError = 0;
    
    eraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;
    eraseInit.Sector = sector;
    eraseInit.NbSectors = nSectors;
    eraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;  // 2.7V-3.6V
    
    status = HAL_FLASHEx_Erase(&eraseInit, &sectorError);
    if(status != HAL_OK) {
        flash_errors++;
        return status;
    }
    
    return HAL_OK;
}

/* 写入数据到Flash */
HAL_StatusTypeDef Flash_WriteData(uint32_t address, uint32_t *data, uint32_t size)
{
    HAL_StatusTypeDef status;
    uint32_t i;
    
    for(i = 0; i < size; i++) {
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address + (i * 4), data[i]);
        if(status != HAL_OK) {
            flash_errors++;
            return status;
        }
    }
    
    return HAL_OK;
}

/* 从Flash读取数据 */
void Flash_ReadData(uint32_t address, uint32_t *data, uint32_t size)
{
    uint32_t i;
    
    for(i = 0; i < size; i++) {
        data[i] = *(__IO uint32_t*)(address + (i * 4));
    }
}

/* 保存配置到Flash */
HAL_StatusTypeDef Flash_SaveConfig(osc_run_msg_def *config)
{
    HAL_StatusTypeDef status;
    uint32_t *pConfig = (uint32_t*)config;
    uint32_t configSize = FLASH_DATA_SIZE / 4;
    
    // 确保结构体大小是4的倍数
    if(FLASH_DATA_SIZE % 4 != 0) {
        configSize++;
    }
    
    // 解锁Flash
    status = Flash_Unlock();
    if(status != HAL_OK) {
        return status;
    }
    
    // 擦除扇区
    status = Flash_EraseSector(FLASH_SECTOR, 1);
    if(status != HAL_OK) {
        Flash_Lock();
        return status;
    }
    
    // 写入数据
    status = Flash_WriteData(FLASH_START_ADDR, pConfig, configSize);
    
    // 锁定Flash
    Flash_Lock();
    
    return status;
}

/* 从Flash读取配置 */
void Flash_ReadConfig(osc_run_msg_def *config)
{
    uint32_t *pConfig = (uint32_t*)config;
    uint32_t configSize = FLASH_DATA_SIZE / 4;
    
    // 确保结构体大小是4的倍数
    if(FLASH_DATA_SIZE % 4 != 0) {
        configSize++;
    }
    
    // 从Flash读取数据
    Flash_ReadData(FLASH_START_ADDR, pConfig, configSize);
    
    // 验证数据有效性(简单检查)
    if(config->run_mode > 2 || config->trig_type > 2) {
        // 数据无效,使用默认值
        memset(config, 0, FLASH_DATA_SIZE);
        config->run_mode = 1;       // 默认单触发模式
        config->trig_type = 0;      // 默认上升沿触发
        config->trig_vol_level_ch[0] = 2048;  // 默认触发阈值为中点(12位ADC)
        config->trig_vol_level_ch[1] = 2048;
        config->sample_rate = 1000; // 默认1MHz采样率
        config->record_length = 1000; // 默认1000点记录长度
        config->display_mode = 0;   // 默认正常显示模式
    }
}

/* 示例: 在主程序中使用Flash存储功能 */
void Main_Function(void)
{
    // 初始化Flash
    Flash_Init();
    
    // 主循环
    while(1) {
        // 检查是否需要保存配置(例如按键触发)
        if(config_change_flag) {
            // 保存当前配置到Flash
            if(Flash_SaveConfig(&osc_run_msg) == HAL_OK) {
                // 保存成功,更新状态LED
                HAL_GPIO_WritePin(LED_SUCCESS_GPIO_Port, LED_SUCCESS_Pin, GPIO_PIN_SET);
                HAL_Delay(200);
                HAL_GPIO_WritePin(LED_SUCCESS_GPIO_Port, LED_SUCCESS_Pin, GPIO_PIN_RESET);
            } else {
                // 保存失败,错误处理
                HAL_GPIO_WritePin(LED_ERROR_GPIO_Port, LED_ERROR_Pin, GPIO_PIN_SET);
                // 增加重试逻辑...
            }
            config_change_flag = 0;
        }
        
        // 其他主程序逻辑...
    }
}

2. 基于寄存器操作的 Flash 底层实现

 

/* Flash操作底层函数 */

/* 解锁Flash控制器 */
void FLASH_Unlock(void)
{
    // 检查是否已锁定
    if(FLASH->CR & FLASH_CR_LOCK) {
        // 写入解锁序列
        FLASH->KEYR = FLASH_KEY1;
        FLASH->KEYR = FLASH_KEY2;
    }
}

/* 锁定Flash控制器 */
void FLASH_Lock(void)
{
    FLASH->CR |= FLASH_CR_LOCK;
}

/* 等待Flash操作完成 */
uint8_t FLASH_WaitForLastOperation(uint32_t Timeout)
{
    uint8_t status = FLASH_COMPLETE;
    
    // 等待操作完成或超时
    while((FLASH->SR & FLASH_SR_BSY) && (Timeout != 0)) {
        Timeout--;
    }
    
    if(Timeout == 0) {
        status = FLASH_TIMEOUT;
    } else {
        // 检查错误标志
        if(FLASH->SR & FLASH_SR_PGERR) {
            status = FLASH_ERROR_PROGRAM;
        }
        if(FLASH->SR & FLASH_SR_WRPERR) {
            status = FLASH_ERROR_WRITE_PROTECTION;
        }
        
        // 清除错误标志
        FLASH->SR = FLASH_SR_PGERR | FLASH_SR_WRPERR | FLASH_SR_OPERR;
    }
    
    return status;
}

/* 擦除Flash扇区 */
uint8_t FLASH_EraseSector(uint32_t Sector, uint8_t VoltageRange)
{
    uint8_t status = FLASH_COMPLETE;
    
    // 检查参数
    if((Sector > FLASH_SECTOR_11) || 
       (VoltageRange > FLASH_VOLTAGE_RANGE_3)) {
        return FLASH_ERROR_INVAL_PARAM;
    }
    
    // 等待上一个操作完成
    status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    
    if(status == FLASH_COMPLETE) {
        // 设置电压范围
        FLASH->CR &= ~FLASH_CR_VRP;
        FLASH->CR |= VoltageRange << 14;
        
        // 选择扇区
        FLASH->CR &= ~FLASH_CR_SNB;
        FLASH->CR |= Sector << 3;
        
        // 使能扇区擦除
        FLASH->CR |= FLASH_CR_SER;
        
        // 开始擦除
        FLASH->CR |= FLASH_CR_STRT;
        
        // 等待操作完成
        status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
        
        // 清除SER位
        FLASH->CR &= ~FLASH_CR_SER;
    }
    
    return status;
}

/* 写入半字(16位)到Flash */
uint8_t FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
    uint8_t status = FLASH_COMPLETE;
    
    // 等待上一个操作完成
    status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    
    if(status == FLASH_COMPLETE) {
        // 使能编程
        FLASH->CR |= FLASH_CR_PG;
        
        // 写入数据
        *(__IO uint16_t*)Address = Data;
        
        // 等待操作完成
        status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
        
        // 清除PG位
        FLASH->CR &= ~FLASH_CR_PG;
    }
    
    return status;
}

/* 写入字(32位)到Flash */
uint8_t FLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
    uint8_t status = FLASH_COMPLETE;
    
    // 等待上一个操作完成
    status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    
    if(status == FLASH_COMPLETE) {
        // 使能编程
        FLASH->CR |= FLASH_CR_PG;
        
        // 写入低16位
        *(__IO uint16_t*)Address = (uint16_t)Data;
        
        // 等待低16位写入完成
        status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
        
        if(status == FLASH_COMPLETE) {
            // 写入高16位
            *(__IO uint16_t*)(Address + 2) = (uint16_t)(Data >> 16);
            
            // 等待高16位写入完成
            status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
        }
        
        // 清除PG位
        FLASH->CR &= ~FLASH_CR_PG;
    }
    
    return status;
}

/* 写入双字(64位)到Flash */
uint8_t FLASH_ProgramDoubleWord(uint32_t Address, uint64_t Data)
{
    uint8_t status = FLASH_COMPLETE;
    
    // 等待上一个操作完成
    status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    
    if(status == FLASH_COMPLETE) {
        // 使能编程
        FLASH->CR |= FLASH_CR_PG;
        
        // 写入低32位
        *(__IO uint32_t*)Address = (uint32_t)Data;
        
        // 等待低32位写入完成
        status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
        
        if(status == FLASH_COMPLETE) {
            // 写入高32位
            *(__IO uint32_t*)(Address + 4) = (uint32_t)(Data >> 32);
            
            // 等待高32位写入完成
            status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
        }
        
        // 清除PG位
        FLASH->CR &= ~FLASH_CR_PG;
    }
    
    return status;
}

/* 读取Flash数据 */
uint32_t FLASH_ReadWord(uint32_t Address)
{
    return *(__IO uint32_t*)Address;
}

/* 读取Flash半字 */
uint16_t FLASH_ReadHalfWord(uint32_t Address)
{
    return *(__IO uint16_t*)Address;
}

四、Flash 操作高级技巧与最佳实践

1. 提高写入效率的技巧

  • 批量写入:将多个小数据合并为一次写入操作
  • 预取缓冲区优化

     

    // 使能预取缓冲区
    FLASH->ACR |= FLASH_ACR_PRFTEN;
    
    // 使能指令缓存
    FLASH->ACR |= FLASH_ACR_ICEN;
    
    // 使能数据缓存
    FLASH->ACR |= FLASH_ACR_DCEN;
    
  • 优化等待周期:根据 CPU 频率调整等待周期数

     

    // 例如: 72MHz时设置为2个等待周期
    FLASH->ACR &= ~FLASH_ACR_LATENCY;
    FLASH->ACR |= FLASH_ACR_LATENCY_2WS;
    

2. 提高可靠性的方法

  • 校验机制
    • CRC 校验:

       

      uint32_t CalculateCRC32(uint32_t *data, uint32_t size)
      {
          uint32_t i;
          uint32_t crc = 0xFFFFFFFF;
          
          for(i = 0; i < size; i++) {
              crc = HAL_CRC_Calculate(&hcrc, &data[i], 1);
          }
          
          return crc;
      }
      
    • 简单校验和:

       

      uint16_t CalculateChecksum(uint8_t *data, uint32_t size)
      {
          uint32_t i;
          uint16_t checksum = 0;
          
          for(i = 0; i < size; i++) {
              checksum += data[i];
          }
          
          return checksum;
      }
      
  • 写入保护

     

     

    // 配置扇区写保护
    void FLASH_EnableSectorWriteProtection(uint32_t SectorMask)
    {
        FLASH_OBProgramInitTypeDef obInit;
        
        HAL_FLASHEx_OBGetConfig(&obInit);
        obInit.WRPState = OB_WRPSTATE_ENABLE;
        obInit.WRPSector = SectorMask;
        
        HAL_FLASHEx_OBProgram(&obInit);
    }
    
  • 掉电保护
    • 在写入前检查电源电压

     

    if(__HAL_PWR_GET_FLAG(PWR_FLAG_VREFINTRDY) == RESET) {
        // 电压不稳定,推迟写入操作
        return;
    }
    

3. 延长 Flash 寿命的策略

  • 磨损均衡算法

     

    // 简化的磨损均衡实现
    uint32_t GetNextFlashAddress(void)
    {
        static uint32_t currentSector = 0;
        uint32_t address;
        
        // 循环使用不同扇区
        address = FLASH_START_ADDR + (currentSector * FLASH_SECTOR_SIZE);
        currentSector = (currentSector + 1) % NUM_FLASH_SECTORS;
        
        return address;
    }
    
  • 减少写入频率
    • 使用脏标志位,仅在数据真正变化时写入

     

    if(dirty_flag) {
        Flash_SaveConfig(&config);
        dirty_flag = 0;
    }
    
  • 数据压缩
    • 对于大量数据,可先压缩再存储

     

    #include "lz4.h"
    
    void CompressAndSaveData(uint8_t *srcData, uint32_t srcSize)
    {
        uint8_t compressedData[1024];
        int compressedSize;
        
        // 压缩数据
        compressedSize = LZ4_compress_default((char*)srcData, (char*)compressedData, 
                                             srcSize, sizeof(compressedData));
        
        if(compressedSize > 0) {
            // 保存压缩后的数据和大小
            Flash_WriteData(FLASH_START_ADDR, (uint32_t*)&compressedSize, 1);
            Flash_WriteData(FLASH_START_ADDR + 4, (uint32_t*)compressedData, 
                           (compressedSize + 3) / 4);
        }
    }
    

五、常见问题与解决方案

1. 写入失败的常见原因及解决

问题表现可能原因解决方案
BSY 位长时间置位电压不稳定检查电源稳定性,添加滤波电容
PGERR 错误尝试对已编程位再次编程确保先擦除再写入
WRPRTERR 错误扇区被写保护检查选项字节,解除写保护
写入数据错误数据对齐问题确保按字 (4 字节) 对齐写入
擦除时间过长Flash 温度过高检查散热条件,降低工作温度

2. 数据损坏的检测与恢复

  • 检测方法

     

    // 读取数据并验证CRC
    uint32_t storedCRC, calculatedCRC;
    
    // 读取存储的CRC
    storedCRC = FLASH_ReadWord(FLASH_START_ADDR + FLASH_DATA_SIZE);
    
    // 读取并计算数据CRC
    calculatedCRC = CalculateCRC32((uint32_t*)FLASH_START_ADDR, FLASH_DATA_SIZE / 4);
    
    if(storedCRC != calculatedCRC) {
        // 数据损坏,使用默认值
        LoadDefaultConfig();
    }
    
  • 恢复机制

     

    // 双备份存储方案
    #define FLASH_BACKUP_ADDR (FLASH_START_ADDR + FLASH_SECTOR_SIZE)
    
    void SaveConfigWithBackup(osc_run_msg_def *config)
    {
        // 先写入主存储区
        Flash_SaveConfig(config);
        
        // 再写入备份区
        Flash_SaveConfigAtAddress(config, FLASH_BACKUP_ADDR);
    }
    
    void LoadConfigWithRecovery(osc_run_msg_def *config)
    {
        // 先尝试从主存储区读取
        Flash_ReadConfig(config);
        
        // 验证数据有效性
        if(!IsConfigValid(config)) {
            // 主存储区数据无效,从备份区恢复
            Flash_ReadConfigAtAddress(config, FLASH_BACKUP_ADDR);
            
            // 若备份区也无效,使用默认值
            if(!IsConfigValid(config)) {
                LoadDefaultConfig(config);
            }
        }
    }
    

3. Flash 操作对系统性能的影响

  • 性能分析
    • 典型扇区擦除时间:50-100ms
    • 典型字写入时间:20-50μs
    • 擦除 / 写入期间 Flash 控制器不可访问,可能导致 CPU 等待
  • 优化建议
    • 在低优先级任务中执行 Flash 操作

     

    void LowPriorityTask(void)
    {
        // 检查是否需要保存配置
        if(config_save_pending) {
            Flash_SaveConfig(&config);
            config_save_pending = 0;
        }
    }
    
     
    • 使用 DMA 加速数据传输(适用于大容量数据)

     

    // 配置DMA传输
    HAL_DMA_Start(&hdma_memtomem_dma2_stream0, 
                  (uint32_t)srcBuffer, 
                  (uint32_t)dstBuffer, 
                  dataSize);
                  
    // 等待DMA传输完成
    HAL_DMA_PollForTransfer(&hdma_memtomem_dma2_stream0, HAL_DMA_FULL_TRANSFER, 100);
    

六、实际应用案例

1. 存储配置参数的应用

  • 使用场景
    • 示波器参数配置(如您提供的代码示例)
    • WiFi 连接信息(SSID、密码)
    • 用户偏好设置(显示亮度、音量等)
  • 实现步骤
    1. 定义配置结构体
    2. 初始化时从 Flash 读取配置
    3. 用户修改配置后标记为脏
    4. 定时或在特定事件(如系统空闲、按键释放)时保存配置

2. 日志记录系统

  • 实现思路

     

    #define LOG_BUFFER_SIZE 1024
    #define LOG_FLASH_SECTOR FLASH_SECTOR_10
    
    typedef struct {
        uint32_t timestamp;
        uint8_t logType;
        uint16_t data[8];
    } log_entry_t;
    
    static log_entry_t logBuffer[LOG_BUFFER_SIZE];
    static uint16_t logIndex = 0;
    static uint8_t logDirty = 0;
    
    void AddLogEntry(uint8_t type, uint16_t *data, uint8_t dataSize)
    {
        // 添加日志条目到缓冲区
        logBuffer[logIndex].timestamp = HAL_GetTick();
        logBuffer[logIndex].logType = type;
        
        // 复制数据
        for(uint8_t i = 0; i < dataSize && i < 8; i++) {
            logBuffer[logIndex].data[i] = data[i];
        }
        
        // 更新索引
        logIndex = (logIndex + 1) % LOG_BUFFER_SIZE;
        logDirty = 1;
    }
    
    void SaveLogsToFlash(void)
    {
        if(logDirty) {
            uint32_t flashAddr = FLASH_BASE + (LOG_FLASH_SECTOR * FLASH_SECTOR_SIZE);
            
            // 解锁Flash
            FLASH_Unlock();
            
            // 擦除扇区
            FLASH_EraseSector(LOG_FLASH_SECTOR, FLASH_VOLTAGE_RANGE_3);
            
            // 写入日志数量
            FLASH_ProgramWord(flashAddr, logIndex);
            flashAddr += 4;
            
            // 写入日志数据
            for(uint16_t i = 0; i < logIndex; i++) {
                FLASH_ProgramWord(flashAddr, logBuffer[i].timestamp);
                flashAddr += 4;
                FLASH_ProgramHalfWord(flashAddr, logBuffer[i].logType);
                flashAddr += 2;
                
                for(uint8_t j = 0; j < 8; j++) {
                    FLASH_ProgramHalfWord(flashAddr, logBuffer[i].data[j]);
                    flashAddr += 2;
                }
            }
            
            // 锁定Flash
            FLASH_Lock();
            logDirty = 0;
        }
    }
    

3. 固件更新应用

  • 双区存储方案

     

    #define BOOTLOADER_SIZE 0x4000      // 16KB Bootloader
    #define APP1_START_ADDR 0x08004000  // 应用程序1起始地址
    #define APP2_START_ADDR 0x08040000  // 应用程序2起始地址
    #define APP_SIZE 0x3C000            // 每个应用程序最大60KB
    
    typedef enum {
        BOOT_APP1,
        BOOT_APP2,
        UPDATE_APP1,
        UPDATE_APP2
    } boot_mode_t;
    
    // 启动模式存储在特定Flash位置
    #define BOOT_MODE_ADDR 0x1FFF7800   // OTP区域起始地址
    
    void SetBootMode(boot_mode_t mode)
    {
        FLASH_Unlock();
        FLASH_ProgramHalfWord(BOOT_MODE_ADDR, (uint16_t)mode);
        FLASH_Lock();
    }
    
    boot_mode_t GetBootMode(void)
    {
        return (boot_mode_t)FLASH_ReadHalfWord(BOOT_MODE_ADDR);
    }
    
    void JumpToApplication(uint32_t appAddress)
    {
        typedef void (*pFunction)(void);
        uint32_t JumpAddress;
        pFunction Jump_To_Application;
        
        // 检查栈顶地址是否合法
        if(((uint32_t*)appAddress)[0] > 0x20000000 && 
           ((uint32_t*)appAddress)[0] < 0x20040000) {
            
            // 关闭所有中断
            __disable_irq();
            
            // 清除所有挂起的中断
            NVIC->ICER[0] = 0xFFFFFFFF;
            NVIC->ICER[1] = 0xFFFFFFFF;
            NVIC->ICPR[0] = 0xFFFFFFFF;
            NVIC->ICPR[1] = 0xFFFFFFFF;
            
            // 设置MSP
            __set_MSP(*(uint32_t*)appAddress);
            
            // 跳转到应用程序
            JumpAddress = *(uint32_t*)(appAddress + 4);
            Jump_To_Application = (pFunction)JumpAddress;
            Jump_To_Application();
        }
    }
    

     

     

     

     

     

     

     

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值