xld0932 发表于 2022-7-15 19:26

【CW32F030CxTx StartKit测评】05.基于SFUD操作SPI FLASH

本帖最后由 xld0932 于 2022-7-16 10:26 编辑

CW32F030系列MCU带有2路串行外设SPI接口,支持3种通讯模式:双向全双工、单线半双工、单工通讯;通过寄存器可将其配置工作在主机或者从机模式,且支持多主机通讯模式,支持直接内存访问(DMA)。主要特点有:
[*]支持主机模式、从机模式
[*]支持全双工、单线半双工、单工
[*]可选的4位到16位数据帧宽度
[*]支持收发数据LSB或MSB方式
[*]可编程时钟极性和时钟相位
[*]主机模式下通讯速率高达PCLK/2
[*]从机模式下通讯速率高达PCLK/4
[*]支持多机通讯模式
[*]8个带标志位的中断源
[*]支持直接内存访问(DMA)


在CW32F030CxTx StartKit开发板上板载了一颗W25Q64的SPI FLASH,它是一颗64M-BIT(8M-BYTE)容量的SPI接口通讯的NOR FLASH;使用CW32F030的硬件SPI接口结合SFUD通用串行SPI FLASH驱动库程序,来实现对W25Q64的读、写、擦除等操作。需要注意的是,开发板上W25Q64电路是一个独立的模块,提供操作接口CN10,当我们需要实现W25Q64实验操作时,需要将CN10上的VDDF连接到VDD电源,将NCS/MISO/MOSI/SCK分别连接到MCU的SPI1端口对应的引脚上。

SFUD 是一款开源的串行SPI FLASH通用驱动库。由于现有市面的串行FLASH种类居多,各个FLASH的规格及命令存在差异,SFUD就是为了解决这些FLASH的差异现状而设计,让我们的产品能够支持不同品牌及规格的FLASH,提高了涉及到FLASH功能的软件的可重用性及可扩展性,同时也可以规避FLASH缺货或停产给产品所带来的风险。
[*]主要特点:支持 SPI/QSPI 接口、面向对象(同时支持多个FLASH对象)、可灵活裁剪、扩展性强、支持 4 字节地址等。
[*]资源占用:标准占用RAM:0.2KB & ROM:5.5KB、最小占用RAM:0.1KB & ROM:3.6KB


为什么选用SFUD
[*]避免项目因FLASH缺货、FLASH停产或产品扩容而带来的风险;
[*]越来越多的项目将固件存储到串行FLASH中,例如:ESP8266 的固件、主板中的BIOS及其他常见电子产品中的固件等等,但是各种FLASH规格及命令不统一。使用SFUD即可避免,在相同功能的软件平台基础下,无法适配不同FLASH种类的硬件平台的问题,提高软件的可重用性;
[*]简化软件流程,降低开发难度。现在只需要配置好SPI通信,即可畅快的开始玩串行FLASH了;
[*]可以用来制作FLASH编程器/烧写器;


SFUD在CW32F030CxTx StartKit开发板上移植和演示
[*]可以通过https://gitee.com/Armink/SFUD下载最新版本的SFUD开源代码,并将程序源码添加到工程中:


[*]在sfud_cfg.h文件添加当前需要支持的SPI FLASH列表信息,如下所示:


[*]其后在sfud_port.c文件中结合CW32F030的SPI底层驱动来完善SFUD所需要的接口功能,具体代码如下所示:
typedef struct {
    SPI_TypeDef*spix;
    GPIO_TypeDef *cs_gpiox;
    uint16_t      cs_gpio_pin;
} spi_user_data, *spi_user_data_t;

static char log_buf;

void sfud_log_debug(const char *file, const long line, const char *format, ...);

/**
* 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;
    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);
    }

    /**
   * add your spi write and read code
   */
    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;
      }

      while (SPI_GetFlagStatus(spi_dev->spix, SPI_FLAG_TXE ) == RESET);
      SPI_SendData(spi_dev->spix, send_data);

      while (SPI_GetFlagStatus(spi_dev->spix, SPI_FLAG_RXNE) == RESET);
      read_data = SPI_ReceiveData(spi_dev->spix);

      if (i >= write_size) {
            *read_buf++ = read_data;
      }
    }

    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 void rcc_initialize(spi_user_data_t spi) {
    if (spi->spix == CW_SPI1) {
      RCC_AHBPeriphClk_Enable( RCC_AHB_PERIPH_GPIOA, ENABLE);
      RCC_APBPeriphClk_Enable2(RCC_APB2_PERIPH_SPI1, ENABLE);
    }
}

static void gpio_initialize(spi_user_data_t spi) {
    GPIO_InitTypeDef GPIO_InitStructure;

    if (spi->spix == CW_SPI1) {
      PA10_AFx_SPI1SCK ();
      PA11_AFx_SPI1MISO();
      PA12_AFx_SPI1MOSI();

      GPIO_InitStructure.Pins= GPIO_PIN_10 | GPIO_PIN_12 | GPIO_PIN_15;
      GPIO_InitStructure.Mode= GPIO_MODE_OUTPUT_PP;
      GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
      GPIO_Init(CW_GPIOA, &GPIO_InitStructure);

      GPIO_InitStructure.Pins = GPIO_PIN_11;
      GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
      GPIO_Init(CW_GPIOA, &GPIO_InitStructure);

      GPIO_WritePin(CW_GPIOA, GPIO_PIN_15, GPIO_Pin_SET);
    }
}

