当前位置: 首页 > news >正文

ZYNQ FLASH读写

一、FLASH简介

1、FLASH存储器的基本概念

        FLASH存储器是一种非易失性存储技术,断电后仍能保留数据。它基于浮栅晶体管结构,通过电荷存储实现数据读写。与传统的EEPROM相比,FLASH具有更高的密度和更低的成本,但只能以块为单位擦除。

2、主要类型

  • NOR FLASH

        以字节为单位随机访问,读取速度快,常用于存储固件代码。写入和擦除速度较慢,容量通常较小(MB级),价格较高。

  • NAND FLASH

        以页为单位顺序访问,读写速度更快,容量更大(GB至TB级),成本更低。主要用于大容量存储设备如SSD、U盘和存储卡,但需要额外的错误校验机制。

3、工作原理

        写入操作:通过热电子注入或F-N隧穿将电子注入浮栅,改变晶体管阈值电压表示数据。典型的单元电压范围在12-20V之间。

        擦除操作:施加反向电压清除浮栅电荷,通常以块为单位进行。擦除周期约需1-10ms,块擦除次数有限(SLC约10万次,QLC约1000次)。

二、FLASH控制器简介

        Quad-SPI闪存控制器是PS内输入/输出外设(IOP)的一部分,用于访问多比特串行闪存设备,实现高吞吐量和低引脚数应用。控制器工作在三种模式之一:I/O模式,线性寻址模式,和传统SPI模式。在I/O模式下,软件与闪存设备协议密切交互。软件通过四个TXD寄存器向控制器写入flash命令和数据。软件读取RXD寄存器,其中包含从闪存设备接收到的数据。线性寻址模式使用设备操作的子集来消除I/O模式读取闪存所需的软件开销。线性模式使硬件向闪存发出命令并控制从闪存总线到AXI接口的数据流。控制器响应AXI接口上的内存请求,就好像闪存是ROM存储器一样。在遗留模式下,QSPI控制器充当普通SPI控制器。控制器可以连接到一个或两个闪存设备。两个设备可以并联连接以获得8位的性能,也可以堆叠4位以减少引脚数。

如下是模式框图:由此我们发现线性模式下只能读数据,虽然可以写数据但只能写命令,无法写我们想要存储的数据。而IO模式如下图可以读写数据,因此我们实验使用IO模式

三、程序设计

1、系统框图

2、实验任务

        通过ZYNQ上的QFLASH读写控制器读FLASH进行读写对比

3、实验流程

1、另存我们的hello_word工程

2、删除SDK,我们待会儿重新设计程序

3、勾选如下。由于我这块ZYNQ开发板只连接一个flash因此我选择第一种,如果你的开发板连接了两个可以选择下面的两种模式。下面两种对应的是两种控制器模式(后面两幅图)。独立连接和复用连接,具体哪种可以看你的原理图。

4、这里的flash读写时钟是200mhz可能太快了,但没关系,我们写的数据少,且可以通过延时控制。

5、重新生成顶层文件

6、重新导出硬件设计,然后打开SDK。

7、新建项目添加源文件,打开示例工程。根据示例工程编写代码。

8、复制以下代码测试。

