SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库
推荐查看官方文档:一款使用 JEDEC SFDP 标准的串行 (SPI) Flash 通用驱动库
主控:MM32F3273G8P火龙果开发板
spi flash : W25Q32
使用SPI2。(PB12 PB13 PB14 PB15)
在移植sfud之前,我们先初始化spi2,用读取函数,读取一下w25q32的manufacturer_id值,如果能正常读取,说明我们的设备没有问题,spi初始化也正常,然后再来移植sfud库。
初始化gpio的函数如下:
CS采用软件控制的方式,读写之前拉低,读写结束拉高。
static bool SPI_GpioConfig(void)
{
/* gpio. */
GPIO_Init_Type gpio_init;
/* PB12 - SPI_CS. */
gpio_init.Pins = GPIO_PIN_12;
gpio_init.PinMode = GPIO_PinMode_Out_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_init);
GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_15);
GPIO_SetBits(GPIOB, gpio_init.Pins);
/* PB13 - SPI_SCK. */
gpio_init.Pins = GPIO_PIN_13;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_init);
GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_5);
/* PB14 - SPI_MISO. */
gpio_init.Pins = GPIO_PIN_14;
gpio_init.PinMode = GPIO_PinMode_In_PullUp;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_init);
GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_5);
/* PB15 - SPI_MOSI. */
gpio_init.Pins = GPIO_PIN_15;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_init);
GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_5);
return true;
}
SPI初始化函数:
bool SPI_Config(void)
{
SPI_GpioConfig();
/* Setup SPI module. */
SPI_Master_Init_Type spi_init;
spi_init.ClockFreqHz = CLOCK_APB1_FREQ;
spi_init.BaudRate = 400000u;
spi_init.XferMode = SPI_XferMode_TxRx;
spi_init.PolPha = SPI_PolPha_Alt1;
spi_init.DataWidth = SPI_DataWidth_8b;
spi_init.LSB = false;
spi_init.AutoCS = true;
SPI_InitMaster(SPI2, &spi_init);
/* Enable SPI. */
SPI_Enable(SPI2, true);
return true;
}
注意SPI_PolPha_Alt1其实就是spi mode0,这里的定义怪怪的。
spi时钟初始化:
/* GPIOB. */
RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOB, true);
RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOB);
/* SPI2. */
RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_SPI2, true);
RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_SPI2);
读取函数:(写入一个读取一个)
static void SPI_WriteReadOnebyte(uint8_t w_data,uint8_t *r_data)
{
while ( SPI_STATUS_TX_FULL & SPI_GetStatus(SPI2) )
{}
SPI_PutData(SPI2, w_data);
while (0u == (SPI_STATUS_RX_DONE & SPI_GetStatus(SPI2)) )
{}
*r_data = SPI_GetData(SPI2);
}
void SPI_WriteReadData(uint8_t *w_data,uint8_t *r_data,int len)
{
GPIO_WriteBit(GPIOB,GPIO_PIN_12,0);
while (len)
{
SPI_WriteReadOnebyte(*w_data++,r_data++);
len--;
}
GPIO_WriteBit(GPIOB,GPIO_PIN_12,1);
}
读取 manufacturer_id函数
#define CMD_JEDEC_ID 0x9f
#define CMD_MANUFACURER_ID 0x90
static void w25q32_read_manufacturer_id(uint16_t *id)
{
uint8_t tx_data[6] = {CMD_MANUFACURER_ID ,0,0,0,0,0};
uint8_t rx_data[6] = {0};
SPI_WriteReadData(tx_data,rx_data,6);
*id = (rx_data[4]<<8)|rx_data[5];
}
static void w25q32_read_jedec_id(uint16_t *id)
{
uint8_t tx_data[4] = {CMD_JEDEC_ID ,0,0,0};
uint8_t rx_data[4] = {0};
SPI_WriteReadData(tx_data,rx_data,4);
*id = (rx_data[2]<<8)|rx_data[3];
}
void SPI_Test(void)
{
uint16_t id = 0;
SPI_Config();
w25q32_read_manufacturer_id(&id);
printf("get manufacturer id == 0x%04x\r\n",id);
w25q32_read_jedec_id(&id);
printf("get jedec id == 0x%04x\r\n",id);
}
调用SPI_Test函数后,打印如下:
说明spi2已经正常工作,w25q32也正常,接下来我们来移植sfud库。
下载加压后,将sfud中的文件copy到工程中。
因为w25q32是在支持列表中的,我们要修改的其实就两个文件,一个是sfud_cfg.h,另一个为sfud_port.c
文件目录结构如图:
#ifndef _SFUD_CFG_H_
#define _SFUD_CFG_H_
//#define SFUD_DEBUG_MODE
#define SFUD_USING_SFDP
#define SFUD_USING_FLASH_INFO_TABLE
enum {
SFUD_SPI_DEVICE_INDEX = 0,
};
#define SFUD_FLASH_DEVICE_TABLE \
{ \
[SFUD_SPI_DEVICE_INDEX] = {.name = "W25Q32", .spi.name = "SPI2"}, \
}
#endif /* _SFUD_CFG_H_ */
mm32平台修改如下:
static uint8_t spi_xfer(const uint8_t value)
{
while(0 == (SPI_GetStatus(SPI2) & SPI_STATUS_TX_EMPTY) )
{}
SPI_PutData(SPI2, value);
while(0 == (SPI_GetStatus(SPI2) & SPI_STATUS_RX_DONE) )
{}
return SPI_GetData(SPI2);
}
/* control the cs pin output. */
static void spi_cs_control(bool enable)
{
if (true == enable)
{
GPIO_ClearBits(GPIOB, GPIO_PIN_12);
}
else
{
GPIO_SetBits(GPIOB, GPIO_PIN_12);
}
}
sfud_err sfud_spi_port_init(sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
SPI_Config();
/* init sfud spi obj. */
flash->spi.wr = spi_write_read;
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = NULL;
flash->retry.delay = retry_delay_100us;
flash->retry.times = 60u * 10000u;
return result;
}
调用官方的demo
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);
}
}
main函数调用:
static uint8_t data [256] = {0};
int main(void)
{
BOARD_InitBootClocks(); // ³õʼ»¯Ê±ÖÓ
BOARD_InitDebugConsole();
BOARD_UserKeyInit();
LED_Init();
BOARD_TIM6_Init();
// for mutilbutton init
button_init(&btn1, read_button_GPIO, 0, btn1_id);
button_attach(&btn1, PRESS_DOWN, BTN1_PRESS_DOWN_Handler);
button_attach(&btn1, PRESS_UP, BTN1_PRESS_UP_Handler);
button_start(&btn1);
BOARD_TIM7_Init();
BOARD_Delay1Ms(1000);
printf("Board Init Success\r\n");
sfud_init();
sfud_demo(0,256,data);
while(1)
{
}
}
代码下载