static void spi_device_initialize(spi_user_data_t spi) {
    SPI_InitTypeDef SPI_InitStructure;

    if (spi->spix == CW_SPI1) {
      SPI_InitStructure.SPI_Direction= SPI_Direction_2Lines_FullDuplex;
      SPI_InitStructure.SPI_Mode       = SPI_Mode_Master;
      SPI_InitStructure.SPI_DataSize   = SPI_DataSize_8b;
      SPI_InitStructure.SPI_CPOL       = SPI_CPOL_High;
      SPI_InitStructure.SPI_CPHA       = SPI_CPHA_2Edge;
      SPI_InitStructure.SPI_NSS      = SPI_NSS_Soft;
      SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
      SPI_InitStructure.SPI_FirstBit   = SPI_FirstBit_MSB;
      SPI_InitStructure.SPI_Speed      = SPI_Speed_Low;
      SPI_Init(CW_SPI1, &SPI_InitStructure);

      SPI_Cmd(CW_SPI1, ENABLE);
    }
}

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--);
}

static spi_user_data spi1 =
{
    .spix      = CW_SPI1,
    .cs_gpiox    = CW_GPIOA,
    .cs_gpio_pin = GPIO_PIN_15
};

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_W25Q64_DEVICE_INDEX:
            rcc_initialize(&spi1);

            gpio_initialize(&spi1);

            spi_device_initialize(&spi1);

            flash->spi.wr      = spi_write_read;
            flash->spi.qspi_read = qspi_read;
            flash->spi.lock      = spi_lock;
            flash->spi.unlock    = spi_unlock;
            flash->spi.user_data = &spi1;
            flash->retry.delay   = retry_delay_100us;
            flash->retry.times   = 60 * 10000;
            break;

      default: 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("(%s:%ld) ", file, line);
    /* must use vprintf to print */
    vsnprintf(log_buf, sizeof(log_buf), format, args);
    printf("%s\r\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("");
    /* must use vprintf to print */
    vsnprintf(log_buf, sizeof(log_buf), format, args);
    printf("%s\r\n", log_buf);
    va_end(args);
}

[*]最后我们初始化SFUD,对SPI FLASH进行读、写、擦除等操作测试,具体代码和运行结果如下所示:
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;
    }

    /* erase test */
    result = sfud_erase(flash, addr, size);

    if(result == SFUD_SUCCESS)
    {
      printf("\r\nErase the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr, size);
    }
    else
    {
      printf("\r\nErase the %s flash data failed.\r\n", flash->name);
      return;
    }

    /* write test */
    result = sfud_write(flash, addr, size, data);

    if(result == SFUD_SUCCESS)
    {
      printf("\r\nWrite the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr, size);
    }
    else
    {
      printf("\r\nWrite the %s flash data failed.\r\n", flash->name);
      return;
    }

    /* read test */
    result = sfud_read(flash, addr, size, data);

    if(result == SFUD_SUCCESS)
    {
      printf("\r\nRead the %s flash data success. Start from 0x%08X, size is %d. 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);

            if(((i + 1) % 16 == 0) || (i == size - 1))
            {
                printf("\r\n");
            }
      }

      printf("\r\n");
    }
    else
    {
      printf("\r\nRead the %s flash data failed.\r\n", flash->name);
    }

    /* data check */
    for(i = 0; i < size; i++)
    {
      if(data != i % 256)
      {
            printf("\r\nRead and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
            break;
      }
    }

    if(i == size)
    {
      printf("\r\nThe %s flash test is success.\r\n", flash->name);
    }
}

void W25Q64_Init(void)
{
    /* SFUD initialize */
    if(sfud_init() == SFUD_SUCCESS)
    {
      sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
    }
}
运行结果:

附件W25Q64数据手册:软件工程源代码:

xiaoyaodz 发表于 2022-8-18 17:26

FLASH通用驱动库需要宏定义吗   

gygp 发表于 2022-8-18 19:58

这个可以对接FATFS吗   

backlugin 发表于 2022-8-20 14:20

spi的速度一般。   

typeof 发表于 2022-8-20 14:54

spi吗   

phoenixwhite 发表于 2022-8-20 19:36

CW32F030CxTx 性能还可以。   

uptown 发表于 2022-8-20 20:29

SFUD是什么接口   

kkzz 发表于 2022-8-21 21:59

SFUD速度快吗

pentruman 发表于 2022-9-8 11:33

RT-Thread的组件吗

jstgotodo 发表于 2022-9-8 12:52

RT-Thread使用 SFUD   

pixhw 发表于 2022-9-8 13:44

支持SPI/QSPI 接口

sanxingnote7 发表于 2022-9-8 14:17

开源的串行 SPI Flash 通用驱动库。

plsbackup 发表于 2022-9-8 15:03

实现对指定 SPI Flash 的操作

wengh2016 发表于 2022-9-8 17:33

论什么型号芯片,都是通用的 ?

yujielun 发表于 2022-9-8 18:31

SPI1的配置吗

cehuafan 发表于 2022-9-8 19:50

SPI Flash 还支持 SFDP

saservice 发表于 2022-9-11 11:16

使用 SFUD 操做 Flash并挂载文件系统

jonas222 发表于 2022-9-11 12:37

SFUD万能SPI flash驱动工具

Bowclad 发表于 2022-10-4 19:22

spi吗?

maqianqu 发表于 2022-10-5 17:15

SPI + SFUD的速度快吗      
页: [1] 2
查看完整版本: 【CW32F030CxTx StartKit测评】05.基于SFUD操作SPI FLASH