发新帖本帖赏金 150.00元(功能说明)我要提问
返回列表
打印
[MM32生态]

换种方式玩转SPI Flash,还支持文件系统

[复制链接]
1994|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 yang377156216 于 2022-5-31 17:01 编辑
#申请原创#  @21小跑堂

整体概览
在很多开发板上几乎都会带有一颗 SPI Nor Flash,用于评估 MCU 的 SPI 外设,手边的灵动 eMiniboard MB-039 也不例外,它板载了一颗 M25P80 串行 Flash 存储器芯片。一般而言,我们会将它应用于某些特定场景中,如:存储 BootLoader 或者其它小的算法程序代码,存储字库(变成非专用的字库芯片),存储图片bin 文件用于刷 LCD ,存储主题或者各种大数据文件(之前提到的系统 log 记录文件就是其中一种),等等。整体来看,SPI Flash 更适合用于存储数据量不是特别大、小封装低成本、读写速度要求不是特别高的一些带存储功能的应用中。本次我会将内容主要分为以下几个部分:
  • MB-039 开发板及 M25P80 SPI Flash 简介
  • 使用 SFUD 组件操作外部串行 Flash
  • 移植 Littlefs 到 SPI Flash 上
  • 将 bin 文件烧录到 SPI Flash 上的不同方法
  • 附件内容

一、MB-039 开发板及 M25P80 SPI Flash 简介
通过查看开发板应用手册得知 ,MB-039 是一款用于评估灵动微 MM32F3270 的开发评估板 ,它配合 ARM Keil/IAR 集成开发环境、MM32 Program 编程软件、MM32 FDS 固件开发平台以及内嵌 MM32-LINK-OB 仿真器,可以更方便地让我们用户评估 MCU 功能和性能。MM32F3270 EVB 具有如下特点:
  • 支持 MindMotion MM32F3270 系列 MCU 开发评估,外部时钟 8MHz/32.768KHz
  • 支持 Keil uVision v5.0 / IAR EWARM v7.80 或以上版本
  • 支持 MM32 FDS 固件开发平台
  • 支持 MM32 Program 编程软件
  • 内嵌 MM32-LINK-OB,支持虚拟串口(CDC)
  • MCU 基于 3.3V 电压设计,评估板 USB(Micro USB)接口供电
  • 4-按键,其中一个支持唤醒高电平有效,三个低电平有效按键
  • 4-发光二极管
  • 2-UART 三芯扩展插座(CMOS 电平)
  • 1-SPI 五芯扩展插座(CMOS 电平)
  • 1-带电平驱动器、终端匹配电阻选择的 CAN 总线驱动接口
  • 1-Type-C 插座,支持 MCU USB Host/Device 工作模式
  • 1-1M 字节 SPI Flash 存储器
  • 1-256 字节 I2C EEPROM 存储器
  • 1-3.5mm 耳机插座,用于 I2S L/R 音频输出
  • 1-SDIO 方式的 TF Card 插座
  • 2-RJ45 10M/100Mbps 以太网接口
  • 1-34 芯并行总线接口,选配支持 2.8’’LCD 显示屏
  • CR1210(用户配制)后备电池
  • 1-无源扬声器
  • 3-模拟输入电位器
  • 3-开关:支持 USB 工作模式,引脚功能和 CAN 终端匹配电阻选择
  • 4-36pin0.1 英寸间距双排连接器, 管脚顺序与 MCU 相同
  • PCB 板面尺寸:6.4*3.2 英寸

涉及到 SPI Flash 部分的原理图如下,其中脚位对应关系为:SPI_NSS_PB12 / SPI_CLK_PB13 / SPI_MISO_PB14 / SPI_MOSI_PB15

由原理图以及实物可得知板载 SPI Flash 型号为 M25P80 ,它是由美光科技有限公司推出的一款低压串行存储芯片,从规格书上可以看出有以下简要参数信息:

