2024年寒假练 - 基于小脚丫FPGA套件STEP BaseBoard V4.0实现两位十进制加、减、乘、除计算器
该项目使用了小脚丫FPGA套件STEP BaseBoard V4.0,实现了两位十进制加、减、乘、除计算器的设计,它的主要功能为:两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制。。
标签
嵌入式系统
计算器
2024年寒假在家一起练
小脚丫FPGA套件
常钰豪
更新2024-03-29
170

1 项目需求

1.1 任务要求

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

1.2 基本要求

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

1.3 扩展要求

使用TFTLCD来做显示,自行设计显示界面,代替数码管显示输入的参数、运算符以及计算后的结果。

image.png

2 需求分析

使用FPGA编程,按下如下图所示的键盘映射(由于项目需求中数字3和1位置与日常使用习惯由差异,故调换位置),输入键盘按键映射信息(0123456789+-*/.),进行计算然后计算结果通过数码管显示,计算完毕或误操可使用左侧复位按键复位。

键位映射.png

3 实现的方式

顶层文件整个系统的输入和输出并链接各模组(calculator),通过行和列扫描判断按键位置完成矩阵键盘输入,用矩阵键盘输入数字和计算符号(array_keyboard),状态机和寄存器的配合实现加减乘除功能。通过对输入前(如:加数),输入中(如:被加数),输入后(如:结果)的三种状态并存储在寄存器内部,实现计算功能得到8位16进制的结果(calculate),通过BCD转换得到10进制结果(bin_bcd),使用74HC595芯片段和位扫描的方式驱动数码管显示(segment_scan)。

image.png

4 功能框图

4.1 系统整体设计框图

系统整体由四个模块构成,分别为array_keyboard,calculate,bin_bcd,segment_scan,array_keyboard模块负责数字,运算符的输入,calculate负责输入后数据进行加减乘除等计算处理,bin_bcd负责calculate处理后的数据的转码,segment_scan负责数码管的显示。

4.2 计算模块状态机框图

image.png

5 主要代码

5.1 顶层模块

 顶层模块的功能调动整个系统的输入和输出并链接各模组。(代码的具体功能看代码注释)

module calculator(
input clk, //系统时钟 12MHz
input rst_n, //系统复位 低有效
input [3:0] col,
output [3:0] row,
output seg_rck, //74HC595的RCK管脚
output seg_sck, //74HC595的SCK管脚
output seg_din //74HC595的SER管脚
);
//寄存器定义
wire [15:0] key_out;
wire [15:0] key_pulse; //键盘按键脉冲
wire [1:0] state; //状态位
wire [3:0] operator; //运算符
wire [15:0] operand1; //操作数1
wire [7:0] operand1_dot;//操作数1小数点
wire [15:0] operand2; //操作数2
wire [7:0] operand2_dot;//操作数2小数点
wire [7:0] seg_sel;
wire [7:0] seg_sel1; //位选择使能
wire [31:0] seg_data; //BCD转码结果
//Array_KeyBoard 按键
array_keyboard u1(
.clk(clk),
.rst_n(rst_n),
.col(col),
.row(row),
.key_out(key_out),
.key_pulse(key_pulse)
);
//calculator 计算
calculate u2(
.clk (clk),
.rst_n (rst_n),
.key_pulse (key_pulse),
.operand1 (operand1),
.operand2 (operand2),
.operand1_dot (operand1_dot),
.seg_sel (seg_sel),
.seg_sel1 (seg_sel1),
.operand2_dot (operand2_dot),
.state (state),
.key_data (key_data),
.operator (operator)
);
//bcd 转码
bin_to_bcd u3(
.rst_n(rst_n),
.bin_code(operand2),
.bcd_code(seg_data)
);
//segment_scan display module 数码管显示
segment_scan u4(
.clk(clk), //系统时钟 12MHz
.rst_n(rst_n), //系统复位 低有效
.dat_1(seg_data[31:28]), //SEG1 显示的数据输入
.dat_2(seg_data[27:24]), //SEG2 显示的数据输入
.dat_3(seg_data[23:20]), //SEG3 显示的数据输入
.dat_4(seg_data[19:16]), //SEG4 显示的数据输入
.dat_5(seg_data[15:12]), //SEG5 显示的数据输入
.dat_6(seg_data[11:8]), //SEG6 显示的数据输入
.dat_7(seg_data[7:4]), //SEG7 显示的数据输入
.dat_8(seg_data[3:0]), //SEG8 显示的数据输入
.dat_en(seg_sel1), //数码管数据位显示使能,[MSB~LSB]=[SEG1~SEG8]
.dot_en(operand2_dot), //数码管小数点位显示使能,[MSB~LSB]=[SEG1~SEG8]
.seg_rck(seg_rck), //74HC595的RCK管脚
.seg_sck(seg_sck), //74HC595的SCK管脚
.seg_din(seg_din) //74HC595的SER管脚
);
endmodule

