2024寒假练-基于STEP BaseBoard V4.0实现的两位十进制加、减、乘、除计算器
该项目使用了STEP BaseBoard V4.0硬件平台、Lattice Diamond软件、Vrerilog语言,实现了两位十进制加、减、乘、除计算器的设计,它的主要功能为:通过4x4键盘来控制运算数和运算符的输入,按键运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。能够实现非负的不超过两位的小数、整数的加减乘除运算。
标签
FPGA
verilog
2024寒假在家一起练
小脚丫STEP BaseBoard V4.0
风飒木萧
更新2024-04-02
北京交通大学
263

一、项目介绍

本项目需实现一个两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制,4x4按键分配如下图所示。

运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管伤上,低位在刚才高位占据的数码管上显示。

二、设计思路

1.键盘输入

开发板上的矩阵键盘电路是通过4根行线(ROW1、ROW2、ROW3、ROW4)和4根列线(COL1、COL2、COL3、COL4)连接在一起的,其硬件电路图如下图所示。

image.png

对于矩阵按键来讲,4根行线是输入的,由FPGA控制拉高或者拉低;4根列线是输出的,由4根行线的输入及按键的状态决定,输出给FPGA。

在某一时刻,当ROW1=0、ROW2=1、ROW3=1、ROW4=1时,按下K1,对应的COL1=0,而COL2=1、COL3=1、COL4=1;按下K2,对应的COL2=0,而COL1=1,COL3=1,COL4=1。K3、K4同理。而对于K5~K16之间的按键,无论是否按下,4根列线输出都为1。

因此,当第一行被拉低时,如果检测到第一列输出低电平,则说明K1被按下;同理,当第三行被拉低时,如果检测到第3列输出低电平,则说明K11被按下。

因此我们可以按照扫描的方式,分为4个状态,分别对应行线中的某一根被拉低,然后检测其中的列线输出。4个状态依次循环,就完成了矩阵按键的全部扫描检测。其流程示意如下图所示。

image.png

检测出按下的按键后,按照要求中的按键分配进行相应的编码,就可以将按键转换成运算符与运算数。而对于运算数和运算符整个输入过程来说,又需要另一状态机来完成读入,其逻辑示意如下图所示。

image.png

2. 加法器实现

首先需要对两个运算数的类型进行判断,情况共分为4种:

(1)运算数1运算数2都为整数;

(2)运算数1为整数,运算数2为小数;

(3)运算数1为小数,运算数2为整数;

(4)运算数1运算数2都为整数。

在(1)和(4)中,我们将寄存器中的值进行转换后,直接进行相加即可。注意(4)中存在1位小数,要将对应的数码管小数点使能。数值转换即实际值=寄存器高4位*10+寄存器低4位。

对于(2)和(3),我们则需要将整数扩大十倍再进行计算。如4+2.5,实际值转换完分别为4和25,我们将4扩大十倍得到40,相加得到65,取一位小数位6.5,这样就得到了正确的结果。

3. 减法器实现

减法器的实现方式与加法器一致。但是因为时间和能力所限,没能实现输入输出涉及负数的相关运算,因此减法只能处理运算数1大于运算数2的情况。

4. 乘法器实现

乘法器的实现较为简单,结果直接为两个数相乘的结果。然后根据运算数情况算好小数点即可:

(1) 若运算数1、2都为整数,无小数点;

(2) 其中一个运算数为小数,结果1位小数;

(3) 若运算数1、2都为小数,结果2位小数。

5. 除法器实现

(1)若运算数1、2都为整数,我们将运算数1(被除数)扩大100倍,再做整除,取两位小数。特别的,如果运算数1小于运算数2的话,我们需要将两位结果的前一位数码管显示0使能。

(2)若运算数1为小数,运算数2为整数,则需要将运算数1扩大10倍,再做整除,取两位小数。因为我们对于小数的存储,如2.5,是分别存储2和5以及小数点标记。转换成实际值实际上是25和小数点标记。因此如果我们要计算2.5÷2=1.25,则实际上是250/2=125,再加两位小数得到的。

(3)若运算数1为整数,运算数2为小数,则需要将运算数1扩大1000倍,再进行运算。