具体有以下特性:
  • SPI总线兼容串行接口
  • 8Mb (1M byte)容量
  • 75 MHz 时钟频率(最大)
  • 2.7V 至 3.6V 单电源工作电压范围
  • 页面编程(最多 256 字节)时间为 0.64ms(典型值)
  • 扇区擦除,0.6 秒 512 Kb(典型值)和大容量擦除,8Mb 8 秒(典型值)
  • 具有硬件写保护功能,保护区大小由非易失性位 BP0、BP1、BP2 定义
  • 深度断电功耗为 1µA(典型值)
  • 具备 JEDEC 标准 2 字节签名ID(存储于 2014h 地址)
  • 具备唯一器件 ID 代码(UID)和 16 个字节的公共闪存接口(CFI)数据
  • 支持 RES 命令,单字节签名ID(存储于 13h 地址),用于向后兼容性
  • 每个扇区超过 100000 个写入周期
  • 超过 20 年的数据保留
  • 汽车级性能,包装符合 RoHS
  • 多种封装形式,小尺寸
上面特性中需要特别关注的有容量大小、页/扇区大小以及 JEDEC ID 等,实际编程时需要根据这些参数进行预定义配置。


二、使用 SFUD 组件操作外部串行 Flash
SFUD 全称 Serial Flash Universal Driver,是一款开源的串行 SPI Flash 通用驱动库,也是 armink 大神朱总的开源之作。由于市面上的串行 Flash 种类非常多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让自己的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。另外使用该套库,也能简化软件流程,降低开发难度,特别是那些制作 Flash 编程器的哥们用它贼省心。它支持 SPI/QSPI 接口、同时支持多个 Flash 对象、可灵活裁剪、扩展性强、支持 4 字节地址,最小占用 RAM:0.1KB ROM:3.6KB。它是参照 SFDP JEDEC 固态技术协会制定的串行 Flash 功能的参数表标准)而设计的,该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数,SFUD 还考虑了不支持 SFDP 标准的 Flash 该如何适配。
下表为已经支持该标准的 Flash 型号概览,几乎常用到的都有涵盖:

话不多说,立马跟着我将该组件适配到 MB-039 开发板上吧。先提前准备好一个官方样例工程,只要能够将 SPI Flash 的 SPI 接口给初始化好即可,由于不太关注和要求极高的读写速率,所以 SPI 主机时钟可以不配置到最高,且使用全双工形式的边收边发的轮询操作方式即可,当然这个在后面的 port 接口会有说明。
到 SFUD Github 仓地址获取源码,后将源码包添加至样例工程且添加头文件路径:
https://github.com/armink/SFUD.git

在 sfud_cfg.h 配置文件中定义 Flash 设备,以使用 SFDP 标准的形式,如下:
#define SFUD_USING_SFDP

enum {
    SFUD_M25P_DEVICE_INDEX = 0,
};

#define SFUD_FLASH_DEVICE_TABLE                                                \
{                                                                              \
    [SFUD_M25P_DEVICE_INDEX] = {.name = "M25P80", .spi.name = "SPI2"},       \
}
需要适配平台的接口文件 sfud_port.c 中的 sfud_err sfud_spi_port_init(sfud_flash *flash) 方法,它是库提供的底层接口操作函数,在里面完成各个设备 SPI 读写驱动(必选)、重试次数(必选)、重试接口(可选)及 SPI 锁(可选)的配置。其中,SPI 读写驱动使用的是轮询方式,SPI 锁驱动为操作总中断的开启与关断,如果要开启调试的话则需要适配好串口打印信息的接口,具体代码如下:
/**
* add your spi flash user data struct typedef
*/
typedef struct {
    SPI_TypeDef *spix;
    GPIO_TypeDef *cs_gpiox;
    uint16_t cs_gpio_pin;
} spi_user_data, *spi_user_data_t;

static spi_user_data spi2 = { .spix = SPI2, .cs_gpiox = GPIOB, .cs_gpio_pin = GPIO_Pin_12 };

static char log_buf[256];

void sfud_log_debug(const char *file, const long line, const char *format, ...);

