[MM32生态] 【EV Board (MM32L0136C7P)测评】+移植SFUD,驱动板载SPI FLASH ZD25WQ80

[复制链接]
1787|12
 楼主| freeelectron 发表于 2022-12-29 17:04 | 显示全部楼层 |阅读模式
本帖最后由 freeelectron 于 2022-12-29 21:03 编辑

SFUD 是一款开源的串行 SPI Flash 通用驱动库。
关于SFUD的详细资料可查看,https://github.com/armink/SFUD

为什么会有通用驱动?
JEDEC (固态技术协会)针对串行 Flash 功能的参数表制定了统一标准,https://www.jedec.org/standards-documents/docs/jesd216b,最新版 V1.6B 。该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数。这也正是SFUD驱动的由来。
对应用来说只需要配置好spi,就可以读写flash了,因为SFUD提供了这些标准驱动。


EV Board (MM32L0136C7P)板载的spi flash型号为ZD25WQ80,查看手册发现可以发现ZD25WQ80是符合JEDEC的标准的,因此可以可以直接使用SFUD 这个驱动。
8780063ad8f3eea379.png


只需要在接口文件sfud_port.c文件中适配spi驱动部分:
  1. void SpiInit(void)
  2. {
  3.         /* Enable GPIO clock for SPIy and SPIz */
  4.         RCC_EnableAHBPeriphs(RCC_AHB_PERIPH_GPIOB, true);
  5.         
  6.         /*  SPI2 :  
  7.                 PB12/CS  
  8.                 PB13/SCK   
  9.                 PB14/MISO   
  10.                 PB15/MOSI   
  11.         */

  12.     /* SPI2. */
  13.     RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_SPI2, true);
  14.     RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_SPI2);
  15.         
  16.     GPIO_Init_Type gpio_init;

  17.     /* PB12 - SPI2_CS. */
  18.     gpio_init.Pins  = GPIO_PIN_12;
  19.     gpio_init.PinMode  = GPIO_PinMode_Out_PushPull;
  20.     gpio_init.Speed = GPIO_Speed_50MHz;
  21.     GPIO_Init(GPIOB, &gpio_init);
  22.         
  23.         /* SPI2 GPIO configuration: SCK/PB13, MISO/PB14, MOSI/PB15 */

  24.     /* Configure SPIy pins: SCK, MISO and MOSI ---------------------------------*/
  25.     /* PB13 - SPI2_SCK. */
  26.     gpio_init.Pins  = GPIO_PIN_13;
  27.     gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
  28.     gpio_init.Speed = GPIO_Speed_50MHz;
  29.     GPIO_Init(GPIOB, &gpio_init);
  30.     GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_0);

  31.     /* PB14 - SPI2_MISO. */
  32.     gpio_init.Pins  = GPIO_PIN_14;
  33.     gpio_init.PinMode  = GPIO_PinMode_In_Floating;
  34.     gpio_init.Speed = GPIO_Speed_50MHz;
  35.     GPIO_Init(GPIOB, &gpio_init);
  36.     GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_0);

  37.     /* PB15 - SPI2_MOSI. */
  38.     gpio_init.Pins  = GPIO_PIN_15;
  39.     gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
  40.     gpio_init.Speed = GPIO_Speed_50MHz;
  41.     GPIO_Init(GPIOB, &gpio_init);
  42.     GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_0);

  43.     /* Setup SPI Master. */
  44.     SPI_Master_Init_Type spi_master_init;
  45.     spi_master_init.ClockFreqHz = 24000000u;
  46.     spi_master_init.BaudRate = 1000000u;
  47.     spi_master_init.XferMode = SPI_XferMode_TxRx;
  48.     spi_master_init.PolPha = SPI_PolPha_Alt0;
  49.     spi_master_init.DataWidth = SPI_DataWidth_8b;
  50.     spi_master_init.LSB = false;
  51.     spi_master_init.AutoCS = false; /* if set false, need SPI_EnableCS after spi enable. */
  52.     SPI_InitMaster(SPI2, &spi_master_init);
  53.    
  54.     /* Enable SPI Master. */
  55.     SPI_Enable(SPI2, true);
  56. }
  1. static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
  2.         size_t read_size)
  3. {
  4.     sfud_err result = SFUD_SUCCESS;
  5.     uint8_t send_data, read_data;

  6.     /**
  7.      * add your spi write and read code
  8.      */
  9.                         
  10.     if (write_size) {
  11.         SFUD_ASSERT(write_buf);
  12.     }
  13.     if (read_size) {
  14.         SFUD_ASSERT(read_buf);
  15.     }

  16.     GPIO_ClearBits(GPIOB, GPIO_PIN_12);
  17.     /* 开始读写数据 */
  18.     for (size_t i = 0, retry_times; i < write_size + read_size; i++) {
  19.         /* 先写缓冲区中的数据到 SPI 总线,数据写完后,再写 dummy(0xFF) 到 SPI 总线 */
  20.         if (i < write_size) {
  21.             send_data = *write_buf++;
  22.         } else {
  23.             send_data = SFUD_DUMMY_DATA;
  24.         }
  25.         /* 发送数据 */
  26.         retry_times = 1000;
  27.         while ( SPI_STATUS_TX_FULL & SPI_GetStatus(SPI2) )
  28.                 {
  29.             SFUD_RETRY_PROCESS(NULL, retry_times, result);
  30.         }
  31.         if (result != SFUD_SUCCESS) {
  32.             goto exit;
  33.         }
  34.                 SPI_PutData(SPI2, send_data);

  35.         /* 接收数据 */
  36.         retry_times = 1000;
  37.                 while (0u == (SPI_STATUS_RX_DONE & SPI_GetStatus(SPI2)) )               
  38.                 {
  39.             SFUD_RETRY_PROCESS(NULL, retry_times, result);
  40.         }
  41.         if (result != SFUD_SUCCESS) {
  42.             goto exit;
  43.         }
  44.         read_data = SPI_GetData(SPI2);
  45.         /* 写缓冲区中的数据发完后,再读取 SPI 总线中的数据到读缓冲区 */
  46.         if (i >= write_size) {
  47.             *read_buf++ = read_data;
  48.         }
  49.     }

  50. exit:
  51.     GPIO_SetBits(GPIOB, GPIO_PIN_12);

  52.     return result;
  53. }
  1. static void spi_lock(const sfud_spi *spi)
  2. {
  3. //    __disable_irq();
  4. }


  5. static void spi_unlock(const sfud_spi *spi)
  6. {
  7. //    __enable_irq();
  8. }


  9. /* about 100 microsecond delay */
  10. static void retry_delay_100us(void)
  11. {
  12.     uint32_t delay = 120;
  13.     while(delay--);
  14. }


  15. #ifdef SFUD_USING_QSPI
  16. /**
  17. * read flash data by QSPI
  18. */
  19. static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
  20.         uint8_t *read_buf, size_t read_size) {
  21.     sfud_err result = SFUD_SUCCESS;

  22.     /**
  23.      * add your qspi read flash data code
  24.      */

  25.     return result;
  26. }
  27. #endif /* SFUD_USING_QSPI */

  28. sfud_err sfud_spi_port_init(sfud_flash *flash) {
  29.     sfud_err result = SFUD_SUCCESS;

  30.     /**
  31.      * add your port spi bus and device object initialize code like this:
  32.      * 1. rcc initialize
  33.      * 2. gpio initialize
  34.      * 3. spi device initialize
  35.      * 4. flash->spi and flash->retry item initialize
  36.      *    flash->spi.wr = spi_write_read; //Required
  37.      *    flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable
  38.      *    flash->spi.lock = spi_lock;
  39.      *    flash->spi.unlock = spi_unlock;
  40.      *    flash->spi.user_data = &spix;
  41.      *    flash->retry.delay = null;
  42.      *    flash->retry.times = 10000; //Required
  43.      */
  44.         
  45.         SpiInit();
  46.         
  47.     flash->spi.wr = spi_write_read; //Required
  48.     flash->spi.lock = spi_lock;
  49.     flash->spi.unlock = spi_unlock;
  50.     flash->retry.delay = retry_delay_100us;
  51.     flash->retry.times = 10000; //Required
  52.         
  53.     return result;
  54. }
