正点原子探索者F4使用片上Flash完整指南(RT-Thread Studio v2.2.8 + RT-Thread v4.1.0)

以下是在正点原子探索者F4开发板上使用片上Flash的详细步骤:

准备工作

  1. 硬件配置

    • 正点原子探索者F4开发板(主控STM32F407ZGT6)
    • USB数据线连接PC和开发板
    • 跳线帽设置:BOOT0置低电平(启动方式为Flash)
  2. 软件环境

    • RT-Thread Studio v2.2.8
    • RT-Thread v4.1.0 BSP包(STM32F407-Explorer)

完整配置步骤

步骤1:创建项目

  1. 打开RT-Thread Studio
  2. 文件 → 新建 → RT-Thread项目
  3. 选择:
    • 项目类型:基于开发板
    • 开发板:搜索"Explorer-F4" → STM32F407-Explorer
    • 工具链:ARM GCC
    • RT-Thread版本:v4.1.0
  4. 点击完成创建项目

步骤2:启用片上Flash驱动

  1. 在项目资源管理器中打开"RT-Thread Settings"

  2. 左侧导航:Hardware → On-chip Peripheral

  3. 启用:

    • [*] Enable On-Chip FLASH (BSP_USING_ON_CHIP_FLASH)
  4. 保存配置(Ctrl+S)

步骤3:配置FAL组件

  1. 在board目录下创建fal_cfg.h文件:
    fal_cfg.h 下配置flash分区表
/* board/fal_cfg.h */
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_

#include <rtconfig.h>
#include <board.h>

/* ============== 片上Flash设备配置 =============== */
extern const struct fal_flash_dev stm32_onchip_flash;
//flash设备表
#define FAL_FLASH_DEV_TABLE                   \
{                                             \
    &stm32_onchip_flash,                      \
}

/* ============== 分区表配置 =============== */
#define FAL_PART_TABLE                                                               \
{                                                                                    \
    {FAL_PART_MAGIC_WORD, "boot",   "onchip_flash",         0,       128*1024, 0},  \
    {FAL_PART_MAGIC_WORD, "app",    "onchip_flash",  128*1024,       512*1024, 0},  \
}

#endif /* _FAL_CFG_H_ */

步骤4:修改片上Flash驱动

在board/ports目录下创建fal_flash_stm32f4_port.c

#include <rtthread.h>
#include <rtdevice.h>
#include <fal.h>

/* STM32F407ZGT6 Flash布局 */
#define STM32_FLASH_START_ADDR        0x08000000
#define STM32_FLASH_SIZE              (1024*1024)

/* 扇区地址 */
#define ADDR_FLASH_SECTOR_0    0x08000000U  /* 16 KB */
#define ADDR_FLASH_SECTOR_1    0x08004000U  /* 16 KB */
#define ADDR_FLASH_SECTOR_2    0x08008000U  /* 16 KB */
#define ADDR_FLASH_SECTOR_3    0x0800C000U  /* 16 KB */
#define ADDR_FLASH_SECTOR_4    0x08010000U  /* 64 KB */
#define ADDR_FLASH_SECTOR_5    0x08020000U  /* 128 KB */
#define ADDR_FLASH_SECTOR_6    0x08040000U  /* 128 KB */
#define ADDR_FLASH_SECTOR_7    0x08060000U  /* 128 KB */
#define ADDR_FLASH_SECTOR_8    0x08080000U  /* 128 KB */
#define ADDR_FLASH_SECTOR_9    0x080A0000U  /* 128 KB */
#define ADDR_FLASH_SECTOR_10   0x080C0000U  /* 128 KB */
#define ADDR_FLASH_SECTOR_11   0x080E0000U  /* 128 KB */

