搜索
bottom↓
回复: 1

【正点原子FPGA连载】第十一章QSPI Flash读写测试实验--摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南

[复制链接]

出0入234汤圆

发表于 2020-7-27 11:36:05 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2020-10-24 10:24 编辑

1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
QQ群头像.png
5)关注正点原子公众号,获取最新资料


100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png


第十一章QSPI Flash读写测试实验



Quad-SPI Flash控制器是PS内部输入/输出外设(IOP)的一部分,用来访问多位串行Flash存储器件,适合于使用较少的引脚数目达到较高数据带宽的应用场景。
本章我们将通过QSPI Flash控制器,来完成对QSPI Flash的读写操作。本章包括以下几个部分:
1111.1简介
11.2实验任务
11.3硬件设计
11.4软件设计
11.5下载验证

11.1简介
ZYNQ中的QSPI Flash控制器可以工作在三种模式下:I/O模式、线性地址模式,以及传统SPI模式。
在I/O模式中,软件负责实现Flash器件的通信协议。软件需要将Flash命令和数据写到控制器中的TXD寄存器中,然后将接收到的数据从RXD寄存器中读出。
而线性地址模式则使用一组器件操作来减小软件从Flash中读取数据的开销。线性模式使用硬件来实现来自AXI接口的命令到Flash指令的转换。对用户来说,在线性模式下读QSPI Flash就像读取ROM一样简单。但是该模式只支持读操作,不支持写操作。
传统模式下的QSPI Flash控制器就像一个普通的SPI控制器,这个模式用的相对较少。
QSPI Flash控制器的系统框图如下所示:
阿莫论坛发帖领航者专用1526.png

图 11.1.1 QSPI系统框图

从图 11.1.1中可以看到,QSPI Flash控制器通过MIO与外部Flash器件连接,支持三种模式:单个从器件模式、双从器件并行模式和双从器件堆模式。通过使用双从器件模式可以扩展QSPI Flash的存储容量。
在使用单个器件的时候,直接存储访问Flash器件的地址映射FC00_0000到FCFF_FFFF(16MB)。在使用双器件模式时,地址空间可以扩展为FC00_0000至FDFF_FFFF(32MB)。
另外,在上图中可以看到控制器左侧有两种类型的接口:AXI接口和APB接口。其中AXI接口用于线性地址模式,而APB接口用于I/O模式。
QSPI Flash控制器的模块示意图如图 11.1.2所示。从中我们可以清晰的看出QSPI Flash控制器两种模式的差异。由于线性地址模式不支持写操作,因此本次实验重点介绍I/O模式。
在I/O模式下,软件需要把命令和数据转化成QSPI Flash协议下的指令,转换之后的指令将被写入Tx FIFO。然后发送逻辑将Tx FIFO中的内容按照QSPI接口规范进行并串转换,最后通过MIO将转换后的数据送到Flash存储器中。在发送逻辑将Tx FIFO中的数据发送出去的同时,接收逻辑会采样所发送的串行数据,进行串并转换后存储到Rx FIFO里面。
如果执行的是读操作,在发送读指令和读地址之后,MIO会在发送逻辑的控制下由输出模式切换成输入模式,从Flash中读出的数据将被存储丰Rx FIFO中。由于Rx FIFO中会同时接收由软件发出的指令,因此我们需要对Rx FIFO中的原始数据进行过滤,从而得到从Flash中读出的有效数据。

阿莫论坛发帖领航者专用11335.png

图 11.1.2 QSPI Flash控制器功能框图

11.2实验任务
本章的实验任务是使用QSPI Flash控制器,先后对领航者核心板上的QSPI Flash进行写、读操作。通过对比读出的数据是否等于写入的数据,从而验证读写操作是否正确。
11.3硬件设计
根据实验任务我们可以画出本次实验的系统框图,如下图所示:
阿莫论坛发帖领航者专用11532.png

图 11.3.1 系统框图

从图 11.3.1中可以看出,本次实验是在“Hello Wold”实验的基础上增加了一个QSPI Flash控制器。我们将通过该控制器对QSPI Flash进行读写操作,并通过串口打印读写数据对比之后的结果。
首先创建Vivado工程,工程名为“qspi_rw_test”,然后创建Block Design设计(system.bd)并添加ZYNQ7 Processing System模块。接下来按照《“Hello World”实验》中的步骤对ZYNQ PS模块进行配置,配置完成 后我们要添加本次实验所使用的QSPI Flash控制器模块。如下图所示:
阿莫论坛发帖领航者专用11893.png

图 11.3.2 QSPI配置界面

如图 11.3.2所示,在左侧导航栏中选择“Peripheral I/O Pins”,然后在右侧勾选“Quad SPI Flash”,并选择“Single SS 4bit IO”模式。“Single SS 4bit IO”指的是单个从器件模式,其中“SS”为“Slave Select”的缩写。看以看出,该模式下控制器使用了MIO1至MIO6共6个引脚。
最后点击右下角的“OK”,本次实验ZYNQ处理系统就配置完成了。接下来在Diagram窗口中选择自动连接PS模块端口,连接完成后模块如下图所示:
阿莫论坛发帖领航者专用12231.png

图 11.3.3 ZYNQ7模块

到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
然后在菜单栏中选择 File > Export > Export hardware导出硬件,最后在菜单栏选择File > Launch SDK,启动SDK软件。
11.4软件设计
在SDK软件中新建一个BSP工程和一个空的应用工程,应用工程名为“qspi_Flash_test”。然后为应用工程新建一个源文件“main.c”,我们在新建的main.c文件中输入本次实验的代码。代码的主体部分如下所示:
  1. 1   #include "xparameters.h"    /* SDK generated parameters */
  2. 2   #include "xqspips.h"        /* QSPI device driver */
  3. 3   #include "xil_printf.h"
  4. 4   
  5. 5   #define QSPI_DEVICE_ID      XPAR_XQSPIPS_0_DEVICE_ID
  6. 6   
  7. 7   //发送到Flash器件的指令
  8. 8   #define WRITE_STATUS_CMD    0x01
  9. 9   #define WRITE_CMD           0x02
  10. 10  #define READ_CMD            0x03
  11. 11  #define WRITE_DISABLE_CMD   0x04
  12. 12  #define READ_STATUS_CMD     0x05
  13. 13  #define WRITE_ENABLE_CMD    0x06
  14. 14  #define FAST_READ_CMD       0x0B
  15. 15  #define DUAL_READ_CMD       0x3B
  16. 16  #define QUAD_READ_CMD       0x6B
  17. 17  #define BULK_ERASE_CMD      0xC7
  18. 18  #define SEC_ERASE_CMD       0xD8
  19. 19  #define READ_ID             0x9F
  20. 20  
  21. 21  //Flash BUFFER中各数据的偏移量
  22. 22  #define COMMAND_OFFSET      0 // Flash instruction
  23. 23  #define ADDRESS_1_OFFSET    1 // MSB byte of address to read or write
  24. 24  #define ADDRESS_2_OFFSET    2 // Middle byte of address to read or write
  25. 25  #define ADDRESS_3_OFFSET    3 // LSB byte of address to read or write
  26. 26  #define DATA_OFFSET         4 // Start of Data for Read/Write
  27. 27  #define DUMMY_OFFSET        4 // Dummy byte offset for reads
  28. 28  
  29. 29  #define DUMMY_SIZE          1 // Number of dummy bytes for reads
  30. 30  #define RD_ID_SIZE          4 // Read ID command + 3 bytes ID response
  31. 31  #define BULK_ERASE_SIZE     1 // Bulk Erase command size
  32. 32  #define SEC_ERASE_SIZE      4 // Sector Erase command + Sector address
  33. 33  
  34. 34  #define OVERHEAD_SIZE       4 // control information: command and address
  35. 35  
  36. 36  #define SECTOR_SIZE         0x10000
  37. 37  #define NUM_SECTORS         0x100
  38. 38  #define NUM_PAGES           0x10000
  39. 39  #define PAGE_SIZE           256
  40. 40  
  41. 41  /* Number of Flash pages to be written.*/
  42. 42  #define PAGE_COUNT      16
  43. 43  
  44. 44  /* Flash address to which data is to be written.*/
  45. 45  #define TEST_ADDRESS    0x00055000
  46. 46  #define UNIQUE_VALUE    0x05
  47. 47  
  48. 48  #define MAX_DATA        (PAGE_COUNT * PAGE_SIZE)
  49. 49  
  50. 50  void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount);
  51. 51  void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command);
  52. 52  void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command);
  53. 53  int  FlashReadID(void);
  54. 54  void FlashQuadEnable(XQspiPs *QspiPtr);
  55. 55  int  QspiFlashPolledExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId);
  56. 56  
  57. 57  static XQspiPs QspiInstance;
  58. 58  
  59. 59  int Test = 5;
  60. 60  
  61. 61  u8 ReadBuffer[MAX_DATA + DATA_OFFSET + DUMMY_SIZE];
  62. 62  u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET];
  63. 63  
  64. 64  int main(void)
  65. 65  {
  66. 66      int Status;
  67. 67  
  68. 68      xil_printf("QSPI Flash Polled Example Test \r\n");
  69. 69  
  70. 70      /* Run the Qspi Interrupt example.*/
  71. 71      Status = QspiFlashPolledExample(&QspiInstance, QSPI_DEVICE_ID);
  72. 72      if (Status != XST_SUCCESS) {
  73. 73          xil_printf("QSPI Flash Polled Example Test Failed\r\n");
  74. 74          return XST_FAILURE;
  75. 75      }
  76. 76  
  77. 77      xil_printf("Successfully ran QSPI Flash Polled Example Test\r\n");
  78. 78      return XST_SUCCESS;
  79. 79  }