/**
* SPI write data then read data
*/
static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
        size_t read_size) {
    sfud_err result = SFUD_SUCCESS;
    uint8_t send_data, read_data;

    spi_user_data_t spi_dev = (spi_user_data_t) spi->user_data;

    if (write_size) {
        SFUD_ASSERT(write_buf);
    }
    if (read_size) {
        SFUD_ASSERT(read_buf);
    }

//    GPIO_ResetBits(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin);/* 软件NSS */
    SPI_CSInternalSelected(SPI2, ENABLE);/* 硬件NSS */
    /* 开始读写数据 */
    for (size_t i = 0, retry_times; i < write_size + read_size; i++) {
        /* 先写缓冲区中的数据到 SPI 总线,数据写完后,再写 dummy(0xFF) 到 SPI 总线 */
        if (i < write_size) {
            send_data = *write_buf++;
        } else {
            send_data = SFUD_DUMMY_DATA;
        }
        /* 发送数据 */
        retry_times = 1000;
        while (SPI_GetFlagStatus(spi_dev->spix, SPI_FLAG_TXEPT) == RESET) {
            SFUD_RETRY_PROCESS(NULL, retry_times, result);
        }
        if (result != SFUD_SUCCESS) {
            goto exit;
        }
        SPI_SendData(spi_dev->spix, send_data);
        
        /* 接收数据 */
        retry_times = 1000;
        while (SPI_GetFlagStatus(spi_dev->spix, SPI_FLAG_RXAVL) == RESET) {
            SFUD_RETRY_PROCESS(NULL, retry_times, result);
        }
        if (result != SFUD_SUCCESS) {
            goto exit;
        }
        read_data = SPI_ReceiveData(spi_dev->spix);
        /* 写缓冲区中的数据发完后,再读取 SPI 总线中的数据到读缓冲区 */
        if (i >= write_size) {
            *read_buf++ = read_data;
        }
    }

exit:
//    GPIO_SetBits(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin);
    SPI_CSInternalSelected(SPI2, DISABLE);
   
    return result;
}

/**
* add your spi flash user function
*/
/* about 100 microsecond delay */
static void retry_delay_100us(void) {
    uint32_t delay = 80; /* 120 [url=home.php?mod=space&uid=442618]@72mhz[/url] */
    while(delay--);
}

static void spi_lock(const sfud_spi *spi) {
    __disable_irq();
}

static void spi_unlock(const sfud_spi *spi) {
    __enable_irq();
}


sfud_err sfud_spi_port_init(sfud_flash *flash) {
    sfud_err result = SFUD_SUCCESS;

    switch (flash->index) {
        case SFUD_M25P_DEVICE_INDEX: {
            SPI_FLASH_Init();
            /* 同步 Flash 移植所需的接口及数据 */
            flash->spi.wr = spi_write_read;
            flash->spi.lock = spi_lock;
            flash->spi.unlock = spi_unlock;
            flash->spi.user_data = &spi2;
            /* about 100 microsecond delay */
            flash->retry.delay = retry_delay_100us;
            /* adout 60 seconds timeout */
            flash->retry.times = 60 * 10000;

            break;
        }
    }

    return result;
}
有了以上的适配移植代码,基本就可以在开发板上使用 SFUD 库来操作 Flash 芯片了,在主函数中编写个简单的功能测试样例,且添加上 #include <sfud.h> 包含语句。测试读写功能前,需要调用 sfud_init() 接口来初始化接口和整个库,具体的测试代码在此不贴出来可以参照附件工程。

三、移植 Littlefs 到 SPI Flash 上
前面提到过内嵌 Flash 存储数据时通常需要考虑用均衡负载和磨损等算法来延长 Flash 的寿命,其实在 SPI Nor Flash 上同样需要考虑这些因素,除了之前提过的小组件,还有一些嵌入式文件系统比较适合用于扩展 SPI Flash 中,这里先提到 littlefs 和 filex+levelx 2种方案,下面会着重讲述前者。Littlefs 是 Mbed OS 中的高完整性嵌入式文件系统,经过优化可与有限数量的 RAM 和 ROM 一起使用。它避免了递归,将动态内存限制为可配置的缓冲区,并且绝不会将整个存储块存储在 RAM 中。通过专注于一小组多用途数据结构,这种高完整性嵌入式文件系统使用的 ROM 比 FAT 少 13K,RAM 少 4K。Littlefs 可以用在自身不带坏块处理、磨损平衡等功能的内存芯片上;同时 Littlefs 也充分考虑了异常掉电情况下的数据保护。 该组件有以下特性:
  • 断电恢复。它需要强有力的保证文件系统保持一致,并且数据被刷新到底层存储。
  • 磨损均衡。 通常存储支持每个块的有限擦除次数,因此使用整个存储设备对可靠性很重要。
  • 占用空间小。物联网设备受到 ROM 和 RAM 的限制,占用资源小可以节省成本。
  • 坏块处理。
