基于ZYNQ FPGA 读写NOR FLASH W25Q256

6 篇文章 1 订阅
2 篇文章 0 订阅

一、功能描述:

通过SPI接口与FLASH芯片,实现对flash任意地址读写、32KB块擦除、64KB块擦除、片擦除功能,SPI通信速率20MHz。

二、NOR FLASH说明

` 由手册可知,FLASH每次最多可编程页256字节,每次最小可擦除4KB扇区。而且FLASH支持标准SPI接口和D/QSPI接口。
作为标准SPI操作时,数据在时钟的下降沿发送,上升沿采样。

工程使用标准SPI通信模式
SPI总线支持	:	[0,0]模式 ,[1,1] 模式;
SCLK		: 	20Mhz;
WP	  		:	1'b1,不保护;

在这里插入图片描述

在这里插入图片描述

2.1 FLASH 部分寄存器操作时序

2.1.1 设备ID 读命令 9Fh
通过SPI接口向FLASH发送8'h9F,返回24‘hef4019,表示读取设备ID正常,也可以作为开机检查芯片的功能

在这里插入图片描述

工程所有操作都是基于三字节地址

读取ID的时序如下:
在这里插入图片描述

2.1.2 读状态寄存器命令 05h 35h 15h

操作时序如下:
在这里插入图片描述

2.1.3 写状态寄存器命令 01h 31h 11h

在这里插入图片描述

2.1.4读数据命令 03h

在这里插入图片描述

2.1.5 写数据命令 02h

在这里插入图片描述

2.1.6 4KB扇区擦除 20h

在这里插入图片描述

2.1.7 32KB扇区擦除 52h

在这里插入图片描述

2.1.8 64KB扇区擦除 D8h

在这里插入图片描述

2.1.9 片擦除 C7h

在这里插入图片描述

2.1.10 写使能 06h

在这里插入图片描述

2.1.11 禁止写使能 04h

在这里插入图片描述

2.2 状态寄存器描述

读写状态寄存器可以改变FLASH
① 状态寄存器1

   bit0		:  FLASH写操作,擦除操作忙信号,busy=0的时候不能进行写操作,可以读状态
   bit1		:   写使能标志
   bit2-bit5:   写保护,默认为0
   bit6		:   顶部/底部块保护
   bit7		:   保留

在这里插入图片描述
② 状态寄存器2

   bit0		:  状态寄存器保护
   bit1		:   四字节操作使能,默认位0即三字节地址
   bit2     :   保留
   bit3-bit5:   安全寄存器锁定位
   bit6		:   
   bit7		:   擦除/程序挂起状态

在这里插入图片描述
③ 状态寄存器3

   bit0		:  当前地址模式
   bit1		:   通电地址模式
   bit2     :   写入保护选择;WPS位用于选择应该使用的写保护方案。当WPS=0时,
   				该设备将使用CMP、TB、BP[3:0]位的组合来保护内存阵列的特定区域。
   				当WPS=1时,设备将使用单个块锁来保护任何单个扇区或块。
   				在设备通电或复位后,所有单独的块锁定位的默认值为1。  
   bit3-bit4:   保留
   bit5-bit6:   输出驱动器强度:
   				0, 0 100%
   				0, 1 75%
   				1, 0 50%
   				1, 1 25% (default setting)
   bit7		:   保留

在这里插入图片描述

三、PL模块描述:

工程包含四个文件:flash_top.v;flash_spi.v;spi_eng.v;Local2DMA.v

模块功能描述
flash_top对外的fifio接口和AXI_Stream接口
flash_spi根据工作模式将FIFO数据写道SPI_ENG模块或者将FLASH数据写道FIFO上
spi_eng根据指令将并行数据转化成串行数据
Local2DMAFIFO接口转化AXI_Stream接口模块

3.1 flash_top :

PS传输给PL的寄存器数据:

flash_en			:FLASH操作使能
flash_cmd			:FLASH操作命令 01:写数据 02:读数据  03:4K擦除 04:32K擦除 05:64K擦除 06:片擦除
flash_wr_addr		:FLASH操作地址
W_R_CNT				:读写字节数

操作流程:
1、上电后首先读取设备ID(读取五次后ID不对直接报错)
2、等待操作使能到来后读取FLASH状态,等待busy=0
3、根据flash_cmd选择操作状态。注:所有写操作之前需要启动写使能
4、操作完成后读取FLASH状态,等待busy=0
操作流程图如下:
在这里插入图片描述

3.2 flash_spi:

module flash_spi(
	input						clk				,
	input						rst_n			,
	input						sys_clk			,
	
	input						flash_operate	,//FLASH操作使能
	input			[23:0]		w_r_addr		,//FLASH操作地址
	input	wire	[31:0]		W_R_CNT			,//读写字节数
	input	wire	[7:0]		state			,//操作状态
	//读取状态************************************
	output 	reg		[7:0]		rd_state1		,//读状态1
	output 	reg		[7:0]		rd_state2		,//读状态2
	output 	reg		[7:0]		rd_state3		,//读状态3
	//写状态**************************************
	input			[7:0]		wr_state1		,//写状态1
	input			[7:0]		wr_state2		,//写状态2
	input			[7:0]		wr_state3		,//写状态3
	
	output	wire  	[23:0] 		reciv_ID      	,//接收FLASH 设备ID号
	output	reg					operate_done  	,//FLASH操作完成信号
	//FIFO写数据接口
	input						fifo_wr0	    ,
	input	[31:0]				fifo_i0		    ,
	output						prog_full0	    ,
	//FIFO读数据接口
	input 						rd_en1			,
	output 	[7:0]				dout1			,
	output						fifo_empty1	    ,
	
	output						WP				,
	output						reset			,
	
	input   wire          		SI_R        	, 
    output  wire	      		sck_T       	,
    output  wire         		CSN	       		,
    output  wire         		SO_T        	    //对于FPGA 是输出控制
	
    );

FLASH操作state:

		case(state)
					0:begin
						Tx_en			<=	'b1				;
						rd_ID			<=	'b1				;
						Cmd_data		<=	RDeviceIdCmd	;
						cur_st			<=	st_id			;
					end
					1:begin									//读取状态1
						Tx_en			<=	'b1				;
						rd_state		<=	'b1				;
						Cmd_data		<=	Rd_St_reg1		;//'h05
						cur_st			<=	st_cfg_rd1		;
					end
					2:begin				   					//读取状态2
						Tx_en			<=	'b1				;
						rd_state		<=	'b1				;
						Cmd_data		<=	Rd_St_reg2		;
						cur_st			<=	st_cfg_rd2		;
					end
					3:begin         						//读取状态3
						Tx_en			<=	'b1				;
						rd_state		<=	'b1				;
						Cmd_data		<=	Rd_St_reg3		;
						cur_st			<=	st_cfg_rd3		;
					end
					4:begin         						//写状态1
						Tx_en			<=	'b1				;
						wr_state		<=	'b1				;
						Status_Reg		<=	wr_state1		;
						Cmd_data		<=	Wr_St_reg1		;
						cur_st			<=	st_cfg_wr1		;
					end
					5:begin         						//写状态2	
						Tx_en			<=	'b1				;
						wr_state		<=	'b1				;
						Status_Reg		<=	wr_state2		;
						Cmd_data		<=	Wr_St_reg2		;
						cur_st			<=	st_cfg_wr2		;
					end
					6:begin         						//写状态3
						Tx_en			<=	'b1				;
						wr_state		<=	'b1				;
						Status_Reg		<=	wr_state3		;
						Cmd_data		<=	Wr_St_reg3		;
						cur_st			<=	st_cfg_wr3		;
					end
					7:begin									//写命令使能
						Tx_en			<=	'b1				;
						config_wr		<=	'b1				;
						Cmd_data		<=	Wr_enableCmd	;
						cur_st			<=	st_WR_En_Cmd	;
					end
					8:begin									//4KB扇区擦除
						Tx_en			<=	'b1				;
						Block_Erase_en	<=	'b1				;
						Cmd_data		<=	Sector_Erase_4KB;
						cur_st			<=	st_chip_erase	;
					end
					9:begin									//32KB扇区擦除
						Tx_en			<=	'b1				;
						Block_Erase_en	<=	'b1				;
						Cmd_data		<=	Block_Erase_32KB;
						cur_st			<=	st_chip_erase	;
					end
					10:begin								//64KB扇区擦除
						Tx_en			<=	'b1				;
						Block_Erase_en	<=	'b1				;
						Cmd_data		<=	Block_Erase_64KB;
						cur_st			<=	st_chip_erase	;
					end
					11:begin								//片擦除
						Tx_en			<=	'b1				;
						Erase_en		<=	'b1				;
						Cmd_data		<=	Chip_Erase		;
						cur_st			<=	st_chip_erase	;
					end
					12:begin         						//读数据
						Tx_en			<=	'b1				;
						flash_read		<=	'b1				;
						Cmd_data		<=	Read_Data		;
						cur_st			<=	st_rd			;
					end
					13:begin         						//读数据_4字节地址
						Tx_en			<=	'b1				;
						flash_read		<=	'b1				;
						Cmd_data		<=	Read_Data_4B	;
						cur_st			<=	st_rd_4B		;
					end
					14:begin         						//写数据
						Tx_en			<=	'b1				;
						flash_write		<=	'b1				;
						Cmd_data		<=	Page_Program	;
						cur_st			<=	st_wr			;
					end
					15:begin         						//写数据_4字节地址
						Tx_en			<=	'b1				;
						flash_write		<=	'b1				;
						Cmd_data		<=	Page_Program_4B	;
						cur_st			<=	st_wr_4B		;
					end

FLASH操作地址:

	parameter		RDeviceIdCmd		=	8'h9f	;//读设备ID
	
	parameter		Wr_enableCmd		=	8'h06	;
	parameter		Wr_en_VSR			=	8'h50	;
	parameter		Wr_Disab			=	8'h04	;
	
	parameter		Rd_St_reg1			=	8'h05	;//读状态寄存器
	parameter		Rd_St_reg2			=	8'h35	;//读状态寄存器
	parameter		Rd_St_reg3			=	8'h15	;//读状态寄存器
	
	parameter		Wr_St_reg1			=	8'h01	;//写状态寄存器
	parameter		Wr_St_reg2			=	8'h31	;//写状态寄存器
	parameter		Wr_St_reg3			=	8'h11	;//写状态寄存器
	
	parameter		En_4B_addr_mode		=	8'hB7	;//使能四字节模式
	parameter		Ex_4B_addr_mode		=	8'hB9	;//退出四字节模式
	
	parameter		Read_Data			=	8'h03	;//读数据
	parameter		Read_Data_4B		=	8'h13	;//读四字节地址数据
	
	parameter		Page_Program		=	8'h02	;//写数据
	parameter		Page_Program_4B		=	8'h12	;//写四字节地址数据
	
	parameter		Sector_Erase_4KB	=	8'h20	;//扇区擦除	4KB
	
	parameter		Sector_Erase_4KB_4B	=	8'h21	;//扇区擦除4字节地址
	
	parameter		Block_Erase_32KB	=	8'h52	;//32KB块擦除
	parameter		Block_Erase_64KB	=	8'hD8	;//64KB块擦除
	
	parameter		Block_Erase_64KB_4B	=	8'hDC	;//64KB块擦除4字节地址
	
	parameter		Chip_Erase			=	8'hC7	;//片擦除 60

3.3 spi_eng:

接口:

 //发送指令********************************************
    input                               Tx_en       	,
    input  	wire	[SEND_cnt-1:0]      Cmd_data  		,   //要发送的指令
	input	wire	[ADDR_cnt-1:0]		w_r_addr		,	//读写操作地址
	input	wire	[31:0]				W_R_CNT			,	//读写字节数
	//指令类别********************************************
	input								config_wr		,	//写配置
	input								Block_Erase_en	,	//块擦除En
	input								Erase_en		,	//片擦除En
	input								rd_state		,	//读状态寄存器
	input								rd_ID			,	//读ID
	input								wr_state		,	//写状态寄存器
	input								flash_read  	,	//读数据
	input								flash_write 	,	//写数据
	//写指令数据、存储数据********************************
	input			[7:0]				Status_Reg		,	//状态寄存器1,2,3
	input			[7:0]				wr_data			,	//状态寄存器1,2,3
	//读数据**********************************************
    output 	reg   	[7:0]     			reciv_data  	,
    output 	reg   	[23:0]     			reciv_ID	  	,
	//读写地址********************************************
	input			[23:0]				B3_addr			,
	input			[31:0]				B4_addr			,
	//fifo接收发送数据************************************
	input			[7:0]				fifo_o0			,
	input								fifo_empty0		,
	output	reg							fifo_rd0		,
	
	output	reg		[7:0]				fifo_i1			,
	input								fifo_full1		,
	output	reg							fifo_wr1		

3.4 Local2DMA:

① DMA2FIFO

//DMA--2--local
	wire							fifo_wr0	    			;
	reg			[31:0]				fifo_i0		    			;
	wire							prog_full0	    			;
	
	assign		DMA_Wr_tready	=	~prog_full0					;
	assign		fifo_wr0		=	DMA_Wr_tvalid				;
	always@(*)
	begin
		case(DMA_Wr_tkeep)
		0001:	fifo_i0			=	{24'b0,DMA_Wr_tdata[7:0]}	;
		0011:	fifo_i0			=	{16'b0,DMA_Wr_tdata[15:0]}	;
		0111:	fifo_i0			=	{8'b0,DMA_Wr_tdata[23:0]}	;
		1111:	fifo_i0			=	DMA_Wr_tdata				;
		default:fifo_i0			=	DMA_Wr_tdata				;
		endcase
	end

② FIFO2DMA

module Local2DMA(
	input							sys_rst_n					,
	input							sys_clk						,//100mhz
	input							flash_en					,
	input		[31:0]				W_R_CNT						,
	
	output 	reg	[31:0]				rd_num						,	
	
	output 	reg	[31:0]				PL_Wr_tdata					,	
	output 	reg	[3:0]				PL_Wr_tkeep					,	
	output 	reg						PL_Wr_tlast					,	
	input 							PL_Wr_tready				,	
	output 	reg						PL_Wr_tvalid				,	
	output 							rd_en						,
	input 	[7:0]					dout						,
	input							fifo_empty	    			
    );
	
	// reg		[31:0]					rd_num						;
	assign	rd_en	=	~fifo_empty & PL_Wr_tready	& (rd_num < W_R_CNT) & ((wr_st == init) || (wr_st == start))	;
	
	reg								flash_en1					;
	reg								flash_en2					;
	always@(posedge sys_clk or negedge sys_rst_n)
	begin
		if(sys_rst_n==0)
		begin
			flash_en1			<=	'b0				;
		    flash_en2			<=	'b0				;
		end
		else
		begin
			flash_en1			<=	flash_en		;
		    flash_en2			<=	flash_en1		;
		end
	end
	assign	flash_en_raising	= ~flash_en2 & flash_en1;
	localparam		idle	=	0						,
					init	=	1						,
					start	=	2						,
					st_over	=	3						;
	reg		[1:0]	wr_st								;
	reg		[31:0]	PL_Wr_tdata_buff					;
	reg		[3:0]	rd_cnt1								;
	always@(posedge sys_clk or negedge sys_rst_n)
	begin
		if(sys_rst_n==0)
		begin
			rd_cnt1						<=	'b0			;
			rd_num						<=	'b0			;
			PL_Wr_tkeep					<=	'b0			;
			PL_Wr_tlast					<=	'b0			;
			PL_Wr_tvalid				<=	'b0			;
			PL_Wr_tdata					<=	'b0			;
			PL_Wr_tdata_buff			<=	'b0			;
			wr_st						<=	idle		;
		end
		else
		begin
			case(wr_st)
			idle	:begin
				if(flash_en_raising)
					wr_st				<=	init		;
				else
					wr_st				<=	idle		;
			end
		    init	:begin
				if(rd_en)
				begin
					rd_cnt1				<=	rd_cnt1 + 1	;
					rd_num				<=	rd_num + 1	;
					PL_Wr_tdata_buff	<=	{PL_Wr_tdata_buff[23:0],dout};
					wr_st				<=	start		;
				end
				else
				begin
					rd_cnt1				<=	'b0			;
					wr_st				<=	init		;
				end
			end
		    start	:begin
				if(rd_en)
				begin
					if(rd_cnt1 < 3)
						rd_cnt1			<=	rd_cnt1 + 1		;
					else
						rd_cnt1			<=	'b0				;
					PL_Wr_tdata_buff	<=	{PL_Wr_tdata_buff[23:0],dout};
					rd_num				<=	rd_num + 1		;
				end
				
				if(rd_num < W_R_CNT -1)
				begin
					if(rd_en)
					begin
						PL_Wr_tdata_buff	<=	{PL_Wr_tdata_buff[23:0],dout};
						if(rd_cnt1 == 3)
						begin
							PL_Wr_tvalid	<=	'b1				;
							PL_Wr_tdata		<=	{PL_Wr_tdata_buff[23:0],dout};
							PL_Wr_tkeep		<=	4'b1111			;
						end
						else
							PL_Wr_tvalid	<=	'b0				;
					end
					else
					begin
						PL_Wr_tvalid		<=	'b0				;
						PL_Wr_tkeep			<=	'b0				;
						PL_Wr_tdata			<=	PL_Wr_tdata		;
						PL_Wr_tdata_buff	<=	PL_Wr_tdata_buff;
					end
				end
				else
				begin
					if(rd_en)
					begin
						wr_st					<=	st_over			;	
						PL_Wr_tvalid			<=	'b1				;
						case(rd_cnt1)
						1:begin 
							PL_Wr_tkeep			<=	4'b0011			;
							PL_Wr_tdata			<=	{16'h0,PL_Wr_tdata_buff[7:0],dout};
						end
						2:begin 
							PL_Wr_tkeep			<=	4'b0011			;
							PL_Wr_tdata			<=	{8'h0,PL_Wr_tdata_buff[15:0],dout};
						end
						3:begin
							PL_Wr_tkeep			<=	4'b1111			;
							PL_Wr_tdata			<=	{PL_Wr_tdata_buff[23:0],dout};
						end
						0:begin
							PL_Wr_tkeep			<=	4'b0001					;
							PL_Wr_tdata			<=	{24'd0,dout}			;
						end
						endcase
					end
					else
					begin
						PL_Wr_tvalid		<=	'b0				;
						PL_Wr_tkeep			<=	'b0				;
						PL_Wr_tdata			<=	PL_Wr_tdata		;
						PL_Wr_tdata_buff	<=	PL_Wr_tdata_buff;
					end
					
				end
				if(rd_num == W_R_CNT -1 && rd_en)
					PL_Wr_tlast				<=	'b1				;
				else
					PL_Wr_tlast				<=	'b0				;
			end
			st_over:begin
				rd_cnt1						<=	'b0			;
				rd_num						<=	'b0			;
				PL_Wr_tkeep					<=	'b0			;
				PL_Wr_tlast					<=	'b0			;
				PL_Wr_tvalid				<=	'b0			;
				PL_Wr_tdata					<=	'b0			;
				PL_Wr_tdata_buff			<=	'b0			;
				wr_st						<=	idle		;
			end
			default:begin
				rd_cnt1						<=	'b0			;
				rd_num						<=	'b0			;
				PL_Wr_tkeep					<=	'b0			;
				PL_Wr_tlast					<=	'b0			;
				PL_Wr_tvalid				<=	'b0			;
				PL_Wr_tdata					<=	'b0			;
				PL_Wr_tdata_buff			<=	'b0			;
				wr_st						<=	idle		;
			end
			endcase
		end
	end
endmodule

四、PS模块说明

在这里插入图片描述
C程序主要是DMA操作,参考正点原子SDK教程—AXI DMA环路测试,PS通知PL更新标志使用EMIO 54

axi_dma.h

/*
 * axi_dma.h
 *
 *  Created on: 2022年10月20日
 *      Author: Kewell-IT
 */

#ifndef SRC_AXI_DMA_H_
#define SRC_AXI_DMA_H_

#include "xil_types.h"
#include "xscugic.h"
#include "xaxidma.h"
#include <stdio.h>
#include "xaxidma.h"
#include "xstatus.h"
#include "xil_printf.h"
#include "xil_io.h"
#include "xparameters.h"	//硬件映射的地址信息,XPAR_MYIP_REGS_0_S00_AXI_BASEADDR 即PL IP核中寄存器的起始偏移地址
#include "xscugic.h"
#include "sleep.h"


#define DMA_DEV_ID          XPAR_AXIDMA_0_DEVICE_ID

#define RX_INTR_ID          XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define TX_INTR_ID          XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
#define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID

#define DDR_BASE_ADDR       XPAR_PS7_DDR_0_S_AXI_BASEADDR   //0x00100000
#define MEM_BASE_ADDR       (DDR_BASE_ADDR + 0x01000000)    //0x01100000
#define TX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00100000)    //0x01200000
#define RX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00300000)    //0x01400000

#define MAX_PKT_LEN    			0x100    //发送包长度
#define RESET_TIMEOUT_COUNTER   10000    //复位时间
#define TEST_START_VALUE 		0x20 	//测试起始值

void tx_intr_handler(void *callback);
void rx_intr_handler(void *callback);
int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
        u16 tx_intr_id, u16 rx_intr_id);
void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
        u16 rx_intr_id);

XScuGic intc;       //中断控制器的实例

#endif /* SRC_AXI_DMA_H_ */

axi_dma.c

/*
 * axi_dma.c
 *
 *  Created on: 2022年10月20日
 *      Author: Kewell-IT
 */

#include "axi_dma.h"
#include "q_gpio.h"

XAxiDma axidma;//XAxiDma实例


volatile int tx_done;      //发送完成标志
volatile int rx_done;      //接收完成标志
volatile int error;        //传输出错标志

#define 	BRAM_PL_2_PS 		0x43C10000
#define 	BRAM_PS_2_PL 		0x43C40000

#define 	FLASH_WRITE 		1
#define 	FLASH_READ	 		2
#define 	FLASH_ERASE_4K 		3
#define 	FLASH_ERASE_32K 	4
#define 	FLASH_ERASE_64K 	5
#define 	FLASH_ERASE_CHIP	6

int dma_init(void)
{
	int i;
	int status;
	int    cnt = 0;
	u8 	value;
	u8 	*tx_buffer_ptr;
	u8 	*rx_buffer_ptr;
	u16 fpga_version;
	u16	flash_state	;

	XAxiDma_Config *config;
	tx_buffer_ptr = (u8 *) TX_BUFFER_BASE;
	rx_buffer_ptr = (u8 *) RX_BUFFER_BASE;

	fpga_version=	*(u16 *)(BRAM_PL_2_PS + 0*2);//版本号
	flash_state	=	*(u16 *)(BRAM_PL_2_PS + 1*2);//flash状态
	config = XAxiDma_LookupConfig(DMA_DEV_ID);
	if (!config) {
	    return XST_FAILURE;
	}

	//初始化DMA引擎
	status = XAxiDma_CfgInitialize(&axidma, config);
	if (status != XST_SUCCESS) {
	    return XST_FAILURE;
	}
	if (XAxiDma_HasSg(&axidma)) {
	    return XST_FAILURE;
	}

	//建立中断系统
	status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
	if (status != XST_SUCCESS) {
	    return XST_FAILURE;
	}

	//初始化标志信号
	tx_done = 0;
	rx_done = 0;
	error   = 0;
	value 	= TEST_START_VALUE;
	for (i = 0; i < MAX_PKT_LEN; i++) {
	    tx_buffer_ptr[i] = value;
	    value = (value + 1) & 0xFF;
	}
	Xil_DCacheFlushRange((UINTPTR) tx_buffer_ptr, MAX_PKT_LEN);   //刷新Data Cache
	status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) tx_buffer_ptr,
	MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
	if (status != XST_SUCCESS) {
	    return XST_FAILURE;
	}


	*(u16 *)(BRAM_PS_2_PL + 58*2)	=	FLASH_ERASE_4K	;//擦除

	*(u16 *)(BRAM_PS_2_PL + 62*2)	=	0	;
	*(u16 *)(BRAM_PS_2_PL + 63*2)	=	0	;

	*(u16 *)(BRAM_PS_2_PL + 64*2)	=	0	;
	*(u16 *)(BRAM_PS_2_PL + 65*2)	=	100	;

	//emio 55
	q_gpio_WriteVal(55, 1);
	q_gpio_WriteVal(55, 0);

	do
	{
		flash_state	=	*(u16 *)(BRAM_PL_2_PS + 1*2);//flash状态
		cnt			=	flash_state & (1<<1) >>1	;
	}
	while(cnt);

	*(u16 *)(BRAM_PS_2_PL + 58*2)	=	FLASH_WRITE	;//写数据
	//emio 55
	q_gpio_WriteVal(55, 1);
	q_gpio_WriteVal(55, 0);

	do
	{
		flash_state	=	*(u16 *)(BRAM_PL_2_PS + 1*2);//flash状态
		cnt			=	flash_state & (1<<1) >>1	;
	}
	while(cnt);
	*(u16 *)(BRAM_PS_2_PL + 58*2)	=	FLASH_READ	;//读数据
	//emio 55
	q_gpio_WriteVal(55, 1);
	q_gpio_WriteVal(55, 0);

	do
	{
		flash_state	=	*(u16 *)(BRAM_PL_2_PS + 1*2);//flash状态
		cnt			=	flash_state & (1<<1) >>1	;
	}
	while(cnt);

	status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) rx_buffer_ptr,
	MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
	if (status != XST_SUCCESS) {
	    return XST_FAILURE;
	}
	Xil_DCacheFlushRange((UINTPTR) rx_buffer_ptr, MAX_PKT_LEN);   //刷新Data Cache
	while (!tx_done && !rx_done && !error);

//	disable_intr_system(&intc, TX_INTR_ID, RX_INTR_ID);
	return XST_SUCCESS;
}


//建立DMA中断系统
//  @param   int_ins_ptr是指向XScuGic实例的指针
//  @param   AxiDmaPtr是指向DMA引擎实例的指针
//  @param   tx_intr_id是TX通道中断ID
//  @param   rx_intr_id是RX通道中断ID
//  @return:成功返回XST_SUCCESS,否则返回XST_FAILURE
int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
        u16 tx_intr_id, u16 rx_intr_id)
{
    int status;
    XScuGic_Config *intc_config;

    //初始化中断控制器驱动
    intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (NULL == intc_config) {
        return XST_FAILURE;
    }
    status = XScuGic_CfgInitialize(int_ins_ptr, intc_config,
            intc_config->CpuBaseAddress);
    if (status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    //设置优先级和触发类型
    XScuGic_SetPriorityTriggerType(int_ins_ptr, tx_intr_id, 0xA0, 0x3);
    XScuGic_SetPriorityTriggerType(int_ins_ptr, rx_intr_id, 0xA0, 0x3);

    //为中断设置中断处理函数
    status = XScuGic_Connect(int_ins_ptr, tx_intr_id,
            (Xil_InterruptHandler) tx_intr_handler, axidma_ptr);
    if (status != XST_SUCCESS) {
        return status;
    }

    status = XScuGic_Connect(int_ins_ptr, rx_intr_id,
            (Xil_InterruptHandler) rx_intr_handler, axidma_ptr);
    if (status != XST_SUCCESS) {
        return status;
    }

    XScuGic_Enable(int_ins_ptr, tx_intr_id);
    XScuGic_Enable(int_ins_ptr, rx_intr_id);

    //启用来自硬件的中断
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
            (Xil_ExceptionHandler) XScuGic_InterruptHandler,
            (void *) int_ins_ptr);
    Xil_ExceptionEnable();

    //使能DMA中断
    XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);

    return XST_SUCCESS;
}

//此函数禁用DMA引擎的中断
void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id, u16 rx_intr_id)
{
    XScuGic_Disconnect(int_ins_ptr, tx_intr_id);
    XScuGic_Disconnect(int_ins_ptr, rx_intr_id);
}
//DMA TX中断处理函数
void tx_intr_handler(void *callback)
{
    int timeout;
    u32 irq_status;
    XAxiDma *axidma_inst = (XAxiDma *) callback;

    //读取待处理的中断
    irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
    //确认待处理的中断
    XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);

    //Tx出错
    if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
        error = 1;
        XAxiDma_Reset(axidma_inst);
        timeout = RESET_TIMEOUT_COUNTER;
        while (timeout) {
            if (XAxiDma_ResetIsDone(axidma_inst))
                break;
            timeout -= 1;
        }
        return;
    }

    //Tx完成
    if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
        tx_done = 1;
}
//DMA RX中断处理函数
void rx_intr_handler(void *callback)
{
    u32 irq_status;
    int timeout;
    XAxiDma *axidma_inst = (XAxiDma *) callback;

    irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);

    //Rx出错
    if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
        error = 1;
        XAxiDma_Reset(axidma_inst);
        timeout = RESET_TIMEOUT_COUNTER;
        while (timeout) {
            if (XAxiDma_ResetIsDone(axidma_inst))
                break;
            timeout -= 1;
        }
        return;
    }

    //Rx完成
    if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
        rx_done = 1;
}

验证结果:
在这里插入图片描述

写在最后

本次只使用FLASH默认配置,即三字节操作地址,低128Mb。标准SPI,如需要四字节操作地址或者D/QSPI模式,可以修改上述三个状态寄存器,以及扩展地址寄存器。

扩展地址寄存器:
在这里插入图片描述

芯片手册地址 : *W25Q256JV*

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
U-Boot(Universal Bootloader)是一款开源的、通用的、模块化的嵌入式系统引导加载程序。它可以被用于各种嵌入式系统中,如计算机主板、网络设备、无线设备等。 在U-Boot中,对于Nor Flash读写,主要是通过SPI接口进行的。下面是Nor Flash读写代码的走读: 1. 首先需要定义Nor Flash的设备结构体,其中包括了Nor Flash的基本信息以及对应的读写函数指针。 ``` struct flash_info { char* name; /* 设备名称 */ unsigned long base; /* 设备物理地址 */ unsigned long size; /* 设备大小 */ unsigned long sector_size; /* 扇区大小 */ unsigned long block_size; /* 块大小 */ void (*erase)(unsigned long); /* 擦除函数指针 */ void (*write)(unsigned long, const uchar*, unsigned long); /* 写函数指针 */ uchar (*read)(unsigned long); /* 读函数指针 */ }; ``` 2. 在U-Boot的配置文件中,需要定义Nor Flash的类型以及对应的设备结构体。 ``` #define CONFIG_SPI_FLASH_SST #define CONFIG_SPI_FLASH_WINBOND #define CONFIG_SPI_FLASH_MACRONIX #define CONFIG_SPI_FLASH_SPANSION #define CONFIG_SPI_FLASH_STMICRO #define CONFIG_SPI_FLASH_SST #define CONFIG_SPI_FLASH_ISSI #define CONFIG_SPI_FLASH_EON #define CONFIG_SPI_FLASH_GIGADEVICE #ifdef CONFIG_SPI_FLASH_SST #define CONFIG_FLASH_CFI_DRIVER #define CONFIG_SYS_MAX_FLASH_BANKS_DETECT 1 #define CONFIG_SYS_FLASH_CFI #define CONFIG_SYS_FLASH_PROTECTION #define CONFIG_SYS_FLASH_BASE 0x80000000 #define CONFIG_SYS_FLASH_EMPTY_INFO #define CONFIG_SYS_FLASH_USE_BUFFER_WRITE #endif #ifdef CONFIG_SPI_FLASH_STMICRO #define CONFIG_FLASH_CFI_DRIVER #define CONFIG_SYS_MAX_FLASH_BANKS_DETECT 1 #define CONFIG_SYS_FLASH_CFI #define CONFIG_SYS_FLASH_PROTECTION #define CONFIG_SYS_FLASH_BASE 0x80000000 #define CONFIG_SYS_FLASH_EMPTY_INFO #define CONFIG_SYS_FLASH_USE_BUFFER_WRITE #endif ``` 3. 在Nor Flash的驱动中,需要实现读写、擦除等具体操作函数。 ``` static void spi_flash_cmd_write(struct spi_flash *flash, const void *buf, size_t size, uint32_t addr) { /* 发送写命令 */ spi_flash_cmd_write_enable(flash); spi_flash_cmd_addr(flash, addr, flash->cmd.write); spi_flash_cmd_buf(flash, buf, size); spi_flash_cmd_wait_ready(flash); } static void spi_flash_cmd_read(struct spi_flash *flash, void *buf, size_t size, uint32_t addr) { /* 发送读命令 */ spi_flash_cmd_addr(flash, addr, flash->cmd.read); spi_flash_cmd_buf(flash, NULL, size); spi_flash_cmd_read_data(flash, buf, size); } ``` 4. 在Nor Flash的驱动中,还需要实现设备初始化、扇区擦除等操作。 ``` static int spi_flash_probe(struct spi_flash *flash) { /* 初始化设备 */ spi_flash_cmd_init(flash); /* 读取设备ID */ spi_flash_cmd_read_id(flash); /* 设置读写擦除函数指针 */ flash->write = spi_flash_cmd_write; flash->read = spi_flash_cmd_read; flash->erase = spi_flash_cmd_erase; return 0; } static int spi_flash_erase_sector(struct spi_flash *flash, uint32_t addr) { /* 发送擦除命令 */ spi_flash_cmd_write_enable(flash); spi_flash_cmd_addr(flash, addr, flash->cmd.erase); spi_flash_cmd_wait_ready(flash); return 0; } ``` 5. 在main函数中,需要初始化SPI总线以及Nor Flash设备,并进行读写操作。 ``` int main(void) { /* 初始化SPI总线 */ spi_init(); /* 初始化Nor Flash设备 */ spi_flash_init(); /* 读取数据 */ spi_flash_read_data(FLASH_BASE_ADDR, buf, sizeof(buf)); /* 写入数据 */ spi_flash_write_data(FLASH_BASE_ADDR, buf, sizeof(buf)); /* 擦除扇区 */ spi_flash_erase_sector(FLASH_BASE_ADDR); return 0; } ``` 以上就是Nor Flash读写的主要代码走读。在实际应用中,还需要根据具体的Nor Flash型号以及使用场景进行相应的优化和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值