#include "xparameters.h"    /* SDK generated parameters */
#include "xqspips.h"        /* QSPI device driver */
#include "xil_printf.h"#define QSPI_DEVICE_ID      XPAR_XQSPIPS_0_DEVICE_ID//发送到Flash器件的指令
#define WRITE_STATUS_CMD    0x01
#define WRITE_CMD           0x02
#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
#define SEC_ERASE_CMD       0xD8
#define READ_ID             0x9F//Flash BUFFER中各数据的偏移量
#define COMMAND_OFFSET      0 // Flash instruction
#define ADDRESS_1_OFFSET    1 // MSB byte of address to read or write
#define ADDRESS_2_OFFSET    2 // Middle byte of address to read or write
#define ADDRESS_3_OFFSET    3 // LSB byte of address to read or write
#define DATA_OFFSET         4 // Start of Data for Read/Write
#define DUMMY_OFFSET        4 // Dummy byte offset for reads#define DUMMY_SIZE          1 // Number of dummy bytes for reads
#define RD_ID_SIZE          4 // Read ID command + 3 bytes ID response
#define BULK_ERASE_SIZE     1 // Bulk Erase command size
#define SEC_ERASE_SIZE      4 // Sector Erase command + Sector address#define OVERHEAD_SIZE       4 // control information: command and address#define SECTOR_SIZE         0x10000
#define NUM_SECTORS         0x100
#define NUM_PAGES           0x10000
#define PAGE_SIZE           256/* Number of Flash pages to be written.*/
#define PAGE_COUNT      16/* Flash address to which data is to be written.*/
#define TEST_ADDRESS    0x00055000
#define UNIQUE_VALUE    0x05#define MAX_DATA        (PAGE_COUNT * PAGE_SIZE)void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount);
void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command);
void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command);
int  FlashReadID(void);
void FlashQuadEnable(XQspiPs *QspiPtr);
int  QspiFlashPolledExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId);static XQspiPs QspiInstance;int Test = 5;u8 ReadBuffer[MAX_DATA + DATA_OFFSET + DUMMY_SIZE];
u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET];int main(void)
{int Status;xil_printf("QSPI Flash Polled Example Test \r\n");/* Run the Qspi Interrupt example.*/Status = QspiFlashPolledExample(&QspiInstance, QSPI_DEVICE_ID);if (Status != XST_SUCCESS) {xil_printf("QSPI Flash Polled Example Test Failed\r\n");return XST_FAILURE;}xil_printf("Successfully ran QSPI Flash Polled Example Test\r\n");return XST_SUCCESS;
}
int QspiFlashPolledExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId)
{//int Status;u8 *BufferPtr;u8 UniqueValue;int Count;int Page;XQspiPs_Config *QspiConfig;//初始化QSPI驱动QspiConfig = XQspiPs_LookupConfig(QspiDeviceId);XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig, QspiConfig->BaseAddress);//初始化读写BUFFERfor (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE;Count++, UniqueValue++) {WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test);}memset(ReadBuffer, 0x00, sizeof(ReadBuffer));//设置手动启动和手动片选模式XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_MANUAL_START_OPTION |XQSPIPS_FORCE_SSELECT_OPTION |XQSPIPS_HOLD_B_DRIVE_OPTION);//设置QSPI时钟的分频系数XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8);//片选信号置为有效XQspiPs_SetSlaveSelect(QspiInstancePtr);//读Flash IDFlashReadID();//使能Flash Quad模式FlashQuadEnable(QspiInstancePtr);//擦除FlashFlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA);//向Flash中写入数据for (Page = 0; Page < PAGE_COUNT; Page++) {FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS,PAGE_SIZE, WRITE_CMD);}//使用QUAD模式从Flash中读出数据FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, QUAD_READ_CMD);//对比写入Flash与从Flash中读出的数据BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE];for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA;Count++, UniqueValue++) {if (BufferPtr[Count] != (u8)(UniqueValue + Test)) {return XST_FAILURE;}}return XST_SUCCESS;
}void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command)
{u8 WriteEnableCmd = { WRITE_ENABLE_CMD };u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 };  /* must send 2 bytes */u8 FlashStatus[2];/** Send the write enable command to the FLASH so that it can be* written to, this needs to be sent as a seperate transfer before* the write*/XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,sizeof(WriteEnableCmd));/** Setup the write command with the specified address and data for the* FLASH*/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);/** Send the write command, address, and data to the FLASH to be* written, no receive buffer is specified since there is nothing to* receive*/XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,ByteCount + OVERHEAD_SIZE);/** Wait for the write command to the FLASH to be completed, it takes* some time for the data to be written*/while (1) {/** Poll the status register of the FLASH to determine when it* completes, by sending a read status command and receiving the* status byte*/XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus,sizeof(ReadStatusCmd));/** If the status indicates the write is done, then stop waiting,* if a value of 0xFF in the status byte is read from the* device and this loop never exits, the device slave select is* possibly incorrect such that the device status is not being* read*/if ((FlashStatus[1] & 0x01) == 0) {break;}}
}
void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command)
{/** Setup the write command with the specified address and data for the* FLASH*/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);if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) ||(Command == QUAD_READ_CMD)) {ByteCount += DUMMY_SIZE;}/** Send the read command to the FLASH to read the specified number* of bytes from the FLASH, send the read command and address and* receive the specified number of bytes of data in the data buffer*/XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, ReadBuffer,ByteCount + OVERHEAD_SIZE);
}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;/** If erase size is same as the total size of the flash, use bulk erase* command*/if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) {/** Send the write enable command to the FLASH so that it can be* written to, this needs to be sent as a seperate transfer* before the erase*/XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,sizeof(WriteEnableCmd));/* Setup the bulk erase command*/WriteBuffer[COMMAND_OFFSET]   = BULK_ERASE_CMD;/** Send the bulk erase command; no receive buffer is specified* since there is nothing to receive*/XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,BULK_ERASE_SIZE);/* Wait for the erase command to the FLASH to be completed*/while (1) {/** Poll the status register of the device to determine* when it completes, by sending a read status command* and receiving the status byte*/XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,FlashStatus,sizeof(ReadStatusCmd));/** If the status indicates the write is done, then stop* waiting; if a value of 0xFF in the status byte is* read from the device and this loop never exits, the* device slave select is possibly incorrect such that* the device status is not being read*/if ((FlashStatus[1] & 0x01) == 0) {break;}}return;}/** If the erase size is less than the total size of the flash, use* sector erase command*/for (Sector = 0; Sector < ((ByteCount / SECTOR_SIZE) + 1); Sector++) {/** Send the write enable command to the SEEPOM so that it can be* written to, this needs to be sent as a seperate transfer* before the write*/XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,sizeof(WriteEnableCmd));/** Setup the write command with the specified address and data* for the FLASH*/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);/** Send the sector erase command and address; no receive buffer* is specified since there is nothing to receive*/XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,SEC_ERASE_SIZE);/** Wait for the sector erse command to the* FLASH to be completed*/while (1) {/** Poll the status register of the device to determine* when it completes, by sending a read status command* and receiving the status byte*/XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,FlashStatus,sizeof(ReadStatusCmd));/** If the status indicates the write is done, then stop* waiting, if a value of 0xFF in the status byte is* read from the device and this loop never exits, the* device slave select is possibly incorrect such that* the device status is not being read*/if ((FlashStatus[1] & 0x01) == 0) {break;}}Address += SECTOR_SIZE;}
}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;
}
void FlashQuadEnable(XQspiPs *QspiPtr)
{u8 WriteEnableCmd = {WRITE_ENABLE_CMD};u8 ReadStatusCmd[] = {READ_STATUS_CMD, 0};u8 QuadEnableCmd[] = {WRITE_STATUS_CMD, 0};u8 FlashStatus[2];if (ReadBuffer[1] == 0x9D) {XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,FlashStatus,sizeof(ReadStatusCmd));QuadEnableCmd[1] = FlashStatus[1] | 1 << 6;XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,sizeof(WriteEnableCmd));XQspiPs_PolledTransfer(QspiPtr, QuadEnableCmd, NULL,sizeof(QuadEnableCmd));}
}


