板上设计有一片SPI FLASH存储器,准备移植SFUD库驱动。

电路接口如下:

首先打开STM32CubeMX,初始化SPI外设参数。打开DMA发送接收。


接下来实现SPI发送接收数据接口对接SFUD驱动。
#define SPI_BUS_NUM 2
SPI_HandleTypeDef * spi_table[SPI_BUS_NUM]=
{
&hspi1,
&hspi2,
};
typedef void (*CallbackHook)(void);
typedef struct stm32_spi_para_t
{
SPI_HandleTypeDef * _spi;
volatile uint16_t spi_transmit_ok;
volatile uint16_t spi_receive_ok;
CallbackHook spi_send_fn;
CallbackHook spi_recv_fn;
}stm32_spi_para_t;
stm32_spi_para_t stm32_spi_p[SPI_BUS_NUM] =
{
{
._spi = &hspi1,
.spi_transmit_ok = 0,
.spi_receive_ok = 0,
.spi_send_fn = NULL,
.spi_recv_fn = NULL,
},
{
._spi = &hspi2,
.spi_transmit_ok = 0,
.spi_receive_ok = 0,
.spi_send_fn = NULL,
.spi_recv_fn = NULL,
},
};
void spi_write_read_buff(uint8_t index ,uint8_t * wbuf,uint32_t wl,uint8_t * rbuf,uint32_t rl)
{
stm32_spi_p[index].spi_send_fn = NULL;
stm32_spi_p[index].spi_recv_fn = NULL;
if((wbuf != NULL) && (wl != 0))
{
stm32_spi_p[index].spi_transmit_ok = 1;
HAL_SPI_Transmit(stm32_spi_p[index]._spi,wbuf, wl, 100);
stm32_spi_p[index].spi_transmit_ok = 0;
}
if((rbuf != NULL) && (rl != 0))
{
memset(rbuf,0,rl);
stm32_spi_p[index].spi_receive_ok = 1;
HAL_SPI_Receive(stm32_spi_p[index]._spi,rbuf, rl, 100);
stm32_spi_p[index].spi_receive_ok = 0;
}
}
SFUD库地址:https://gitee.com/Armink/SFUD
[SFUD]是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。
下载SFUD库,将源码添加到keil工程内。移植SFUD库主要关注配置sfud_cfg.h和接口驱动sfud_port.c。

配置文件很简单,所有配置位于 /sfud/inc/sfud_cfg.h ,请参考下面的配置介绍,选择适合自己项目的配置。
打开/关闭 SFUD_DEBUG_MODE 宏定义,用于打印调试信息。
再就是打开 SFUD_USING_SFDP 和 SFUD_USING_FLASH_INFO_TABLE 宏定义。
继续移植接口文件sfud_port.c中的 sfud_err sfud_spi_port_init(sfud_flash *flash)函数,方法是库提供的移植方法,在里面完成各个设备 SPI 读写驱动(必选)、重试次数(必选)、重试接口(可选)及 SPI 锁(可选)的配置。
下面就是我移植的接口程序:
#include <sfud.h>
#include <stdarg.h>
#include "main.h"
#include "drv_spi.h"
static char log_buf[256];
void sfud_log_debug(const char *file, const long line, const char *format, ...);
void sfud_log_info(const char *format, ...);
typedef struct
{
uint32_t spi_index;
void (*spi_cs_gpio_set)(uint32_t bitflag);
void (*spi_write_read)(uint8_t index,uint8_t * wbuf,uint32_t wl,uint8_t * rbuf,uint32_t rl);
} spi_user_data, *spi_user_data_t;
/* about 100 microsecond delay */
static void retry_delay_ms(void)
{
delay_ms(1);
}
static void spi_lock(const sfud_spi *spi)
{
// __disable_irq();
}
static void spi_unlock(const sfud_spi *spi)
{
// __enable_irq();
}
static void spi_cs_gpio(uint32_t bitflag)
{
if(bitflag) HAL_GPIO_WritePin(F_CS_GPIO_Port, F_CS_Pin, GPIO_PIN_SET);
else HAL_GPIO_WritePin(F_CS_GPIO_Port, F_CS_Pin, GPIO_PIN_RESET);
}
static spi_user_data spi_cfg =
{
.spi_index = 0,
.spi_cs_gpio_set = spi_cs_gpio,
.spi_write_read = spi_write_read_buff,
};
/**
* SPI write data then read data
*/
static sfud_err sfud_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;
/**
* add your spi write and read code
*/
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);
}
spi_dev->spi_cs_gpio_set(0);
spi_dev->spi_write_read(spi_dev->spi_index, (uint8_t *)write_buf, write_size, read_buf, read_size);
spi_dev->spi_cs_gpio_set(1);
return result;
}
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
*/
/* 4 同步 Flash 移植所需的接口及数据 */
flash->spi.wr = sfud_write_read;
#ifdef SFUD_USING_QSPI
flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable
#endif
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = &spi_cfg;
/* about 100 microsecond delay */
flash->retry.delay = retry_delay_ms;
/* adout 1 seconds timeout */
flash->retry.times = 1000; //1000 * retry_delay_ms()
return result;
}
以上就完成了移植,下面测试一下效果。
在需要sfud接口文件中包含#include "sfud.h"头文件。然后使用前先调用sfud_err sfud_init(void)初始化所有flash。sfud_init()将会调用 sfud_err sfud_device_init(sfud_flash *flash) 初始化 Flash 设备表中的全部设备。如果只有一个 Flash 也可以只使用 sfud_device_init 进行单一初始化。

测试flash擦除,读写数据,使用shell命令测试,代码如下:
#define SFUD_DEMO_TEST_BUFFER_SIZE 1024
static void sfud_demo(uint32_t addr, size_t size, uint8_t *data);
uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];
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;
if(flash->init_ok == false) sfud_init();
/* prepare write data */
for (i = 0; i < size; i++) {
data[i] = i+0xa0;
}
/* erase test */
result = sfud_erase(flash, addr, size);
if (result == SFUD_SUCCESS) {
printf("Erase the %s flash data finish. Start from 0x%08X, size is %u.\r\n", flash->name, addr,
size);
} else {
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) {
printf("Write the %s flash data finish. Start from 0x%08X, size is %u.\r\n", flash->name, addr,
size);
} else {
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) {
printf("Read the %s flash data success. Start from 0x%08X, size is %u. The data is:\r\n", flash->name, addr,
size);
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) {
printf("[%08X] ", addr + i);
}
printf("%02X ", data[i]);
if (((i + 1) % 16 == 0) || i == size - 1) {
printf("\r\n");
}
}
printf("\r\n");
} else {
printf("Read the %s flash data failed.\r\n", flash->name);
}
/* data check */
for (i = 0; i < size; i++) {
if (data[i] != i % 256) {
printf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
break;
}
}
if (i == size) {
printf("The %s flash test is success.\r\n", flash->name);
}
}
#if UART_SHELL == 2
#include "nr_micro_shell.h"
void sfud_flash_demo(char argc, char *argv)
{
sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
}
NR_SHELL_CMD_EXPORT(sfud_flash, sfud_flash_demo, "Test flash write and read");
#elif UART_SHELL == 3
//LSY_SHELL
#include "lsy_shell_core.h"
int cmd_sfud_demo(int argc, char **argv)
{
sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
return SHELL_OK;
}
LSY_SHELL_CMD_EXPORT(sfud, cmd_sfud_demo, "test sfud demo.");
#else
#endif
通过串口命令测试,可以看到数据成功写入读取。测试效果:

