赞
踩
FPGA内板载flash芯片没有使用spi-flash进行操作,而是单纯使用S29GL064N的flash芯片,所有引脚直接连接到fpga板块。
利用FPGA板及flash外设,编写代码实现按键按下写入flash,并且断电验证是否写入成功,读出的数据显示在lcd1602显示屏上,并且可以实现擦除数据功能。
通过阅读S29GL064N的flash芯片手册,再对比板载芯片名称,我们知道该flash属于S29GL064N (Model 04)。
其中引脚:
22位地址线输入
8/16位数据线
其他一些功能配置位
下图为S29GL064N (Model 04)的引脚图:
下图为DE2-115中flash的连线图:
我们可以看到:
地址位多了一位A-1,变为23位,这样我们选择地址的时候就需要注意一下,最低位为A-1,自己随意配置即可
数据位使用了8位
由flash手册p25页可知整个flash分为134个扇区,写入读出数据的话都需要先确定扇区地址,比如在该实验中我们操作读写扇区SA45,得知地址位为: 0100110xxx
选择完扇区后,我们再从中任意选择一个地址使用: 比如23’010 0110 0000 0000 0000 0001 即23’h260001,该实验都在这个地址操作。
选择完地址后,我们就需要查看如何对flash进行读写操作了。
在手册p16页中我们可以找到flash设备总线操作:
在上节中,我们找到了flash操作指令表:
其中划线的4个指令我们都是需要使用到的。
其中RA/PA为目标地址,PD/RD为数据。
很容易看出:
读操作不需要指令,直接输入需要读写的地址,并配置好目录2.2节的总线要求即可。
写操作与擦除指令都需要先往固定的地址写入固定的指令才能启动。
读时序(p62)如图:
写时序(p65)如图:
可以看到,在写入操作完成后,RY脚产生了一段时间的低脉冲,我们可以根据这个来判断是否执行完成过该写入操作。
擦除时序(p66)如图:
调用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
相关时序图:
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 ,//读取的数据给lcd1602 inout 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) begin if (!sys_rst_n) begin state <= IDLE; end else begin case (state) IDLE: begin//空闲态 case (cmd) 3'b001: begin // 读操作 if(read_en) state <= READ; end 3'b010: begin // 写操作 if(write_en) state <= WRITE; end 3'b100: begin // 擦除操作 if(erase_en) state <= ERASE; end default: begin state <= IDLE; end endcase end READ: begin//读 if(cnt_300ns == 5'd14) //需要连续读的话,14改成任意大于14的值,注意要在位宽内,同时波特率=9600 state <= IDLE; end WRITE: begin//写 if(cnt_240us == CNT_240US_MAX) state <= IDLE; end ERASE: begin//擦除 if(cnt_500ms == CNT_500MS_MAX) state <= IDLE; end default:begin state <= IDLE; end endcase end 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; else cnt_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; else cnt_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)//11999 cnt_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; endcase else 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'h000AAA default:fl_addr <= 23'd0; endcase else fl_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; else fl_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; endcase else 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'h10 default: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; else pi_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
//将输入的8位数据,16进制输出显示2位在lcd上 module lcd1602 ( input sys_clk ,//系统时钟输入50M input 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-f else data_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; else data_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) begin cnt <= 1'b0; lcd_clk_en <= 1'b0; end else if(cnt == 32'd24999) //0.5ms begin lcd_clk_en <= 1'b1; cnt <= 1'b0; end else begin cnt <= 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) begin if(!sys_rst_n) begin state <= 1'b0; lcd_rs <= 1'b0; lcd_en <= 1'b0; lcd_dat <= 1'b0; end else if(lcd_clk_en) begin case(state) //-------------------init_state--------------------- 5'd0: begin lcd_rs <= 1'b0; lcd_en <= 1'b1; lcd_dat <= Mode_Set; state <= state + 1'd1; end 5'd1: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd2: begin lcd_rs <= 1'b0; lcd_en <= 1'b1; lcd_dat <= Cursor_Set; state <= state + 1'd1; end 5'd3: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd4: begin lcd_rs <= 1'b0; lcd_en <= 1'b1; lcd_dat <= Address_Set; state <= state + 1'd1; end 5'd5: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd6: begin lcd_rs <= 1'b0; lcd_en <= 1'b1; lcd_dat <= Clear_Set; state <= state + 1'd1; end 5'd7: begin lcd_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 addr state <= state + 1'd1; end 5'd9: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd10: begin lcd_rs <= 1'b1; lcd_en <= 1'b1; lcd_dat <= "V"; //write data state <= state + 1'd1; end 5'd11: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd12: begin lcd_rs <= 1'b1; lcd_en <= 1'b1; lcd_dat <= "a"; //write data state <= state + 1'd1; end 5'd13: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd14: begin lcd_rs <= 1'b1; lcd_en <= 1'b1; lcd_dat <= "l"; //write data state <= state + 1'd1; end 5'd15: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd16: begin lcd_rs <= 1'b1; lcd_en <= 1'b1; lcd_dat <= ":"; //write data state <= state + 1'd1; end 5'd17: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd18: begin lcd_rs <= 1'b1; lcd_en <= 1'b1; lcd_dat <= data_h; //write data: tens digit state <= state + 1'd1; end 5'd19: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd20: begin lcd_rs <= 1'b1; lcd_en <= 1'b1; lcd_dat <= data_l; //write data: single digit state <= state + 1'd1; end 5'd21: begin lcd_en <= 1'b0; state <= state + 1'd1; end 5'd22: begin lcd_rs <= 1'b1; lcd_en <= 1'b1; lcd_dat <= 8'h68; //write data: single digit state <= state + 1'd1; end 5'd23: begin lcd_en <= 1'b0; state <= 5'd8; end default: state <= 5'bxxxxx; endcase end end endmodule
//功能:将FPGA数据以固定的速率发送给PC端,PC端通过串口调试助手接收并打印出来 `timescale 1ns/1ns module uart_tx #( parameter UART_BPS = 'd9600, //串口波特率 parameter CLK_FREQ = 'd50_000_000 //时钟频率 ) ( input wire sys_clk , //系统时钟50MHz input 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; else bit_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
//输入按键信号,输出一个系统时钟的高电平脉冲 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; else cnt_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; else key_flag<=1'b0; endmodule
按键key3写入,key2读出,key0扇区擦除
第一次按下读操作键,数据读出为FFh,证明无数据存储在里面
按下写操作键
可以看到,ry_by产生了一段时间的低电平,并且dq数据线也显示5Ah,代表写入成功
这是我们放大中间红线密集处: 可以看到我们写入的一些命令
再按下读操作键,正确读出
按下扇区擦除键
同样我们放大红线处:
再按下读操作键,无数值,ff
读操作:
写操作后再读:
擦除操作后再读:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。