完成对SPiflash的读写
功能:1.flash初始化2.flash擦除3.flash写入数据4.flash读数据
1可以选择是ps的mio引脚也可以是emio的PL侧引出来的引脚
1.1mio

1.2emio



2综合完后,分配引脚
还是一样的需要导出然后设置顶层,最后进行综合,分配引脚,生成bit打开SDK

3打开SDK,在进行创建工层


4.打开示例工程发现一些指令

指令
基本读写操作指令
这是最核心、最常用的一组指令。
| 指令宏 | 操作码 | 功能描述 |
|---|---|---|
READ_CMD |
0x03 |
读取数据。主控制器发送此命令后,再发送要读取的内存地址,Flash芯片就会从该地址开始返回数据。 |
FAST_READ_CMD |
0x08 |
快速读取。比普通读取更快,通常在命令和地址后会有一个"哑元"字节,以支持更高的SPI时钟频率。 |
WRITE_CMD |
0x02 |
页编程 。用于向Flash写入数据 。主控制器先发送此命令,再发送目标地址,然后是待写入的数据。写入前必须先擦除。 |
AAI_WRITE_CMD |
0xAD |
自动地址增量写入。某些品牌(如SST)Flash的特色功能,可以连续写入多个字而无需重复发送写命令和地址,提高了写入效率。 |
🗑️ 擦除操作指令
Flash存储器的重要特性是:在写入(编程)之前,必须先进行擦除(将位从"0"变为"1")。擦除操作的单位不同。
| 指令宏 | 操作码 | 功能描述 |
|---|---|---|
CHIP_ERASE_CMD |
0x60或 0xC7 |
整片擦除 。擦除整个Flash芯片的所有数据,操作需极其谨慎,因为会清空所有内容。 |
BULK_ERASE_CMD |
0xC7 |
批量擦除 ,通常是CHIP_ERASE_CMD的另一个别名。 |
BLOCK_ERASE_64K_CMD |
0xD8 |
块擦除。擦除一个指定地址开始的、大小为64KB的存储块。这是更精细的擦除操作。 |
⚙️ 状态与控制指令
用于管理写操作、查询芯片状态和配置。
| 指令宏 | 操作码 | 功能描述 |
|---|---|---|
WRITE_ENABLE_CMD |
0x06 |
写使能 。在执行任何写或擦除操作前,必须首先发送此命令,将芯片置为可写状态。 |
WRITE_DISABLE_CMD |
0x04 |
写禁止。用于取消写使能状态,保护数据。 |
READ_STATUS_CMD |
0x05 |
读状态寄存器 。Flash芯片内部有一个状态寄存器,通过此命令可以读取其值,关键目的是检查bit0的"忙"位,以判断写/擦除操作是否完成。 |
WRITE_STATUS_CMD |
0x01 |
写状态寄存器。用于配置写保护区域等参数。 |
🔍 识别与保护指令
用于识别芯片型号和管理硬件保护功能。
| 指令宏 | 操作码 | 功能描述 |
|---|---|---|
READ_ID |
0x90 |
读ID。读取制造商标识和设备标识。 |
SST_READ_ID |
0x9F |
SST读ID。另一种标准的读ID命令,JEDEC标准命令,绝大多数SPI Flash都支持。 |
SST_FLUSH_ID |
0xBF |
注释说明是SST闪存的ID值,可能用于识别。 |
GLOBAL_BLK_PROT_UNLK |
0x98 |
全局块保护解锁。用于解除SST Flash的硬件写保护功能。 |
💎 总结与工作流程
这些指令共同构成了操作SPI Flash的完整流程。一个典型的写入流程如下:
-
发送
WRITE_ENABLE_CMD。 -
发送擦除指令(如
BLOCK_ERASE_64K_CMD)擦除目标区域。 -
轮询
READ_STATUS_CMD直到芯片就绪。 -
再次发送
WRITE_ENABLE_CMD。 -
发送
WRITE_CMD或AAI_WRITE_CMD进行数据写入。 -
轮询
READ_STATUS_CMD直到写入完成。
4找到spi.h库文件看看函数有哪些