文章转载自:

http://zHCZrz8m.gctgc.cn
http://HajnRifh.gctgc.cn
http://NYgEWkui.gctgc.cn
http://3katYf2R.gctgc.cn
http://rf2Jm5Xg.gctgc.cn
http://CnsbpCLs.gctgc.cn
http://XQwJ29I7.gctgc.cn
http://n1dynJpo.gctgc.cn
http://4K6qOsfD.gctgc.cn
http://43OcuS4N.gctgc.cn
http://qkcmloU4.gctgc.cn
http://PpciQusm.gctgc.cn
http://PhFZUSVQ.gctgc.cn
http://dSdqS6a6.gctgc.cn
http://sjoXxPM6.gctgc.cn
http://aWnkTBbS.gctgc.cn
http://RhmZX44O.gctgc.cn
http://LIcMlbPr.gctgc.cn
http://fooGvO1g.gctgc.cn
http://0ETxAet0.gctgc.cn
http://gfkTyLr7.gctgc.cn
http://BmxnXM0B.gctgc.cn
http://2EPIvBzp.gctgc.cn
http://2h9Bc2CU.gctgc.cn
http://EjT2M499.gctgc.cn
http://588Y00s9.gctgc.cn
http://UGAJO9m7.gctgc.cn
http://RBDY2njN.gctgc.cn
http://0wriwII6.gctgc.cn
http://Qm5lw3mq.gctgc.cn
http://www.dtcms.com/a/371583.html

相关文章:

  • 容器元素的滚动条回到顶部
  • 【音频字幕】构建一个离线视频字幕生成系统:使用 WhisperX 和 Faster-Whisper 的 Python 实现
  • ncnn-Android-mediapipe_hand 踩坑部署实录
  • java面试中经常会问到的mysql问题有哪些(基础版)
  • SoundSource for Mac 音频控制工具
  • Unity学习----【进阶】Input System学习(一)--导入与基础的设备调用API
  • 第11篇:降维算法:PCA、t-SNE、UMAP
  • 【Leetcode100】算法模板之二叉树
  • 深入理解假设检验:从抛硬币到药物实验的全景讲解
  • JavaScript笔记之JS 和 HTML5 的关系
  • 第4篇 conda install pytorch==2.0.0报错
  • 基于Echarts+HTML5可视化数据大屏展示-学生综合成绩评价系统大屏
  • 探索OpenResty:高性能Web开发利器
  • Lua 核心知识点详解
  • 26考研——内存管理_内存管理策略(3)
  • MySQL索引和B+Tree的关系
  • 《云原生配置危机:从服务瘫痪到韧性重建的实战全解》
  • 论文阅读-SelectiveStereo
  • 架构思维:重温限流算法原理与实战
  • 【面试题】关于RAG的五道题
  • redis的数据类型:List
  • 【mysql】SQL自连接:什么时候需要,什么时候不需要?
  • Android网络之WIFI技术网络模型概述
  • 【Pandas】3.1-数据预处理:列的基本操作
  • 【数据结构】经典 Leetcode 题
  • vector的使用和模拟实现
  • 开发思路篇:转账接口设计
  • 20250907-03:LangChain的六大核心模块概览
  • Python-LLMChat
  • 【C++】C++入门—(下)