sfus flash 操作库的分析
sfus 抽象
/**
* serial flash device
*/
typedef struct {
char *name; /**< serial flash name */
size_t index; /**< index of flash device information table @see flash_table */
sfud_flash_chip chip; /**< flash chip information */
sfud_spi spi; /**< SPI device spi操作函数集*/
bool init_ok; /**< initialize OK flag */
bool addr_in_4_byte; /**< flash is in 4-Byte addressing */
struct {
void (*delay)(void); /**< every retry's delay 每次重试的延迟*/
size_t times; /**< default times for error retry每次重试的次数 */
} retry;
void *user_data; /**< some user data */
#ifdef SFUD_USING_QSPI
sfud_qspi_read_cmd_format read_cmd_format; /**< fast read cmd format */
#endif
#ifdef SFUD_USING_SFDP
sfud_sfdp sfdp; /**< serial flash discoverable parameters by JEDEC standard */
#endif
} sfud_flash, *sfud_flash_t;
spi接口操作函数
typedef struct __sfud_spi {
/* SPI device name */
char *name;
/* SPI bus write read data function spi总线读写数据函数*/
sfud_err (*wr)(const struct __sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
size_t read_size);
#ifdef SFUD_USING_QSPI
/* QSPI fast read function qspi总线读写数据函数*/
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);
#endif
/* lock SPI bus spi总线加锁函数,中断关闭,或是互斥锁实现*/
void (*lock)(const struct __sfud_spi *spi);
/* unlock SPI bus */
void (*unlock)(const struct __sfud_spi *spi);
/* some user data */
void *user_data;
} sfud_spi, *sfud_spi_t;
spi flash 信息
由下面的信息可知,不同的spi flash擦除命令不一致,读写命令是一致的
/* flash chip information */
typedef struct {
char *name; /**< flash chip name 名字*/
uint8_t mf_id; /**< 制造商 ID */
uint8_t type_id; /**< 存储器类型 ID */
uint8_t capacity_id; /**< 容量 ID */
uint32_t capacity; /**< flash capacity (bytes) */
uint16_t write_mode; /**< write mode @see sfud_write_mode */
uint32_t erase_gran; /**< erase granularity 擦除粒度 (bytes) */
uint8_t erase_gran_cmd; /**< erase granularity size block command 擦除粒度大小块命令*/
} sfud_flash_chip;
写模式
/**
* flash program(write) data mode
*/
enum sfud_write_mode {
SFUD_WM_PAGE_256B = 1 << 0, /**< write 1 to 256 bytes per page */
SFUD_WM_BYTE = 1 << 1, /**< byte write */
SFUD_WM_AAI = 1 << 2, /**< auto address increment */
SFUD_WM_DUAL_BUFFER = 1 << 3, /**< dual-buffer write, like AT45DB series */
};
目前支持的spi flash的目标
这里也是以后可以添加的
#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}, \
}
如何找到芯片信息
sfus 是根据flash_table 数组内部提供信息初始化spi总线和初始化sfud_flash_chip (spi flash抽象结构)信息
在sfus_def.h内部定了 flash都支持的命令列表
sfus的初始化流程
- 用户应用层调用sfus_init
- 从flash_table数组内部取出sfus对象
- 内部调用底层的sfus_device_init
- 进入用户移植提供的函数
- spi外设初始化(时钟,gpio等)
- 初始化sfud_flash对象
- spi读写数据函数
- spi加锁解锁函数
- 错误延时函数,错误重试次数
- 从flash_chip_table里边找到对用flash设备的id,进而找到flash信息,最后初始化芯片先关信息(扇区大小,厂家名字)
基于STM32F103RCT6+CUBEMX的SFUS移植教程
- 使用cubemx创建一个工程
- 添加sfus代码到工程
- 添加spi读写函数和中断使能控制函数,延时函数
- 修改flash设备列表和flash id
- 添加测试代码
硬件
使用cubemx创建一个工程
添加串口打印
main.c 文件添加如下内容
#include "stdio.h"
#include "stdint.h"
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,1000);
/* Loop until the end of transmission */
return ch;
}
到此移植的基本条件满足
添加sfus代码到工程
添加spi读写函数和中断使能控制函数,延时函数
修改文件sfus_port.c
spi读写函数
/**
* SPI write data then read data
*/
static sfud_err spi_write_read(const sfud_spi *spi, 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 cs 使能
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1,write_buf,write_size,100000);
HAL_SPI_Receive(&hspi1,read_buf,read_size,100000);
//spi cs 失能
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,GPIO_PIN_SET);
return result;
}
加锁,解锁,错误延时函数
static void spi_lock(const sfud_spi *spi) {
__disable_irq();
}
static void spi_unlock(const sfud_spi *spi) {
__enable_irq();
}
static void retry_delay_100us(void) {
uint32_t delay = 120;
while(delay--);
}
sfud_spi_port_init函数
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
*/
/* 同步 Flash 移植所需的接口及数据 */
flash->spi.wr = spi_write_read;
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
/* about 100 microsecond delay */
flash->retry.delay = retry_delay_100us;
/* adout 60 seconds timeout */
flash->retry.times = 60 * 10000;
return result;
}
修改sfus_cfg.h文件
修改flash设备列表和flash id
enum {
SFUD_W25Q16BV_DEVICE_INDEX = 0,
};
#define SFUD_FLASH_DEVICE_TABLE \
{ \
[SFUD_W25Q16BV_DEVICE_INDEX] = {.name = "W25Q16BV", .spi.name = "SPI1"}, \
}
添加测试代码
#define SFUD_DEMO_TEST_BUFFER_SIZE 1024
static 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;
/* prepare write data */
for (i = 0; i < size; i++) {
data[i] = i;
}
/* 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 %ld.\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 %ld.\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 %ld. 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);
}
}