加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • 设计原理
    • 设计架构
    • 仿真测试
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

源码系列:基于FPGA的计算器设计(附源工程)

16小时前
242
阅读需 24 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。

今天给大侠带来基于FPGA的计算器设计,附源码,获取源码,请在“FPGA技术江湖”公众号内回复“ 计算器设计源码”,可获取源码文件。话不多说,上货。

设计原理

在日常的生活和学习中,我们经常能用到计算器,计算器的设计可以让我们加深对设计思想以及设计方法的理解,训练实操能力,紧密的联系各模块, 对我们的学习有很大的帮助和提升。下面咱们就来一起看一下。

本次的设计主要通过矩阵键盘来实现按键的加减乘除运算,通过按下有效键值来当被加数或者被除数等等,按下10 -- 13等数字来表示对应的运算符。按键键值15表示等于号。

此次的设计是通过数码管来实现显示的,通过按下对应的按键来显示到数码管上,百位十位个位等等。当按下运算算符的时候显示清0不显示东西,之后通过继续按下别的键值来显示出对应的加数和除数等等,之后通过按下对应的键值15表示等于后,然后数码管清0之后立马显示出对应的等于的数。

设计架构

设计框架图:

设计代码:

顶层模块calc代码:

module calc(clk,rst_n,row,col,sel,seg7);  //端口列表  input clk;    //时钟  input rst_n;  //复位  input [3:0] row;  //行信号
  output [3:0] col;  //列信号  output  [2:0] sel;  //数码管位选信号  output  [7:0] seg7;  //数码管段选信号
  wire [23:0] data;
  //例化数码管模,和矩阵键盘模块  key_borad key_borad_dut(         .clk(clk),        .rst_n(rst_n),        .row(row),        .col(col),        .data(data)      );  seg seg_dut(        .clk(clk),        .rst_n(rst_n),        .sel(sel),        .seg7(seg7),        .data_in(data)      );
endmodule

key_borad代码:

module key_borad(clk,rst_n,row,col,data);  input clk;      //时钟 50M  input rst_n;    //复位  input [3:0] row;      //输入行信号
  output reg [3:0] col;    //输出列信号  output reg [23:0] data;
  //状态变量,表示  parameter s0 = 3'b00;  parameter s1 = 3'b01;  parameter s2 = 3'b10;  parameter s3 = 3'b11;  parameter s4 = 3'b100;  parameter s5 = 3'b101;
  //parameter T1ms = 50000;    //扫描间隔  parameter T1ms = 2;  //parameter T10ms= 500_000;   //按键消抖时间  parameter T10ms = 20;
  wire flag;  reg [15:0] count;  always @ (posedge clk or negedge rst_n)    if(!rst_n)      begin        count <= 16'd0;      end    else      begin        if(count <  T1ms - 1 )      //计数1K的频率时间          count <= count + 1'b1;        else          begin            count <= 16'b0;          end      end
  assign flag = (count == T1ms - 1) ? 1'b1 : 1'b0;  //计数到了就给一个高脉冲,反之低脉冲
  reg [2:0] state;  reg [7:0] row_col;  reg [18:0] cnt;  reg data_flag;  always @ (posedge clk or negedge rst_n)    if(!rst_n)      begin        state <= 3'b0;        row_col <= 8'b1111_1111;          data_flag <= 1'b0;        col <= 4'b0000;        cnt <= 19'b0;      end    else      begin        case (state)          s0: begin              if(row == 4'b1111)  //如果没有按下                begin                  data_flag <= 1'b0;                  col <= 4'b0000;                end              else      //表示按下,跳转下一个状态                begin                  data_flag <= 1'b0;                    state <= s1;                end            end          s1: begin              if(row == 4'b1111)  //如果是抖动跳转0状态                begin                  cnt <= 19'b0;                  state <= s0;                end              else                begin                  if(cnt < T10ms - 1)  //计数相应的时间,消抖处理                    begin                      cnt <= cnt + 1'b1;                    end                  else                    begin                      cnt <= 19'b0;                      state <= s2;                      col <= 4'b0111;   //消抖完表示按键有效                    end                end            end          s2: begin              if (row != 4'b1111)      //表示导通                begin                    state <= s3;      //导通后跳转下一个状态                  row_col <= {row,col};  //拼接行和列信号                end              else      //行信号不导通,开始进行列扫描                begin                  if(flag)                    begin                      col <= {col[2:0],col[3]};  //1ms进行一次列扫描                    end                  else                    begin                      col <= col;                    end                end            end          s3:begin              if(row == 4'b1111)  //按键抬起                begin                  state <= s0;                  data_flag <= 1'b1;  //表示一次成功的按键,输出一个高脉冲                end              else                begin                  state <= s3;                end            end          default: state <= s0;        endcase      end
  reg [3:0] key_num;    //键值的翻译模块的表示  always @ (posedge clk or negedge rst_n)    if(!rst_n)      key_num = 4'd0;    else      case ({row_col})         8'b0111_0111:key_num = 4'hf;        8'b0111_1011:key_num = 4'he;        8'b0111_1101:key_num = 4'hd;        8'b0111_1110:key_num = 4'hc;
        8'b1011_0111:key_num = 4'hb;        8'b1011_1011:key_num = 4'ha;        8'b1011_1101:key_num = 4'h9;        8'b1011_1110:key_num = 4'h8;
        8'b1101_0111:key_num = 4'h7;        8'b1101_1011:key_num = 4'h6;        8'b1101_1101:key_num = 4'h5;        8'b1101_1110:key_num = 4'h4;
        8'b1110_0111:key_num = 4'h3;        8'b1110_1011:key_num = 4'h2;        8'b1110_1101:key_num = 4'h1;        8'b1110_1110:key_num = 4'h0;        default: ;    endcase


  //计算模块的表示  reg [2:0] state_s;   //状态变量  reg [23:0] num1,num2,data_in,data_t;  //信号变量  reg [3:0]flag_s;    //运算符  always @ (posedge clk or negedge rst_n)    begin      if(!rst_n)        begin          data <= 24'b0;          state_s <= s0;          num1 <= 24'b0;          num2 <= 24'b0;           data_t <= 24'b0;          flag_s <= 4'b0;          data_in <= 24'b0;        end      else        begin          case (state_s)            s0:begin                if(data_flag)  //如果有一次按下                  begin                    if(key_num < 4'd9)  //键值小于9便是有效                      begin                        num1 <= num1*10 + key_num;  //BCD码转为2进制                        data <= {data[19:0],key_num};  //数码管移位                      end                    if(key_num > 4'd9 && key_num < 4'd14) //10 -- 13 表示运算符                      begin                        data <= 24'b0;                        state_s <= s1;                        flag_s <= key_num;                      end                    else   //否则无效信号                      state_s <= s0;                  end              end            s1:begin                if(data_flag)//如果有一次按下                 begin                  if(key_num <4'd9 )  //键值小于9便是有效                    begin                      num2 <= 10*num2 +key_num;//BCD码转为2进制                      data <= {data[19:0],key_num};//数码管移位                    end                  if(key_num > 4'd9 && key_num < 4'd14)//10 -- 13 表示运算符                      begin                        state_s <= s1;                      end                    if(key_num == 15) //表示等于                    begin                          state_s <= s2;                    end                end                  end            s2:begin                state_s <= s3;                case (flag_s)
                  4'd10 :begin  //加运算                      data_in <= num1 + num2;                        state_s <= s3;                  end
                  4'd13 :begin  //乘运算                    data_in <= num1 * num2;                    state_s <= s3;                  end                endcase              end            s3:begin    //二进制转为BCD码显示到对应的数码管上                data[3:0] = data_in % 10;                data[7:4] = data_in / 10 % 10;                data[11:8] = data_in / 100 % 10;                  data[15:12] = data_in / 1000 % 10;                data[19:16] = data_in / 10000 % 10;                data[23:20] = data_in / 100000;                state_s <= s0;                data_in <= 24'b0;              end            default: state_s <= s0;          endcase        end    end
  /*  always @ (posedge clk or negedge rst_n)    if(!rst_n)      begin        data <= 24'b0;      end    else      begin        if(data_flag)          begin            data <= {data[19:0],key_num};   
            if(key_num == 4'hf)              data <= {data[15:0],4'hf,data[11:8] - data [3:0]};          end        else          begin            data <= data;          end      end  */endmodule

seg代码:

module seg(clk,rst_n,sel,seg7,data_in);
  input clk;  input rst_n;  input [23:0] data_in;
  output reg [2:0] sel;  output reg [7:0] seg7;
  parameter s0 = 3'b000;  parameter s1 = 3'b001;  parameter s2 = 3'b010;  parameter s3 = 3'b011;  parameter s4 = 3'b100;  parameter s5 = 3'b101;
  `define T1ms  50_000  //`define T1ms  5  reg [15:0] count;  wire flag;  always @ (posedge clk or negedge rst_n)    if(!rst_n)      begin        count <= 15'b0;      end    else      if(count == `T1ms - 1)        begin          count <= 15'b0;        end      else        begin          count <= count + 1'b1;        end
  assign flag =(count == `T1ms  - 1) ? 1'b1 : 1'b0;
  reg [2:0] state;   reg [3:0] num;  always @ (posedge clk or negedge rst_n)    if(!rst_n)      begin        sel <= 3'b0;        state <= 3'b0;        num <= 4'b0;      end    else      begin        case (state)          s0:begin              if(flag)                state <= s1;              else                begin                  sel <= 3'b000;                  num <= data_in[23:20];                end            end          s1:begin              if(flag)                state <= s2;              else                begin                  sel <= 3'b001;                  num <= data_in[19:16];                end            end          s2:begin              if(flag)                state <= s3;              else                begin                  sel <= 3'b010;                  num <= data_in[15:12];                end            end          s3:begin              if(flag)                state <= s4;              else                begin                  sel <= 3'b011;                  num <= data_in[11:8];                end            end          s4:begin              if(flag)                state <= s5;              else                begin                  sel <= 3'b100;                  num <= data_in[7:4];                end            end          s5:begin              if(flag)                state <= s0;              else                begin                  sel <= 3'b101;                  num <= data_in[3:0];                end            end          default:state <= s0;        endcase      end
  always @ (*)      begin        case (num)          0:seg7 <= 8'b1100_0000;          1:seg7 <= 8'b1111_1001;          2:seg7 <= 8'b1010_0100;          3:seg7 <= 8'b1011_0000;          4:seg7 <= 8'b1001_1001;          5:seg7 <= 8'b1001_0010;          6:seg7 <= 8'b1000_0010;          7:seg7 <= 8'b1111_1000;          8:seg7 <= 8'b1000_0000;          9:seg7 <= 8'b1001_0000;          10:seg7 <= 8'b1000_1000;             11:seg7 <= 8'b1111_0111;  // '-'          12:seg7 <= 8'b1100_0110;          13:seg7 <= 8'b1010_0001;          14:seg7 <= 8'b1000_0110;          15:seg7 <= 8'b1111_0110;  // '= '          default:;        endcase      endendmodule

yingjian模块代码:

module yingjian(clk,rst_n,col,row,pressnum);
  input clk;  input rst_n;  input [3:0] col;  input [4:0] pressnum;
  output reg [3:0] row;

  always @ (*)      begin        case (pressnum)          0: row = {1'b1,1'b1,1'b1,col[0]};          1: row = {1'b1,1'b1,1'b1,col[1]};          2: row = {1'b1,1'b1,1'b1,col[2]};          3: row = {1'b1,1'b1,1'b1,col[3]};
          4: row = {1'b1,1'b1,col[0],1'b1};          5: row = {1'b1,1'b1,col[1],1'b1};          6: row = {1'b1,1'b1,col[2],1'b1};          7: row = {1'b1,1'b1,col[3],1'b1};
          8: row = {1'b1,col[0],1'b1,1'b1};          9: row = {1'b1,col[1],1'b1,1'b1};          10: row = {1'b1,col[2],1'b1,1'b1};          11: row = {1'b1,col[3],1'b1,1'b1};
          12: row = {col[0],1'b1,1'b1,1'b1};          13: row = {col[1],1'b1,1'b1,1'b1};          14: row = {col[2],1'b1,1'b1,1'b1};          15: row = {col[3],1'b1,1'b1,1'b1};
          16: row = {1'b1,1'b1,1'b1,1'b1};          default:;        endcase      endendmodule

仿真测试

测试模块calc_tb代码:

`timescale 1ns/1ps
module calc_tb();  reg clk;  reg rst_n;  reg [4:0] pressnum;  wire [3:0] row;
  wire [3:0] col;  wire [3:0] key_num;
  initial begin      clk = 1'b1;      rst_n = 1'b0;      pressnum = 5'd16;
      #200.1        rst_n = 1'b1;      #2000        pressnum = 5'd16;
      #1000        pressnum = 5'd5;
      #1000        pressnum = 5'd16;
      #1250        pressnum = 5'd10;      #1250        pressnum = 5'd16;      #1250        pressnum = 5'd2;      #1250        pressnum = 5'd16;      #1250        pressnum = 5'd15;      #1250        pressnum = 5'd16;      #2000      #2000        $stop;
    end  always #10 clk = ~clk;
  calc calc_dut(      .clk(clk),      .rst_n(rst_n),      .row(row),      .col(col),      .sel(sel),      .seg7(seg7)    );  yingjian yingjian_dut(      .clk(clk),      .rst_n(rst_n),      .col(col),      .row(row),      .pressnum(pressnum)    );endmodule

仿真图:

从仿真图中可以看出,在仿真中我们设置的是先按下5,再10,之后2,然后按下等于15.通过观察仿真正确,之后由于设计中我们10是表示加法,那么5 + 2 = 7 :结果显示正确。

相关推荐

电子产业图谱

任何技术的学习就好比一个江湖,对于每一位侠客都需要不断的历练,从初入江湖的小白到归隐山林的隐世高人,需要不断的自我感悟自己修炼,让我们一起仗剑闯FPGA乃至更大的江湖。