4.2模式函数作用
根据是哪个spi进行操作
XSpiPs Setoptlons(SpiInstancePtr, XSPIPS MANUAL START OPTION
XSPIPS MASTER OPTIONXSPIPS FORCE SSELECT OPTION);
XSpiPs_SetOptions(SpiInstancePtr, XSPIPS_MANUAL_START_OPTION | XSPIPS_MASTER_OPTION | XSPIPS_FORCE_SSELECT_OPTION);
这行代码的意思是:对 SpiInstancePtr所指向的SPI控制器实例,同时启用"手动启动"、"主模式"和"强制片选"这三个选项。
🔧 三种模式详解
下面的表格清晰地展示了代码中设置的三种模式及其作用:
| 模式选项(宏定义) | 含义 | 作用与解释 |
|---|---|---|
XSPIPS_MASTER_OPTION |
主模式 | 将当前SPI设备设置为主设备 。主设备控制通信,提供时钟信号(SCLK),并决定何时开始和结束传输。这是最常见的设置,因为CPU/FPGA通常作为主设备去控制Flash、传感器等从设备。 |
XSPIPS_FORCE_SSELECT_OPTION |
强制片选模式 | 强制SPI控制器的片选信号(SS或CS)在整个数据传输期间始终保持有效(低电平)。这确保了从设备在接收一整个完整的数据帧期间不会被意外中断,是通信稳定性的关键。 |
XSPIPS_MANUAL_START_OPTION |
手动启动模式 | 将传输的控制权交给程序员。启用后,调用XSpiPs_Transfer函数不会立即开始传输 ,而是需要再调用XSpiPs_Start函数来真正启动SPI传输。这提供了对传输时序的精确控制。 |
4.3时钟分频函数作用

spi控制器是连接总线的,axi总线的时钟比较快,所以必须进行分频一般在25mhz以下
分频的参数

4.4FLASH读ID 指令

在手册中找到读ID指令

需要给FLASH写入一个字节的90h,然后在发送三个字节,
第五个字节返回厂商的字节以及他的设备id

根据自己的设备ID 读出来了进行核对
根据自己的器件手册把指令定义
根据是对读id进行6个字节的操作

设置6个字节的写操作

4.5传输指令
spi控制器以及发送的地址和接收的地址,没有接收就不接收等等spi是包括读和写的

byteconunt是写和读的总共字节数

在传输的时候,如果对设备进行读ID,需要先发送4个字节,然后接收的是2个字节,就是得从第5个字节和第6个字节进行接收

4.6片擦除指令
spi控制器最少发送两个字节

查看状态寄存器的busy最低位是不是0 0 表示擦除结束


int FlashChipErase(XSpiPs *SpiPs)
{
u8 WriteBuf[2];
u8 ReadBuf[2];
WriteBuf[0] = ChipEraseCmd;
WriteBuf[1] = 0;
XSpiPs_PolledTransfer(SpiPs,WriteBuf,NULL,2); 不关心接收数据可以填写null空指针
while(1) //判断擦除有没有结束查看状态寄存器的busy最低位是不是0 0 表示擦除结束
{
WriteBuf[0] = ReadStatus1Cmd;
WriteBuf[1] = 0;
XSpiPs_PolledTransfer(SpiPs,WriteBuf,ReadBuf,2);//
if(ReadBuf[1]&0x01 == 0)break; //如果最低位是1与0x01就是看最低位 是否为10
}
return XST_SUCCESS;
}
4.7FLASH写操作


