基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

​1.项目要求:数码管显示范围为0~999 999,当数码管显示999 999,若此时按下加的按键,则数码管显示数清零,若数码管显示为0 ,若按下减的按键时,数码管为999 999

2.编写分频计数模块,用1KHz(系统时钟为5Mhz)作为驱动时钟。将分频出来的时钟输出给key_jitter模块

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

代码如下:(仿真时可以把T设小一点,不然跑不完)

module freq(         //系统时钟是50M,产生一个1KHZ的慢时钟
    input clk,
    input rst_n,
    output reg clk_1KHz
);
parameter T = 50000;   //50000 仿真时要选一个小一点的值
reg [19:0] counter;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
            counter <= 0;
            clk_1KHz <= 1;
        end 
    else
        begin
            if(counter < (T/2-1))
                counter <= counter+1;
            else
                begin
                    counter <= 0;
                    clk_1KHz <= ~clk_1KHz;
                end
        end
    
end
endmodule
//系统时钟是50M,产生一个1KHZ的慢时钟

3.按键消抖模块,并将加/减的信号传输给数据处理模块

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

代码如下


module key_jitter(      //按键消抖
    input clk,
    input rst_n,
    input key_add,     //表示加的按键
    input key_sub,     //表示减的按键
    
    output reg pos_add_flag,       //当加的按键按下之后,该标志位为1
    output reg pos_sub_flag        //当减的按键按下之后,该标志位为1
    
 //   output reg [3:0] flag_sub,       //记录减按键按下的次数
 //   output reg [3:0] flag_add        //记录加按键按下的次数
);
reg [10:0] counter1;       //加按下计数寄存器
reg [10:0] counter2;       //减按下计数寄存器
//reg pos_add_flag;         //按键按下成功标志位
//reg pos_sub_flag;         //按键按下成功标志位


reg [3:0] sum_sub;       //记录减按键按下的次数
reg [3:0] sum_add;  
  
  
reg state1;             //状态寄存器
reg state2;     
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)          //复位时,参数置0
        begin
            counter1 <= 11'b0;
            counter2 <= 11'b0;
            pos_add_flag <= 0;
            pos_sub_flag <= 0;
            sum_sub <= 0;
            sum_add <= 0;
            state1 <= 0;
            state2 <= 0;
        end
    else
        begin
            case(state1)
            0:begin         //当为起始状态时 先判断按键按下的状态计数是否超过10
                if(counter1<10)
                    begin
                        if(!key_add)
                            counter1 <= counter1 + 1;
                        else
                            counter1 <= 11'b0;  //如果按键没有按下,则清零  
                    end
                else
                    begin        //当按键按下的状态计数超过10,说明按键已经按下
                        pos_add_flag <= 1;  //尖峰脉冲到达
                        sum_add <= sum_add + 1;
                        counter1 <= 11'b0; 
                        state1 <= 1;
                    end
               end 
            1: begin
                 pos_add_flag <= 0;
                 if(key_add)
                    state1 <= 0;
               end
            default: state1 <= 0;
            endcase
            
            
            case(state2)
            0:begin         //当为起始状态时 先判断按键按下的状态计数是否超过10
                if(counter2<10)
                    begin
                        if(!key_sub)
                            counter2 <= counter2 + 1;
                        else
                            counter2 <= 11'b0;  //如果按键没有按下,则清零  
                    end
                else
                    begin        //当按键按下的状态计数超过10,说明按键已经按下
                        pos_sub_flag <= 1;  //尖峰脉冲到达
                        sum_sub <= sum_sub + 1;
                        counter2 <= 11'b0; 
                        state2 <= 1;
                    end
               end 
            1: begin
                 pos_sub_flag <= 0;
                 if(key_sub)
                    state2 <= 0;
               end
            default: state2 <= 0;
            endcase
        end 
    
end
endmodule

4. data_ctrl用于实现数据处理功能,当加按下时,显示的数字加一,当减时,显示的数字减一

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