在 Git 仓中获取到源码 :https://github.com/littlefs-project/littlefs
将获取到的源码包添加到上面的工程中,并且添加头文件路径。这里可以将前面的 FAL 、SFUD 以及 littlefs 的关系捋一下:

在适配 littlefs 的过程中,主要需要实现以下几个与平台有关的接口,分别为 Flash 的读、写、擦除以及同步,前3个是必须的,最后一个可以直接返回 LFS_ERR_OK
static int lfs_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size);
static int lfs_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size);
static int lfs_flash_erase(const struct lfs_config *c, lfs_block_t block);
static int lfs_flash_sync(const struct lfs_config *c);
具体实现代码中使用到了 SFUD 的 API 函数,如下:
static int lfs_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
{
    uint32_t addr;
    addr = ((uint32_t)block << (uint32_t)12);
    addr += off;

    const sfud_flash *flash = sfud_get_device_table() + 0;

#ifdef SFUD_DEBUG_MODE
    char message[64];
    if (Flash_Debug_Flag)
    {
        memset(message, 0x00, sizeof(message));

        sprintf(message, "Flash Read Block is %d, Offset is %d, Size is %d\n\r", block, off, size);
        debug_log(message, strlen(message));
    }

#endif

    sfud_read(flash, addr, size, (uint8_t *)buffer);
    return LFS_ERR_OK;
}

static int
lfs_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
{
    uint32_t addr;
    addr = ((uint32_t)block << (uint32_t)12);
    addr += off;

    const sfud_flash *flash = sfud_get_device_table() + 0;

#ifdef SFUD_DEBUG_MODE
    char message[64];
    if (Flash_Debug_Flag)
    {
        memset(message, 0x00, sizeof(message));

        sprintf(message, "Flash Prog Block is %d, Offset is %d, Size if %d\n\r", block, off, size);
        debug_log(message, strlen(message));
    }

#endif

    sfud_write(flash, addr, size, (uint8_t *)buffer);
    return LFS_ERR_OK;
}

static int lfs_flash_erase(const struct lfs_config *c, lfs_block_t block)
{
    const sfud_flash *flash = sfud_get_device_table() + 0;

#ifdef SFUD_DEBUG_MODE
    char message[64];

    if (Flash_Debug_Flag)
    {
        memset(message, 0x00, sizeof(message));

        sprintf(message, "Erase Block is %d\n\r", block);
        debug_log(message, strlen(message));
    }

#endif

    sfud_erase(flash, block << 12, BLOCK_SIZE);
    return LFS_ERR_OK;
}

static int lfs_flash_sync(const struct lfs_config *c)
{
    return LFS_ERR_OK;
}
移植完接口函数后,需要使用 lfs_mem_init、lfs_mount、lfs_format 这几个 API 函数来对 littlefs 文件系统进行初始化和格式化,然后编写读写文件的功能测试函数,这里只做简单评估,并未开启时间戳计算操作时间,具体代码同样见附件,可以通过板载的 TFT-LCD 直接查看测试结果:

另外,再给大家分享一个东西,一般用到文件系统了就会想到 FAT 格式的能在电脑上查看文件的,但是 littlefs 是专门为嵌入式控制器而设计的,它的文件数据需要被 MTD 后才能被电脑识别,为了简化这个流程,有大神专门设计了一个 littlefs explorer for windows 上位机工具。使用该工具的流程如下:
  • 下载并安装好 osfmount.exe 文件系统挂载工具,按照 https://bluscape.blog/2019/10/01 ... r-lfse-for-windows/  中提到的说明进行操作



    2. 挂载好文件系统后之前的数据被分成了一个虚拟硬盘,可以打开 littlefs explorer for windows 上位机工具查看该盘中的文件情况

    3. 可以通过该上位机查看到 littlefs 内的文件了,但不知道是否我系统不适配的原因,无法通过记事本打开该文件,这是个疑问点。后面在重新打开该工具时,发现一直会弹出一个错误警告“无法删除前面的一个盘内容,退出”,一番操作后才知道,原来是需要删除工具目录下单 Temp 文件夹即可再次开启


四、将 bin 文件烧录到 SPI Flash 上的不同方法
前面几点内容都与应用相关,接着再来聊聊与工具相关的话题。大家都知道烧写片内 Flash 时一般都是用 Jlink 等烧录工具通过 JTAG/SWD 口配合一些上位机来完成的,但如何将已有的 bin 文件烧录到外部扩展的 SPI Nor Flash 中呢?是否也可以使用 Jlink ?做为 ARM 仿真调试器,Jlink 还真能烧写 SPI Flash 和读取存储器中的数据,这里会用到 JLink 软件包含的 JFlashSPI 工具中。
Jlink 内部集成了 SPI 协议,部分接口是做为 SPI 复用功能的,具体硬件连接如下图所示:

根据 MB-039 原理图找到对应接口后正确连接好,然后启动 JFlashSPI.exe 图形化工具,接着像操作熟悉的 J-Flash 一样,连接目标,读取整个芯片信息、数据保存以及烧录数据等等操作都极为雷同,下图为正确读出的 SPI Flash 信息,还包括了生产厂商信息,当通过观看丝印不能辨别时可以用此方法确定型号:


另外一种烧录外扩 SPI Flash 的方法是 使用 KEIL MDK 中的 FLM 烧录算法在烧录程序的时候同时将 c 数组数据烧录到外部 Flash 中,在安装目录下有提供模板工程,也可以参照 ST 的实现方式。简单来说,该方案实现框图如下:



具体实现流程可以参照 ST 的模板,我这里重点说明一下几点注意事项:
  • 编写 FlashPrg.c 文件的底层 SPI Flash 驱动函数时,尽可能使用寄存器方式而不用库函数,且该 SPI 底层配置是参照 MB-039 开发板制作的,如果换到其它板子上需要先看 SPI 引脚是否一致;
  • FlashPrg.c 文件中的几个 API 原型函数都必须得保留,因为 MDK 在驱动烧录算法工作时会有调用,如果删掉了则会导致整个流程中断 ;
  • 在分散加载文件的选择上,模板工程中的 Targetlin.sct 文件必须得用上 ;
  • 在需要烧录数据的工程中,data.c 文件属性中的 Memory Assignment 设置需要与工程设置中的 ROM IROM RAM IRAM 相匹配,特别注意 default 选项需要对应起来,另外在烧录工程中需要添加上前面生成的 FLM 算法且还可以指定数据需要烧录的位置 ;
  • 需要先使用 FLM data 工程进行烧录数据然后再运行前面的 sfud_littlefs 工程来验证烧录算法是否起到作用,也可以将这 2 个工程合并为一个工程 ;
  • 该 SPI Flash 能够被烧录 APP 代码,但是不能像 FSMC Nor Flash 一样能够在其中运行代码,因为与 AHB 不在同一总线上不能通过片内 flash 的 BootLoader 跳到片外 SPI Flash 去运行,除非是支持 XIP 的 QSPI 总线形式的外扩 Flash 。
  • const unsigned char flm_data[4*1024] 这种定义数组的形式需要注意如果未别代码使用,会有被编译器优化的可能 ;


