FPGA—ZYNQ学习spi(六)

完成对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 0x600xC7 ​整片擦除​ ​。擦除整个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的完整流程。一个典型的​​写入流程​​如下:

  1. 发送 WRITE_ENABLE_CMD

  2. 发送擦除指令(如 BLOCK_ERASE_64K_CMD)擦除目标区域。

  3. 轮询 READ_STATUS_CMD直到芯片就绪。

  4. 再次发送 WRITE_ENABLE_CMD

  5. 发送 WRITE_CMDAAI_WRITE_CMD进行数据写入。

  6. 轮询 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);

这两个为什么放到的是最低位发送流程是什么

相关推荐
我命由我123452 小时前
Photoshop - Photoshop 工具栏(24)磁性套索工具
学习·ui·职场和发展·求职招聘·职场发展·课程设计·美工
清风6666663 小时前
基于单片机的智能高温消毒与烘干系统设计
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
自由日记3 小时前
css学习9
前端·css·学习
Jerry丶Li3 小时前
二十四、STM32的DMA
stm32·单片机·嵌入式硬件
Larry_Yanan4 小时前
QML学习笔记(五十二)QML与C++交互:数据转换——时间和日期
开发语言·c++·笔记·qt·学习·ui·交互
TL滕4 小时前
从0开始学算法——第二天(时间、空间复杂度)
数据结构·笔记·学习·算法
kblj55554 小时前
学习Linux——网络基础管理
linux·网络·学习
天狗下凡4 小时前
【OpenGL学习】第1课: 概念与流程理解
学习
黑岚樱梦4 小时前
计算机网络第六章学习
学习·计算机网络