5.2 键盘模块

按键数量较多,为了减少I/O口的占用,将按键排列成矩阵形式,使用行和列分别连接到按键开关的两端,通过4根行和4根列线连接16个按键,按照扫描的方式,一共分为4个时刻,分别对应4根行线中的一根拉低,4个时刻依次循环,这样就完成了矩阵按键的全部扫描检测, 4×4矩阵键盘模块使用小脚丫STEP开源社区的例程,下面附上源地址:

https://www.stepfpga.com/doc/%E7%9F%A9%E9%98%B5%E6%8C%89%E9%94%AE%E6%A8%A1%E5%9D%97

5.3 计算模块

通过状态机和寄存器的配合实现加减乘除功能。通过对输入前(加数),输入中(被加数),输入后(结果)的三种状态并存储在寄存器内部,实现计算功能。

module calculate( 
input clk,
input rst_n,
input [15:0] key_pulse, //按键消抖后动作脉冲信号//
output reg [15:0] operand1,
output reg [15:0] operand2,
output reg [7:0] operand1_dot,
output reg [7:0] operand2_dot,
output reg [7:0] seg_sel,
output reg [7:0] seg_sel1,
output reg [1:0] state,
output reg [3:0] key_data,
output reg [3:0] operator
);
parameter S0 = 4'h0;
parameter S1 = 4'h1;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_data = 4'h0;
operator = 4'ha;
operand1 = 16'h00;
operand1_dot = 4'h0;
seg_sel = 4'h1;
seg_sel1 = 4'h0;
operand2 = 16'h00;
operand2_dot = 4'h0;
state = S0;
end
else if(key_pulse!=0) begin //对应键码值
case(key_pulse)
16'h1000: begin key_data = 4'h0; end //0
16'h0100: begin key_data = 4'h1; end //1
16'h0200: begin key_data = 4'h2; end //2
16'h0400: begin key_data = 4'h3; end //3
16'h0010: begin key_data = 4'h4; end //4
16'h0020: begin key_data = 4'h5; end //5
16'h0040: begin key_data = 4'h6; end //6
16'h0001: begin key_data = 4'h7; end //7
16'h0002: begin key_data = 4'h8; end //8
16'h0004: begin key_data = 4'h9; end //9
16'h8000: begin key_data = 4'ha; end //+
16'h0800: begin key_data = 4'hb; end //-
16'h0080: begin key_data = 4'hc; end //*
16'h0008: begin key_data = 4'hd; end ///
16'h2000: begin key_data = 4'he; end //.(非运算特殊情况)
16'h4000: begin key_data = 4'hf; end //= (state特殊情况)
default : begin key_data = key_data;end//无按键按下时保持
endcase
case(state)//进入状态机
S0: begin //
if(key_data <= 4'h9) begin//输入值小于4'h9即为0-9之间任意一位数,输入操作数1
operand2 = operand2 * 10 + key_data;//移位操作
seg_sel1 = seg_sel1 + seg_sel;//位选显示操作操作
seg_sel = seg_sel * 2 ;
if(operand2_dot) operand2_dot = operand2_dot * 2;//小数点计算操作
state = S0;
end
else if(key_data >= 4'ha && key_data <= 4'hd) begin // +-*/进行+-*/等操作
seg_sel1 = 0;
seg_sel = 1;
operator = key_data;
state = S1;
operand1 = operand2;
operand1_dot = operand2_dot;
operand2_dot = 0;
operand2 = 0;
end
else if(key_data==4'hf) begin // 输入=返回状态0
state = S0;
end
end
S1: begin
if(key_data<=4'h9) begin//输入操作数2
operand2 = operand2 * 10 + key_data;
seg_sel1 = seg_sel1 + seg_sel;
seg_sel = seg_sel * 2 ;
if(operand2_dot) operand2_dot = operand2_dot * 2;
state = S1;
//if(operand1_dot) operand1_dot = operand1_dot+1;
end
else if(key_data>=4'ha && key_data<=4'hd) begin // +-*/
operator = key_data;
state = S1;
end
else if(key_data==4'hf) begin //计算代码块
seg_sel1 = 0;
seg_sel = 0;
case(operator)//通过operator=4'ha 4'hb 4'hc 4'hd判断加减乘除运算
4'ha:begin
if(operand2_dot == 4'b0) begin //不输入小数点进行加法计算
operand2 = operand1 + operand2;
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin//计算结果如果大于0小于9显示1位
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin//计算结果如果大于10小于99显示2位
seg_sel1 = 8'b00000011;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin //计算结果如果大于100小于999显示3位
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h2710) begin//计算结果大于1000小于9999显示4位
seg_sel1 = 8'b00001111;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin//计算结果大于10000小于99999显示5位
seg_sel1 = 8'b00011111;
end
end
else if(operand2_dot == 4'b10) begin //输入1位小数点进行加法计算
operand2 = ((operand2) + operand1);//计算结果由两位操作数相加

//数码管位显示
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin//计算结果如果大于0小于9显示1位
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin//计算结果如果大于10小于99显示2位
seg_sel1 = 8'b00000011;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin //计算结果如果大于100小于999显示3位
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h2710) begin//计算结果大于1000小于9999显示4位
seg_sel1 = 8'b00001111;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin//计算结果大于10000小于99999显示5位
seg_sel1 = 8'b00011111;
end
end
else if(operand2_dot == 4'b100) begin //输入两位小数点进行加法运算 以下同理
operand2 = ((operand2) + operand1);
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h2710) begin
seg_sel1 = 8'b00001111;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin
seg_sel1 = 8'b00011111;
end
end
end
4'hb:begin
if(operand2_dot == 4'b0) begin //不输入小数点进行减法运算以下同理
operand2 = operand1 - operand2;
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h270f) begin
seg_sel1 = 8'b00001111;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin
seg_sel1 = 8'b00011111;
end
end
else if(operand2_dot == 4'b10) begin //输入1位小数点进行减法运算以下同理
operand2 = operand1 - operand2;
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h270f) begin
seg_sel1 = 8'b00001111;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin
seg_sel1 = 8'b00011111;
end
end
else if(operand2_dot == 4'b100) begin//输入两位小数点进行减法运算以下同理
operand2 = operand1 - operand2;
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h270f) begin
seg_sel1 = 8'b00001111;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin
seg_sel1 = 8'b00011111;
end
end
else if(operand2_dot == 4'b1000) begin
operand2 = operand1 - operand2;
seg_sel1 = 8'b00001111;
end
end
4'hc:begin
if(operand2_dot == 4'b0) begin
operand2 = operand1 * operand2;//输入两位小数点进行乘法运算以下同理
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h270f) begin
seg_sel1 = 8'b00001111;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin
seg_sel1 = 8'b00011111;
end
end
else if(operand2_dot == 4'b10 && operand1_dot == 4'b10) begin
operand2 = operand1 * operand2;
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
operand2_dot = 4'b10;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
operand2_dot = 4'b100;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h2710) begin
seg_sel1 = 8'b00001111;
operand2_dot = 4'b100;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin
seg_sel1 = 8'b00011111;
operand2_dot = 4'b100;
end
end
else if(operand2_dot == 4'b10) begin
operand2 = operand1 * operand2;
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
operand2_dot = 4'b10;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
operand2_dot = 4'b10;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h2710) begin
seg_sel1 = 8'b00001111;
operand2_dot = 4'b100;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin
seg_sel1 = 8'b00011111;
operand2_dot = 4'b100;
end
end
else if(operand1_dot == 4'b10) begin
operand2 = operand1 * operand2;
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
operand2_dot = 4'b10;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
operand2_dot = operand1_dot;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h270f) begin
seg_sel1 = 8'b00001111;
operand2_dot = 4'b100;
end
else if(operand2 >= 16'h2710 && operand2 <= 16'hffff) begin
seg_sel1 = 8'b00011111;
operand2_dot = 4'b100;
end
end
end
4'hd:begin
if(operand1 % operand2) begin
operand2 = ((operand1 *10 ) / operand2);//除法不被整除
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
operand2_dot = 4'b10;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h2710) begin
seg_sel1 = 8'b00001111;
end
end
else begin
operand2 = (operand1 / operand2);//除法显示整数
if(operand2 >= 16'h0 && operand2 <= 16'h9) begin
seg_sel1 = 8'b00000001;
end
else if(operand2 >= 16'ha && operand2 <= 16'h63) begin
seg_sel1 = 8'b00000011;
end
else if(operand2 >= 16'h64 && operand2 <= 16'h3e7) begin
seg_sel1 = 8'b00000111;
end
else if(operand2 >= 16'h3e8 && operand2 <= 16'h2710) begin
seg_sel1 = 8'b00001111;
end
end
end
endcase
state = S0;
end
else if(key_data==4'he) begin //.
operand2_dot = 4'b1;
state = S1;
end
end
endcase //end
end
end
endmodule

5.4 转码模块

使用满5加3操作把计算模块的4位16进制数转换成BCD码输出,在本项目中将输出的计算结果转BCD码程序实现如下:

module bin_to_bcd(
rst_n,
bin_code,
bcd_code
);
parameter NUM_WID = 16 ;
parameter result_bcd_wid = 32 ;
input rst_n ;
input [NUM_WID-1:0] bin_code ;//输入4位16进制数
output [result_bcd_wid-1:0] bcd_code ;//输出BCD码
reg [result_bcd_wid-1:0] bcd_code ; ;
reg [47:0] shift_reg ;
//BCD码数据满5加3
always@(bin_code or rst_n)begin
shift_reg = {32'h0,bin_code};
if(!rst_n)
bcd_code = 0;
else begin
repeat(16) begin
if(shift_reg[19:16] >= 5) shift_reg[19:16] = shift_reg[19:16] + 2'b11;
if(shift_reg[23:20] >= 5) shift_reg[23:20] = shift_reg[23:20] + 2'b11;
if(shift_reg[27:24] >= 5) shift_reg[27:24] = shift_reg[27:24] + 2'b11;
if(shift_reg[31:28] >= 5) shift_reg[31:28] = shift_reg[31:28] + 2'b11;
if(shift_reg[35:32] >= 5) shift_reg[35:32] = shift_reg[35:32] + 2'b11;
if(shift_reg[39:36] >= 5) shift_reg[39:36] = shift_reg[39:36] + 2'b11;
if(shift_reg[43:40] >= 5) shift_reg[43:40] = shift_reg[43:40] + 2'b11;
if(shift_reg[47:44] >= 5) shift_reg[47:44] = shift_reg[47:44] + 2'b11;
shift_reg = shift_reg << 1;
end
bcd_code = shift_reg[47:16];
end
end
endmodule

5.5 显示模块

为了减少I/O口的使用,FPGA通过控制段选接口和位选接口,以扫描的方式在每个时刻只有一位数码管处于使能状态,依次显示每一位数字。这个过程需要循环进行,当扫描频率足够高时,人眼看到的就是连续的数字显示,并引入了74HC595芯片,这是一款串行转并行的驱动芯片,可以通过3个I/O口控制得到8个并行输出信号,而且可以级联使用,从而轻松完成数码管的驱动任务。

驱动数码管基于74HC595的芯片,代码使用小脚丫STEP开源社区的例程,下面附上源地址:

https://www.stepfpga.com/doc/%E6%95%B0%E7%A0%81%E7%AE%A1%E6%A8%A1%E5%9D%97

6 仿真波形图

6.1 array_keyboard模块

测试代码和波形图如下:

`timescale 1ns/1ps
module tb();
reg clk;
reg rst_n;
reg [3:0] col;
wire [3:0] row;
wire [15:0] key_pulse;
// 实例化array_keyboard模块
array_keyboard uut (
.clk(clk),
.rst_n(rst_n),
.col(col),
.row(row),
.key_pulse(key_pulse)
);
// 时钟信号生成
always begin
#5 clk = ~clk;
end
// 测试激励
initial begin
// 初始化信号
clk = 0;
rst_n = 0;
col = 4'b1111;
// 释放复位信号
#10 rst_n = 1;
// 模拟按键按下和释放
#10 col = 4'b1010; // 按下第2行第3列的按键
#10 col = 4'b1111; // 释放按键
#10 col = 4'b0110; // 按下第4行第2列的按键
#10 col = 4'b1111; // 释放按键
// 结束仿真
#10 $finish;
end
endmodule

image.png

image.png

6.2 calculate和BCD转码模块

仿真测试四个计算等式:22+11=33,22-11=11,21*11=231,22/11=2,以下为测试代码和仿真波形图:

`timescale 1ns/1ps
module calculate_tb();
reg clk;
reg rst_n;
reg [15:0] key_pulse;//键盘按键脉冲
wire [15:0] operand2; //操作数1
calculate u1(
.clk (clk),
.rst_n (rst_n),
.key_pulse (key_pulse),
.operand2 (operand2)
);
bin_to_bcd u2(
.rst_n(rst_n),
.bin_code(operand2),
.bcd_code(seg_data)
);
// 时钟信号生成
always begin
#5 clk = ~clk;
end
// 测试激励
initial begin
// 初始化信号
clk = 0;
rst_n = 0;
key_pulse = 16'h0000;
// 释放复位信号
#5 rst_n = 1;
#10 key_pulse = 16'h0200; // 按下2
#10 key_pulse = 16'h0200; // 按下2
#10 key_pulse = 16'h8000; // 按下+
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h4000; // 按下=
#5 rst_n = 0;
#5 rst_n = 1;
#10 key_pulse = 16'h0200; // 按下2
#10 key_pulse = 16'h0100; // 按下2
#10 key_pulse = 16'h0800; // 按下-
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h4000; // 按下=
#5 rst_n = 0;
#5 rst_n = 1;
#10 key_pulse = 16'h0200; // 按下2
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h0080; // 按下*
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h4000; // 按下=
#5 rst_n = 0;
#5 rst_n = 1;
#10 key_pulse = 16'h0200; // 按下2
#10 key_pulse = 16'h0200; // 按下2
#10 key_pulse = 16'h0008; // 按下/
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h0100; // 按下1
#10 key_pulse = 16'h4000; // 按下=
#30 $finish;
end
endmodule

图1.PNG 图2.PNG 图3.PNG

6.3 segment_scan模块

仿真代码和仿真波形图如下:

`timescale 1ns/1ps
module tb_segment_scan();
reg clk;
reg rst_n;
reg [3:0] dat_1, dat_2, dat_3, dat_4, dat_5, dat_6, dat_7, dat_8;
reg [7:0] dat_en, dot_en;
wire seg_rck, seg_sck, seg_din;
// 实例化segment_scan模块
segment_scan uut (
.clk(clk),
.rst_n(rst_n),
.dat_1(dat_1),
.dat_2(dat_2),
.dat_3(dat_3),
.dat_4(dat_4),
.dat_1(dat_5),
.dat_2(dat_6),
.dat_3(dat_7),
.dat_4(dat_8),
.dat_en(dat_en),
.dot_en(dot_en),
.seg_rck(seg_rck),
.seg_sck(seg_sck),
.seg_din(seg_din)
);
always begin
#5 clk = ~clk;
end
initial begin
clk = 0;
rst_n = 0;
dat_1 = 4'h3;
dat_2 = 4'h2;
dat_3 = 4'h1;
dat_4 = 4'h0;
dat_5 = 4'hF;
dat_6 = 4'hE;
dat_7 = 4'hD;
dat_8 = 4'hC;
dat_en = 8'hFF;
dot_en = 8'h00;
#10 rst_n = 1;
#1000 $finish;
end
endmodule

image.png

7 FPGA的资源利用说明

7.1 I/O口分配片上资源.PNG

7.2 资源占用率

资源占用.PNG

8 主要难题及解决方法

verilog代码的编写及管脚映射,矩阵键盘计算BCD转码,数码管显示,这四个模块中,个人感觉比较难一点的就是计算模块和转码模块,因为计算模块涉及到进位和借位,小数点的计算等等复杂流程,还有转码模块因为涉及到十六进制转十进制数所以参考网上常用的Double-Dabble Binary-to-BCD Conversion Algorithm算法即移位加三算法来实现的,以下为转码模块的实现原理,计算模块实验原理在标题2.2.2中。

移位加3算法: 有多少位二进制,就进行多少次移位,以八位的二进制为例,其数值最高可为三位十进制数,进行如下表左移,在移位的过程中,如果移位出的数值大于4,则将改为的数值加3后再进行移位,BCD码是四位二进制数表示一个十进制数的一位,如果这以为大于4,比如54b0101,下一次移位后变成了4b1010,本次项目十六位二进制的转换同理。

image.png

二进制移位表

9 未来的计划

由于项目时间紧张,扩展要求使用TFTLCD来做显示,自行设计显示界面,代替数码管显示输入的参数、运算符以及计算后的结果没有完全完成,接下来将接着努力实现使用LCD屏显示此计算器功能。

附件下载
burn.jed
可以直接烧录的文件
code.zip
源代码
simulation.zip
仿真源代码
团队介绍
郑州原创电子科技有限公司
团队成员
常钰豪
郑州原创电子科技有限公司
评论
0 / 100
查看更多
猜你喜欢
2024年寒假练 - 基于小脚丫FPGA套件STEP BaseBoard V4.0 实现两位十进制加、减、乘、除计算器该项目使用了WebIDE平台以及小脚丫FPGA套件STEP BaseBoard V4.0,实现了两位十进制加、减、乘、除计算器的设计,它的主要功能为:通过读取矩阵键盘的得到操作数和运算符,实现两位十进制加、减、乘、除计算。。
Remo
226
2024年寒假练-小脚丫FPGA套件STEP BaseBoard V4.0-两位十进制加、减、乘、除计算器该项目使用了小脚丫FPGA套件STEP BaseBoard V4.0,实现了两位十进制加、减、乘、除计算器的设计,它的主要功能为:实现一个两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制,运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。。
zzkuner
127
2024年寒假练 - 基于小脚丫FPGA套件STEP Baseboard V4.0实现两位十进制加、减、乘、除计算器该项目使用了Lattice Diamond软件,Verilog语言,实现了一个十进制计算器的设计,它的主要功能为:1.两位十进制数加、减、乘、除 2.运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。3.输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。。
wapoci
406
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号