FPGA-DE2-115-实验四-flash读写操作-S29GL064N

2024-01-08 19:59

本文主要是介绍FPGA-DE2-115-实验四-flash读写操作-S29GL064N,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

flash读写操作-S29GL064N

  • 1. 实验要求
  • 2. 实验原理
    • 2.1 选择地址扇区
    • 2.2 设备总线操作
    • 2.3 查看操作指令
    • 2.4 查看时序图
  • 3. 实验代码
    • 3.1 顶层模块flash
    • 3.2 flash读写操作模块flash_control
    • 3.3 lcd1602显示模块lcd1602
    • 3.4 串口发送模块uart_tx
    • 3.5 按键滤波器模块key_filiter
  • 4. SignalTap仿真图
  • 5. 实物图

前言:
本文主要介绍了集成电路EDA这门课程的相关实验及代码。使用的软件是Quartus Ⅱ,该实验使用fpga芯片为cyclone IV EP4CE115F29C7。

FPGA内板载flash芯片没有使用spi-flash进行操作,而是单纯使用S29GL064N的flash芯片,所有引脚直接连接到fpga板块。

1. 实验要求

利用FPGA板及flash外设,编写代码实现按键按下写入flash,并且断电验证是否写入成功,读出的数据显示在lcd1602显示屏上,并且可以实现擦除数据功能。

2. 实验原理

通过阅读S29GL064N的flash芯片手册,再对比板载芯片名称,我们知道该flash属于S29GL064N (Model 04)。
其中引脚:22位地址线输入8/16位数据线其他一些功能配置位

下图为S29GL064N (Model 04)的引脚图:
在这里插入图片描述

下图为DE2-115中flash的连线图:
我们可以看到:
地址位多了一位A-1,变为23位,这样我们选择地址的时候就需要注意一下,最低位为A-1,自己随意配置即可
数据位使用了8位

在这里插入图片描述

2.1 选择地址扇区

由flash手册p25页可知整个flash分为134个扇区,写入读出数据的话都需要先确定扇区地址,比如在该实验中我们操作读写扇区SA45,得知地址位为: 0100110xxx
选择完扇区后,我们再从中任意选择一个地址使用: 比如23’010 0110 0000 0000 0000 0001 即23’h260001,该实验都在这个地址操作。

在这里插入图片描述

2.2 设备总线操作

选择完地址后,我们就需要查看如何对flash进行读写操作了。
在手册p16页中我们可以找到flash设备总线操作:
在这里插入图片描述

  1. 通过该图我们得知: 读写操作的一些引脚简单配置
  2. 接下来我们简单阅读下表格下面的一些解释: 比如8.3 Writing Commands/Command Sequences:从中我们得知第40页上的Word程序命令序列包含了关于使用标准和解锁旁路命令序列向设备编程数据的详细信息。
  3. 跳转到p40的10.4.1 Word Program Command Sequence:
    由该节内容得知: 编程是一个四总线循环的操作。程序命令序列由写两个解锁写周期启动,然后是程序设置命令。然后写入程序地址和数据,进而启动嵌入式程序算法。
    第47页的表17和第50页的表19分别显示了字程序命令序列的地址和数据要求。
  4. 我们跳转到第47页
    可以看到是一些命令定义,不过我们的数据位为8位,所以找到p50页x8 Mode,这里就是我们需要找到的操作指令表。

2.3 查看操作指令

在上节中,我们找到了flash操作指令表:
在这里插入图片描述
其中划线的4个指令我们都是需要使用到的。
其中RA/PA为目标地址,PD/RD为数据。
很容易看出:
读操作不需要指令,直接输入需要读写的地址,并配置好目录2.2节的总线要求即可。
写操作与擦除指令都需要先往固定的地址写入固定的指令才能启动。

2.4 查看时序图

读时序(p62)如图:
在这里插入图片描述

写时序(p65)如图:
可以看到,在写入操作完成后,RY脚产生了一段时间的低脉冲,我们可以根据这个来判断是否执行完成过该写入操作。
在这里插入图片描述
擦除时序(p66)如图:
在这里插入图片描述

3. 实验代码

3.1 顶层模块flash