下面是烧录算法工程中的 FlashPrg.c 和 FlashDev.c 实现代码:
<blockquote>#include "FlashOS.H"        // FlashOS Structures[backcolor=rgb(255, 255, 255)]struct FlashDevice const FlashDevice  =  {[/backcolor]
[backcolor=rgb(255, 255, 255)]   FLASH_DRV_VERS,             // Driver Version, do not modify![/backcolor]
[backcolor=rgb(255, 255, 255)]   "MM32F3270_M25P80 SPI Flash",//Device Name[/backcolor]
[backcolor=rgb(255, 255, 255)]   EXTSPI,                     // Device Type[/backcolor]
[backcolor=rgb(255, 255, 255)]   0xC0000000,                 // Device Start Address[/backcolor]
[backcolor=rgb(255, 255, 255)]   0x00800000,                 // Device Size in Bytes (8MB)[/backcolor]
[backcolor=rgb(255, 255, 255)]   256,                        // Programming Page Size[/backcolor]
[backcolor=rgb(255, 255, 255)]   0,                          // Reserved, must be 0[/backcolor]
[backcolor=rgb(255, 255, 255)]   0xFF,                       // Initial Content of Erased Memory[/backcolor]
[backcolor=rgb(255, 255, 255)]   6000,                        // Program Page Timeout 6000 mSec[/backcolor]
[backcolor=rgb(255, 255, 255)]   6000,                       // Erase Sector Timeout 6000 mSec[/backcolor]

[backcolor=rgb(255, 255, 255)]// Specify Size and Address of Sectors[/backcolor]
[backcolor=rgb(255, 255, 255)]   0x1000, 0x000000,          // Sector Size 4kB (2048 Sectors)[/backcolor]
[backcolor=rgb(255, 255, 255)]   SECTOR_END[/backcolor]
[backcolor=rgb(255, 255, 255)]};[/backcolor]
/******************************************************************************/
/*  This file is part of the ARM Toolchain package                            */
/*  Copyright KEIL ELEKTRONIK GmbH 2003 - 2008                                */
/******************************************************************************/
/*                                                                            */
/*  FlashPrg.C:  Flash Programming Functions adapted for External SPI Flash   */
/*               M25P80 programming with MM32F3270 MB-039 Board               */
/*                                                                            */
/******************************************************************************/
#include "spi_flash.h"
#include "FlashOS.H"        // FlashOS Structures


#define BUFFER_SIZE 256
#define BLOCK_SIZE 4096
#define BOLCK_NUM 2048  // 2M--512   8M--2048   16M--4096

#define M8(adr)  (*((vu8  *) (adr)))
#define M16(adr) (*((vu16 *) (adr)))
#define M32(adr) (*((vu32 *) (adr)))

unsigned char aux_buf[256];
unsigned long base_adr;        // Base Address


/*- Init (...) -----------------------------------------------------------------
*
*  Initialize Flash Programming Functions
*    Parameter:      adr:  Device Base Address
*                    clk:  Clock Frequency (Hz)
*                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
*    Return Value:   0 - OK,  1 - Failed
*/

int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {

    base_adr = adr;
    SPI_FLASH_Init();

  return (0);
}


/*- UnInit (...) ---------------------------------------------------------------
*
*  De-Initialize Flash Programming Functions
*    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
*    Return Value:   0 - OK,  1 - Failed
*/

int UnInit (unsigned long fnc) {

//    SPI_FLASH_Init();
  return (0);
}


/*
*  Erase complete Flash Memory
*    Return Value:   0 - OK,  1 - Failed
*/
int EraseChip (void) {

    /* Add your Code */
    SPI_FLASH_ChipErase();
    return (0);                     // Finished without Errors
}

/*- EraseSector (...) ----------------------------------------------------------
*
*  Erase Sector in Flash Memory
*    Parameter:      adr:  Sector Address
*    Return Value:   0 - OK,  1 - Failed
*/

int EraseSector (unsigned long adr)
{
    SPI_FLASH_SectorErase((adr-base_adr));
   
    return (0);                                   // Done
}


/*- BlankCheck (...) -----------------------------------------------------------
*
*  Blank Check Checks if Memory is Blank
*    Parameter:      adr:  Block Start Address
*                    sz:   Block Size (in bytes)
*                    pat:  Block Pattern
*    Return Value:   0 - OK,  1 - Failed
*/

int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat) {

  return (1);                                   // Always Force Erase
}


