本次移植是在官方源码的基础上进行移植的
本次介绍的两个软件包SFUD/FAL都与FLASH有关,并且都可以独立使用或者结合在一起使用,两个软件包都对操作系统无依赖,可以使用裸机移植,也很方便移植到各种系统。
这两个软件包的作者都是armink,armink的开源仓库地址:https://github.com/armink,更多好玩的软件,请到作者仓库查询。
下面给出官方源码的下载链接
SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。
相关的说明大家可以进去链接详细的看一下
目前支持的FLASH类型列表
总结一下:SFUD就是一个SPI-FLASH的底层驱动,只要是遵循 JEDEC (固态技术协会)的芯片都是可以适配的,目前我理解的好处就是如果更换了硬件,只要是这个表里面的,就不需要改代码!!!
本来项目上是想用FlashDB
在移植嵌入式数据库的基础 上 首先第一步大多数都是先移植SFUD
工具准备STM32CubeMX:用于配置QSPI外设和串口外设,并生成 MDK 工程;
本文采用的模板是固件库版本
Keil MDK:用于编译和下载工程;
串口助手:用于查看开发板串口调试信息输出;
裸机工程准备
下载完源码后 直接将源码中的SFUD相关的.c .h文件 添加到你准备的工程模板文件中


填写相关枚举与宏。因为事例工程所使用的芯片STM32F429IG并没有QSPI功能,可以注释掉。
如果Flash型号支持SFDP,可以注释SFUD_USING_SFDP。可以减少需要编译的代码量。
- /*
- * This file is part of the Serial Flash Universal Driver Library.
- *
- * Copyright (c) 2016-2018, Armink,
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * 'Software'), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * Function: It is the configure head file for this library.
- * Created on: 2016-04-23
- */
-
- #ifndef _SFUD_CFG_H_
- #define _SFUD_CFG_H_
-
- //#define SFUD_DEBUG_MODE //使能SFUD的打印日志
-
- #define SFUD_USING_SFDP //使能SFDP:JEDEC标准(JESD216)标准接口 注意:关闭后只会查询该库在 /sfud/inc/sfud_flash_def.h 中提供的 Flash 信息表。\
- 这样虽然会降低软件的适配性,但减少代码量。
-
- #define SFUD_USING_FLASH_INFO_TABLE //是否使用该库自带的 Flash 参数信息表注意:关闭后该库只驱动支持 SFDP 规范的 Flash,也会适当的降低部分代码量。\
- 另外 2.3.2 及 2.3.3 这两个宏定义至少定义一种,也可以两种方式都选择
-
- //支持多路外设
-
- enum
- {
- SFUD_W25Q64_DEVICE_INDEX = 0,
- };
- //把实际使用的硬件和外设接口对应配置好
- #define SFUD_FLASH_DEVICE_TABLE \
- { \
- [SFUD_W25Q64_DEVICE_INDEX] = {.name = "W25Q64JV", .spi.name = "SPI1"}, \
- }
-
- #define SFUD_USING_QSPI
-
- #endif /* _SFUD_CFG_H_ */
-
-
-
-
1.FLASH芯片支持SFDP标准;(在芯片datesheet中全局搜索SFDP)。
2.如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( \sfud\inc\sfud_flash_def.h ) 中提供的 Flash 参数信息表 是否支持该款 Flash。
- #ifdef SFUD_USING_FLASH_INFO_TABLE
- /* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
- * then the SFUD will find the flash chip information by this table. You can add other flash to here then
- * notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
- * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
- */
- #define SFUD_FLASH_CHIP_TABLE \
- { \
- {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81}, \
- {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
- {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
- {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
- {"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
- {"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
- {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
- {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
- }
- #endif /* SFUD_USING_FLASH_INFO_TABLE */
但是在添加spi来读取外部FLASH的时候,遇到了一些情况。
例如,找不到设备,或者说无法读取FLASH的ID。
读出的设备ID、厂商ID等是正确的,但是检测出来不符合SFDP标准。
- [SFUD](..\..\..\sfud\src\sfud.c:116) Start initialize Serial Flash Universal Driver(SFUD) V1.1.0.
- [SFUD](..\..\..\sfud\src\sfud.c:117) You can get the latest version on https://github.com/armink/SFUD .
- [SFUD](..\..\..\sfud\src\sfud.c:861) The flash device manufacturer ID is 0xEF, memory type ID is 0x40, capacity ID is 0x16.
- [SFUD](..\..\..\sfud\src\sfud_sfdp.c:122) Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P').
- [SFUD]Warning: Read SFDP parameter header information failed. The W25Q32BV is not support JEDEC SFDP.
- [SFUD]Find a Winbond W25Q32BV flash chip. Size is 4194304 bytes.
- [SFUD](..\..\..\sfud\src\sfud.c:840) Flash device reset success.
- [SFUD]W25Q32BV flash device is initialize success.
- [SFUD]Error: Can't enable write status.
- Erase the W25Q32BV flash data failed.

以上全部修改完之后
在主函数添加测试代码
- static void sfud_demo(uint32_t addr, size_t size, uint8_t *data)
- {
- sfud_err result = SFUD_SUCCESS;
- const sfud_flash *flash = sfud_get_device_table() + 0;
- size_t i;
- /* prepare write data */
- for (i = 0; i < size; i++)
- {
- data[i] = i;
- }
- /* erase test */
- result = sfud_erase(flash, addr, size);
- if (result == SFUD_SUCCESS)
- {
- RTT_Printf("Erase the %s flash data finish. Start from 0x%08X, size is %ld.\r\n", flash->name, addr,
- size);
- }
- else
- {
- RTT_Printf("Erase the %s flash data failed.\r\n", flash->name);
- return;
- }
- /* write test */
- result = sfud_write(flash, addr, size, data);
- if (result == SFUD_SUCCESS)
- {
- RTT_Printf("Write the %s flash data finish. Start from 0x%08X, size is %ld.\r\n", flash->name, addr,
- size);
- }
- else
- {
- RTT_Printf("Write the %s flash data failed.\r\n", flash->name);
- return;
- }
- /* read test */
- result = sfud_read(flash, addr, size, data);
- if (result == SFUD_SUCCESS)
- {
- RTT_Printf("Read the %s flash data success. Start from 0x%08X, size is %ld. The data is:\r\n", flash->name, addr,
- size);
- RTT_Printf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
- for (i = 0; i < size; i++)
- {
- if (i % 16 == 0)
- {
- RTT_Printf("[%08X] ", addr + i);
- }
- RTT_Printf("%02X ", data[i]);
- if (((i + 1) % 16 == 0) || i == size - 1)
- {
- RTT_Printf("\r\n");
- }
- }
- RTT_Printf("\r\n");
- }
- else
- {
- RTT_Printf("Read the %s flash data failed.\r\n", flash->name);
- }
- /* data check */
- for (i = 0; i < size; i++)
- {
- if (data[i] != i % 256)
- {
- RTT_Printf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
- break;
- }
- }
- if (i == size)
- {
- RTT_Printf("The %s flash test is success.\r\n", flash->name);
- }
- }
打印的结果显示

本文中所使用的裸机工程是基于HAL库的,在SFUD源码的Demo中也有一份HAL库的工程,因为基于HAL库的移植接口实现都是一样的,所以我直接将Demo中的sfud_port.c文件复制过来替换:
推荐看官方文档SFUD: 一款使用 JEDEC SFDP 标准的串行 (SPI) Flash 通用驱动库 (gitee.com)
FLASH芯片型号:W25Q64JVSSIQ
RTOS:liteos-m
MCU:STM32F407ZGT6 HAL库
1.FLASH芯片支持SFDP标准;(在芯片datesheet中全局搜索SFDP)。
2.如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( \sfud\inc\sfud_flash_def.h ) 中提供的 Flash 参数信息表 是否支持该款 Flash。
- #ifdef SFUD_USING_FLASH_INFO_TABLE
- /* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
- * then the SFUD will find the flash chip information by this table. You can add other flash to here then
- * notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
- * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
- */
- #define SFUD_FLASH_CHIP_TABLE \
- { \
- {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81}, \
- {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
- {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
- {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
- {"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
- {"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
- {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
- {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
- {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
- }
- #endif /* SFUD_USING_FLASH_INFO_TABLE */
3.如果列表中还没有你用的芯片,按照格式要求在表的末尾添加自己的芯片即可。

- #ifndef _SFUD_CFG_H_
- #define _SFUD_CFG_H_
-
- #define SFUD_DEBUG_MODE
-
- #define SFUD_USING_SFDP
-
- // #define SFUD_USING_FLASH_INFO_TABLE
-
- enum {
- SFUD_W25Q64JV_DEVICE_INDEX = 0,
- };
-
- #define SFUD_FLASH_DEVICE_TABLE \
- { \
- [SFUD_W25Q64JV_DEVICE_INDEX] = {.name = "W25Q64JV", .spi.name = "SPI2"}, \
- }
-
- // #define SFUD_USING_QSPI
-
- #endif /* _SFUD_CFG_H_ */
我这里用的是FLASH芯片支持SFDP标准,用的是SPI通信。所以可以屏蔽掉SFUD_USING_FLASH_INFO_TABLE和SFUD_USING_QSPI两个宏。SFUD_FLASH_DEVICE_TABLE中定义了自己使用的FLASH芯片。
配置完成后,需要初始化spi。我这里使用cubemx生成spi初始化代码。并增加spi_cs的初始化代码。为了让SFUD能支持一个MCU通过任意SPI接任意数量的FLASH芯片,增加spi_cs_low()和spi2_cs_high()。源码如下:
- /* Includes ------------------------------------------------------------------*/
- #include "spi.h"
-
- /* USER CODE BEGIN 0 */
- #define SPI2_CS_PIN GPIO_PIN_0
- #define SPI2_CS_GPIO_PORT GPIOC
- /* USER CODE END 0 */
-
- SPI_HandleTypeDef hspi2;
-
- /* SPI2 init function */
- void MX_SPI2_Init(void)
- {
-
- /* USER CODE BEGIN SPI2_Init 0 */
-
- /* USER CODE END SPI2_Init 0 */
-
- /* USER CODE BEGIN SPI2_Init 1 */
-
- /* USER CODE END SPI2_Init 1 */
- hspi2.Instance = SPI2;
- hspi2.Init.Mode = SPI_MODE_MASTER;
- hspi2.Init.Direction = SPI_DIRECTION_2LINES;
- hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
- hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
- hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
- hspi2.Init.NSS = SPI_NSS_SOFT;
- hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
- hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
- hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
- hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
- hspi2.Init.CRCPolynomial = 10;
- if (HAL_SPI_Init(&hspi2) != HAL_OK)
- {
- Error_Handler();
- }
- /* USER CODE BEGIN SPI2_Init 2 */
-
- /* USER CODE END SPI2_Init 2 */
- }
-
- void HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
- {
-
- GPIO_InitTypeDef GPIO_InitStruct = {0};
- if (spiHandle->Instance == SPI2)
- {
- /* USER CODE BEGIN SPI2_MspInit 0 */
-
- /* USER CODE END SPI2_MspInit 0 */
- /* SPI2 clock enable */
- __HAL_RCC_SPI2_CLK_ENABLE();
-
- __HAL_RCC_GPIOC_CLK_ENABLE();
- __HAL_RCC_GPIOB_CLK_ENABLE();
- /**SPI2 GPIO Configuration
- PC2 ------> SPI2_MISO
- PC3 ------> SPI2_MOSI
- PB10 ------> SPI2_SCK
- */
- GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
- HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
- GPIO_InitStruct.Pin = GPIO_PIN_10;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
- /* USER CODE BEGIN SPI2_MspInit 1 */
- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
-
- /*Configure GPIO pin : SPI_FLASH_CS_Pin */
- GPIO_InitStruct.Pin = GPIO_PIN_0;
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
- /* USER CODE END SPI2_MspInit 1 */
- }
- }
-
- void HAL_SPI_MspDeInit(SPI_HandleTypeDef *spiHandle)
- {
-
- if (spiHandle->Instance == SPI2)
- {
- /* USER CODE BEGIN SPI2_MspDeInit 0 */
-
- /* USER CODE END SPI2_MspDeInit 0 */
- /* Peripheral clock disable */
- __HAL_RCC_SPI2_CLK_DISABLE();
-
- /**SPI2 GPIO Configuration
- PC2 ------> SPI2_MISO
- PC3 ------> SPI2_MOSI
- PB10 ------> SPI2_SCK
- */
- HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);
-
- HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10);
-
- /* USER CODE BEGIN SPI2_MspDeInit 1 */
-
- /* USER CODE END SPI2_MspDeInit 1 */
- }
- }
-
- /* USER CODE BEGIN 1 */
- void spi2_cs_low(void)
- {
- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
- }
-
- void spi2_cs_high(void)
- {
- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
- }
- /* USER CODE END 1 */
为了让SFUD能支持一个MCU通过任意SPI接任意数量的FLASH芯片,定义了结构体sfud_port_t。sfud_spi_port_init()对sfud_port_t初始化。源码如下:
- #include <sfud.h>
- #include <stdarg.h>
- #include "main.h"
- #include "los_mux.h"
- #include "los_task.h"
- #include "los_memory.h"
- #include "string.h"
- #include "spi.h"
- static char log_buf[256];
-
- typedef struct
- {
- SPI_HandleTypeDef *hspi;
- UINT32 lock;
- void (*cs_low)(void);
- void (*cs_high)(void);
- } sfud_port_t;
-
- void sfud_log_debug(const char *file, const long line, const char *format, ...);
-
- static void spi_lock(const struct __sfud_spi *spi)
- {
- sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
- LOS_MuxPend(sfud_port->lock, 1000);
- }
-
- static void spi_unlock(const struct __sfud_spi *spi)
- {
- sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
- LOS_MuxPost(sfud_port->lock);
- }
-
- /**
- * 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;
- sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
- // uint8_t send_data, read_data;
-
- /**
- * add your spi write and read code
- */
- sfud_port->cs_low();
- if (HAL_SPI_Transmit(sfud_port->hspi, (uint8_t *)write_buf, write_size, 3000) != HAL_OK)
- {
- // sfud_log_debug(__FILE__, __LINE__, "spi write fail.");
- result = SFUD_ERR_WRITE;
- goto ERR;
- }
- if ((read_buf != NULL) && (read_size != 0))
- {
- if (HAL_SPI_Receive(sfud_port->hspi, read_buf, read_size, 3000) != HAL_OK)
- {
- // sfud_log_debug(__FILE__, __LINE__, "spi read fail.");
- result = SFUD_ERR_READ;
- }
- }
-
- // return result;
- ERR:
- sfud_port->cs_high();
- return result;
- }
-
- #ifdef SFUD_USING_QSPI
- /**
- * read flash data by QSPI
- */
- static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
- uint8_t *read_buf, size_t read_size)
- {
- sfud_err result = SFUD_SUCCESS;
-
- /**
- * add your qspi read flash data code
- */
- return result;
- }
- #endif /* SFUD_USING_QSPI */
-
- static void retry_delay(void)
- {
- LOS_TaskDelay(1);
- }
-
- sfud_err sfud_spi_port_init(sfud_flash *flash)
- {
- sfud_err result = SFUD_SUCCESS;
- sfud_port_t *sfud_port = NULL;
- /**
- * add your port spi bus and device object initialize code like this:
- * 1. rcc initialize
- * 2. gpio initialize
- * 3. spi device initialize
- * 4. flash->spi and flash->retry item initialize
- * flash->spi.wr = spi_write_read; //Required
- * flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable
- * flash->spi.lock = spi_lock;
- * flash->spi.unlock = spi_unlock;
- * flash->spi.user_data = &spix;
- * flash->retry.delay = null;
- * flash->retry.times = 10000; //Required
- */
- /*Configure GPIO pin Output Level */
- if (strcmp(flash->spi.name, "SPI2") == 0)
- {
- MX_SPI2_Init();
-
- sfud_port = LOS_MemAlloc(m_aucSysMem0, sizeof(sfud_port_t));
- if (sfud_port == NULL)
- {
- sfud_log_debug(__FILE__, __LINE__, "memory alloc fail.");
- goto ERR_MEMALLOC;
- }
- if (LOS_MuxCreate(&sfud_port->lock) != LOS_OK)
- {
- sfud_log_debug(__FILE__, __LINE__, "mux create fail.");
- result = SFUD_ERR_NOT_FOUND;
- goto ERR_MUX;
- }
- sfud_port->hspi = &hspi2;
- sfud_port->cs_high = spi2_cs_high;
- sfud_port->cs_low = spi2_cs_low;
-
- flash->spi.wr = spi_write_read; // Required
- flash->spi.lock = spi_lock;
- flash->spi.unlock = spi_unlock;
- flash->spi.user_data = sfud_port;
- flash->retry.delay = retry_delay;
- flash->retry.times = 10000; // Required
- }
- else
- {
- result = SFUD_ERR_NOT_FOUND;
- }
-
- return result;
-
- ERR_MUX:
- LOS_MemFree(m_aucSysMem0, sfud_port);
- ERR_MEMALLOC:
- HAL_SPI_DeInit(sfud_port->hspi);
-
- return result;
- }
-
- /**
- * This function is print debug info.
- *
- * @param file the file which has call this function
- * @param line the line number which has call this function
- * @param format output format
- * @param ... args
- */
- void sfud_log_debug(const char *file, const long line, const char *format, ...)
- {
- va_list args;
-
- /* args point to the first variable parameter */
- va_start(args, format);
- printf("[SFUD](%s:%ld) ", file, line);
- /* must use vprintf to print */
- vsnprintf(log_buf, sizeof(log_buf), format, args);
- printf("%s\n", log_buf);
- va_end(args);
- }
-
- /**
- * This function is print routine info.
- *
- * @param format output format
- * @param ... args
- */
- void sfud_log_info(const char *format, ...)
- {
- va_list args;
-
- /* args point to the first variable parameter */
- va_start(args, format);
- printf("[SFUD]");
- /* must use vprintf to print */
- vsnprintf(log_buf, sizeof(log_buf), format, args);
- printf("%s\n", log_buf);
- va_end(args);
- }
业务层/应用层只需要关注sfud.h中的api。初始化过程如下:
- sfud_flash *w25q64_handle;
-
- sfud_init();
- // SFUD_W25Q64JV_DEVICE_INDEX在sfud_cfg.h中自己定义。
- w25q64_handle = sfud_get_device(SFUD_W25Q64JV_DEVICE_INDEX);
获得句柄w25q64_handle后,就调用读、写、擦除等API了。
测试结果如下:
- [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.
- [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 .
- [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.
- [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.
- [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.
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:203) JEDEC basic flash parameter table info:
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:204) MSB-LSB 3 2 1 0
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0001] 0xFF 0xF9 0x20 0xE5
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0002] 0x03 0xFF 0xFF 0xFF
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0003] 0x6B 0x08 0xEB 0x44
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0004] 0xBB 0x42 0x3B 0x08
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0005] 0xFF 0xFF 0xFF 0xFE
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0006] 0x00 0x00 0xFF 0xFF
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0007] 0xEB 0x40 0xFF 0xFF
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0008] 0x52 0x0F 0x20 0x0C
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0009] 0x00 0x00 0xD8 0x10
- [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.
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:234) Write granularity is 64 bytes or larger.
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:245) Target flash status register is non-volatile.
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:271) 3-Byte only addressing.
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:305) Capacity is 8388608 Bytes.
- [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.
- [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.
- [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.
- [SFUD]Find a Winbond flash chip. Size is 8388608 bytes.
- [SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:840) Flash device reset success.
- [SFUD]W25Q64JV flash device is initialize success.
参考文章
关于万能SPI-Flash驱动库SFUD移植心得
万能SPI-Flash驱动库SFUD移植心得 (amobbs.com 阿莫电子论坛 - 东莞阿莫电子网站)
SFUD | 一款串行 Flash 通用驱动库
SFUD | 一款串行 Flash 通用驱动库_Mculover666的博客-CSDN博客
【学习笔记】RT-Thread中flash管理 — [SFUD组件 和 FAL驱动组件介绍]_黄逸芬的博客-CSDN博客