zynq7000 PS端flash芯片读写(程序固化)
本文记录了基于Zynq平台裸机开发中验证Flash芯片读写功能及程序固化的完整过程。作者针对自研板卡上IS25LP256D Flash芯片,详细阐述了QSPI初始化、读写擦操作流程,并通过串口打印验证数据一致性。调试过程中解决了读取ID异常、数据异常、程序固化失败等问题。并附完整工程代码(包含QSPI驱动和测试函数)。最终实验成功实现Flash读写验证,为开发者提供了宝贵的调试经验。。
以下内容仅记录本人工作中发现的问题以及解决方法,仅供参考。
文章记录了作者在基于zynq平台裸机开发过程中,为验证新制板卡上的flash芯片读写功能和程序的固化,遇到的软硬件问题,并在文章中详细说明了flash芯片读写功能验证的工作流程、vitis的程序固化流程、调试中的问题记录,以及完整的工程代码。
目录
一、前言
区别于以前的工作环境,使用的硬件开发板外购,硬件外设接口的基本电路及功能都正常,到手进行一些简单的电源测试直接开发即可。 本人使用自研的非标准开发板,硬件接口及所有外设芯片的功能均要验证一遍,验证flash芯片的主要目的是vitis程序的固化,第一次缺乏经验未验证flash芯片功能,直接进行固化流程,不出意外的烧录失败,也因此有了这篇文章的调试踩坑记,将问题一一记录,希望可以帮助到有同样问题的开发者。
本文验证flash芯片读写功能建立在vivadoPL端PS的IP核配置完成,串口已经打通的基础上,验证flash芯片读写功能需要使用串口打印flash芯片ID以及读出写入的数据和数据比对结果。flash芯片型号是IS25LP256D。串口相关内容可参考上一篇笔记。zynq7000 PS端串口中断开发及调试问题记录:https://blog.csdn.net/weixin_46168087/article/details/155485280?fromshare=blogdetail&sharetype=blogdetail&sharerId=155485280&sharerefer=PC&sharesource=weixin_46168087&sharefrom=from_link
二、开发样例解析
初学者拿到一块新板卡后,除了了解硬件原理图和芯片手册,到了开发阶段,vitis软件本身提供了可参考的开发样例。

打开参考样例,需要注意的是define下的全部内容,每个定义都有对应的英文注释,所有的内容都与芯片手册一一对应。
下图所示为qspi的设备ID,在xparamerters.h头文件中定义,这个文件的生成方式是 Vivado 里通过 BD(Block Design)或者 IP Integrator 配置后,导出硬件(Export Hardware)时自动生成的。


下图所示是flash的操作指令,每一项都可以在芯片手册目录中找到对应的章节。


下图所示定义了 FlashBuffer 数据类型中每种数据的偏移量即flash操作指令、地址、数据等在读写buffer中的位置。请注意,读取数据的偏移量与写入数据的偏移量不同,因为 QSPI 驱动程序设计为允许全双工传输,因此接收到的字节数等于发送和接收的字节数。

下图所示定义了flash的各种操作所需的字节长度,这些字节不是数据,而是控制信息,包括命令和地址。

下图所示定义了 flash的页面大小、扇区大小以及页面和扇区的数量。页面大小指定了单次传输可以写入 flash 的最大字节数。这些数据需要根据自己使用的flash芯片手册定义。

IS25LP256D的技术手册如图:
因此最终的程序定义如下:

下图所示定义测试数据的起始地址、起始值、以及占用内存的大小,用户可以根据自己的需求更改测试内存块和测试值。

本实验测试数据部分,最终的程序定义如下:

根据芯片手册以及代码样例,可以总结flash芯片读写功能验证的工作流程基本分为以下几个阶段:
Step1:初始化QSPI :包括初始化QSPI、读取芯片ID,启用四线模式。
Step2:擦flash:清空测试区
Step3:写flash:向测试区写入测试数据
Step4:读flash:从测试区读出测试数据
Step5:将写入和读出的数据进行比较。
三、flash芯片读写功能验证的工作流程
1、初始化QSPI
通过flash芯片的唯一ID配置qspi初始化,设置qspi的工作模式、复位引脚、预分频系数等参数,清空读写操作的buffer,使能qspi片选信号,读取芯片ID,启用四线模式。

读取ID函数内容如下:
启用四线模式函数内容如下:

2、擦flash
清除flash操作分为全部擦除和局部擦除,通过设置擦除的起始地址和擦除的内存大小(字节数)对应不同的操作。


3、写flash
通过设置写入数据的起始地址、写入数据的内存大小(字节数)、写数据指令对flash进行写数据操作。需要写入的数据提前存储在写buffer中。写flash函数没有返回值。

4、读flash
通过设置读出数据的起始地址、读出数据的内存大小(字节数)、读数据指令对flash进行读数据操作。读操作函数的返回值即为读出的数据,存储在读buffer中。

5、读写数据比较
主函数中通过串口打印写buffer的数据、读buffer的数据,将数据进行比较,比较结果打印到串口调试助手显示区。

四、调试中的问题记录
1、如何判断读取ID正确
从开发样例可以看到读ID的指令号为0x9F,结合flash芯片数据手册可知,芯片ID三字节内容应为9D 60 19。


2、读取ID异常问题
第一次读取ID串口调试助手全部打印出00 00 00 ,且写入的数据全部读出来00,这里串口调试助手只是打印,所以可以判断数据根本没有写入成功,通信不通。

换了一块开发板先跑程序,第二块板ID正常,数据异常,见问题3。问题3解决后开始固化进入问题4,在问题4解决后同样按照更改方案将R369、R373换成两个阻值较小的电阻、更改了读ID异常的第一块板卡,所有功能都正常了,由此推断,芯片读取ID异常还是跟WP#IO2、HOLD#RESET#IO3这两个复用引脚有关。
3、读取ID正常数据异常问题
下载程序后发现读取ID正常,但数据异常全为136,如下图所示:

由于最开始的工作思路是先看硬件及芯片手册,网上搜索开发例程,用别人的代码,编译完成不报错直接跑,出现异常以后,进入断点一步步调试发现,在初始化读取ID后,配置四线模式的函数中,ID判断正确后的下一步是进行四线模式的配置,这个ID判断的首字节应该是别人使用的flash型号的首字节,由此判断数据写入不成功的原因就是四线模式根本没有配置成功,将0xEF改成0x9D即可。后面打开vitis官方自带的例程发现,例程里的ID判断直接就是0x9D,不需要更改。


更改成自己使用的芯片对应ID后,测试正确。

4、程序固化异常问题
flash读写功能验证完成后,烧录程序失败,vitis下方代码运行提示框提示设备处于qspi模式所以无法固化,但拨码开关跳线帽明明按照JTAG接法来接的。
硬件原理图如图所示:

无论在哪种启动模式下,均可在线调试,但固化程序必须在JTAG模式下固化,再断电将拨码开关拨到QSPI模式即可。
|
模式 |
MIO4 |
MIO5 |
|
JTAG |
0 |
0 |
|
QSPI |
0 |
1 |
|
SD |
1 |
1 |
|
NAND |
1 |
0 |
换到其他模式接法继续测试固化能否成功。同时用万用表测量MIO4、MIO5工作电平。
接SD模式:TP102=0.5V~0.1V跳,TP103=3.3V

接QSPI模式:测量TP102=0,TP103=3.3V

接JTAG模式:测量TP102=0,TP103=0.6V~0.1V跳

拨码开关拨到NAND模式:测量TP102=0.6V~0.1V跳,TP103=0.6V~0.1V跳

由此推断之前的测试均在QSPI模式下完成,板卡从未在JTAG模式下工作过。
为测试jtag功能,将R369、R373拆掉,焊上焊锡强行拉低到0V,再测试,能认到JTAG模式,可以完成固化程序流程。但是验证flash读写异常,如下图:


尝试将代码中的四通道读指令屏蔽掉,换成其他三种方式测试,都正常,可以判断将R369、R373拆掉,焊上焊锡强行拉低到0V,影响了芯片的WP#IO2、HOLD#RESET#IO3复用引脚。
最终的解决方案是R369、R373换成两个阻值较小的电阻、测试读写正常,也能认到JTAG模式,可以固化程序。
五、vitis的程序固化流程
程序的固化流程基本分为boot.bin文件的生成和boot.bin文件的烧录。
注:固化程序前,程序的改动上,需要将QSPI的测试函数屏蔽掉。
1、生成boot.bin文件
方式一:如下图所示,在新建vitis工程时,勾选上Boot Components,后续开发好的程序编译完成后,会在应用工程的debug-sdcard路径下自动生成boot.bin文件。


方式二:如下图所示,点击vitis软件左上方导航栏Xilinx-点击Create Boot Image-点击Zynq and Zynq Ultrascale。

点击选择生成BOOT.bin文件的路径,推荐在ps工程文件夹下建立一个名称为boot的文件夹,方便后续快速查找。


路径建立好以后,依次添加启动文件fsbl、PL端bit文件、PS端elf文件。注意:添加的顺序不能改变,启动文件fsbl路径在平台工程export文件下,PS端elf文件在应用工程debug路径下。详见截图。






添加完三个文件,点击Create Image,生成boot.bin文件成功,打开之前建立的存放boot.bin文件的文件夹boot,可以看见生成的boot.bin文件和output.bif文件。


2、boot.bin文件的烧录
点击vitis软件左上方导航栏Xilinx-点击Program Flash。


操作一选择boot.bin路径。
若boot.bin的生成由方式一完成,则路径在下图所示:

若boot.bin的生成由方式二完成,则路径在下图所示,可以看到两种方式生成的文件大小是一样的。

操作二选择flash芯片类型。参考硬件设计原理图,使用的时qspi四线单通道。这与BD中的PS_IP设置也是一致的。


操作三选择启动文件fsbl路径。与方式二生成boot.bin的启动文件路径一致。
三步操作完成后,板卡上电,拨码开关必须拨到JTAG模式,点击Program。

烧录中:

烧录成功:

将拨码开关切换到qspi模式,即可正常工作。
六、工程源码
1、qspi.h
#ifndef __QSPI_H_
#define __QSPI_H_
#include "xqspips.h"
#include <stdio.h>
#include "xil_printf.h"
//QSPI器件ID
#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID
//flash操作指令
#define WRITE_STATUS_CMD 0x01 //写状态指令
#define WRITE_CMD 0x02 //写指令-页编程命令,数据字节最多128个
#define READ_CMD 0x03 //读指令
#define WRITE_DISABLE_CMD 0x04 //禁止写使能指令
#define READ_STATUS_CMD 0x05 //读状态指令
#define WRITE_ENABLE_CMD 0x06 //写使能指令
#define FAST_READ_CMD 0x0B //单通道读取
#define DUAL_READ_CMD 0x3B //双通道读取-半双工
#define QUAD_READ_CMD 0x6B //四通道读取-半双工
#define BULK_ERASE_CMD 0xC7 //擦除整片flash-全部写1 只发指令
#define SEC_ERASE_CMD 0xD8 //擦除一个64K扇区-全部写1 指令+3Byte地址
#define READ_ID 0x9F //读取flash ID指令
//定义flash操作指令、地址、数据等在读写buffer中的位置
#define COMMAND_OFFSET 0 //flash操作指令在写buffer中的位置(第0个字节)
#define ADDRESS_1_OFFSET 1 //操作flash数据起始地址的高字节在写buffer中的位置(第1个字节)
#define ADDRESS_2_OFFSET 2 //操作flash数据起始地址的中字节在写buffer中的位置(第2个字节)
#define ADDRESS_3_OFFSET 3 //操作flash数据起始地址的低字节在写buffer中的位置(第3个字节)
#define DATA_OFFSET 4 //操作flash的数据,读取或写入的数据,在写buffer中的位置(第4个字节开始是纯数据区)
#define DUMMY_OFFSET 4 //空闲字节的位置,当使用快速、双线、四线模式读取数据时,空闲字节占读buffer的第4个字节,纯数据区从第5个字节开始
//定义各种操作所需字节长度
#define DUMMY_SIZE 1 //空闲字节大小占1个字节(当使用快速、双线、四线模式读取数据时存在dummy byte)
#define RD_ID_SIZE 4 //读取flash_ID占字节数,其中第0个字节为读取ID指令号,后3个字节为读取到的flash ID号
#define BULK_ERASE_SIZE 1 //清空整片flash指令占字节数,只需要一个清空整片flash的指令号
#define SEC_ERASE_SIZE 4 //按扇区清空flash指令占字节数,其中第0字节为按扇区清空flash的指令号,后3个字节是起始清空的flash地址
#define OVERHEAD_SIZE 4 //定义读写buffer头部数据长度,包括指令号1字节和操作地址3字节
//根据使用的flash芯片手册定义
#define SECTOR_SIZE 0x10000 //定义单个操作块大小64KB 可选4K扇区/32K块/64K块
#define NUM_SECTORS 0x200 //定义操作块数量512个 64K*512=32M
#define NUM_PAGES 0x2000 //定义页数量8192个 4K*8192=32M
#define PAGE_SIZE 256 //定义每页字节数256个字节 =4K/16bit
//定义实际读写操作的范围和数据大小
#define PAGE_COUNT 16 //定义需要操作读写的页数
#define TEST_ADDRESS 0x01FF0000 //定义读写操作的起始地址 最后一个64k块
#define MAX_DATA (PAGE_COUNT * PAGE_SIZE) //定义读写操作的最大数据量 256字节*16页
#ifdef __cplusplus
extern "C" {
#endif
//初始化QSPI控制器
int Qspi_Init(u8 QspiDeviceId,XQspiPs *QspiInstancePtr);
//通过QSPI将数据写入flash中
void FlashWrite(XQspiPs *QspiPtr, u32 Address, u8 *WriteBuf, u32 ByteCount, u8 Command);
//通过QSPI读取flash中的数据
void FlashRead(XQspiPs *QspiPtr, u32 Address, u8 *ReadBuf, u32 ByteCount, u8 Command);
//擦除flash
void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount);
//读取flash ID
int FlashReadID(void);
//使能四线模式
void FlashQuadEnable(XQspiPs *QspiPtr);
//jtag模式下解除写保护
void remove_wp(XQspiPs *QspiPtr);
#ifdef __cplusplus
}
#endif
void QSPI_Flash_Opt();
#endif
2、qspi.c
#include "qspi.h"
//定义QSPI操作结构体对象
static XQspiPs QspiInstance;
u8 ReadBuffer[MAX_DATA + DATA_OFFSET + DUMMY_SIZE];
u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET];
/* 功能:初始化QSPI控制器
* 入参1:QSPI设备ID
* 入参2:QSPI控制器实例化对象指针
*/
int Qspi_Init(u8 QspiDeviceId,XQspiPs *QspiInstancePtr){
int Status;
XQspiPs_Config *QspiConfig;
//初始化QSPI控制器
QspiConfig = XQspiPs_LookupConfig(QspiDeviceId);
if (QspiConfig == NULL) {
return XST_FAILURE;
}
Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig,
QspiConfig->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//QSPI控制器自检,保证初始化成功
Status = XQspiPs_SelfTest(QspiInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//将flash配置为手动启动、手动片选模式,将hold(reset)引脚配置为高电平,hold低电平暂停收发数据,高电平恢复收发数据
XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_MANUAL_START_OPTION |
XQSPIPS_FORCE_SSELECT_OPTION |
XQSPIPS_HOLD_B_DRIVE_OPTION);
//设置QSPI预分频系数
XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8);
//清空读写操作的buffer
memset(WriteBuffer, 0x00, sizeof(WriteBuffer));
memset(ReadBuffer, 0x00, sizeof(ReadBuffer));
//将片选信号置为有效
XQspiPs_SetSlaveSelect(QspiInstancePtr);
//读取flash ID
FlashReadID();
//使能QSPI Quad模式
FlashQuadEnable(QspiInstancePtr);
return Status;
}
/**
* @brief 通过QSPI将数据写入flash中
* @param QSPI结构体指针
* @param 要写入数据的起始地址
* @param 要写入数据的数量-按字节
* @param 写数据指令
* @return 无
* @note 无
* */
void FlashWrite(XQspiPs *QspiPtr, u32 Address, u8 *WriteBuf, u32 ByteCount, u8 Command)
{
u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */
u8 FlashStatus[2];
//发送写使能指令
XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
sizeof(WriteEnableCmd));
//将写操作指令以及数据地址写入对应待发送buffer的前4个字节,第五个字节开始才是写入的数据
WriteBuffer[COMMAND_OFFSET] = Command;
WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);
//将要写入的纯数据,写入到flash写操作buffer中(flash写操作buffer包括写指令、地址、数据等内容)
memcpy(WriteBuffer + 4, WriteBuf, ByteCount);
//将写指令、写起始地址信息、写入数据内容,写到flash中
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
ByteCount + OVERHEAD_SIZE);
//等待数据写入完毕
while (1) {
//通过发送读状态指令,获取是否已经将数据写完,如果为可读状态,则数据写入完毕
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus,
sizeof(ReadStatusCmd));
//如果读取的状态值是0xff,则证明数据还未写完
FlashStatus[1] |= FlashStatus[0];
if ((FlashStatus[1] & 0x01) == 0) {
break;
}
}
}
/**
* @brief 通过QSPI读取flash中的数据
* @param QSPI结构体指针
* @param 读取数据的起始地址
* @param 读取数据的数量-按字节
* @param 读取数据指令-普通读取、快速、双线、四线读取等
* @return 无
* @note 无
* */
void FlashRead(XQspiPs *QspiPtr, u32 Address, u8 *ReadBuf, u32 ByteCount, u8 Command)
{
//将读指令和读数据首地址写入到要发送的buffer中,它们占4个字节
WriteBuffer[COMMAND_OFFSET] = Command;
WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);
//如果是快速读取、双线读取和四线读取,则需要增加一个空闲字节的长度DUMMY_SIZE
if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) ||
(Command == QUAD_READ_CMD)) {
ByteCount += DUMMY_SIZE;
}
//将读指令和读地址发送到通过QSPI发送到flash,等待数据读取至ReadBuffer中
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, ReadBuffer,
ByteCount + OVERHEAD_SIZE);
//如果是快速读取、双线读取和四线读取,则需要增加一个虚拟字节的长度DUMMY_SIZE
if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) ||
(Command == QUAD_READ_CMD)) {
//非普通模式读取,有空闲字节,从第5个字节开始是纯数据
memcpy(ReadBuf, ReadBuffer + 5, ByteCount - 1);
}else{
//普通模式读取,无空闲字节,从第4个字节开始是纯数据
memcpy(ReadBuf, ReadBuffer + 4, ByteCount);
}
}
/**
* @brief 擦除flash
* @param QSPI结构体指针
* @param 擦除的起始地址
* @param 擦除数据的数量-按字节
* @return 无
* @note 无
* */
void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount)
{
u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */
u8 FlashStatus[2];
int Sector;
//如果是擦除整片flash,则使用整片擦除指令chip erase
if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) {
//发送写使能指令
XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
sizeof(WriteEnableCmd));
//将整片擦除指令写入到发送buffer的首个字节的位置
WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD;
//将整片擦除指令发送到flash
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
BULK_ERASE_SIZE);
//等待擦除完成
while (1) {
//通过发送读状态指令,获取是否已经将数据写完,如果为可读状态,则数据写入完毕
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
FlashStatus,
sizeof(ReadStatusCmd));
//如果读取的状态值是0xff,则证明数据还未写完
FlashStatus[1] |= FlashStatus[0];
if ((FlashStatus[1] & 0x01) == 0) {
break;
}
}
return;
}
//如果是部分擦除,则使用扇区sector擦除的指令进行擦除操作
for (Sector = 0; Sector < ((ByteCount / SECTOR_SIZE) + 1); Sector++) {
//发送写使能指令
XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
sizeof(WriteEnableCmd));
//将扇区擦除指令,以及开始擦除首地址写入到发送buffer的前四个字节
WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD;
WriteBuffer[ADDRESS_1_OFFSET] = (u8)(Address >> 16);
WriteBuffer[ADDRESS_2_OFFSET] = (u8)(Address >> 8);
WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);
//将扇区擦除指令,以及开始擦除首地址发送到flash
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
SEC_ERASE_SIZE);
//等待擦除完成
while (1) {
//通过发送读状态指令,获取是否已经将数据写完,如果为可读状态,则数据写入完毕
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
FlashStatus,
sizeof(ReadStatusCmd));
//如果读取的状态值是0xff,则证明数据还未写完
FlashStatus[1] |= FlashStatus[0];
if ((FlashStatus[1] & 0x01) == 0) {
break;
}
}
Address += SECTOR_SIZE;
}
}
/**
* @brief 读取flash ID
* @param 无
* @return 无
* @note 无
* */
int FlashReadID(void)
{
int Status;
/* Read ID in Auto mode.*/
WriteBuffer[COMMAND_OFFSET] = READ_ID;
WriteBuffer[ADDRESS_1_OFFSET] = 0x23; /* 3 dummy bytes */
WriteBuffer[ADDRESS_2_OFFSET] = 0x08;
WriteBuffer[ADDRESS_3_OFFSET] = 0x09;
Status = XQspiPs_PolledTransfer(&QspiInstance, WriteBuffer, ReadBuffer,
RD_ID_SIZE);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
xil_printf("FlashID=0x%x 0x%x 0x%x\n\r", ReadBuffer[1], ReadBuffer[2],
ReadBuffer[3]);
return XST_SUCCESS;
}
/**
* @brief 使能四线模式
* @param QSPI结构体指针
* @return 无
* @note 无
* */
void FlashQuadEnable(XQspiPs *QspiPtr)
{
u8 WriteEnableCmd = {WRITE_ENABLE_CMD};
u8 ReadStatusCmd[] = {READ_STATUS_CMD, 0};
u8 QuadEnableCmd[] = {WRITE_STATUS_CMD, 0};
u8 FlashStatus[2];
//判断读取的flash ID是否正确,不加这个判断也行
if (ReadBuffer[1] == 0x9D) {
//获取读状态
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
FlashStatus,
sizeof(ReadStatusCmd));
QuadEnableCmd[1] = FlashStatus[1] | 1 << 6;
//发送写使能指令
XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
sizeof(WriteEnableCmd));
//发送Quad配置指令
XQspiPs_PolledTransfer(QspiPtr, QuadEnableCmd, NULL,
sizeof(QuadEnableCmd));
while (1) {
//获取读状态,等待指令写入完毕
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus,
sizeof(ReadStatusCmd));
/*
* 第6it置1,第0bit置0,则Quad模式设置成功、且设备状态准备就绪
*/
if ((FlashStatus[0] == 0x40) && (FlashStatus[1] == 0x40)) {
break;
}
}
}
}
//qspi读写测试函数
//读写buffer数据长度
#define test_buf_len 255
//QSPI读写flash测试
void QSPI_Flash_Opt(){
u8 nRet = XST_SUCCESS;
u8 write_buf[test_buf_len] = {0};
u8 read_buf[test_buf_len] = {0};
//初始化QSPI控制器
Qspi_Init(QSPI_DEVICE_ID,&QspiInstance);
//write buffer填写数据
for(int i = 0; i < test_buf_len; i++){
write_buf[i] = i + 1;
}
//清除要写入的flash区域
FlashErase(&QspiInstance, TEST_ADDRESS, test_buf_len);
//将write buffer数据写入flash中
FlashWrite(&QspiInstance, TEST_ADDRESS, write_buf, test_buf_len, WRITE_CMD);
//将写入flash中的数据再进行读取
// FlashRead(&QspiInstance, TEST_ADDRESS, read_buf, test_buf_len, READ_CMD); //常规读写指令
// FlashRead(&QspiInstance, TEST_ADDRESS, read_buf, test_buf_len, FAST_READ_CMD);//单通道读写指令
// FlashRead(&QspiInstance, TEST_ADDRESS, read_buf, test_buf_len, DUAL_READ_CMD);//双通道读写指令
FlashRead(&QspiInstance, TEST_ADDRESS, read_buf, test_buf_len, QUAD_READ_CMD);//四通道读写指令
//打印写buffer区数据
for(int i = 0; i < test_buf_len; i++){
printf("%d ", write_buf[i]);
if(i == test_buf_len - 1)
printf("\n");
}
//打印读buffer区数据
for(int i = 0; i < test_buf_len; i++){
printf("%d ", read_buf[i]);
if(i == test_buf_len - 1)
printf("\n");
}
//对比写入和读出的数据是否一致
for(int i = 0; i < test_buf_len; i++){
if(read_buf[i] != write_buf[i]){
nRet = XST_FAILURE;
}
}
if(nRet == XST_SUCCESS){
printf("QSPI Operate flash successful!\n");
}else{
printf("QSPI Operate flash failed!\n");
}
}
3、main
#include "platform.h"
#include "qspi.h"
int main()
{
init_platform();
//FLASH 芯片测试代码
QSPI_Flash_Opt();
cleanup_platform();
return 0;
}
4、实验结果
硬件连接好串口调试设备,打开vitis自带的串口调试助手,运行程序,可以看到串口调试界面依次打印flash芯片ID,写入数据缓存器write_buf的值,读出数据缓存器read_buf的值,并将两组数据进行比较,确认读写数据一致后打印QSPI Operate flash successful!。

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)