一、功能描述:
通过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 | 根据指令将并行数据转化成串行数据 |
Local2DMA | FIFO接口转化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*