大俠好,歡迎來(lái)到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。
今天給大俠帶來(lái)基于FPGA的計(jì)算器設(shè)計(jì),附源碼,獲取源碼,請(qǐng)?jiān)凇癋PGA技術(shù)江湖”公眾號(hào)內(nèi)回復(fù)“?計(jì)算器設(shè)計(jì)源碼”,可獲取源碼文件。話不多說(shuō),上貨。
設(shè)計(jì)原理
在日常的生活和學(xué)習(xí)中,我們經(jīng)常能用到計(jì)算器,計(jì)算器的設(shè)計(jì)可以讓我們加深對(duì)設(shè)計(jì)思想以及設(shè)計(jì)方法的理解,訓(xùn)練實(shí)操能力,緊密的聯(lián)系各模塊,?對(duì)我們的學(xué)習(xí)有很大的幫助和提升。下面咱們就來(lái)一起看一下。
本次的設(shè)計(jì)主要通過(guò)矩陣鍵盤來(lái)實(shí)現(xiàn)按鍵的加減乘除運(yùn)算,通過(guò)按下有效鍵值來(lái)當(dāng)被加數(shù)或者被除數(shù)等等,按下10 -- 13等數(shù)字來(lái)表示對(duì)應(yīng)的運(yùn)算符。按鍵鍵值15表示等于號(hào)。
此次的設(shè)計(jì)是通過(guò)數(shù)碼管來(lái)實(shí)現(xiàn)顯示的,通過(guò)按下對(duì)應(yīng)的按鍵來(lái)顯示到數(shù)碼管上,百位十位個(gè)位等等。當(dāng)按下運(yùn)算算符的時(shí)候顯示清0不顯示東西,之后通過(guò)繼續(xù)按下別的鍵值來(lái)顯示出對(duì)應(yīng)的加數(shù)和除數(shù)等等,之后通過(guò)按下對(duì)應(yīng)的鍵值15表示等于后,然后數(shù)碼管清0之后立馬顯示出對(duì)應(yīng)的等于的數(shù)。
設(shè)計(jì)架構(gòu)
設(shè)計(jì)框架圖:
設(shè)計(jì)代碼:
頂層模塊calc代碼:
module calc(clk,rst_n,row,col,sel,seg7); //端口列表
input clk; //時(shí)鐘
input rst_n; //復(fù)位
input [3:0] row; //行信號(hào)
output [3:0] col; //列信號(hào)
output [2:0] sel; //數(shù)碼管位選信號(hào)
output [7:0] seg7; //數(shù)碼管段選信號(hào)
wire [23:0] data;
//例化數(shù)碼管模,和矩陣鍵盤模塊
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; //時(shí)鐘 50M
input rst_n; //復(fù)位
input [3:0] row; //輸入行信號(hào)
output reg [3:0] col; //輸出列信號(hào)
output reg [23:0] data;
//狀態(tài)變量,表示
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; //按鍵消抖時(shí)間
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 ) //計(jì)數(shù)1K的頻率時(shí)間
count <= count + 1'b1;
else
begin
count <= 16'b0;
end
end
assign flag = (count == T1ms - 1) ? 1'b1 : 1'b0; //計(jì)數(shù)到了就給一個(gè)高脈沖,反之低脈沖
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 //表示按下,跳轉(zhuǎn)下一個(gè)狀態(tài)
begin
data_flag <= 1'b0;
state <= s1;
end
end
s1: begin
if(row == 4'b1111) //如果是抖動(dòng)跳轉(zhuǎn)0狀態(tài)
begin
cnt <= 19'b0;
state <= s0;
end
else
begin
if(cnt < T10ms - 1) //計(jì)數(shù)相應(yīng)的時(shí)間,消抖處理
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) //表示導(dǎo)通
begin
state <= s3; //導(dǎo)通后跳轉(zhuǎn)下一個(gè)狀態(tài)
row_col <= {row,col}; //拼接行和列信號(hào)
end
else //行信號(hào)不導(dǎo)通,開始進(jìn)行列掃描
begin
if(flag)
begin
col <= {col[2:0],col[3]}; //1ms進(jìn)行一次列掃描
end
else
begin
col <= col;
end
end
end
s3:begin
if(row == 4'b1111) //按鍵抬起
begin
state <= s0;
data_flag <= 1'b1; //表示一次成功的按鍵,輸出一個(gè)高脈沖
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
//計(jì)算模塊的表示
reg [2:0] state_s; //狀態(tài)變量
reg [23:0] num1,num2,data_in,data_t; //信號(hào)變量
reg [3:0]flag_s; //運(yùn)算符
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碼轉(zhuǎn)為2進(jìn)制
data <= {data[19:0],key_num}; //數(shù)碼管移位
end
if(key_num > 4'd9 && key_num < 4'd14) //10 -- 13 表示運(yùn)算符
begin
data <= 24'b0;
state_s <= s1;
flag_s <= key_num;
end
else //否則無(wú)效信號(hào)
state_s <= s0;
end
end
s1:begin
if(data_flag)//如果有一次按下
begin
if(key_num <4'd9 ) //鍵值小于9便是有效
begin
num2 <= 10*num2 +key_num;//BCD碼轉(zhuǎn)為2進(jìn)制
data <= {data[19:0],key_num};//數(shù)碼管移位
end
if(key_num > 4'd9 && key_num < 4'd14)//10 -- 13 表示運(yùn)算符
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 //加運(yùn)算
data_in <= num1 + num2;
state_s <= s3;
end
4'd13 :begin //乘運(yùn)算
data_in <= num1 * num2;
state_s <= s3;
end
endcase
end
s3:begin //二進(jìn)制轉(zhuǎn)為BCD碼顯示到對(duì)應(yīng)的數(shù)碼管上
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
end
endmodule
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
end
endmodule
仿真測(cè)試
測(cè)試模塊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
仿真圖:
從仿真圖中可以看出,在仿真中我們?cè)O(shè)置的是先按下5,再10,之后2,然后按下等于15.通過(guò)觀察仿真正確,之后由于設(shè)計(jì)中我們10是表示加法,那么5 + 2 = 7 :結(jié)果顯示正確。