设计背景:
计算器是设计中经常用到的一个操作软件,设计和学习计算器使我们亲密的联系所学的各模块, 对我们的学习有很大的帮助和提升。希望大家来学习
设计原理:
本次的设计主要通过矩阵键盘来实现按键的加减乘除运算,通过按下有效键值来当被加数或者被除数等等,按下10 -- 13等数字来表示对应的运算符。按键键值15表示等于号。
此次的设计是通过数码管来实现显示的,通过按下对应的按键来显示到数码管上,百位十位个位等等。当按下运算算符的时候显示清0不显示东西,之后通过继续按下别的键值来显示出对应的加数和除数等等,之后通过按下对应的键值15表示等于后,然后数码管清0之后立马显示出对应的等于的数。
这样来完成我们此次的设计。
设计架构图:
设计代码:
顶层模块
0modulecalc(clk,rst_n,row,col,sel,seg7); //端口列表
1 inputclk; //时钟
2 inputrst_n; //复位
3 input[3:0]row; //行信号
4
5 output[3:0]col; //列信号
6 output[2:0]sel; //数码管位选信号
7 output[7:0]seg7; //数码管段选信号
8
9 wire[23:0]data;
10
11 //例化数码管模,和矩阵键盘模块
12 key_borad key_borad_dut(
13 .clk(clk),
14 .rst_n(rst_n),
15 .row(row),
16 .col(col),
17 .data(data)
18 );
19 seg seg_dut(
20 .clk(clk),
21 .rst_n(rst_n),
22 .sel(sel),
23 .seg7(seg7),
24 .data_in(data)
25 );
26
27endmodule
设计模块
0modulekey_borad(clk,rst_n,row,col,data);
1 inputclk; //时钟 50M
2 inputrst_n; //复位
3 input[3:0]row; //输入行信号
4
5 outputreg[3:0]col; //输出列信号
6 outputreg[23:0]data;
7
8 //状态变量,表示
9 parameters0 =3'b00;
10 parameters1 =3'b01;
11 parameters2 =3'b10;
12 parameters3 =3'b11;
13 parameters4 =3'b100;
14 parameters5 =3'b101;
15
16 parameterT1ms =50000;//扫描间隔
17 //parameter T1ms = 2;
18 parameterT10ms=500_000;//按键消抖时间
19 //parameter T10ms = 20;
20
21 wireflag;
22 reg[15:0]count;
23 always@(posedgeclk ornegedgerst_n)
24 if(!rst_n)
25 begin
26 count <=16'd0;
27 end
28 else
29 begin
30 if(count <T1ms -1) //计数1K的频率时间
31 count <=count +1'b1;
32 else
33 begin
34 count <=16'b0;
35 end
36 end
37
38 assignflag =(count ==T1ms -1)?1'b1:1'b0; //计数到了就给一个高脉冲,反之低脉冲
39
40 reg[2:0]state;
41 reg[7:0]row_col;
42 reg[18:0]cnt;
43 regdata_flag;
44 always@(posedgeclk ornegedgerst_n)
45 if(!rst_n)
46 begin
47 state <=3'b0;
48 row_col <=8'b1111_1111;
49 data_flag <=1'b0;
50 col <=4'b0000;
51 cnt <=19'b0;
52 end
53 else
54 begin
55 case(state)
56 s0:begin
57 if(row ==4'b1111) //如果没有按下
58 begin
59 data_flag <=1'b0;
60 col <=4'b0000;
61 end
62 else //表示按下,跳转下一个状态
63 begin
64 data_flag <=1'b0;
65 state <=s1;
66 end
67 end
68 s1:begin
69 if(row ==4'b1111) //如果是抖动跳转0状态
70 begin
71 cnt <=19'b0;
72 state <=s0;
73 end
74 else
75 begin
76 if(cnt <T10ms -1) //计数相应的时间,消抖处理
77 begin
78 cnt <=cnt +1'b1;
79 end
80 else
81 begin
82 cnt <=19'b0;
83 state <=s2;
84 col <=4'b0111;//消抖完表示按键有效
85 end
86 end
87 end
88 s2:begin
89 if(row !=4'b1111) //表示导通
90 begin
91 state <=s3; //导通后跳转下一个状态
92 row_col <={row,col}; //拼接行和列信号
93 end
94 else //行信号不导通,开始进行列扫描
95 begin
96 if(flag)
97 begin
98 col <={col[2:0],col[3]}; //1ms进行一次列扫描
99 end
100 else
101 begin
102 col <=col;
103 end
104 end
105 end
106 s3:begin
107 if(row ==4'b1111) //按键抬起
108 begin
109 state <=s0;
110 data_flag <=1'b1; //表示一次成功的按键,输出一个高脉冲
111 end
112 else
113 begin
114 state <=s3;
115 end
116 end
117 default:state <=s0;
118 endcase
119 end
120
121 reg[3:0]key_num;
122 //键值的翻译模块的表示
123 always@(posedgeclk ornegedgerst_n)
124 if(!rst_n)
125 key_num =4'd0;
126 else
127 case({row_col})
128 8'b0111_0111:key_num =4'hf;
129 8'b0111_1011:key_num =4'he;
130 8'b0111_1101:key_num =4'hd;
131 8'b0111_1110:key_num =4'hc;
132
133 8'b1011_0111:key_num =4'hb;
134 8'b1011_1011:key_num =4'ha;
135 8'b1011_1101:key_num =4'h9;
136 8'b1011_1110:key_num =4'h8;
137
138 8'b1101_0111:key_num =4'h7;
139 8'b1101_1011:key_num =4'h6;
140 8'b1101_1101:key_num =4'h5;
141 8'b1101_1110:key_num =4'h4;
142
143 8'b1110_0111:key_num =4'h3;
144 8'b1110_1011:key_num =4'h2;
145 8'b1110_1101:key_num =4'h1;
146 8'b1110_1110:key_num =4'h0;
147 default:;
148 endcase
149
150
151
152 //计算模块的表示
153 reg[2:0]state_s;//状态变量
154 reg[23:0]num1,num2,data_in,data_t; //信号变量
155 reg[3:0]flag_s;//运算符
156 always@(posedgeclk ornegedgerst_n)
157 begin
158 if(!rst_n)
159 begin
160 data <=24'b0;
161 state_s <=s0;
162 num1 <=24'b0;
163 num2 <=24'b0;
164 data_t <=24'b0;
165 flag_s <=4'b0;
166 data_in <=24'b0;
167 end
168 else
169 begin
170 case(state_s)
171 s0:begin
172 if(data_flag) //如果有一次按下
173 begin
174 if(key_num <4'd9)//键值小于9便是有效
175 begin
176 num1 <=num1*10+key_num;//BCD码转为2进制
177 data <={data[19:0],key_num};//数码管移位
178 end
179 if(key_num >4'd9&&key_num <4'd14)//10 -- 13 表示运算符
180 begin
181 data <=24'b0;
182 state_s <=s1;
183 flag_s <=key_num;
184 end
185 else//否则无效信号
186 state_s <=s0;
187 end
188 end
189 s1:begin
190 if(data_flag)//如果有一次按下
191 begin
192 if(key_num <4'd9)//键值小于9便是有效
193 begin
194 num2 <=10*num2 +key_num;//BCD码转为2进制
195 data <={data[19:0],key_num};//数码管移位
196 end
197 if(key_num >4'd9&&key_num <4'd14)//10 -- 13 表示运算符
198 begin
199 state_s <=s1;
200 end
201 if(key_num ==15)//表示等于
202 begin
203 state_s <=s2;
204 end
205 end
206 end
207 s2:begin
208 state_s <=s3;
209 case(flag_s)
210
211 4'd10:begin//加运算
212 data_in <=num1 +num2;
213 state_s <=s3;
214 end
215
216 4'd13:begin//乘运算
217 data_in <=num1 *num2;
218 state_s <=s3;
219 end
220 endcase
221 end
222 s3:begin//二进制转为BCD码显示到对应的数码管上
223 data[3:0]=data_in %10;
224 data[7:4]=data_in /10%10;
225 data[11:8]=data_in /100%10;
226 data[15:12]=data_in /1000%10;
227 data[19:16]=data_in /10000%10;
228 data[23:20]=data_in /100000;
229 state_s <=s0;
230 data_in <=24'b0;
231 end
232 default:state_s <=s0;
233 endcase
234 end
235 end
236
237
258endmodule
测试模块
0`timescale1ns/1ps
1
2modulecalc_tb();
3 regclk;
4 regrst_n;
5 reg[4:0]pressnum;
6 wire[3:0]row;
7
8 wire[3:0]col;
9 wire[3:0]key_num;
10
11 initialbegin
12 clk =1'b1;
13 rst_n =1'b0;
14 pressnum =5'd16;
15
16 #200.1
17 rst_n =1'b1;
18 #2000
19 pressnum =5'd16;
20
21 #1000
22 pressnum =5'd5;
23
24 #1000
25 pressnum =5'd16;
26
27 #1250
28 pressnum =5'd11;
29 #1250
30 pressnum =5'd16;
31 #1250
32 pressnum =5'd2;
33 #1250
34 pressnum =5'd16;
35 #1250
36 pressnum =5'd15;
37 #1250
38 pressnum =5'd16;
39 #2000
40 #2000
41 $stop;
42
43 end
44 always#10clk =~clk;
45
46 calc calc_dut(
47 .clk(clk),
48 .rst_n(rst_n),
49 .row(row),
50 .col(col),
51 .sel(sel),
52 .seg7(seg7)
53 );
54 yingjian yingjian_dut(
55 .clk(clk),
56 .rst_n(rst_n),
57 .col(col),
58 .row(row),
59 .pressnum(pressnum)
60 );
61endmodule
仿真图:
从仿真图中可以看出,在放着中我们设置的是先按下5,再10,之后2,然后按下等于15.通过观察仿真正确,之后由于设计中我们10是表示加法,那么5 + 2 = 7 :结果显示正确。