/*- ProgramPage (...) ----------------------------------------------------------
*
*  Program Page in Flash Memory
*    Parameter:      adr:  Page Start Address
*                    sz:   Page Size
*                    buf:  Page Data
*    Return Value:   0 - OK,  1 - Failed
*/

int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {

    SPI_FLASH_PageProgram(adr-base_adr,buf,sz);
   
    return (0);                                   // Done
}


/*- Verify (...) ---------------------------------------------------------------
*
*  Verify Flash Contents
*    Parameter:      adr:  Start Address
*                    sz:   Size (in bytes)
*                    buf:  Data
*    Return Value:   (adr+sz) - OK, Failed Address
*/

unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf) {

    int i;

    SPI_FLASH_ReadData(adr-base_adr,aux_buf,sz);
   
    for (i = 0; i< 256; i++) {
        if (aux_buf[i] != buf[i])
            return (adr+i);                   // Verification Failed (return address)
    }

    return (adr+sz);                      // Done successfully
}
可以通过验证工程进行测试验证,数据是否有被下载到指定的外扩 SPI Flash 地址中去:

至此,验证 ok ,已经全部分享完了如何换个方式去玩 SPI Nor Flash ,在附件中还会分享一个正点原子的工具 C2B.exe ,该工具可以将 bin 文件和 c 数组来回转换,极大方便了用户。

五、附件内容
附件资源包中有以下内容可供参考下载:
  • 基于 MB-039 开发板的 SPI Flash 测试工程、烧录算法工程以及数据烧录工程 —— 01. F3270_M25P80_SFUD_LittleFS_FLM Data.zip
  • 文中提及的 littlefs 上位机工具以及bin转数组工具 —— 02. Tools 下载地址.zip
  • MB-039 开发板的原理图以及 M25P80 SPI Nor Flash 手册及其它应用说明参考文档 —— 03. 硬件原理图以及参考 AN 和 SPEC.zip

01. F3270_M25P80_SFUD_LittleFS_FLM Data.zip (6.63 MB)
02. Tools 下载地址.zip (312 Bytes)
03. 硬件原理图以及参考 AN 和 SPEC.zip (4.23 MB)


使用特权

评论回复

打赏榜单

21小跑堂 打赏了 150.00 元 2022-06-02
理由:恭喜通过原创文章审核!请多多加油哦!

评论
90houyidai 2022-6-6 12:26 回复TA
不错的spiflash操作方法 
21小跑堂 2022-6-2 10:37 回复TA
使用 SFUD组件灵活操作板载M25P80 FLASH芯片,同时移植了Littlefs 来均衡负载和减少FLASH磨损,在一定程度上可加速SPI FLASH芯片的开发。灵活高效的使用工具,这也是高手! 
沙发
xiaoqi976633690| | 2022-6-11 18:01 | 只看该作者
谢谢分享..............

使用特权

评论回复
板凳
麻花油条| | 2022-6-17 11:12 | 只看该作者
看看,了解一下,感谢分享

使用特权

评论回复
地板
tail066| | 2022-6-18 21:41 | 只看该作者
最近坛友们玩的都很高级啊

使用特权

评论回复
5
gyh974| | 2022-6-21 11:04 | 只看该作者
好高级的样子

使用特权

评论回复
6
tpgf| | 2022-7-3 09:54 | 只看该作者
现在主流的文件系统都是什么系统啊

使用特权

评论回复
7
nawu| | 2022-7-3 09:58 | 只看该作者
这个还需要用到其他工具啊

使用特权

评论回复
8
chenjun89| | 2022-7-3 10:17 | 只看该作者
写的详细,谢谢讲解。

使用特权

评论回复
9
aoyi| | 2022-7-3 10:20 | 只看该作者
都还没有用的这么深入啊

使用特权

评论回复
10
zljiu| | 2022-7-3 10:28 | 只看该作者
这种系统数据的吞吐量是多少啊

使用特权

评论回复
11
gwsan| | 2022-7-3 10:38 | 只看该作者
楼主真的是大拿啊

使用特权

评论回复
12
tfqi| | 2022-7-3 10:49 | 只看该作者
一颗独立的flash吗

使用特权

评论回复
发新帖 本帖赏金 150.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

204

帖子

10

粉丝