前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >源码系列:基于FPGA的计算器设计(附源工程)

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

作者头像
FPGA技术江湖
发布2020-12-29 17:28:57
7280
发布2020-12-29 17:28:57
举报
文章被收录于专栏:FPGA技术江湖FPGA技术江湖

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

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

设计原理

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

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

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

设计架构

设计框架图:

设计代码:

顶层模块calc代码:

代码语言:javascript
复制
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代码:

代码语言:javascript
复制
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代码:

代码语言:javascript
复制
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
      end
endmodule

yingjian模块代码:

代码语言:javascript
复制
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
      end
endmodule

仿真测试

测试模块calc_tb代码:

代码语言:javascript
复制
`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 :结果显示正确。

END

后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。

大侠们,江湖偌大,继续闯荡,愿一切安好,有缘再见!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FPGA技术江湖 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档