最后测试:
  1. #define SFUD_DEMO_TEST_BUFFER_SIZE                     1024


  2. static uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];

  3. /**
  4. * SFUD demo for the first flash device test.
  5. *
  6. * @param addr flash start address
  7. * @param size test flash size
  8. * @param size test flash data buffer
  9. */
  10. static void sfud_demo(uint32_t addr, size_t size, uint8_t *data) {
  11.     sfud_err result = SFUD_SUCCESS;
  12.     const sfud_flash *flash = sfud_get_device_table() + 0;
  13.     size_t i;
  14.     /* prepare write data */
  15.     for (i = 0; i < size; i++) {
  16.         data[i] = i;
  17.     }
  18.     /* erase test */
  19.     result = sfud_erase(flash, addr, size);
  20.     if (result == SFUD_SUCCESS) {
  21.         printf("Erase the %s flash data finish. Start from 0x%08X, size is %ld.\r\n", flash->name, addr,
  22.                 size);
  23.     } else {
  24.         printf("Erase the %s flash data failed.\r\n", flash->name);
  25.         return;
  26.     }
  27.     /* write test */
  28.     result = sfud_write(flash, addr, size, data);
  29.     if (result == SFUD_SUCCESS) {
  30.         printf("Write the %s flash data finish. Start from 0x%08X, size is %ld.\r\n", flash->name, addr,
  31.                 size);
  32.     } else {
  33.         printf("Write the %s flash data failed.\r\n", flash->name);
  34.         return;
  35.     }
  36.     /* read test */
  37.     result = sfud_read(flash, addr, size, data);
  38.     if (result == SFUD_SUCCESS) {
  39.         printf("Read the %s flash data success. Start from 0x%08X, size is %ld. The data is:\r\n", flash->name, addr,
  40.                 size);
  41.         printf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
  42.         for (i = 0; i < size; i++) {
  43.             if (i % 16 == 0) {
  44.                 printf("[%08X] ", addr + i);
  45.             }
  46.             printf("%02X ", data[i]);
  47.             if (((i + 1) % 16 == 0) || i == size - 1) {
  48.                 printf("\r\n");
  49.             }
  50.         }
  51.         printf("\r\n");
  52.     } else {
  53.         printf("Read the %s flash data failed.\r\n", flash->name);
  54.     }
  55.     /* data check */
  56.     for (i = 0; i < size; i++) {
  57.         if (data[i] != i % 256) {
  58.             printf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
  59.                         break;
  60.         }
  61.     }
  62.     if (i == size) {
  63.         printf("The %s flash test is success.\r\n", flash->name);
  64.     }
  65. }



  66. void SFUDTest(void)
  67. {
  68.         if(sfud_init() == SFUD_SUCCESS)
  69.         {
  70.         sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
  71.     }
  72. }