调用flash_control模块实现flash读写操作,读出数据显示在lcd1602模块上,并通过uart_tx串口发送模块发送给pc端,key_filiter为按键滤波器。

module flash (input   wire        sys_clk         ,// 时钟信号input   wire        sys_rst_n       ,// 复位信号input   wire        wr              ,//写按键input   wire        rd              ,//读按键input   wire        rset            ,//扇区擦除按键input   wire        fl_ry_by        ,//忙碌位,初始1为高,当写入完成后输出一段时间的低脉冲作为写入成功的判断    output  wire        fl_ce           ,//芯片使能输入,低电平有效output  wire        fl_oe           ,//输出使能,低电平读出有效output  wire        fl_we           ,//输入使能,低电平写入有效output  wire        fl_reset        ,//复位引脚输入,低电平复位        output  wire        fl_wp           ,//硬件写保护输入,默认为1即可  output  wire [22:0] fl_addr         ,//地址链接到flash的23个地址脚output  wire        tx              ,//传输给串口的数据output 	wire [7:0]  lcd_dat		    ,//LCD的8位数据口output  wire		lcd_rs		    ,//数据命令选择信号,高电平表示数据,低电平表示命令output	wire		lcd_rw		    ,//读写标志,高电平表示读,低电平表示写,该程序我们只对液晶屏进行写操作output	wire		lcd_en		    ,//LCD的控制脚inout   wire [7:0]  fl_dq           //数据输入/输出,链接到flash的8个地址脚
);parameter   UART_BPS    =   'd115200,         //串口波特率CLK_FREQ    =   'd50_000_000;    //时钟频率//reg  [2:0]   cmd;
wire [22:0] addr;      //地址
wire [7:0]  data_in;   //数据输入
wire [7:0]  pi_data;   //模块输入的8bit数据
wire        pi_flag;   //并行数据有效标志信号
wire [7:0]  dat_out;
wire [7:0]  lcd_out;   //读取的数据给lcd1602 
wire        wr_flag;
wire        rd_flag;
wire        rset_flag;     assign addr = 23'h260001;   //23'010 0110 0000 0000 0000 0001
assign data_in = 8'h5a;  flash_control flash_control_inst(.sys_clk  (sys_clk  )       ,// 时钟信号.sys_rst_n(sys_rst_n)       ,// 复位信号.wr_flag  (wr_flag  )       ,//按下,写入数据一次.rd_flag  (rd_flag  )       ,//按下,读出数据一次.rset_flag(rset_flag )      ,//.addr     (addr     )       ,//写入/读出数据的地址.data_in  (data_in  )       ,//需要写入的数据.fl_ry_by (fl_ry_by )       ,//忙碌位,初始1为高,当写入完成后输出一段时间的低脉冲作为写入成功的判断.fl_ce    (fl_ce    )       ,//芯片使能输入,低电平有效.fl_oe    (fl_oe    )       ,//输出使能,低电平读出有效.fl_we    (fl_we    )       ,//输入使能,低电平写入有效.fl_reset (fl_reset )       ,//复位引脚输入,低电平复位    .fl_wp    (fl_wp    )       ,//硬件写保护输入,默认为1即可.fl_addr  (fl_addr  )       ,//地址链接到flash的23个地址脚.dat_out  (dat_out  )       ,//读取数据输出给串口.pi_flag  (pi_flag  )       ,//打印数据到串口标志位.lcd_out  (lcd_out  )       ,//读取的数据给lcd1602                                 .fl_dq    (fl_dq    )        //数据输入/输出
);lcd1602 lcd1602_inst( .sys_clk	(sys_clk	),//系统时钟输入50M.sys_rst_n	(sys_rst_n	),//复位,低电平有效.data       (lcd_out    ),.lcd_dat	(lcd_dat	),//LCD的8位数据口.lcd_rs		(lcd_rs		),//数据命令选择信号,高电平表示数据,低电平表示命令.lcd_rw		(lcd_rw		),//读写标志,高电平表示读,低电平表示写,该程序我们只对液晶屏进行写操作.lcd_en		(lcd_en		) //LCD的控制脚);uart_tx
#(.UART_BPS(UART_BPS), //串口波特率.CLK_FREQ(CLK_FREQ) //时钟频率
)
uart_tx_inst
(.sys_clk  (sys_clk  )   ,   //系统时钟50MHz.sys_rst_n(sys_rst_n)   ,   //全局复位.pi_data  (dat_out  )   ,   //模块输入的8bit数据.pi_flag  (pi_flag  )   ,   //并行数据有效标志信号.tx       (tx       )       //串转并后的1bit数据
);key_filiter key_filiter_inst0
(.sys_clk   (sys_clk  )  ,.sys_rst_n (sys_rst_n)  ,.key_in    (wr      )  ,.key_flag  (wr_flag)
);
key_filiter key_filiter_inst1
(.sys_clk   (sys_clk  )  ,.sys_rst_n (sys_rst_n)  ,.key_in    (rd      )  ,.key_flag  (rd_flag)
);
key_filiter key_filiter_inst2
(.sys_clk   (sys_clk  )  ,.sys_rst_n (sys_rst_n)  ,.key_in    (rset      )  ,.key_flag  (rset_flag)
);endmodule

