• STM32 GD32 标准库移植SFUD


    本次移植是在官方源码的基础上进行移植的

    本次介绍的两个软件包SFUD/FAL都与FLASH有关,并且都可以独立使用或者结合在一起使用,两个软件包都对操作系统无依赖,可以使用裸机移植,也很方便移植到各种系统。
    这两个软件包的作者都是armink,armink的开源仓库地址:https://github.com/armink,更多好玩的软件,请到作者仓库查询。
     

    下面给出官方源码的下载链接 

    https://gitee.com/Armink/SFUD

    0.SFUD 是什么

    SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。

    • 主要特点:支持 SPI/QSPI 接口、面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址
    • 资源占用
      • 标准占用:RAM:0.2KB ROM:5.5KB
      • 最小占用:RAM:0.1KB ROM:3.6KB
    • 设计思路:
      • 什么是 SFDP :它是 JEDEC (固态技术协会)制定的串行 Flash 功能的参数表标准,最新版 V1.6B (点击这里查看)。该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数。
      • 不支持 SFDP 怎么办 :如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( /sfud/inc/sfud_flash_def.h ) 中提供的 Flash 参数信息表 中是否支持该款 Flash。如果不支持,则可以在配置文件中添加该款 Flash 的参数信息(添加方法详细见 2.5 添加库目前不支持的 Flash)。获取到了 Flash 的规格参数后,就可以实现对 Flash 的全部操作。

    1、为什么选择 SFUD

    • 避免项目因 Flash 缺货、Flash 停产或产品扩容而带来的风险;
    • 越来越多的项目将固件存储到串行 Flash 中,例如:ESP8266 的固件、主板中的 BIOS 及其他常见电子产品中的固件等等,但是各种 Flash 规格及命令不统一。使用 SFUD 即可避免,在相同功能的软件平台基础下,无法适配不同 Flash 种类的硬件平台的问题,提高软件的可重用性;
    • 简化软件流程,降低开发难度。现在只需要配置好 SPI 通信,即可畅快的开始玩串行 Flash 了;
    • 可以用来制作 Flash 编程器/烧写器

    相关的说明大家可以进去链接详细的看一下

    目前支持的FLASH类型列表
    在这里插入图片描述

    总结一下:SFUD就是一个SPI-FLASH的底层驱动,只要是遵循 JEDEC (固态技术协会)的芯片都是可以适配的,目前我理解的好处就是如果更换了硬件,只要是这个表里面的,就不需要改代码!!!

     

    RT-Thread中flash管理 — [SFUD组件 和 FAL驱动组件介绍]

    本来项目上是想用FlashDB  

    在移植嵌入式数据库的基础 上   首先第一步大多数都是先移植SFUD

    使用rtthread移植

            在移植过程中一定要参考两个资料:项目的readme文档和demo工程。
    对于使用rtthread完整版来说,作者已经把SFUD制作成了rtthread的内置组件了,对于使用者只需要勾选就可以了:

    标准库移植:

    工具准备
    STM32CubeMX:用于配置QSPI外设和串口外设,并生成 MDK 工程;

    本文采用的模板是固件库版本
    Keil MDK:用于编译和下载工程;
    串口助手:用于查看开发板串口调试信息输出;
    裸机工程准备
    下载完源码后    直接将源码中的SFUD相关的.c  .h文件  添加到你准备的工程模板文件中

    • 先去下载库,然后放入自己的工程中(先不动),基本上就是这个样子的
      在这里插入图片描述
    • 接着就是修改头文件了
      基本上不需要修改什么,先把配置文件改一下

     

     

    sfud_cfg.h

    填写相关枚举与宏。因为事例工程所使用的芯片STM32F429IG并没有QSPI功能,可以注释掉。

    如果Flash型号支持SFDP,可以注释SFUD_USING_SFDP。可以减少需要编译的代码量。

    1. /*
    2. * This file is part of the Serial Flash Universal Driver Library.
    3. *
    4. * Copyright (c) 2016-2018, Armink,
    5. *
    6. * Permission is hereby granted, free of charge, to any person obtaining
    7. * a copy of this software and associated documentation files (the
    8. * 'Software'), to deal in the Software without restriction, including
    9. * without limitation the rights to use, copy, modify, merge, publish,
    10. * distribute, sublicense, and/or sell copies of the Software, and to
    11. * permit persons to whom the Software is furnished to do so, subject to
    12. * the following conditions:
    13. *
    14. * The above copyright notice and this permission notice shall be
    15. * included in all copies or substantial portions of the Software.
    16. *
    17. * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
    18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    20. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    21. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    22. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    23. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    24. *
    25. * Function: It is the configure head file for this library.
    26. * Created on: 2016-04-23
    27. */
    28. #ifndef _SFUD_CFG_H_
    29. #define _SFUD_CFG_H_
    30. //#define SFUD_DEBUG_MODE //使能SFUD的打印日志
    31. #define SFUD_USING_SFDP //使能SFDP:JEDEC标准(JESD216)标准接口 注意:关闭后只会查询该库在 /sfud/inc/sfud_flash_def.h 中提供的 Flash 信息表。\
    32. 这样虽然会降低软件的适配性,但减少代码量。
    33. #define SFUD_USING_FLASH_INFO_TABLE //是否使用该库自带的 Flash 参数信息表注意:关闭后该库只驱动支持 SFDP 规范的 Flash,也会适当的降低部分代码量。\
    34. 另外 2.3.22.3.3 这两个宏定义至少定义一种,也可以两种方式都选择
    35. //支持多路外设
    36. enum
    37. {
    38. SFUD_W25Q64_DEVICE_INDEX = 0,
    39. };
    40. //把实际使用的硬件和外设接口对应配置好
    41. #define SFUD_FLASH_DEVICE_TABLE \
    42. { \
    43. [SFUD_W25Q64_DEVICE_INDEX] = {.name = "W25Q64JV", .spi.name = "SPI1"}, \
    44. }
    45. #define SFUD_USING_QSPI
    46. #endif /* _SFUD_CFG_H_ */

    判断芯片支持SFUD

    1.FLASH芯片支持SFDP标准;(在芯片datesheet中全局搜索SFDP)。
    2.如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( \sfud\inc\sfud_flash_def.h ) 中提供的 Flash 参数信息表 是否支持该款 Flash。

    1. #ifdef SFUD_USING_FLASH_INFO_TABLE
    2. /* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
    3. * then the SFUD will find the flash chip information by this table. You can add other flash to here then
    4. * notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
    5. * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
    6. */
    7. #define SFUD_FLASH_CHIP_TABLE \
    8. { \
    9. {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81}, \
    10. {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    11. {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    12. {"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    13. {"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    14. {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    15. {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    16. {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    17. {"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
    18. {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
    19. {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
    20. {"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
    21. {"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
    22. {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    23. {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    24. {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    25. {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    26. {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    27. {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    28. {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
    29. {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
    30. }
    31. #endif /* SFUD_USING_FLASH_INFO_TABLE */


     

    但是在添加spi来读取外部FLASH的时候,遇到了一些情况。

    例如,找不到设备,或者说无法读取FLASH的ID。

    读出的设备ID、厂商ID等是正确的,但是检测出来不符合SFDP标准。

    1. [SFUD](..\..\..\sfud\src\sfud.c:116) Start initialize Serial Flash Universal Driver(SFUD) V1.1.0.
    2. [SFUD](..\..\..\sfud\src\sfud.c:117) You can get the latest version on https://github.com/armink/SFUD .
    3. [SFUD](..\..\..\sfud\src\sfud.c:861) The flash device manufacturer ID is 0xEF, memory type ID is 0x40, capacity ID is 0x16.
    4. [SFUD](..\..\..\sfud\src\sfud_sfdp.c:122) Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P').
    5. [SFUD]Warning: Read SFDP parameter header information failed. The W25Q32BV is not support JEDEC SFDP.
    6. [SFUD]Find a Winbond W25Q32BV flash chip. Size is 4194304 bytes.
    7. [SFUD](..\..\..\sfud\src\sfud.c:840) Flash device reset success.
    8. [SFUD]W25Q32BV flash device is initialize success.
    9. [SFUD]Error: Can't enable write status.
    10. Erase the W25Q32BV flash data failed.

    sfud_port.c 修改引脚

     以上全部修改完之后

    在主函数添加测试代码

    1. static void sfud_demo(uint32_t addr, size_t size, uint8_t *data)
    2. {
    3. sfud_err result = SFUD_SUCCESS;
    4. const sfud_flash *flash = sfud_get_device_table() + 0;
    5. size_t i;
    6. /* prepare write data */
    7. for (i = 0; i < size; i++)
    8. {
    9. data[i] = i;
    10. }
    11. /* erase test */
    12. result = sfud_erase(flash, addr, size);
    13. if (result == SFUD_SUCCESS)
    14. {
    15. RTT_Printf("Erase the %s flash data finish. Start from 0x%08X, size is %ld.\r\n", flash->name, addr,
    16. size);
    17. }
    18. else
    19. {
    20. RTT_Printf("Erase the %s flash data failed.\r\n", flash->name);
    21. return;
    22. }
    23. /* write test */
    24. result = sfud_write(flash, addr, size, data);
    25. if (result == SFUD_SUCCESS)
    26. {
    27. RTT_Printf("Write the %s flash data finish. Start from 0x%08X, size is %ld.\r\n", flash->name, addr,
    28. size);
    29. }
    30. else
    31. {
    32. RTT_Printf("Write the %s flash data failed.\r\n", flash->name);
    33. return;
    34. }
    35. /* read test */
    36. result = sfud_read(flash, addr, size, data);
    37. if (result == SFUD_SUCCESS)
    38. {
    39. RTT_Printf("Read the %s flash data success. Start from 0x%08X, size is %ld. The data is:\r\n", flash->name, addr,
    40. size);
    41. RTT_Printf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
    42. for (i = 0; i < size; i++)
    43. {
    44. if (i % 16 == 0)
    45. {
    46. RTT_Printf("[%08X] ", addr + i);
    47. }
    48. RTT_Printf("%02X ", data[i]);
    49. if (((i + 1) % 16 == 0) || i == size - 1)
    50. {
    51. RTT_Printf("\r\n");
    52. }
    53. }
    54. RTT_Printf("\r\n");
    55. }
    56. else
    57. {
    58. RTT_Printf("Read the %s flash data failed.\r\n", flash->name);
    59. }
    60. /* data check */
    61. for (i = 0; i < size; i++)
    62. {
    63. if (data[i] != i % 256)
    64. {
    65. RTT_Printf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
    66. break;
    67. }
    68. }
    69. if (i == size)
    70. {
    71. RTT_Printf("The %s flash test is success.\r\n", flash->name);
    72. }
    73. }

    打印的结果显示

     

     关于HAL库移植SFUD 参考以下的文章

    SFUD移植 - 简书

    本文中所使用的裸机工程是基于HAL库的,在SFUD源码的Demo中也有一份HAL库的工程,因为基于HAL库的移植接口实现都是一样的,所以我直接将Demo中的sfud_port.c文件复制过来替换:

    推荐看官方文档SFUD: 一款使用 JEDEC SFDP 标准的串行 (SPI) Flash 通用驱动库 (gitee.com)

    FLASH芯片型号:W25Q64JVSSIQ
    RTOS:liteos-m
    MCU:STM32F407ZGT6 HAL库

    判断芯片支持SFUD

    1.FLASH芯片支持SFDP标准;(在芯片datesheet中全局搜索SFDP)。
    2.如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( \sfud\inc\sfud_flash_def.h ) 中提供的 Flash 参数信息表 是否支持该款 Flash。

    1. #ifdef SFUD_USING_FLASH_INFO_TABLE
    2. /* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
    3. * then the SFUD will find the flash chip information by this table. You can add other flash to here then
    4. * notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
    5. * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
    6. */
    7. #define SFUD_FLASH_CHIP_TABLE \
    8. { \
    9. {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81}, \
    10. {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    11. {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    12. {"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    13. {"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    14. {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    15. {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    16. {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    17. {"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
    18. {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
    19. {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
    20. {"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
    21. {"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
    22. {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    23. {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    24. {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    25. {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    26. {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    27. {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
    28. {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
    29. {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
    30. }
    31. #endif /* SFUD_USING_FLASH_INFO_TABLE */

    3.如果列表中还没有你用的芯片,按照格式要求在表的末尾添加自己的芯片即可。

    接口适配

    1.\sfud\inc\sfud_cfg.h

     

    1. #ifndef _SFUD_CFG_H_
    2. #define _SFUD_CFG_H_
    3. #define SFUD_DEBUG_MODE
    4. #define SFUD_USING_SFDP
    5. // #define SFUD_USING_FLASH_INFO_TABLE
    6. enum {
    7. SFUD_W25Q64JV_DEVICE_INDEX = 0,
    8. };
    9. #define SFUD_FLASH_DEVICE_TABLE \
    10. { \
    11. [SFUD_W25Q64JV_DEVICE_INDEX] = {.name = "W25Q64JV", .spi.name = "SPI2"}, \
    12. }
    13. // #define SFUD_USING_QSPI
    14. #endif /* _SFUD_CFG_H_ */

    我这里用的是FLASH芯片支持SFDP标准,用的是SPI通信。所以可以屏蔽掉SFUD_USING_FLASH_INFO_TABLE和SFUD_USING_QSPI两个宏。
    SFUD_FLASH_DEVICE_TABLE中定义了自己使用的FLASH芯片。

    2.spi.c

    配置完成后,需要初始化spi。我这里使用cubemx生成spi初始化代码。并增加spi_cs的初始化代码。为了让SFUD能支持一个MCU通过任意SPI接任意数量的FLASH芯片,增加spi_cs_low()spi2_cs_high()。源码如下:

    1. /* Includes ------------------------------------------------------------------*/
    2. #include "spi.h"
    3. /* USER CODE BEGIN 0 */
    4. #define SPI2_CS_PIN GPIO_PIN_0
    5. #define SPI2_CS_GPIO_PORT GPIOC
    6. /* USER CODE END 0 */
    7. SPI_HandleTypeDef hspi2;
    8. /* SPI2 init function */
    9. void MX_SPI2_Init(void)
    10. {
    11. /* USER CODE BEGIN SPI2_Init 0 */
    12. /* USER CODE END SPI2_Init 0 */
    13. /* USER CODE BEGIN SPI2_Init 1 */
    14. /* USER CODE END SPI2_Init 1 */
    15. hspi2.Instance = SPI2;
    16. hspi2.Init.Mode = SPI_MODE_MASTER;
    17. hspi2.Init.Direction = SPI_DIRECTION_2LINES;
    18. hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
    19. hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
    20. hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
    21. hspi2.Init.NSS = SPI_NSS_SOFT;
    22. hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
    23. hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
    24. hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
    25. hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    26. hspi2.Init.CRCPolynomial = 10;
    27. if (HAL_SPI_Init(&hspi2) != HAL_OK)
    28. {
    29. Error_Handler();
    30. }
    31. /* USER CODE BEGIN SPI2_Init 2 */
    32. /* USER CODE END SPI2_Init 2 */
    33. }
    34. void HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
    35. {
    36. GPIO_InitTypeDef GPIO_InitStruct = {0};
    37. if (spiHandle->Instance == SPI2)
    38. {
    39. /* USER CODE BEGIN SPI2_MspInit 0 */
    40. /* USER CODE END SPI2_MspInit 0 */
    41. /* SPI2 clock enable */
    42. __HAL_RCC_SPI2_CLK_ENABLE();
    43. __HAL_RCC_GPIOC_CLK_ENABLE();
    44. __HAL_RCC_GPIOB_CLK_ENABLE();
    45. /**SPI2 GPIO Configuration
    46. PC2 ------> SPI2_MISO
    47. PC3 ------> SPI2_MOSI
    48. PB10 ------> SPI2_SCK
    49. */
    50. GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
    51. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    52. GPIO_InitStruct.Pull = GPIO_NOPULL;
    53. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    54. GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    55. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    56. GPIO_InitStruct.Pin = GPIO_PIN_10;
    57. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    58. GPIO_InitStruct.Pull = GPIO_NOPULL;
    59. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    60. GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    61. HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    62. /* USER CODE BEGIN SPI2_MspInit 1 */
    63. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
    64. /*Configure GPIO pin : SPI_FLASH_CS_Pin */
    65. GPIO_InitStruct.Pin = GPIO_PIN_0;
    66. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    67. GPIO_InitStruct.Pull = GPIO_NOPULL;
    68. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    69. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    70. /* USER CODE END SPI2_MspInit 1 */
    71. }
    72. }
    73. void HAL_SPI_MspDeInit(SPI_HandleTypeDef *spiHandle)
    74. {
    75. if (spiHandle->Instance == SPI2)
    76. {
    77. /* USER CODE BEGIN SPI2_MspDeInit 0 */
    78. /* USER CODE END SPI2_MspDeInit 0 */
    79. /* Peripheral clock disable */
    80. __HAL_RCC_SPI2_CLK_DISABLE();
    81. /**SPI2 GPIO Configuration
    82. PC2 ------> SPI2_MISO
    83. PC3 ------> SPI2_MOSI
    84. PB10 ------> SPI2_SCK
    85. */
    86. HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);
    87. HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10);
    88. /* USER CODE BEGIN SPI2_MspDeInit 1 */
    89. /* USER CODE END SPI2_MspDeInit 1 */
    90. }
    91. }
    92. /* USER CODE BEGIN 1 */
    93. void spi2_cs_low(void)
    94. {
    95. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
    96. }
    97. void spi2_cs_high(void)
    98. {
    99. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
    100. }
    101. /* USER CODE END 1 */

    3.\sfud\port\sfud_port.c

    为了让SFUD能支持一个MCU通过任意SPI接任意数量的FLASH芯片,定义了结构体sfud_port_t
    sfud_spi_port_init()sfud_port_t初始化。源码如下:

    1. #include <sfud.h>
    2. #include <stdarg.h>
    3. #include "main.h"
    4. #include "los_mux.h"
    5. #include "los_task.h"
    6. #include "los_memory.h"
    7. #include "string.h"
    8. #include "spi.h"
    9. static char log_buf[256];
    10. typedef struct
    11. {
    12. SPI_HandleTypeDef *hspi;
    13. UINT32 lock;
    14. void (*cs_low)(void);
    15. void (*cs_high)(void);
    16. } sfud_port_t;
    17. void sfud_log_debug(const char *file, const long line, const char *format, ...);
    18. static void spi_lock(const struct __sfud_spi *spi)
    19. {
    20. sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
    21. LOS_MuxPend(sfud_port->lock, 1000);
    22. }
    23. static void spi_unlock(const struct __sfud_spi *spi)
    24. {
    25. sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
    26. LOS_MuxPost(sfud_port->lock);
    27. }
    28. /**
    29. * SPI write data then read data
    30. */
    31. static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
    32. size_t read_size)
    33. {
    34. sfud_err result = SFUD_SUCCESS;
    35. sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
    36. // uint8_t send_data, read_data;
    37. /**
    38. * add your spi write and read code
    39. */
    40. sfud_port->cs_low();
    41. if (HAL_SPI_Transmit(sfud_port->hspi, (uint8_t *)write_buf, write_size, 3000) != HAL_OK)
    42. {
    43. // sfud_log_debug(__FILE__, __LINE__, "spi write fail.");
    44. result = SFUD_ERR_WRITE;
    45. goto ERR;
    46. }
    47. if ((read_buf != NULL) && (read_size != 0))
    48. {
    49. if (HAL_SPI_Receive(sfud_port->hspi, read_buf, read_size, 3000) != HAL_OK)
    50. {
    51. // sfud_log_debug(__FILE__, __LINE__, "spi read fail.");
    52. result = SFUD_ERR_READ;
    53. }
    54. }
    55. // return result;
    56. ERR:
    57. sfud_port->cs_high();
    58. return result;
    59. }
    60. #ifdef SFUD_USING_QSPI
    61. /**
    62. * read flash data by QSPI
    63. */
    64. static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
    65. uint8_t *read_buf, size_t read_size)
    66. {
    67. sfud_err result = SFUD_SUCCESS;
    68. /**
    69. * add your qspi read flash data code
    70. */
    71. return result;
    72. }
    73. #endif /* SFUD_USING_QSPI */
    74. static void retry_delay(void)
    75. {
    76. LOS_TaskDelay(1);
    77. }
    78. sfud_err sfud_spi_port_init(sfud_flash *flash)
    79. {
    80. sfud_err result = SFUD_SUCCESS;
    81. sfud_port_t *sfud_port = NULL;
    82. /**
    83. * add your port spi bus and device object initialize code like this:
    84. * 1. rcc initialize
    85. * 2. gpio initialize
    86. * 3. spi device initialize
    87. * 4. flash->spi and flash->retry item initialize
    88. * flash->spi.wr = spi_write_read; //Required
    89. * flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable
    90. * flash->spi.lock = spi_lock;
    91. * flash->spi.unlock = spi_unlock;
    92. * flash->spi.user_data = &spix;
    93. * flash->retry.delay = null;
    94. * flash->retry.times = 10000; //Required
    95. */
    96. /*Configure GPIO pin Output Level */
    97. if (strcmp(flash->spi.name, "SPI2") == 0)
    98. {
    99. MX_SPI2_Init();
    100. sfud_port = LOS_MemAlloc(m_aucSysMem0, sizeof(sfud_port_t));
    101. if (sfud_port == NULL)
    102. {
    103. sfud_log_debug(__FILE__, __LINE__, "memory alloc fail.");
    104. goto ERR_MEMALLOC;
    105. }
    106. if (LOS_MuxCreate(&sfud_port->lock) != LOS_OK)
    107. {
    108. sfud_log_debug(__FILE__, __LINE__, "mux create fail.");
    109. result = SFUD_ERR_NOT_FOUND;
    110. goto ERR_MUX;
    111. }
    112. sfud_port->hspi = &hspi2;
    113. sfud_port->cs_high = spi2_cs_high;
    114. sfud_port->cs_low = spi2_cs_low;
    115. flash->spi.wr = spi_write_read; // Required
    116. flash->spi.lock = spi_lock;
    117. flash->spi.unlock = spi_unlock;
    118. flash->spi.user_data = sfud_port;
    119. flash->retry.delay = retry_delay;
    120. flash->retry.times = 10000; // Required
    121. }
    122. else
    123. {
    124. result = SFUD_ERR_NOT_FOUND;
    125. }
    126. return result;
    127. ERR_MUX:
    128. LOS_MemFree(m_aucSysMem0, sfud_port);
    129. ERR_MEMALLOC:
    130. HAL_SPI_DeInit(sfud_port->hspi);
    131. return result;
    132. }
    133. /**
    134. * This function is print debug info.
    135. *
    136. * @param file the file which has call this function
    137. * @param line the line number which has call this function
    138. * @param format output format
    139. * @param ... args
    140. */
    141. void sfud_log_debug(const char *file, const long line, const char *format, ...)
    142. {
    143. va_list args;
    144. /* args point to the first variable parameter */
    145. va_start(args, format);
    146. printf("[SFUD](%s:%ld) ", file, line);
    147. /* must use vprintf to print */
    148. vsnprintf(log_buf, sizeof(log_buf), format, args);
    149. printf("%s\n", log_buf);
    150. va_end(args);
    151. }
    152. /**
    153. * This function is print routine info.
    154. *
    155. * @param format output format
    156. * @param ... args
    157. */
    158. void sfud_log_info(const char *format, ...)
    159. {
    160. va_list args;
    161. /* args point to the first variable parameter */
    162. va_start(args, format);
    163. printf("[SFUD]");
    164. /* must use vprintf to print */
    165. vsnprintf(log_buf, sizeof(log_buf), format, args);
    166. printf("%s\n", log_buf);
    167. va_end(args);
    168. }

    跑起来

    业务层/应用层只需要关注sfud.h中的api。初始化过程如下:

    1. sfud_flash *w25q64_handle;
    2. sfud_init();
    3. // SFUD_W25Q64JV_DEVICE_INDEX在sfud_cfg.h中自己定义。
    4. w25q64_handle = sfud_get_device(SFUD_W25Q64JV_DEVICE_INDEX);

    获得句柄w25q64_handle后,就调用读、写、擦除等API了。
    测试结果如下:

    1. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:116) Start initialize Serial Flash Universal Driver(SFUD) V1.1.0.
    2. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:117) You can get the latest version on https://github.com/armink/SFUD .
    3. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:861) The flash device manufacturer ID is 0xEF, memory type ID is 0x40, capacity ID is 0x17.
    4. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:131) Check SFDP header is OK. The reversion is V1.5, NPN is 0.
    5. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:173) Check JEDEC basic flash parameter header is OK. The table id is 0, reversion is V1.5, length is 16, parameter table pointer is 0x000080.
    6. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:203) JEDEC basic flash parameter table info:
    7. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:204) MSB-LSB 3 2 1 0
    8. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0001] 0xFF 0xF9 0x20 0xE5
    9. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0002] 0x03 0xFF 0xFF 0xFF
    10. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0003] 0x6B 0x08 0xEB 0x44
    11. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0004] 0xBB 0x42 0x3B 0x08
    12. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0005] 0xFF 0xFF 0xFF 0xFE
    13. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0006] 0x00 0x00 0xFF 0xFF
    14. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0007] 0xEB 0x40 0xFF 0xFF
    15. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0008] 0x52 0x0F 0x20 0x0C
    16. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0009] 0x00 0x00 0xD8 0x10
    17. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:215) 4 KB Erase is supported throughout the device. Command is 0x20.
    18. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:234) Write granularity is 64 bytes or larger.
    19. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:245) Target flash status register is non-volatile.
    20. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:271) 3-Byte only addressing.
    21. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:305) Capacity is 8388608 Bytes.
    22. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:311) Flash device supports 4KB block erase. Command is 0x20.
    23. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:311) Flash device supports 32KB block erase. Command is 0x52.
    24. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:311) Flash device supports 64KB block erase. Command is 0xD8.
    25. [SFUD]Find a Winbond flash chip. Size is 8388608 bytes.
    26. [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:840) Flash device reset success.
    27. [SFUD]W25Q64JV flash device is initialize success.

    参考文章

    关于万能SPI-Flash驱动库SFUD移植心得

    万能SPI-Flash驱动库SFUD移植心得 (amobbs.com 阿莫电子论坛 - 东莞阿莫电子网站)

     SFUD | 一款串行 Flash 通用驱动库

    SFUD | 一款串行 Flash 通用驱动库_Mculover666的博客-CSDN博客

    SFUD简介和移植感受_¥风笛¥的博客-CSDN博客

    【学习笔记】RT-Thread中flash管理 — [SFUD组件 和 FAL驱动组件介绍]_黄逸芬的博客-CSDN博客

  • 相关阅读:
    【RabbitMQ】常用消息模型详解
    【前端精进之路】JS篇:第11期 深拷贝与浅拷贝
    D-Desthiobiotin Amine_D-脱硫生物素-胺相关的产品性质
    面试必考精华版Leetcode994. 腐烂的橘子
    代码随想录训练营day46, 单词拆分和多重背包
    机器学习之线性回归
    C语言重点突破(2)指针(二)
    AWS香港Web3方案日,防御云安全实践案例受关注
    Pandas数据分析27——pandas画各类图形以及用法参数详解
    机器学习---CNN(创建和训练一个卷积神经网络并评估其性能)下
  • 原文地址:https://blog.csdn.net/qq_36958104/article/details/127920360