现象:
3076663ad57108a45a.png


3558063ad57272392a.png

1282663ad8fecb34aa.png
其中device manufacturer ID是0xBA, memory type ID是0x60, capacity ID 是0x14.
JEDEC basic flash parameter table info:与手册的是一样的,也验证了我们驱动成功。







2957563ad55c1e39b2.png
ingramward 发表于 2023-1-9 14:57 | 显示全部楼层
ZD25WQ80和w28q一样的吗?
tabmone 发表于 2023-1-9 16:03 | 显示全部楼层
SFUD不错,移植简单多了。              
uiint 发表于 2023-1-9 17:02 | 显示全部楼层
ZD25WQ80的速度怎么样?              

评论

最影响的还是spi的速率吧  发表于 2023-1-15 16:33
houjiakai 发表于 2023-1-10 21:09 | 显示全部楼层
可移植fatfs吗?              
wwppd 发表于 2023-2-2 09:49 | 显示全部楼层
这个可以移动fatfs吗?              
jimmhu 发表于 2023-2-2 11:39 | 显示全部楼层
SFUD现在都支持哪些芯片了?              
51xlf 发表于 2023-2-2 13:30 | 显示全部楼层
sfud的性能怎么样?              
kmzuaz 发表于 2023-2-2 13:58 | 显示全部楼层
通用驱动库怎么兼容硬件接口呢?              
jtracy3 发表于 2023-2-2 15:51 | 显示全部楼层
能否通过U盘通过usb口读取内部数据?
 楼主| freeelectron 发表于 2023-2-2 17:44 | 显示全部楼层
kmzuaz 发表于 2023-2-2 13:58
通用驱动库怎么兼容硬件接口呢?

spi接口肯定要自己配置了
caizhiwei 发表于 2023-3-2 18:25 | 显示全部楼层
支持文件的读写嘛?比如 fopen, fread,fwrite ,fclose
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:stm32/LoRa物联网:304350312

66

主题

786

帖子

11

粉丝
快速回复 在线客服 返回列表 返回顶部