因为是32位的地址只用了24为 在给地址的时候需要把第三位与ff相与 在左移16位放在最低位。以此类推
4.8FLASH读操作
int FlashRead(XSpiPs *SpiPs,u32 Address,u8 *ReadBuf,u32 Readlen)
{
u8 *WriteBuf = (u8 *)malloc(Readlen * sizeof(u8));
WriteBuf[0] = ReadDataCmd;
WriteBuf[1] = (Address & 0xFF0000) >> 16;
WriteBuf[2] = (Address & 0xFF00) >> 8;
WriteBuf[3] = (Address & 0xFF);
XSpiPs_PolledTransfer(SpiPs,WriteBuf,ReadBuf,Readlen);
return XST_SUCCESS;
5,完整代码
5.1 flash.driver.c
/*
* flash_drive.c
* 功能:1.flash初始化 2.flash擦除 3.flash写入数据 4.flash读数据
*/
#include "flash_drive.h"
int FlashInit(XSpiPs *SpiPs,u16 DeviceId)
{
int Status;
XSpiPs_Config *SpiConfig;
u8 ManID;
u8 DeviceID;
SpiConfig = XSpiPs_LookupConfig(DeviceId);
if (NULL == SpiConfig) {
return XST_FAILURE;
}
Status = XSpiPs_CfgInitialize(SpiPs, SpiConfig,
SpiConfig->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XSpiPs_SetOptions(SpiPs, XSPIPS_MASTER_OPTION | XSPIPS_FORCE_SSELECT_OPTION);
XSpiPs_SetClkPrescaler(SpiPs,XSPIPS_CLK_PRESCALE_32);
if(FlashID(SpiPs,&ManID,&DeviceID) == XST_FAILURE)
return XST_FAILURE;
return XST_SUCCESS;
}
int FlashID(XSpiPs *SpiPs,u8 *ManID,u8 *DeviceID)
{
u8 WriteBuf[6];
u8 ReadBuf[6];
WriteBuf[0] = ReadIDCmd;
WriteBuf[1] = 0;
WriteBuf[2] = 0;
WriteBuf[3] = 0;
if(XSpiPs_PolledTransfer(SpiPs,WriteBuf,ReadBuf,6) == XST_FAILURE)
return XST_FAILURE;
//ReadBuf[5] Man ID
//ReadBuf[6] Device ID
*ManID = ReadBuf[5];
*DeviceID = ReadBuf[6];
return XST_SUCCESS;
}
int FlashChipErase(XSpiPs *SpiPs)
{
u8 WriteBuf[2];
u8 ReadBuf[2];
WriteBuf[0] = ChipEraseCmd;
WriteBuf[1] = 0;
XSpiPs_PolledTransfer(SpiPs,WriteBuf,NULL,2);
while(1)
{
WriteBuf[0] = ReadStatus1Cmd;
WriteBuf[1] = 0;
XSpiPs_PolledTransfer(SpiPs,WriteBuf,ReadBuf,2);
if(ReadBuf[1]&0x01 == 0)break;
}
return XST_SUCCESS;
}
int FlashWrite(XSpiPs *SpiPs,u32 Address,u8 *Num,u32 Numlen)
{
u8 WriteDataBuf[5];
u32 i;
for(i = 0 ;i < Numlen ;i++)
{
WriteDataBuf[0] = WriteDataCmd;
WriteDataBuf[1] = (Address & 0xFF0000) >> 16;
WriteDataBuf[2] = (Address & 0xFF00) >> 8;
WriteDataBuf[3] = (Address & 0xFF);
WriteDataBuf[4] = *(Num + i);
XSpiPs_PolledTransfer(SpiPs,WriteDataBuf,NULL,5);
}
return XST_SUCCESS;
}
int FlashRead(XSpiPs *SpiPs,u32 Address,u8 *ReadBuf,u32 Readlen)
{
u8 *WriteBuf = (u8 *)malloc(Readlen * sizeof(u8));
WriteBuf[0] = ReadDataCmd;
WriteBuf[1] = (Address & 0xFF0000) >> 16;
WriteBuf[2] = (Address & 0xFF00) >> 8;
WriteBuf[3] = (Address & 0xFF);
XSpiPs_PolledTransfer(SpiPs,WriteBuf,ReadBuf,Readlen);
return XST_SUCCESS;
}
5.2 .h
/*
*/
#ifndef SRC_FLASH_DRIVE_H_
#define SRC_FLASH_DRIVE_H_
#include "xparameters.h" /* SDK generated parameters */
#include "xplatform_info.h"
#include "xspips.h" /* SPI device driver */
#include "xil_printf.h"
#include "xstatus.h"
#include "stdlib.h"
#define WriteEnableCmd 0x16
#define WriteDisEnableCmd 0x04
#define ReadStatus1Cmd 0x05
#define ReadStatus2Cmd 0x35
#define WriteStatusCmd 0x01
#define WriteDataCmd 0x02
#define BlockErase64Cmd 0xd8
#define BlockErase32Cmd 0x52
#define SectorEraseCmd 0x20
#define ChipEraseCmd 0xc7
#define ReadIDCmd 0x90
#define ReadDataCmd 0x03
#endif /* SRC_FLASH_DRIVE_H_ */
遗漏问题:
1.对spi的写id时候为什么写3个字节,读的字节是要跟写字节后面的
2.对于写操作怎么计算和读操作 u8 *WriteBuf = (u8 *)malloc(Readlen * sizeof(u8));
WriteDataBuf[1] = (Address & 0xFF0000) >> 16;
WriteDataBuf[2] = (Address & 0xFF00) >> 8;
WriteDataBuf[3] = (Address & 0xFF);
WriteDataBuf[4] = *(Num + i);
XSpiPs_PolledTransfer(SpiPs,WriteDataBuf,NULL,5);
这两个为什么放到的是最低位发送流程是什么