3.2 flash读写操作模块flash_control

相关时序图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

module flash_control (input   wire        sys_clk         ,// 时钟信号input   wire        sys_rst_n       ,// 复位信号input   wire        wr_flag         ,//按下,写入数据一次input   wire        rd_flag         ,//按下,读出数据一次input   wire        rset_flag       ,//按下,擦除input   wire [22:0] addr            ,//写入/读出数据的地址input   wire [7:0]  data_in         ,//需要写入的数据input   wire        fl_ry_by        ,//忙碌位,初始1为高,当写入完成后输出一段时间的低脉冲作为写入成功的判断output  reg         fl_ce           ,//芯片使能输入,低电平有效output  reg         fl_oe           ,//输出使能,低电平读出有效output  reg         fl_we           ,//输入使能,低电平写入有效output  wire        fl_reset        ,//复位引脚输入,低电平复位    output  wire        fl_wp           ,//硬件写保护输入,默认为1即可output  reg [22:0]  fl_addr         ,//地址链接到flash的23个地址脚output  wire [7:0]  dat_out         ,//读取数据输出output  reg         pi_flag         ,//打印数据到串口标志位output  reg  [7:0]  lcd_out         ,//读取的数据给lcd1602inout   wire [7:0]  fl_dq           //数据输入/输出
);parameter   CNT_240US_MAX    =   'd11999;    parameter   CNT_500MS_MAX    =   28'd24_999_999;     
// Flash控制器状态机
localparam IDLE = 2'b00; // 空闲状态
localparam READ = 2'b01; // 读操作状态
localparam WRITE = 2'b10; // 写操作状态
localparam ERASE = 2'b11; // 擦除操作状态reg [2:0]   cmd;//状态控制寄存器,001:读操作 010:写操作 100:擦除操作       
reg [1:0]   state;//状态,四个状态
reg [3:0]   cnt_200ns;//写入操作使用的计时器
reg [4:0]   cnt_300ns;//读操作使用计时器
reg [2:0]   cnt_addr;//写入地址次数,因为写入前要先写命令,在写数据
reg [15:0]  cnt_240us;//写入指令输入后,等待一段时间再回到空闲态
reg         cnt_240us_flag;
reg [27:0]  cnt_500ms;
reg         cnt_500ms_flag;
reg [7:0]   data_reg;//写入的数据寄存器
reg         write_en;//写入控制,为高时才能写入
reg         read_en;//读控制位,为高时才能读数据
reg         erase_en;//擦除控制位,为高时才会擦除操作//assign fl_dq[7:0] = (state == READ) ? 8'hzz : data_reg[7:0];
assign fl_dq[7:0] = ((state != WRITE)&&(state != ERASE)) ? 8'hzz : data_reg[7:0];
assign fl_wp = 1'b1;
assign fl_reset = 1'b1;
assign dat_out = fl_dq;always @(posedge sys_clk or negedge sys_rst_n) beginif (!sys_rst_n) beginstate <= IDLE;end else begincase (state)IDLE: begin//空闲态case (cmd)3'b001: begin // 读操作if(read_en)state <= READ;end3'b010: begin // 写操作                  if(write_en)state <= WRITE;          end3'b100: begin // 擦除操作if(erase_en)state <= ERASE;enddefault: beginstate <= IDLE;endendcaseendREAD: begin//读if(cnt_300ns == 5'd14)  //需要连续读的话,14改成任意大于14的值,注意要在位宽内,同时波特率=9600state <= IDLE;endWRITE: begin//写if(cnt_240us == CNT_240US_MAX)state <= IDLE;endERASE: begin//擦除if(cnt_500ms == CNT_500MS_MAX)state <= IDLE;enddefault:beginstate <= IDLE;end endcaseend
end//cmd 状态控制寄存器,001:读操作 010:写操作 100:擦除操作     
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) cmd <= 3'b000;else if(wr_flag)cmd <= 3'b010;else if(rd_flag)cmd <= 3'b001;else if(rset_flag)cmd <= 3'b100;
//write_en 写入控制,为高时才能写入
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) write_en <= 1'b1;else if(cnt_240us == CNT_240US_MAX)write_en <= 1'b0;else if(wr_flag)write_en <= 1'b1;
//read_en 读控制位,为高时才能读数据
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) read_en <= 1'b1;else if(cnt_300ns == 5'd14)read_en <= 1'b0;else if(rd_flag)read_en <= 1'b1;
//erase_en 擦除控制位,为高时才会擦除操作
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) erase_en <= 1'b1;else if(cnt_500ms == CNT_500MS_MAX)erase_en <= 1'b0;        else if(rset_flag)erase_en <= 1'b1;
//cnt_300ns 读操作使用计时器
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) cnt_300ns <= 5'd0;else if(cnt_300ns == 5'd14)cnt_300ns <= 5'd0;else if(state == READ)cnt_300ns <= cnt_300ns + 1'd1;elsecnt_300ns <= 5'd0;
//cnt_200ns 写入操作使用的计时器
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) cnt_200ns <= 4'd0;else if((state == WRITE)&&((cnt_200ns == 4'd9)||((cnt_200ns == 4'd0)&&(cnt_addr == 3'd4))))cnt_200ns <= 4'd0;else if(state == WRITE)cnt_200ns <= cnt_200ns + 1'd1;else if((state == ERASE)&&((cnt_200ns == 4'd9)||((cnt_200ns == 4'd0)&&(cnt_addr == 3'd6))))cnt_200ns <= 4'd0;else if(state == ERASE)cnt_200ns <= cnt_200ns + 1'd1;    elsecnt_200ns <= 4'd0;
//cnt_addr 写入地址次数,因为写入前要先写命令,在写数据
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) cnt_addr <= 3'd0;else if((state != WRITE)&&(state != ERASE))cnt_addr <= 3'd0;else if((state == WRITE) && (cnt_200ns == 4'd0)&&(cnt_addr < 3'd4))cnt_addr <= cnt_addr + 1'd1;else if((state == ERASE) && (cnt_200ns == 4'd0)&&(cnt_addr < 3'd6))cnt_addr <= cnt_addr + 1'd1;
//cnt_240us_flag
always @(posedge sys_clk or negedge sys_rst_n) if(!sys_rst_n) cnt_240us_flag <= 1'b0;else if(state != WRITE) cnt_240us_flag <= 1'b0; else if((cnt_addr == 3'd4)&&(cnt_200ns == 4'd6))cnt_240us_flag <= 1'b1; 
//cnt_240us 写入指令输入后,等待一段时间再回到空闲态
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) cnt_240us <= 16'd0;else if(cnt_240us == CNT_240US_MAX)//11999cnt_240us <= 16'd0; else if(cnt_240us_flag)cnt_240us <= cnt_240us + 1'd1;
//cnt_500ms_flag
always @(posedge sys_clk or negedge sys_rst_n) if(!sys_rst_n) cnt_500ms_flag <= 1'b0;else if(state != ERASE) cnt_500ms_flag <= 1'b0; else if((cnt_addr == 3'd6)&&(cnt_200ns == 4'd6))cnt_500ms_flag <= 1'b1; 
//cnt_500ms 擦除指令输入后,等待一段时间再回到空闲态
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) cnt_500ms <= 28'd0;else if(cnt_500ms == CNT_500MS_MAX)cnt_500ms <= 28'd0; else if(cnt_500ms_flag)cnt_500ms <= cnt_500ms + 1'd1;
//fl_addr 写入或者读出的地址 
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) fl_addr <= 23'd0;else if(state == READ)fl_addr <= addr;else if(state == WRITE) case(cnt_addr)3'd0:   fl_addr <= 23'd0;3'd1:   fl_addr <= 23'h000AAA; //这三个地址位写入操作需要写入命令的对应地址3'd2:   fl_addr <= 23'h000555;3'd3:   fl_addr <= 23'h000AAA;3'd4:   fl_addr <= addr;  //实际需要写入数据的地址default:fl_addr <= 23'd0;endcaseelse if(state == ERASE) case(cnt_addr)3'd0:   fl_addr <= 23'd0;3'd1:   fl_addr <= 23'h000AAA; //这三个地址位写入操作需要写入命令的对应地址3'd2:   fl_addr <= 23'h000555;3'd3:   fl_addr <= 23'h000AAA;3'd4:   fl_addr <= 23'h000AAA;3'd5:   fl_addr <= 23'h000555;3'd6:   fl_addr <= 23'h0F0000;  //全擦除改成23'h000AAAdefault:fl_addr <= 23'd0;endcaseelsefl_addr <= 23'd0;//fl_ce 芯片使能输入,低电平有效
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) fl_ce <= 1'b1;else if((state == READ)&&(cnt_300ns <= 5'd14))fl_ce <= 1'b0;       else if((state == WRITE)&&(cnt_200ns == 4'd1))fl_ce <= 1'b1;else if(state == WRITE)fl_ce <= 1'b0;else if((state == ERASE)&&(cnt_200ns == 4'd1))fl_ce <= 1'b1;else if(state == ERASE)fl_ce <= 1'b0;else fl_ce <= 1'b1;
//fl_oe 输出使能,低电平读出有效
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) fl_oe <= 1'b1;else if((state == READ)&&(cnt_300ns <= 5'd14))fl_oe <= 1'b0;         else if(state == WRITE)fl_oe <= 1'b1; else if(state == ERASE)fl_oe <= 1'b1;         elsefl_oe <= 1'b0; 
//fl_we 输入使能,低电平写入有效
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) fl_we <= 1'b0;else if((state == READ)&&(cnt_300ns <= 5'd14))fl_we <= 1'b1;else if((state == WRITE)&&((cnt_addr == 3'd0)||(cnt_200ns == 4'd7)))fl_we <= 1'b1; else if((state == WRITE)&&(cnt_200ns == 4'd3)) fl_we <= 1'b0;else if((state == ERASE)&&((cnt_addr == 3'd0)||(cnt_200ns == 4'd7)))fl_we <= 1'b1; else if((state == ERASE)&&(cnt_200ns == 4'd3)) fl_we <= 1'b0;
//data_reg 写入的数据寄存器
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) data_reg <= 8'd0;else if((state == WRITE)&&(cnt_200ns == 4'd3)) case(cnt_addr)3'd0:   data_reg <= 8'h0;3'd1:   data_reg <= 8'hAA;3'd2:   data_reg <= 8'h55;3'd3:   data_reg <= 8'hA0;3'd4:   data_reg <= data_in;default:data_reg <= 8'h0;endcaseelse if((state == ERASE)&&(cnt_200ns == 4'd3)) case(cnt_addr)3'd0:   data_reg <= 8'h0;3'd1:   data_reg <= 8'hAA;3'd2:   data_reg <= 8'h55;3'd3:   data_reg <= 8'h80;3'd4:   data_reg <= 8'hAA;3'd5:   data_reg <= 8'h55;   3'd6:   data_reg <= 8'h30;  //全擦除改成8'h10default:data_reg <= 8'h0;endcase//pi_flag  打印数据到串口标志位
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n) pi_flag <= 1'b0;else if((state == READ)&&(cnt_300ns == 5'd9)) pi_flag <= 1'b1;    else if((state == WRITE)&&(cnt_200ns == 4'd6)) pi_flag <= 1'b1;elsepi_flag <= 1'b0;
//lcd_out
always @(posedge sys_clk or negedge sys_rst_n) if (!sys_rst_n)lcd_out <= 8'd0;else if((state == READ)&&(pi_flag == 1'b1))lcd_out <= dat_out;endmodule