复制代码

首先,本次实验的C程序是在官方提供的示例程序“xqspips_Flash_polled_example.c”的基础上修改得到的,该示例程序演示了如何使用轮询模式对QSPI Flash进行读写操作。
在程序的开头,我们定义了一系列的参数,包括Flash器件的指令、Flash BUFFER中各数据段的偏移量、Flash器件PAGE、SECTOR的数目和大小等信息。这些信息针对不同型号的Flash器件有所不同,需要通过查看器件的数据手册得到。
接下来在程序第50至55行声明了六个函数,这些函数是前面我们提到的示例程序中所提供的。我们对其中最后一个函数QspiFlashPolledExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId)进行修改,从而简化读写测试过程。而其他的函数如擦除FlashErase( )、写操作FlashWrite( )、读操作FlashRead( )等,我们可以将其当作库函数来使用。
程序的主函数特别简单,就是通过调用修改之后的示例函数QspiFlashPolledExample( )来对Flash进行读写测试,并打印最终的测试结果。下面是该示例函数的代码:
  1. 81  int QspiFlashPolledExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId)
  2. 82  {
  3. 83      int Status;
  4. 84      u8 *BufferPtr;
  5. 85      u8 UniqueValue;
  6. 86      int Count;
  7. 87      int Page;
  8. 88      XQspiPs_Config *QspiConfig;
  9. 89  
  10. 90      //初始化QSPI驱动
  11. 91      QspiConfig = XQspiPs_LookupConfig(QspiDeviceId);
  12. 92      XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig, QspiConfig->BaseAddress);
  13. 93      //初始化读写BUFFER
  14. 94      for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE;
  15. 95           Count++, UniqueValue++) {
  16. 96          WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test);
  17. 97      }
  18. 98      memset(ReadBuffer, 0x00, sizeof(ReadBuffer));
  19. 99  
  20. 100     //设置手动启动和手动片选模式
  21. 101     XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_MANUAL_START_OPTION |
  22. 102             XQSPIPS_FORCE_SSELECT_OPTION |
  23. 103             XQSPIPS_HOLD_B_DRIVE_OPTION);
  24. 104     //设置QSPI时钟的分频系数
  25. 105     XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8);
  26. 106     //片选信号置为有效
  27. 107     XQspiPs_SetSlaveSelect(QspiInstancePtr);
  28. 108     //读Flash ID
  29. 109     FlashReadID();
  30. 110     //使能Flash Quad模式
  31. 111     FlashQuadEnable(QspiInstancePtr);
  32. 112     //擦除Flash
  33. 113     FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA);
  34. 114     //向Flash中写入数据
  35. 115     for (Page = 0; Page < PAGE_COUNT; Page++) {
  36. 116         FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS,
  37. 117                PAGE_SIZE, WRITE_CMD);
  38. 118     }
  39. 119     //使用QUAD模式从Flash中读出数据
  40. 120     FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, QUAD_READ_CMD);
  41. 121
  42. 122     //对比写入Flash与从Flash中读出的数据
  43. 123     BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE];
  44. 124     for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA;
  45. 125          Count++, UniqueValue++) {
  46. 126         if (BufferPtr[Count] != (u8)(UniqueValue + Test)) {
  47. 127             return XST_FAILURE;
  48. 128         }
  49. 129     }
  50. 130
  51. 131     return XST_SUCCESS;
  52. 132 }