module data_ctrl(      //用于实现数据处理功能,当加按下时,显示的数字加一,当减时,显示的数字减一
    input clk,
    input rst_n,
//    input flag_add,
 //   input flag_sub,
    input pos_add_flag,
    input pos_sub_flag,
    output reg[19:0] data
 //   input  data_en   //数据使能信号
    
);

reg [19:0] data_temp;   //数据暂存器
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
            data_temp <= 19'd0;
            data <= 19'd0;
           
        end
    else
        begin
          //  if (data_en)
           //     begin
                    if(pos_add_flag)        //当加按键按下时
                        begin
                            if(data_temp == 19'd999999)   //如果加按键按下后,但之前的数据已经是999999了,就要清零
                                data_temp <= 0;
                            else
                                data_temp <= data_temp + 1;  //否则就加一
                              //  data <= data_temp;
                        end
                    if(pos_sub_flag)
                        begin
                            if(data_temp == 0)  //如果减按键按下后,但之前的数据已经是0了,就要变为999999
                                data_temp <= 999999;
                            else       
                                data_temp <= data_temp - 1;  //否则就减一
                        end
                    data <= data_temp;
            //    end
        end
    
end

endmodule

5.实现二进制转化为BCD码的功能

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

代码如下

module bin_bcd(      //实现二进制转化为BCD码的功能
    input clk,
    input rst_n,
    input  [19:0] data,   //因为数码管能显示的数最大为999999,故该为20位
 //   output reg data_en,   //数据使能信号
    output reg [23:0] bcd_out      //6位数的bcd输出(因为最大的十进制数为6位,每一位需要4位表示bcd,所以需要24位)
);

reg [43:0] bcd_out_r;   //bcd转换码移位寄存器
reg [1:0] state3;   //状态寄存器
reg [5:0] shift_cnt;  //移位计数器 
//reg  data1 =1;

//assign data_en = data1;

/*always @(*)
begin
    data1 <= data;
end*/
always @(posedge clk or negedge rst_n)
begin
   
    if(!rst_n)
        begin
            bcd_out_r <= 0;
            state3 <= 0;
            shift_cnt <= 0;
            bcd_out <= 0;
         //   data_en = 1;
         //   data_en = data1;
        end
    else
        begin
            case(state3)
                2'd0:begin   //刚开始时,将数据赋值给bcd码的移位寄存器
                         bcd_out_r <= {24'b0,data};
                         state3 <= state3 + 1;
                         shift_cnt <= 0;
                     //    data_en = 0;
                     end 
                2'd1:begin   //移位
                         if(shift_cnt < 20)  //如果移位次数小于数据长度的话,就将bcd码进行移位
                            begin
                                bcd_out_r <= bcd_out_r << 1;
                                shift_cnt <= shift_cnt + 1;
                                state3 <= state3 + 1;
                            end
                         else
                            begin
                                state3 <= 0;
                                shift_cnt <= 0;
                            end
                     end 
          /*      2'd2:begin
                        bcd_out_r <= {bcd_out_r[],data[19:0],1'b0};
                     end */
                     2'd2:begin      //移位之后就进行大四加   *****是对BCD码进行大四加三,不能所有进行大四加三
                         if(bcd_out_r[43:40] > 4)
                            bcd_out_r[43:40] <= bcd_out_r[43:40] + 3;
                         if(bcd_out_r[39:36] > 4)
                            bcd_out_r[39:36] <= bcd_out_r[39:36] + 3;
                         if(bcd_out_r[35:32] > 4)
                            bcd_out_r[35:32] <= bcd_out_r[35:32] + 3;
                         if(bcd_out_r[31:28] > 4)
                            bcd_out_r[31:28] <= bcd_out_r[31:28] + 3;
                         if(bcd_out_r[27:24] > 4)
                            bcd_out_r[27:24] <= bcd_out_r[27:24] + 3;
                         if(bcd_out_r[23:20] > 4)
                            bcd_out_r[23:20] <= bcd_out_r[23:20] + 3;
                       /*  if(bcd_out_r[19:16] > 4)
                            bcd_out_r[19:16] <= bcd_out_r[19:16] + 3;
                         if(bcd_out_r[15:12] > 4)
                            bcd_out_r[15:12] <= bcd_out_r[15:12] + 3;
                         if(bcd_out_r[11:8] > 4)
                            bcd_out_r[11:8] <= bcd_out_r[11:8] + 3; 
                         if(bcd_out_r[7:4] > 4)
                            bcd_out_r[7:4] <= bcd_out_r[7:4] + 3; 
                         if(bcd_out_r[3:0] > 4)
                            bcd_out_r[3:0] <= bcd_out_r[3:0] + 3;*/
                         
                        
                         state3 <= 3;    
                     end
                2'd3:begin
                        state3 <= 1;    //继续进行移位,直到把数据全部移位
                     end 
                default : state3 <= 0;
            endcase
            bcd_out = (state3 == 3) && (shift_cnt == 20) ? bcd_out_r[43:20] : bcd_out; //如果移位和比较都完成之后就把值给输出
         //   data_en = (state3 == 3) && (shift_cnt == 20) ? 1 : 0;
      end 
end 
endmodule

6.实现数码管的译码

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

代码如下:

module seg_driver(  //实现数码管的译码
    input clk,
    input rst_n,
    input [23:0] bcd_out,         //输入的数据
    output reg[7:0] seg,     //数码管段选信号
    output reg[5:0] sel     //数码管位选信号
);
reg [3:0] data_temp;          //数码管显示的数值,因为一个数码管显示的最大数为9,就需要4位
reg [2:0] state4;         //状态寄存器

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
            sel <= 0;
            data_temp <= 0;
            state4 <= 0;
      //      data <= 24'h234567;
        end
    else
    begin
        case (state4)
            0:begin
                sel <= 0;     //将最高位显示在第一个数码管上
                data_temp <= bcd_out[23:20];
                state4 <= 1;
              end
            1:begin
                sel <= 1;     //将最高位显示在第二个数码管上
                data_temp <= bcd_out[19:16];
                state4 <= 2;
              end
            2:begin
                sel <= 2;     //将最高位显示在第3个数码管上
                data_temp <= bcd_out[15:12];
                state4 <= 3;
              end
            3:begin
                sel <= 3;     //将最高位显示在第4个数码管上
                data_temp <= bcd_out[11:8];
                state4 <= 4;
              end
            4:begin
                sel <= 4;     //将最高位显示在第5个数码管上
                data_temp <= bcd_out[7:4];
                state4 <= 5;
              end
            5:begin
                sel <= 5;     //将最高位显示在第6个数码管上
                data_temp <= bcd_out[3:0];
                state4 <= 0;
              end
            default : state4 <= 0;
        endcase
    end 
end 

always @(*)
begin
    if(!rst_n)
        begin
            seg = 8'b0000_0000;  //复位时数码管熄灭
        end
    else
        begin
            case(data_temp)
                0:seg =8'b1100_0000;    //数码管显示0
                1:seg =8'b1111_1001;
                2:seg =8'b1010_0100;
                3:seg =8'b1011_0000;
                4:seg =8'b1001_1001;
                5:seg =8'b1001_0010;
                6:seg =8'b1000_0010;
                7:seg =8'b1111_1000;
                8:seg =8'b1000_0000;
                9:seg =8'b1001_0000;
                10:seg =8'b1000_1000;
                11:seg =8'b1000_0011;
                12:seg =8'b1100_0110;
                13:seg =8'b1010_0001;
                14:seg =8'b1000_1110;
                15:seg =8'b1000_1110;   数码管显示F
            
                default : seg = 8'b1111_1111; 
            endcase
        end
    
end

endmodule

7.顶层模块

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

代码如下:

module key_seg(   //顶层模块
    input clk,
    input rst_n,
    input key_add,     //表示加的按键
    input key_sub, 
  //  input [23:0] data,
    output [7:0] seg,     //数码管段选信号
    output [5:0] sel 
);


wire clk_1KHz;
wire [19:0] data;
wire [23:0] bcd_out;

freq freq(
    .clk(clk),
    .rst_n(rst_n),
    .clk_1KHz(clk_1KHz)
);
key_jitter key_jitter(
    .clk(clk_1KHz),
    .rst_n(rst_n),
    .key_add(key_add),
    .key_sub(key_sub),
    .pos_sub_flag(pos_sub_flag),
    .pos_add_flag(pos_add_flag)
);

data_ctrl data_ctrl(
    .clk(clk_1KHz),
    .rst_n(rst_n),
    .data(data),
    .pos_sub_flag(pos_sub_flag),
   // .data_en(data_en),
    .pos_add_flag(pos_add_flag)
);


bin_bcd bin_bcd(
    .clk(clk_1KHz),
    .rst_n(rst_n),
    .data(data),
 //   .data_en(data_en),
    .bcd_out(bcd_out)
);


seg_driver seg_driver(
    .clk(clk_1KHz),
    .rst_n(rst_n),
    .bcd_out(bcd_out),
    .seg(seg),
    .sel(sel)
);



endmodule

7.测试文件及仿真图:

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

代码如下:

`timescale 1 ns/ 1 ns
module key_seg_tb();
parameter T = 20;
/********************系统输入**********************/
reg clk;
reg rst_n;                                           
reg   key_add;
reg   key_sub;
/*********************系统输出*****************/
wire [7:0]  seg;
wire [5:0]  sel;

wire [19:0] data;                    
wire [23:0] bcd_out;
wire data_en;
initial 
begin                                                  
    clk = 1'b1;
	rst_n = 1'b0;  
    key_add = 1;
    key_sub = 1;
	#1000 rst_n = 1'b1;
    #10000000 key_add = 0;
    #10000000 key_add = 1;
 /*   #100000000000000 key_add = 0;
    #100000000000000 key_add = 1;
    #100000000000000 key_sub = 0;
    #100000000000000 key_sub = 1;
    #10000000 key_add = 0;
    #10000000 key_add = 1;
    #10000000 key_add = 0;
    #10000000 key_add = 1;
    #10000000 key_add = 0;
    #10000000 key_add = 1;*/
    $stop;  	                 
end                                                                                                 
always #(T/2)  clk = ~clk;     
freq freq(
    .clk(clk),
    .rst_n(rst_n),
    .clk_1KHz(clk_1KHz)
);
key_jitter key_jitter(
    .clk(clk_1KHz),
    .rst_n(rst_n),
    .key_add(key_add),
    .key_sub(key_sub),
    .pos_sub_flag(pos_sub_flag),
    .pos_add_flag(pos_add_flag)
);

data_ctrl data_ctrl(
    .clk(clk_1KHz),
    .rst_n(rst_n),
    .data(data),
    .pos_sub_flag(pos_sub_flag),
 //   .data_en(data_en),
    .pos_add_flag(pos_add_flag)
);


bin_bcd bin_bcd(
    .clk(clk_1KHz),
    .rst_n(rst_n),
    .data(data),
 //   .data_en(data_en),
    .bcd_out(bcd_out)
);


seg_driver seg_driver(
    .clk(clk_1KHz),
    .rst_n(rst_n),
    .bcd_out(bcd_out),
    .seg(seg),
    .sel(sel)
);
key_seg key_seg(

	.rst_n(rst_n),
    .clk(clk),
    .key_add(key_add),
    .seg(seg),
    .sel(sel),
    .key_sub(key_sub)
);                              
endmodule


仿真图:

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

ps:

1)各个模块之间的连线一定要在顶层文件和testbench文件定义申明wire类型,因为input默认是wire类型,而大多数output是reg类型,连接的起来相当于是一根线,即要定义为wire类型,还要标明宽度,因为默认好像是2,如果不定义就不能接受宽度大的数据了。比如:我这里就是data_ctrol可以输出data,但是bin_bcd 里面就接收不了,如下图:

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

2)在转化为bcd码时,本来想的是在移位完成和比较完成之后就可以把值给输出,但是看程序可以看出,在当移位完成之后,还没有比较完成时就已经把值给输出了,所以会出错。

基于FPGA的按键计数(通过按键实现对FPGA板子的数码管显示数进行控制)

点击此处添加图片说明文字

上一篇:FPGA神经网络部署 Zynq Ultrascale Yolov3-tiny VGG16


下一篇:python学习笔记-day4笔记 常用内置函数与装饰器