当前位置:   article > 正文

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

s29gl读写操作


前言:
本文主要介绍了集成电路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位数据线
	其他一些功能配置位
  • 1
  • 2
  • 3
  • 4
  • 5

下图为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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

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         ,//读取的数据给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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309

3.3 lcd1602显示模块lcd1602

//将输入的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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205

3.4 串口发送模块uart_tx


//功能:将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


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

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;
	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


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

4. SignalTap仿真图

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

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

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

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

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

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

5. 实物图

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/577535
推荐阅读
相关标签
  

闽ICP备14008679号