打印
[CW32F030系列]

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

[复制链接]
633|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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[256];

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("[SFUD](%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("[SFUD]");
    /* 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] = 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[i]);

            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] != 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数据手册: W25Q64JVSSIQ.PDF (2.75 MB)
软件工程源代码: Template.zip (5.65 MB)

使用特权

评论回复
沙发
xiaoyaodz| | 2022-8-18 17:26 | 只看该作者
FLASH通用驱动库需要宏定义吗   

使用特权

评论回复
板凳
gygp| | 2022-8-18 19:58 | 只看该作者
这个可以对接FATFS吗   

使用特权

评论回复
地板
backlugin| | 2022-8-20 14:20 | 只看该作者
spi的速度一般。   

使用特权

评论回复
5
typeof| | 2022-8-20 14:54 | 只看该作者
spi吗   

使用特权

评论回复
6
phoenixwhite| | 2022-8-20 19:36 | 只看该作者
CW32F030CxTx 性能还可以。   

使用特权

评论回复
7
uptown| | 2022-8-20 20:29 | 只看该作者
SFUD是什么接口   

使用特权

评论回复
8
kkzz| | 2022-8-21 21:59 | 只看该作者
SFUD速度快吗  

使用特权

评论回复
9
pentruman| | 2022-9-8 11:33 | 只看该作者
RT-Thread的组件吗  

使用特权

评论回复
10
jstgotodo| | 2022-9-8 12:52 | 只看该作者
RT-Thread使用 SFUD     

使用特权

评论回复
11
pixhw| | 2022-9-8 13:44 | 只看该作者
支持SPI/QSPI 接口  

使用特权

评论回复
12
sanxingnote7| | 2022-9-8 14:17 | 只看该作者
开源的串行 SPI Flash 通用驱动库。

使用特权

评论回复
13
plsbackup| | 2022-9-8 15:03 | 只看该作者
实现对指定 SPI Flash 的操作  

使用特权

评论回复
14
wengh2016| | 2022-9-8 17:33 | 只看该作者
论什么型号芯片,都是通用的 ?

使用特权

评论回复
15
yujielun| | 2022-9-8 18:31 | 只看该作者
SPI1的配置吗

使用特权

评论回复
16
cehuafan| | 2022-9-8 19:50 | 只看该作者
SPI Flash 还支持 SFDP

使用特权

评论回复
17
saservice| | 2022-9-11 11:16 | 只看该作者
使用 SFUD 操做 Flash并挂载文件系统

使用特权

评论回复
18
jonas222| | 2022-9-11 12:37 | 只看该作者
SFUD万能SPI flash驱动工具  

使用特权

评论回复
19
Bowclad| | 2022-10-4 19:22 | 只看该作者
spi吗?

使用特权

评论回复
20
maqianqu| | 2022-10-5 17:15 | 只看该作者
SPI + SFUD的速度快吗      

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

67

主题

2993

帖子

29

粉丝