(4)若运算数1和运算数2都为整数,将运算数1扩大100倍,再进行计算。

6.结果显示

我们所有的计算结果,都是以16进制的形式储存在寄存器中的。因此我们需要将结果转换成BCD码后,再进行数码管扫描输出。这里采用经典的加3移位法。

例如,以1110(十进制14)为例:

我们取一个寄存器,首先送入结果最高位1,得到0001;然后送入第二位1,得到0011;然后送入第三位1,得到0111,这时,0111>0100(大于等于十进制的5,即大于十进制的4),我们进行修正,即0111+0011=1010;然后再送入最后一位0,得到10100,即0001_0100,这就是1110的BCD码。这样,我们就可以正常显示。

三、功能框图

image.png

四、核心代码

键盘输入读取及计算模块

always@(posedge clk or negedge rst_n)
begin
if(!rst_n)begin//复位,所有寄存器清空
state=STATE0;
seg_data1=8'b0;
seg_data2=8'b0;
ope=3'b0;
num1=16'b0;
num2=16'b0;
seg_out=16'b0;
flag=2'b0;
dot_en=8'b0;
dat=8'b0;
end else begin
case(state)
STATE0:begin
flag[0]=1'b0;//运算数1的小数点标记
case(key_pulse)
16'h0001: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h7;state = STATE1;end//输入运算数1低位
16'h0002: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h8;state = STATE1;end
16'h0004: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h9;state = STATE1;end
16'h0010: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h4;state = STATE1;end
16'h0020: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h5;state = STATE1;end
16'h0040: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h6;state = STATE1;end
16'h0100: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h3;state = STATE1;end
16'h0200: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h2;state = STATE1;end
16'h0400: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h1;state = STATE1;end
16'h1000: begin seg_data1[7:4]=4'h0;seg_data1[3:0] = 4'h0;dat[2]=1'b1;state = STATE1;end//输入为0的时候,要特别的将该位使能置高
default: ;
endcase
end
STATE1:begin
case(key_pulse)
16'h0001: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h7;end//输入运算数1高位
16'h0002: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h8;end
16'h0004: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h9;end
16'h0010: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h4;end
16'h0020: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h5;end
16'h0040: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h6;end
16'h0100: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h3;end
16'h0200: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h2;end
16'h0400: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h1;end
16'h1000: begin seg_data1[7:4] = flag[0]?seg_data1[7:4]:seg_data1[3:0];seg_data1[3:0] = 4'h0;dat[2]=1'b1;end
16'h2000: begin seg_data1[7:4] = seg_data1[3:0];seg_data1[3:0]=0;flag[0]=1'b1;dot_en[3]=1'b1;dat[3]=1'b1;dat[2]=1'b0;end
16'h0008:begin ope = divi ;state=STATE2;end//存储运算符
16'h0080:begin ope = multi;state=STATE2;end
16'h0800:begin ope = minus;state=STATE2;end
16'h8000:begin ope = plus ;state=STATE2;end
default: ;
endcase
end
STATE2:begin
flag[1]=1'b0;
case(key_pulse)
16'h0001: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h7;state = STATE3;end //输入运算数2低位
16'h0002: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h8;state = STATE3;end
16'h0004: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h9;state = STATE3;end
16'h0010: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h4;state = STATE3;end
16'h0020: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h5;state = STATE3;end
16'h0040: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h6;state = STATE3;end
16'h0100: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h3;state = STATE3;end
16'h0200: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h2;state = STATE3;end
16'h0400: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h1;state = STATE3;end
16'h1000: begin seg_data2[7:4]=4'h0;seg_data2[3:0] = 4'h0;dat[0]=1'b1;state = STATE3;end
default: ;
endcase
end
STATE3:begin
case(key_pulse)
16'h0001: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h7;end //输入运算数2高位
16'h0002: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h8;end
16'h0004: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h9;end
16'h0010: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h4;end
16'h0020: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h5;end
16'h0040: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h6;end
16'h0100: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h3;end
16'h0200: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h2;end
16'h0400: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h1;end
16'h1000: begin seg_data2[7:4] = flag[1]?seg_data2[7:4]:seg_data2[3:0];seg_data2[3:0] = 4'h0;dat[0]=1'b1;end
16'h2000: begin seg_data2[7:4] = seg_data2[3:0];seg_data2[3:0]=0;flag[1]=1'b1;dot_en[1]=1'b1;dat[1]=1'b1;dat[0]=1'b0;end
16'h4000:begin
num1[15:0]=seg_data1[7:4]*10+seg_data1[3:0];
num2[15:0]=seg_data2[7:4]*10+seg_data2[3:0];
case(ope)
plus :begin
case(flag)
2'b00:begin seg_out[15:0]=num1[15:0]+num2[15:0];end
2'b01:begin seg_out[15:0]=num1[15:0]+num2[15:0]*10;dot_en[5]=1'b1;end
2'b10:begin seg_out[15:0]=num1[15:0]*10+num2[15:0];dot_en[5]=1'b1;end
2'b11:begin seg_out[15:0]=num1[15:0]+num2[15:0];dot_en[5]=1'b1;end
default:;
endcase
end
minus:begin
case(flag)
2'b00:begin seg_out[15:0]=num1[15:0]-num2[15:0];end
2'b01:begin seg_out[15:0]=num1[15:0]-num2[15:0]*10;dot_en[5]=1'b1;dat[5]=1'b1;end
2'b10:begin seg_out[15:0]=num1[15:0]*10-num2[15:0];dot_en[5]=1'b1;dat[5]=1'b1;end
2'b11:begin seg_out[15:0]=num1[15:0]-num2[15:0];dot_en[5]=1'b1;dat[5]=1'b1;end
default:;
endcase
end
multi:begin
seg_out[15:0]=num1[15:0]*num2[15:0];
case(flag)
2'b01:dot_en[5]=1'b1;
2'b10:dot_en[5]=1'b1;
2'b11:dot_en[6]=1'b1;
default:;
endcase
end
divi:begin
case(flag)
2'b00:
begin
seg_out[15:0]=(num1[15:0]*100)/num2[15:0];
dot_en[6]=1'b1;
if(num1[15:0]<num2[15:0])
dat[6]=1'b1;
end
2'b01:
begin
seg_out[15:0]=(num1[15:0]*10)/num2[15:0];
dot_en[6]=1'b1;
dat[6]=1'b1;
end
2'b10:
begin
seg_out[15:0]=(num1[15:0]*1000)/num2[15:0];
dot_en[6]=1'b1;
dat[6]=1'b1;
end
2'b11:
begin
seg_out[15:0]=(num1[15:0]*100)/num2[15:0];
dot_en[6]=1'b1;
dat[6]=1'b1;
end
default:;
endcase
end
default:;
endcase
state=STATE0;
if(seg_out==0)
dat[4]=1'b1;
end
endcase
end
default:;

endcase

end
end

HEX2BCD模块

always@(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
ge=4'b0;
shi=4'b0;
bai=4'b0;
qian=4'b0;
seg_ans=16'b0;
end
else begin
ge=4'b0;
shi=4'b0;
bai=4'b0;
qian=4'b0;
seg_ans=16'b0;
for(i=15;i>=0;i=i-1)
begin
if(ge[3:0]>=4'd5)ge[3:0]=ge[3:0]+4'd3;
if(shi[3:0]>=4'd5)shi[3:0]=shi[3:0]+4'd3;
if(bai[3:0]>=4'd5)bai[3:0]=bai[3:0]+4'd3;
if(qian[3:0]>=4'd5)qian[3:0]=qian[3:0]+4'd3;

qian={qian[2:0],bai[3]};
bai={bai[2:0],shi[3]};
shi={shi[2:0],ge[3]};
ge={ge[2:0],seg_out[i]};
end
seg_ans={qian,bai,shi,ge};
end
end

五、资源使用

image.png

image.png


附件下载
calculator_impl1.jed
可直接下载到Lattice的u盘中验证
calculator.rar
所有项目文件
团队介绍
20级本科生
团队成员
风飒木萧
北京交通大学电子信息工程学院
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号