大侠好,欢迎来到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; //时钟 50Minput 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)begincount <= 16'd0;endelsebeginif(count < T1ms - 1 ) //计数1K的频率时间count <= count + 1'b1;elsebegincount <= 16'b0;endendassign 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)beginstate <= 3'b0;row_col <= 8'b1111_1111;data_flag <= 1'b0;col <= 4'b0000;cnt <= 19'b0;endelsebegincase (state)s0: beginif(row == 4'b1111) //如果没有按下begindata_flag <= 1'b0;col <= 4'b0000;endelse //表示按下,跳转下一个状态begindata_flag <= 1'b0;state <= s1;endends1: beginif(row == 4'b1111) //如果是抖动跳转0状态begincnt <= 19'b0;state <= s0;endelsebeginif(cnt < T10ms - 1) //计数相应的时间,消抖处理begincnt <= cnt + 1'b1;endelsebegincnt <= 19'b0;state <= s2;col <= 4'b0111; //消抖完表示按键有效endendends2: beginif (row != 4'b1111) //表示导通beginstate <= s3; //导通后跳转下一个状态row_col <= {row,col}; //拼接行和列信号endelse //行信号不导通,开始进行列扫描beginif(flag)begincol <= {col[2:0],col[3]}; //1ms进行一次列扫描endelsebegincol <= col;endendends3:beginif(row == 4'b1111) //按键抬起beginstate <= s0;data_flag <= 1'b1; //表示一次成功的按键,输出一个高脉冲endelsebeginstate <= s3;endenddefault: state <= s0;endcaseendreg [3:0] key_num;//键值的翻译模块的表示always @ (posedge clk or negedge rst_n)if(!rst_n)key_num = 4'd0;elsecase ({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)beginif(!rst_n)begindata <= 24'b0;state_s <= s0;num1 <= 24'b0;num2 <= 24'b0;data_t <= 24'b0;flag_s <= 4'b0;data_in <= 24'b0;endelsebegincase (state_s)s0:beginif(data_flag) //如果有一次按下beginif(key_num < 4'd9) //键值小于9便是有效beginnum1 <= num1*10 + key_num; //BCD码转为2进制data <= {data[19:0],key_num}; //数码管移位endif(key_num > 4'd9 && key_num < 4'd14) //10 -- 13 表示运算符begindata <= 24'b0;state_s <= s1;flag_s <= key_num;endelse //否则无效信号state_s <= s0;endends1:beginif(data_flag)//如果有一次按下beginif(key_num <4'd9 ) //键值小于9便是有效beginnum2 <= 10*num2 +key_num;//BCD码转为2进制data <= {data[19:0],key_num};//数码管移位endif(key_num > 4'd9 && key_num < 4'd14)//10 -- 13 表示运算符beginstate_s <= s1;endif(key_num == 15) //表示等于beginstate_s <= s2;endendends2:beginstate_s <= s3;case (flag_s)4'd10 :begin //加运算data_in <= num1 + num2;state_s <= s3;end4'd13 :begin //乘运算data_in <= num1 * num2;state_s <= s3;endendcaseends3: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;enddefault: state_s <= s0;endcaseendend/*always @ (posedge clk or negedge rst_n)if(!rst_n)begindata <= 24'b0;endelsebeginif(data_flag)begindata <= {data[19:0],key_num};if(key_num == 4'hf)data <= {data[15:0],4'hf,data[11:8] - data [3:0]};endelsebegindata <= data;endend*/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 5reg [15:0] count;wire flag;always @ (posedge clk or negedge rst_n)if(!rst_n)begincount <= 15'b0;endelseif(count == `T1ms - 1)begincount <= 15'b0;endelsebegincount <= count + 1'b1;endassign 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)beginsel <= 3'b0;state <= 3'b0;num <= 4'b0;endelsebegincase (state)s0:beginif(flag)state <= s1;elsebeginsel <= 3'b000;num <= data_in[23:20];endends1:beginif(flag)state <= s2;elsebeginsel <= 3'b001;num <= data_in[19:16];endends2:beginif(flag)state <= s3;elsebeginsel <= 3'b010;num <= data_in[15:12];endends3:beginif(flag)state <= s4;elsebeginsel <= 3'b011;num <= data_in[11:8];endends4:beginif(flag)state <= s5;elsebeginsel <= 3'b100;num <= data_in[7:4];endends5:beginif(flag)state <= s0;elsebeginsel <= 3'b101;num <= data_in[3:0];endenddefault:state <= s0;endcaseendalways @ (*)begincase (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:;endcaseendendmodule
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 @ (*)begincase (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:;endcaseendendmodule
仿真测试
测试模块calc_tb代码:
`timescale 1ns/1psmodule 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 beginclk = 1'b1;rst_n = 1'b0;pressnum = 5'd16;#200.1rst_n = 1'b1;#2000pressnum = 5'd16;#1000pressnum = 5'd5;#1000pressnum = 5'd16;#1250pressnum = 5'd10;#1250pressnum = 5'd16;#1250pressnum = 5'd2;#1250pressnum = 5'd16;#1250pressnum = 5'd15;#1250pressnum = 5'd16;#2000#2000$stop;endalways #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 :结果显示正确。
1167