/* 扇区映射函数 */
static uint32_t get_sector(uint32_t address)
{
    if ((address >= ADDR_FLASH_SECTOR_0) && (address < ADDR_FLASH_SECTOR_1))
        return FLASH_SECTOR_0;
    else if ((address >= ADDR_FLASH_SECTOR_1) && (address < ADDR_FLASH_SECTOR_2))
        return FLASH_SECTOR_1;
    else if ((address >= ADDR_FLASH_SECTOR_2) && (address < ADDR_FLASH_SECTOR_3))
        return FLASH_SECTOR_2;
    else if ((address >= ADDR_FLASH_SECTOR_3) && (address < ADDR_FLASH_SECTOR_4))
        return FLASH_SECTOR_3;
    else if ((address >= ADDR_FLASH_SECTOR_4) && (address < ADDR_FLASH_SECTOR_5))
        return FLASH_SECTOR_4;
    else if ((address >= ADDR_FLASH_SECTOR_5) && (address < ADDR_FLASH_SECTOR_6))
        return FLASH_SECTOR_5;
    else if ((address >= ADDR_FLASH_SECTOR_6) && (address < ADDR_FLASH_SECTOR_7))
        return FLASH_SECTOR_6;
    else if ((address >= ADDR_FLASH_SECTOR_7) && (address < ADDR_FLASH_SECTOR_8))
        return FLASH_SECTOR_7;
    else if ((address >= ADDR_FLASH_SECTOR_8) && (address < ADDR_FLASH_SECTOR_9))
        return FLASH_SECTOR_8;
    else if ((address >= ADDR_FLASH_SECTOR_9) && (address < ADDR_FLASH_SECTOR_10))
        return FLASH_SECTOR_9;
    else if ((address >= ADDR_FLASH_SECTOR_10) && (address < ADDR_FLASH_SECTOR_11))
        return FLASH_SECTOR_10;
    else /* >= 0x080C0000 */
        return FLASH_SECTOR_11;
}

/* 初始化函数 */
static int init(void)
{
    return 0;
}

/* 读函数 */
static int read(long offset, uint8_t *buf, size_t size)
{
    uint32_t addr = stm32_onchip_flash.addr + offset;
    memcpy(buf, (const void*)addr, size);
    return size;
}

/* 写函数 */
static int write(long offset, const uint8_t *buf, size_t size)
{
    uint32_t addr = stm32_onchip_flash.addr + offset;
    HAL_StatusTypeDef status;
    
    if (HAL_FLASH_Unlock() != HAL_OK)
        return -1;
        
    for (size_t i = 0; i < size; i++)
    {
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, 
                                   addr + i, 
                                   buf[i]);
        if (status != HAL_OK)
        {
            HAL_FLASH_Lock();
            return -1;
        }
    }
    
    if (HAL_FLASH_Lock() != HAL_OK)
        return -1;
        
    return size;
}

/* 擦除函数 */
static int erase(long offset, size_t size)
{
    HAL_StatusTypeDef status;
    size_t erased_size = 0;
    uint32_t addr = stm32_onchip_flash.addr + offset;
    
    if (HAL_FLASH_Unlock() != HAL_OK)
        return -1;
        
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
    
    while (erased_size < size)
    {
        uint32_t sector = get_sector(addr + erased_size);
        
        FLASH_EraseInitTypeDef erase_init = {
            .TypeErase = FLASH_TYPEERASE_SECTORS,
            .Sector = sector,
            .NbSectors = 1,
            .VoltageRange = FLASH_VOLTAGE_RANGE_3
        };
        
        uint32_t sector_error;
        status = HAL_FLASHEx_Erase(&erase_init, &sector_error);
        if (status != HAL_OK)
        {
            HAL_FLASH_Lock();
            return -1;
        }
        
        erased_size += (sector < 4) ? 16 * 1024 : 
                      (sector == 4) ? 64 * 1024 : 128 * 1024;
    }
    
    if (HAL_FLASH_Lock() != HAL_OK)
        return -1;
        
    return size;
}

/* Flash设备定义 */
const struct fal_flash_dev stm32_onchip_flash = 
{
    .name = "onchip_flash",
    .addr = STM32_FLASH_START_ADDR,
    .len = STM32_FLASH_SIZE,
    .blk_size = 16 * 1024,  // 使用最小擦除块(16KB)
    .ops = {init, read, write, erase},
    .write_gran = 1  // 1字节写入粒度
};

