本帖最后由 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);
- }
- }
运行结果:
附件
|