复制代码

在示例函数中,首先对QSPI Flash控制器驱动进行初始化。然后对读写BUFFER进行初始化,初始化完成后WriteBuffer中为需要写入Flash的测试数据;而ReadBuffer则全部清零,准备用于接收从Flash中读回的数据,进而与WriteBuffer中的数据进行对比。
接下来,通过调用xqspips.h头文件中的库函数来对QSPI Flash控制器进行配置。将其配置成手动启动和手动片选模式,并将片选信号置为有效状态。QSPI Flash控制器在I/O模式下,有两种流控(Flow Control)方法:手动模式和自动模式。手动模式下,将由用户来控制数据传输的开始。而在手动模式下,又分为“手动片选”和“自动片选”,它们指的是片选信号的控制权。自动片选所传输的数据量受限于Tx FIFO的深度,而手动片选更适合批量数据的传输。片选信号置为有效状态意味着Flash传输序列的开始。
在程序的108至120行,我们调用示例程序“xqspips_Flash_polled_example.c”所提供的函数,来执行一系列Flash操作,包括读Flash ID、使能Flash Quad模式、擦除Flash等。其中最核心的是通过FlashWrite( )函数向Flash指定地址写入测试数据,然后通过FlashRead( )函数将数据从该地址读出,放至读BUFFER中。
最后,在程序的122至129行,通过对比写BUFFER与读BUFFER中的数据是否一致,从而判断Flash读写测试实验是否成功。
程序的剩余部分是前面所声明的一系列操作Flash的函数的实现,因为我们将其当作库函数直接调用,因此代码就不再贴出来了。大家有兴趣的话也可以研究一下,这些函数是如何将读写指令和数据转换成QSPI Flash所要求的命令格式的。实际上,这些函数的功能也都是通过调用xqspips.h头文件中的库函数XQspiPs_PolledTransfer( )来实现的。
11.5下载验证
首先我们将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用Mini USB连接线将开发板左侧的USB_UART接口与电脑连接,用于串口通信。最后连接开发板的电源,并打开电源开关。
在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。然后下载本次实验软件程序,下载完成后,在下方的SDK Terminal中可以看到应用程序打印的信息如下图所示:
阿莫论坛发帖领航者专用19132.png

图 11.5.1 串口打印结果

从图 11.5.1中可以看出,本次实验所实现的QSPI Flash读写测试功能,在领航者ZYNQ开发板上面下载验证成功。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出16170入6148汤圆

发表于 2020-8-3 19:10:20 来自手机 | 显示全部楼层
打赏!

庆祝论坛“打赏”功能实施, 现在开始发技术主题,可以获得打赏
https://www.amobbs.com/thread-5735948-1-1.html
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 04:30

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表