步骤5:修改SConscript文件

确保在board/SConscript中添加驱动文件:

# 添加以下内容
if GetDepend(['BSP_USING_ON_CHIP_FLASH']):
    src += Glob('ports/fal_flash_stm32f4_port.c')
    # 或者
    # src += ['ports/fal_flash_stm32f4_port.c']

步骤6:初始化FAL

在application目录下的main.c中添加:

#include <fal.h>
#include <rtthread.h>

int main(void)
{
    /* 初始化FAL */
    fal_init();
    
    /* 打印分区表 */
    const struct fal_partition *part;
    part = fal_partition_find("param");
    if (part != RT_NULL) 
    {
        rt_kprintf("Found param partition! Size: %d\n", part->len);
    }
    
    return RT_EOK;
}

步骤7:配置时钟

确保系统时钟配置正确(180MHz),在board/CubeMX_Config/Src/main.c中:

// 确保Flash等待状态设置为5(180MHz时需要)
__HAL_RCC_FLASH_CLK_ENABLE();
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_5); // 关键设置!

编译与测试

步骤8:编译与烧录

  1. 在RT-Thread Studio中:
    • 项目 → 构建项目(Ctrl+B)
    • 项目 → 烧录项目(Ctrl+D)

步骤9:测试Flash功能

  1. 打开串口终端(波特率115200)
  2. 在msh中输入以下命令测试:
# 查看FAL信息
msh >fal
FAL (Flash Abstract Layer) - flash partition info:
Partition      | Flash device | Offset     | Size
boot           | onchip_flash | 0x00000000 | 131072
app            | onchip_flash | 0x00020000 | 524288

# 擦除测试
msh >fal erase param 0 4096
Erase [param] from 0x00000000, size 4096: OK.

# 写入测试
msh >fal write param 0 "RT-Thread Flash Test!"
Write [param] from 0x00000000, size 21: OK.

# 读取验证
msh >fal read param 0 21
Read: 52 54 2D 54 68 72 65 61 64 20 46 6C 61 73 68 20 54 65 73 74 21 
String: RT-Thread Flash Test!

常见问题解决

  1. 擦除/写入失败

    • 确认__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_5)
    • 检查Flash解锁顺序
    • 验证地址范围在有效分区内
  2. 分区未被识别

    • 检查fal_cfg.h中的设备名称和分区名称匹配
    • 确认.blk_size设置正确(必须是最小擦除块大小)
  3. 编译错误

    // 在stm32f4xx_hal_conf.h中启用Flash模块
    #define HAL_FLASH_MODULE_ENABLED
    
  4. 性能优化

    // 使用字/双字编程提高写入速度
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, (uint32_t)data);
    
  5. 低功耗优化

    // 操作完成后降低等待状态
    __HAL_FLASH_SET_LATENCY(FLASH_LATENCY_2); // 在降频后设置
    

通过以上步骤,您可以在正点原子探索者F4开发板上成功使用片上Flash进行数据存储、固件升级等操作。

总结:

1.使用fal(Flash抽象管理层) 来统一接口管理 Flash 设备,提供分区划分能力。其可以将多种flash,诸如片上flash和spi Flash等进行抽象为Init()/read()/write()/erase()等接口,并提供了类似诸如C/D/E/F等分区划分能力,以便在嵌入式系统中,将Flash划分为不同功能的分区,如bootload,app,log等分区。
2.对于分区表配置信息,需要在fal_cfg.h文件中,进行定义。
3.当使用片上Flash时,需要根据不同MCU的片上Flash大小和分布信息对Flash进行配置,还有需要开发者实现片上Flash的Init()/read()/write()/erase()等接口的底层驱动实现。如fal_flash_stm32f4_port.c
4、如使用spi Flash时,可以使用sfud驱动来控制SPI Flash,然后,将分区表配置信息在fal_cfg,h中进行配置。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