3.3 lcd1602显示模块lcd1602

//将输入的8位数据,16进制输出显示2位在lcd上
module lcd1602 ( input 				sys_clk		,//系统时钟输入50Minput				sys_rst_n	,//复位,低电平有效input       [7:0]   data        ,output 	reg	[7:0] 	lcd_dat		,//LCD的8位数据口output  reg			lcd_rs		,//数据命令选择信号,高电平表示数据,低电平表示命令output				lcd_rw		,//读写标志,高电平表示读,低电平表示写,该程序我们只对液晶屏进行写操作output  reg		    lcd_en		 //LCD的控制脚);					 
//--------------------lcd1602 order----------------------------
parameter    Mode_Set    =  8'h38,//*设置8位格式,2行,5*7*Cursor_Set  =  8'h0c,//*整体显示,关光标,不闪烁*/  Address_Set =  8'h06,//*设定输入方式,增量不移位*/Clear_Set   =  8'h01;//*清除显示*//****************************LCD1602 Display Data****************************/	
wire [7:0] addr;   //write address
reg  [7:0] data_h,data_l;assign addr = 8'h80;//设置显示第一行
assign lcd_rw = 1'b0;always @(posedge sys_clk or negedge sys_rst_n)     if(!sys_rst_n)   data_h <= 8'd0;else if((data[7:4]>=10)&&(data[7:4]<=15))   data_h <= data[7:4]+87;  //a-felsedata_h <= data[7:4]+8'h30; //数字
always @(posedge sys_clk or negedge sys_rst_n)     if(!sys_rst_n)   data_l <= 8'd0;else if((data[3:0]>=10)&&(data[3:0]<=15))data_l <= data[3:0]+87;elsedata_l <= data[3:0]+8'h30;    
/****************************LCD1602 Driver****************************/			 
//-----------------------lcd1602 clk_en---------------------
reg [31:0] cnt;
reg lcd_clk_en;
always @(posedge sys_clk or negedge sys_rst_n)      
begin if(!sys_rst_n)begincnt <= 1'b0;lcd_clk_en <= 1'b0;endelse if(cnt == 32'd24999)   //0.5msbeginlcd_clk_en <= 1'b1;cnt <= 1'b0;endelsebegincnt <= cnt + 1'b1;lcd_clk_en <= 1'b0;end
end //-----------------------lcd1602 display state-------------------------------------------
reg [5:0] state;
always@(posedge sys_clk or negedge sys_rst_n)
beginif(!sys_rst_n)beginstate <= 1'b0;lcd_rs <= 1'b0;lcd_en <= 1'b0;lcd_dat <= 1'b0;   endelse if(lcd_clk_en)     begincase(state)//-------------------init_state---------------------5'd0: begin                lcd_rs <= 1'b0;lcd_en <= 1'b1;lcd_dat <= Mode_Set;   state <= state + 1'd1;end5'd1: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd2: beginlcd_rs <= 1'b0;lcd_en <= 1'b1;lcd_dat <= Cursor_Set;state <= state + 1'd1;end5'd3: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd4: beginlcd_rs <= 1'b0;lcd_en <= 1'b1;lcd_dat <= Address_Set;state <= state + 1'd1;end5'd5: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd6: beginlcd_rs <= 1'b0;lcd_en <= 1'b1;lcd_dat <= Clear_Set;state <= state + 1'd1;end5'd7: beginlcd_en <= 1'b0;state <= state + 1'd1;end//--------------------work state--------------------5'd8: begin              lcd_rs <= 1'b0;lcd_en <= 1'b1;lcd_dat <= addr;   //write addrstate <= state + 1'd1;end5'd9: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd10: beginlcd_rs <= 1'b1;lcd_en <= 1'b1;lcd_dat <= "V";   //write datastate <= state + 1'd1;end5'd11: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd12: beginlcd_rs <= 1'b1;lcd_en <= 1'b1;lcd_dat <= "a";   //write datastate <= state + 1'd1;end5'd13: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd14: beginlcd_rs <= 1'b1;lcd_en <= 1'b1;lcd_dat <= "l";   //write datastate <= state + 1'd1;end5'd15: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd16: beginlcd_rs <= 1'b1;lcd_en <= 1'b1;lcd_dat <= ":";   //write datastate <= state + 1'd1;end5'd17: beginlcd_en <= 1'b0;state <= state + 1'd1;end    5'd18: beginlcd_rs <= 1'b1;lcd_en <= 1'b1;lcd_dat <= data_h;   //write data: tens digitstate <= state + 1'd1;end5'd19: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd20: beginlcd_rs <= 1'b1;lcd_en <= 1'b1;lcd_dat <= data_l;   //write data: single digitstate <= state + 1'd1;end5'd21: beginlcd_en <= 1'b0;state <= state + 1'd1;end5'd22: beginlcd_rs <= 1'b1;lcd_en <= 1'b1;lcd_dat <= 8'h68;   //write data: single digitstate <= state + 1'd1;end5'd23: beginlcd_en <= 1'b0;state <= 5'd8;enddefault: state <= 5'bxxxxx;endcaseend
endendmodule

3.4 串口发送模块uart_tx


//功能:将FPGA数据以固定的速率发送给PC端,PC端通过串口调试助手接收并打印出来
`timescale  1ns/1nsmodule  uart_tx
#(parameter   UART_BPS    =   'd9600,         //串口波特率parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(input   wire            sys_clk     ,   //系统时钟50MHzinput   wire            sys_rst_n   ,   //全局复位input   wire    [7:0]   pi_data     ,   //模块输入的8bit数据input   wire            pi_flag     ,   //并行数据有效标志信号output  reg             tx              //串转并后的1bit数据
);//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;//reg   define
reg [12:0]  baud_cnt;
reg         bit_flag;
reg [3:0]   bit_cnt ;
reg         work_en ;//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)work_en <= 1'b0;else    if(pi_flag == 1'b1)work_en <= 1'b1;else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))work_en <= 1'b0;//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)baud_cnt <= 13'b0;else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))baud_cnt <= 13'b0;else    if(work_en == 1'b1)baud_cnt <= baud_cnt + 1'b1;
//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)bit_flag <= 1'b0;else if((baud_cnt == 13'b1)&&(work_en == 1'b1))bit_flag <= 1'b1;elsebit_flag <= 1'b0;        //bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)bit_cnt <= 4'b0;else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))bit_cnt <= 4'b0;else    if((bit_flag == 1'b1) && (work_en == 1'b1))bit_cnt <= bit_cnt + 1'b1;
//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)tx <= 1'b1; //空闲状态时为高电平else    if(bit_flag == 1'b1)case(bit_cnt)0       : tx <= 1'b0;1       : tx <= pi_data[0];2       : tx <= pi_data[1];3       : tx <= pi_data[2];4       : tx <= pi_data[3];5       : tx <= pi_data[4];6       : tx <= pi_data[5];7       : tx <= pi_data[6];8       : tx <= pi_data[7];9       : tx <= 1'b1;default : tx <= 1'b1;endcase
endmodule

3.5 按键滤波器模块key_filiter


//输入按键信号,输出一个系统时钟的高电平脉冲
module	key_filiter
(input		sys_clk     ,input		sys_rst_n   ,input		key_in      ,output	reg	key_flag
);reg	[19:0]	cnt_20ms;
parameter	CNT_MAX=20'd999999;always@(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)cnt_20ms<=20'd0;else	if(key_in)cnt_20ms<=20'd0;else	if(cnt_20ms==CNT_MAX)	//最大值保持cnt_20ms<=CNT_MAX;elsecnt_20ms<=cnt_20ms+1'd1;always@(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)key_flag<=1'b0;else	if(cnt_20ms==CNT_MAX-20'd1)key_flag<=1'b1;elsekey_flag<=1'b0;endmodule

4. SignalTap仿真图

按键key3写入,key2读出,key0扇区擦除

  1. 第一次按下读操作键,数据读出为FFh,证明无数据存储在里面
    在这里插入图片描述

  2. 按下写操作键
    在这里插入图片描述
    可以看到,ry_by产生了一段时间的低电平,并且dq数据线也显示5Ah,代表写入成功
    这是我们放大中间红线密集处: 可以看到我们写入的一些命令
    在这里插入图片描述

  3. 再按下读操作键,正确读出
    在这里插入图片描述

  4. 按下扇区擦除键
    在这里插入图片描述
    同样我们放大红线处:
    在这里插入图片描述

  5. 再按下读操作键,无数值,ff
    在这里插入图片描述

5. 实物图

读操作:
在这里插入图片描述
写操作后再读:
在这里插入图片描述擦除操作后再读:
在这里插入图片描述

这篇关于FPGA-DE2-115-实验四-flash读写操作-S29GL064N的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/584639

相关文章

使用Python进行文件读写操作的基本方法

《使用Python进行文件读写操作的基本方法》今天的内容来介绍Python中进行文件读写操作的方法,这在学习Python时是必不可少的技术点,希望可以帮助到正在学习python的小伙伴,以下是Pyth... 目录一、文件读取:二、文件写入:三、文件追加:四、文件读写的二进制模式:五、使用 json 模块读写

什么是 Flash Attention

Flash Attention 是 由 Tri Dao 和 Dan Fu 等人在2022年的论文 FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness 中 提出的, 论文可以从 https://arxiv.org/abs/2205.14135 页面下载,点击 View PDF 就可以下载。 下面我

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,

STM32 ADC+DMA导致写FLASH失败

最近用STM32G070系列的ADC+DMA采样时,遇到了一些小坑记录一下; 一、ADC+DMA采样时进入死循环; 解决方法:ADC-dma死循环问题_stm32 adc dma死机-CSDN博客 将ADC的DMA中断调整为最高,且增大ADCHAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_Buffer_Size); 的ADC_Bu

HNU-2023电路与电子学-实验3

写在前面: 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能,设计 8 重 2-1 多路复用器。 4.分析模型机的工作原理,设计模型机控制信号产生逻辑。 二、实验内容 1.用 VERILOG 语言设计模型机的 8 重 3-1 多路复用器; 2.用 VERILOG 语言设计模型机的 8 重 2-1 多

FPGA编译与部署方法全方位介绍

FPGA编译与部署是FPGA开发中的核心环节,涉及从代码编写、调试到将设计部署到FPGA硬件的全过程。这个流程需要经过创建项目、编写FPGA VI、模拟调试、编译生成比特流文件,最后将设计部署到硬件上运行。编译的特点在于并行执行能力、定制化硬件实现以及复杂的时钟管理。通过LabVIEW的FPGA模块和NI硬件,可以快速完成开发和部署,尤其适用于复杂控制与高性能数据处理系统。 1. FPG

FPGA开发:条件语句 × 循环语句

条件语句 if_else语句 if_else语句,用来判断是否满足所给定的条件,根据判断的结果(真或假)决定执行给出的两种操作之一。 if(表达式)语句; 例如: if(a>b) out1=int1; if(表达式)         语句1; else         语句2; 例如: if(a>b)out1=int1;elseout1=int2; if(表达式1) 语句1; els

61.以太网数据回环实验(4)以太网数据收发器发送模块

(1)状态转移图: (2)IP数据包格式: (3)UDP数据包格式: (4)以太网发送模块代码: module udp_tx(input wire gmii_txc ,input wire reset_n ,input wire tx_start_en , //以太网开始发送信

FPGA开发:模块 × 实例化

模块的结构 对于C语言,其基本单元为函数。与此类似,Verilog的基本设计单元称之为"模块"(block)。对于整个项目的设计思想就是模块套模块。 一个模块由两个部分组成:一部分描述接口,一部分描述逻辑功能。 每个Verilog模块包含4个部分:端口定义、IO说明、内部信号声明、功能定义。且位于module和endmodule之间,如下: module block(a,b,c);inpu