1、环境
1、单片机STM32F401RCT6
2、编辑器KEIL
2、准备
1、STM32CUBEMX配置SPI1,使用自定义CS引脚。

生成keil工程。
2、下载SFUD。https://gitee.com/Armink/SFUD[添加链接描述](https://gitee.com/Armink/SFUD)
解压后,获取sfud文件夹。

3、将文件夹复制到keil工程

4、添加sfud文件夹的内容到keil工程。并添加文件目录。


3、移植
1、打开sfud_port.c和sfud_cfg.h文件。
编辑sfud_cfg.h文件(此文件在F7编译一次后,就可以此sfud_port.c的包含文件里面查找到)

我没有使用串口输出调试信息,所以就禁用了调试。主要是2,如果有多个flash,就按顺序定义。步骤3里面的内容只是自己看的,实际意义不大。
c
/*
* This file is part of the Serial Flash Universal Driver Library.
*
* Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
*
* 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
#define SFUD_USING_SFDP
// #define SFUD_USING_FAST_READ
#define SFUD_USING_FLASH_INFO_TABLE
enum {
SFUD_W25Q128_DEVICE_INDEX = 0,
};
#define SFUD_FLASH_DEVICE_TABLE \
{ \
[SFUD_W25Q128_DEVICE_INDEX] = {.name = "W25Q128", .spi.name = "SPI1"}, \
}
//#define SFUD_USING_QSPI
#endif /* _SFUD_CFG_H_ */
2、编辑sfud_port.c文件。
因为禁用了调试,可以注释掉此文件里面的一些内容:



剩下的就是:
定义一个结构体,用来管理flash的CS引脚和spi句柄
c
typedef struct {
SPI_HandleTypeDef *hspi; // 直接保存HAL句柄指针
GPIO_TypeDef *cs_gpiox;
uint16_t cs_gpio_pin;
} spi_user_data, *spi_user_data_t;
实现一个延时100us的函数
c
/* about 100 microsecond delay */
/**
* @brief 100微秒延时函数
* @note 针对 STM32F401RCT6 @ 84MHz 精确校准
* 通过逻辑分析仪或示波器实测调整循环次数
*/
static void retry_delay_100us(void) {
/* 84MHz 下,实测约 100us 的循环次数 */
/* 编译器优化级别会影响循环次数,建议使用 -O0 或 -O1 */
for(volatile uint32_t i = 0; i < 210; i++);
}
编辑SPI读写函数
c
/**
* SPI写读函数 - 逐字节方式(兼容旧版SFUD,某些版本需要这种方式)
* 如果上面的批量方式不行,用这个版本
*/
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);
}
HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_PIN_RESET);
// 逐字节读写
for (size_t i = 0; i < write_size + read_size; i++) {
if (i < write_size) {
send_data = *write_buf++;
} else {
send_data = SFUD_DUMMY_DATA;
}
// 收发一个字节
if (HAL_SPI_TransmitReceive(spi_dev->hspi, &send_data, &read_data, 1,
HAL_MAX_DELAY) != HAL_OK) {
result = SFUD_ERR_TIMEOUT;
goto exit;
}
if (i >= write_size) {
*read_buf++ = read_data;
}
}
exit:
HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_PIN_SET);
return result;
}
初始化SFUD
c
sfud_err sfud_spi_port_init(sfud_flash *flash) {
sfud_err result = SFUD_SUCCESS;
/**
* 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
*/
switch(flash->index)
{
case SFUD_W25Q128_DEVICE_INDEX:
{
static spi_user_data spi1_dev =
{
.hspi = &hspi1,
.cs_gpiox = GPIOA,
.cs_gpio_pin = GPIO_PIN_4
};
//同步flash移植所需的接口及数量
flash->spi.wr = spi_write_read;
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = &spi1_dev;
flash->retry.delay = retry_delay_100us;
flash->retry.times = 60 * 10000;
break;
}
default:
result = SFUD_ERR_NOT_FOUND;
break;
}
return result;
}
整个 sfud_port.c文件如下:
c
/*
* This file is part of the Serial Flash Universal Driver Library.
*
* Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
*
* 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: Portable interface for each platform.
* Created on: 2016-04-23
*/
#include <sfud.h>
#include <stdarg.h>
#include "main.h"
#include "spi.h"
typedef struct {
SPI_HandleTypeDef *hspi; // 直接保存HAL句柄指针
GPIO_TypeDef *cs_gpiox;
uint16_t cs_gpio_pin;
} spi_user_data, *spi_user_data_t;
//static char log_buf[256];
void sfud_log_debug(const char *file, const long line, const char *format, ...);
static void spi_lock(const sfud_spi *spi) {
__disable_irq();
}
static void spi_unlock(const sfud_spi *spi) {
__enable_irq();
}
/* about 100 microsecond delay */
/**
* @brief 100微秒延时函数
* @note 针对 STM32F401RCT6 @ 84MHz 精确校准
* 通过逻辑分析仪或示波器实测调整循环次数
*/
static void retry_delay_100us(void) {
/* 84MHz 下,实测约 100us 的循环次数 */
/* 编译器优化级别会影响循环次数,建议使用 -O0 或 -O1 */
for(volatile uint32_t i = 0; i < 210; i++);
}
///**
// * 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;
//
// 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);
// }
//
// // CS引脚拉低(选中设备)
// HAL_GPIO_WritePin(spi_dev->cs_gpiox,spi_dev->cs_gpio_pin,GPIO_PIN_RESET);
//
// /* 开始读写数据 */
// if (write_size > 0 && read_size > 0) {
// // 同时读写(全双工)
// if (HAL_SPI_TransmitReceive(spi_dev->hspi, (uint8_t*)write_buf, read_buf,
// write_size, HAL_MAX_DELAY) != HAL_OK) {
// result = SFUD_ERR_TIMEOUT;
// }
// } else if (write_size > 0) {
// // 只写
// if (HAL_SPI_Transmit(spi_dev->hspi, (uint8_t*)write_buf, write_size,
// HAL_MAX_DELAY) != HAL_OK) {
// result = SFUD_ERR_TIMEOUT;
// }
// } else if (read_size > 0) {
// // 只读
// if (HAL_SPI_Receive(spi_dev->hspi, read_buf, read_size,
// HAL_MAX_DELAY) != HAL_OK) {
// result = SFUD_ERR_TIMEOUT;
// }
// }
// // CS引脚拉高(释放设备)
// HAL_GPIO_WritePin(spi_dev->cs_gpiox,spi_dev->cs_gpio_pin,GPIO_PIN_SET);
//
// /**
// * add your spi write and read code
// */
// return result;
//}
/**
* SPI写读函数 - 逐字节方式(兼容旧版SFUD,某些版本需要这种方式)
* 如果上面的批量方式不行,用这个版本
*/
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);
}
HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_PIN_RESET);
// 逐字节读写
for (size_t i = 0; i < write_size + read_size; i++) {
if (i < write_size) {
send_data = *write_buf++;
} else {
send_data = SFUD_DUMMY_DATA;
}
// 收发一个字节
if (HAL_SPI_TransmitReceive(spi_dev->hspi, &send_data, &read_data, 1,
HAL_MAX_DELAY) != HAL_OK) {
result = SFUD_ERR_TIMEOUT;
goto exit;
}
if (i >= write_size) {
*read_buf++ = read_data;
}
}
exit:
HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_PIN_SET);
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 spi_user_data spi1 = {.hspi = &hspi1,.cs_gpiox = GPIOA,.cs_gpio_pin = GPIO_PIN_4};
sfud_err sfud_spi_port_init(sfud_flash *flash) {
sfud_err result = SFUD_SUCCESS;
/**
* 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
*/
switch(flash->index)
{
case SFUD_W25Q128_DEVICE_INDEX:
{
static spi_user_data spi1_dev =
{
.hspi = &hspi1,
.cs_gpiox = GPIOA,
.cs_gpio_pin = GPIO_PIN_4
};
//同步flash移植所需的接口及数量
flash->spi.wr = spi_write_read;
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = &spi1_dev;
flash->retry.delay = retry_delay_100us;
flash->retry.times = 60 * 10000;
break;
}
default:
result = SFUD_ERR_NOT_FOUND;
break;
}
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);
}
4、使用
1、main.c文件包含#include "sfud.h"
2、初始化sfud,启用读写

