内容介绍
内容介绍
项目需求
实现一个两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制,4×4键盘按键分配如下图所示。
基本要求:
运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。
需求分析
这个项目的目标是使用Verilog和FPGA来实现一个两位十进制数的计算器。这个计算器需要能够进行加、减、乘、除四种运算,运算数和运算符由4*4矩阵按键来控制。
- 输入: 用户通过4*4矩阵按键输入两位十进制数和运算符。每个按键对应一个特定的数字或运算符。当用户按下一个按键时,这个模块需要能够识别出用户按下的是哪一个按键,并将对应的数字或运算符传递给计算模块。
- 显示: 计算器需要有8个八段数码管来显示运算数和计算结果。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。
- 运算: 计算器需要能够进行加、减、乘、除四种运算。运算的结果需要在数码管上显示。需要能够接收来自输入模块的运算数和运算符,进行相应的运算,并将运算结果传递给显示模块。同时需要能够根据用户的输入,控制显示模块和计算模块的工作。
实现的方式
- 输入模块: 这个模块负责处理用户通过4*4矩阵按键输入的数据。每个按键对应一个特定的数字或运算符,当用户按下一个按键时,这个模块需要能够识别出用户按下的是哪一个按键,并将对应的数字或运算符传递给计算模块。为了保证减少误触发,这个模块需要一个状态机来处理用户的输入,以及一个按键去抖动的电路来确保输入的准确性。
- 显示模块: 这个模块负责控制八段数码管的显示。它需要能够接收来自输入模块和计算模块的数据,并将这些数据转换为可以在数码管上显示的格式。这个模块可能需要一个数码管驱动电路,以及一个动态扫描电路来实现多个数码管的同时显示。
- 计算模块: 这个模块负责进行加、减、乘、除四种运算。它需要能够接收来自输入模块的运算数和运算符,进行相应的运算,并将运算结果传递给显示模块。需要一个算术逻辑单元来进行四种运算。
- 控制模块: 这个模块负责协调输入模块、显示模块和计算模块的工作。它需要能够根据用户的输入,控制显示模块和计算模块的工作。这个模块需要一个状态机来控制整个系统的工作流程。
功能框图
代码及说明
矩阵键盘检测
原理
矩阵键盘是一种常见的输入设备,由行和列组成。在这个4x4的矩阵键盘中,有4行和4列,总共16个按键。每个按键都位于一个特定的行和列的交叉点上。为了检测哪个按键被按下,我们需要一个扫描过程。
扫描的基本原理是:通过逐行或逐列地使能(设置为低电平或高电平),然后检测其他行或列的状态。如果某个按键被按下,那么它所在的行和列就会导通,从而可以检测到这个按键。
功能实现
- 时钟分频:首先,程序使用了一个计数器来实现时钟分频,生成一个5ms周期的
clk_200hz
信号。这是基于系统时钟12MHz进行分频得到的。这个信号用于触发状态机和按键扫描。 - 状态机:程序中使用了一个2位的状态机
c_state
,它有4个状态(STATE0到STATE3),对应4行键盘。状态机根据clk_200hz
信号在4个状态之间循环切换。在每个状态下,程序设置对应的行为低电平(使能),然后检测列的状态。 - 按键扫描:在每个状态下,程序通过读取
col
端口的值来检测当前使能的行中哪些按键被按下。col
端口是一个4位的输入,表示4列的状态。如果某个按键被按下,那么它所在的列会被拉低。 - 去抖动和确认:为了避免按键抖动造成的误判,程序使用了两次采样确认的方法。当连续两次采样都检测到按键被按下时,才确认按键被按下。这通过
key_out
、key_r
和key
三个寄存器实现。 - 按键输出:确认按键被按下后,程序将按键的值存储在
key_out
寄存器中。这个寄存器的每个4位段对应一个状态(一行键盘),表示该行中哪些按键被按下。 - 按键脉冲生成:为了提供一个按键按下的脉冲信号,程序还使用了一个寄存器
key_out_r
来存储key_out
的前一个值。然后,通过比较key_out_r
和key_out
的值,生成一个按键脉冲信号key_pulse
。当某个按键从未被按下变为被按下时,key_pulse
对应的位会被置为1。
这样就实现了对4x4矩阵键盘的扫描和按键检测的功能。
module array_keyboard #
(
parameter CNT_200HZ = 60000
)
(
input clk,
input rst_n,
input [3:0] col,
output reg [3:0] row,
output reg [15:0]key_out,
output [15:0]key_pulse
);
localparam STATE0 = 2'b00;
localparam STATE1 = 2'b01;
localparam STATE2 = 2'b10;
localparam STATE3 = 2'b11;
//计数器计数分频实现5ms周期信号clk_200hz,系统时钟12MHz
reg [15:0] cnt;
reg clk_200hz;
always@(posedge clk or negedge rst_n) begin //复位时计数器cnt清零,clk_200hz信号起始电平为低电平
if(!rst_n) begin
cnt <= 16'd0;
clk_200hz <= 1'b0;
end else begin
if(cnt >= ((CNT_200HZ>>1) - 1)) begin //数字逻辑中右移1位相当于除2
cnt <= 16'd0;
clk_200hz <= ~clk_200hz; //clk_200hz信号取反
end else begin
cnt <= cnt + 1'b1;
clk_200hz <= clk_200hz;
end
end
end
reg [1:0] c_state;
//状态机根据clk_200hz信号在4个状态间循环,每个状态对矩阵按键的行接口单行有效
always@(posedge clk_200hz or negedge rst_n) begin
if(!rst_n) begin
c_state <= STATE0;
row <= 4'b1110;
end else begin
case(c_state)
//状态c_state跳转及对应状态下矩阵按键的row输出
STATE0: begin c_state <= STATE1; row <= 4'b1101; end
STATE1: begin c_state <= STATE2; row <= 4'b1011; end
STATE2: begin c_state <= STATE3; row <= 4'b0111; end
STATE3: begin c_state <= STATE0; row <= 4'b1110; end
default:begin c_state <= STATE0; row <= 4'b1110; end
endcase
end
end
reg [15:0] key,key_r;
//因为每个状态中单行有效,通过对列接口的电平状态采样得到对应4个按键的状态,依次循环
always@(negedge clk_200hz or negedge rst_n) begin
if(!rst_n) begin
key_out <= 16'hffff; key_r <= 16'hffff; key <= 16'hffff;
end else begin
case(c_state)
//采集当前状态的列数据赋值给对应的寄存器位
//对键盘采样数据进行判定,连续两次采样低电平判定为按键按下
STATE0: begin key_out[ 3: 0] <= key_r[ 3: 0]|key[ 3: 0]; key_r[ 3: 0] <= key[ 3: 0]; key[ 3: 0] <= col; end
STATE1: begin key_out[ 7: 4] <= key_r[ 7: 4]|key[ 7: 4]; key_r[ 7: 4] <= key[ 7: 4]; key[ 7: 4] <= col; end
STATE2: begin key_out[11: 8] <= key_r[11: 8]|key[11: 8]; key_r[11: 8] <= key[11: 8]; key[11: 8] <= col; end
STATE3: begin key_out[15:12] <= key_r[15:12]|key[15:12]; key_r[15:12] <= key[15:12]; key[15:12] <= col; end
default:begin key_out <= 16'hffff; key_r <= 16'hffff; key <= 16'hffff; end
endcase
end
end
reg [15:0] key_out_r;
always @ ( posedge clk or negedge rst_n )
if (!rst_n) key_out_r <= 16'hffff;
else key_out_r <= key_out; //将前一刻的值延迟锁存
assign key_pulse= key_out_r & ( ~key_out); //通过前后两个时刻的值判断
endmodule
数码管显示
在开发板上使用一个八位数码管显示程序,使用了74HC595D作为串行到并行的数据转换芯片。以下是对该代码的详细解释:
原理
- 74HC595D:这是一个8位串行输入、串行或并行输出、带有存储寄存器和三态输出的移位寄存器。在本程序中,它被用作串行到并行的转换器,将16位数据(8位用于段选,8位用于位选)通过串行接口传送到数码管。
- 数码管:每个数码管有8个段(A-G和一个小数点),可以通过控制这些段的亮灭来显示0-9和A-F的数字或字符。在本程序中,每次只点亮一个数码管,通过快速扫描的方式,使人眼看到所有数码管同时亮的效果。
功能实现
- 字库:程序首先创建了一个数码管的字库
seg
,包含了0-9和A-F的字形数据。这些数据是根据数码管的段码顺序排列的。 - 分频:使用计数器
cnt
对系统时钟clk
(12MHz)进行计数,生成一个40KHz的时钟信号clk_40khz
。这个信号用于触发状态机和74HC595D的数据传输。 - 状态机:状态机有三个状态:
IDLE
、MAIN
和WRITE
。在MAIN
状态下,程序根据dat_en
信号选择要点亮的数码管,并从字库中取出对应的数据和dot_en
信号组合成16位的数据data
。在WRITE
状态下,程序通过控制seg_sck
、seg_din
和seg_rck
信号,将数据data
串行传送到74HC595D。 - 数据传输:在
WRITE
状态下,程序使用了一个6位的计数器cnt_write
来控制数据的传输。每两个时钟周期传输一位数据(一个在seg_sck
的上升沿,一个在下降沿),共需要64个时钟周期来传输16位数据。传输完成后,拉高seg_rck
信号,使74HC595D的输出生效。 - 扫描:通过不断改变
dat_en
信号的值,程序可以依次点亮每个数码管。由于人眼的视觉暂留效应,当扫描速度足够快时,人眼会看到所有数码管同时亮的效果。
module segment(
input clk, //系统时钟 12MHz
input rst_n, //系统复位 低有效
input [3:0] dat_1, //SEG1 显示的数据输入
input [3:0] dat_2, //SEG2 显示的数据输入
input [3:0] dat_3, //SEG3 显示的数据输入
input [3:0] dat_4, //SEG4 显示的数据输入
input [3:0] dat_5, //SEG5 显示的数据输入
input [3:0] dat_6, //SEG6 显示的数据输入
input [3:0] dat_7, //SEG7 显示的数据输入
input [3:0] dat_8, //SEG8 显示的数据输入
input [7:0] dat_en, //数码管数据位显示使能,[MSB~LSB]=[SEG1~SEG8]
input [7:0] dot_en, //数码管小数点位显示使能,[MSB~LSB]=[SEG1~SEG8]
output reg seg_rck, //74HC595的RCK管脚
output reg seg_sck, //74HC595的SCK管脚
output reg seg_din //74HC595的SER管脚
);
localparam CNT_40KHz = 300; //分频系数
localparam IDLE = 3'd0;
localparam MAIN = 3'd1;
localparam WRITE = 3'd2;
localparam LOW = 1'b0;
localparam HIGH = 1'b1;
//创建数码管的字库,字库数据依段码顺序有关
//这里字库数据[MSB~LSB]={G,F,E,D,C,B,A}
reg[6:0] seg [15:0];
always @(negedge rst_n) begin
seg[0] = 7'h3f; // 0
seg[1] = 7'h06; // 1
seg[2] = 7'h5b; // 2
seg[3] = 7'h4f; // 3
seg[4] = 7'h66; // 4
seg[5] = 7'h6d; // 5
seg[6] = 7'h7d; // 6
seg[7] = 7'h07; // 7
seg[8] = 7'h7f; // 8
seg[9] = 7'h6f; // 9
seg[10] = 7'h77; // A
seg[11] = 7'h7c; // b
seg[12] = 7'h39; // C
seg[13] = 7'h5e; // d
seg[14] = 7'h79; // E
seg[15] = 7'h71; // F
end
//计数器对系统时钟信号进行计数
reg [9:0] cnt = 1'b0;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 1'b0;
else if(cnt>=(CNT_40KHz-1)) cnt <= 1'b0;
else cnt <= cnt + 1'b1;
end
//根据计数器计数的周期产生分频的脉冲信号
reg clk_40khz = 1'b0;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) clk_40khz <= 1'b0;
else if(cnt<(CNT_40KHz>>1)) clk_40khz <= 1'b0;
else clk_40khz <= 1'b1;
end
//使用状态机完成数码管的扫描和74HC595时序的实现
reg [15:0] data;
reg [2:0] cnt_main;
reg [5:0] cnt_write;
reg [2:0] state = IDLE;
always@(posedge clk_40khz or negedge rst_n) begin
if(!rst_n) begin //复位状态下,各寄存器置初值
state <= IDLE;
cnt_main <= 3'd0; cnt_write <= 6'd0;
seg_din <= 1'b0; seg_sck <= LOW; seg_rck <= LOW;
end else begin
case(state)
IDLE:begin //IDLE作为第一个状态,相当于软复位
state <= MAIN;
cnt_main <= 3'd0; cnt_write <= 6'd0;
seg_din <= 1'b0; seg_sck <= LOW; seg_rck <= LOW;
end
MAIN:begin
cnt_main <= cnt_main + 1'b1;
state <= WRITE; //在配置完发给74HC595的数据同时跳转至WRITE状态,完成串行时序
case(cnt_main)
//对8位数码管逐位扫描
//data [15:8]为段选, [7:0]为位选
3'd0: data <= {{dot_en[7],seg[dat_1]},dat_en[7]?8'hfe:8'hff};
3'd1: data <= {{dot_en[6],seg[dat_2]},dat_en[6]?8'hfd:8'hff};
3'd2: data <= {{dot_en[5],seg[dat_3]},dat_en[5]?8'hfb:8'hff};
3'd3: data <= {{dot_en[4],seg[dat_4]},dat_en[4]?8'hf7:8'hff};
3'd4: data <= {{dot_en[3],seg[dat_5]},dat_en[3]?8'hef:8'hff};
3'd5: data <= {{dot_en[2],seg[dat_6]},dat_en[2]?8'hdf:8'hff};
3'd6: data <= {{dot_en[1],seg[dat_7]},dat_en[1]?8'hbf:8'hff};
3'd7: data <= {{dot_en[0],seg[dat_8]},dat_en[0]?8'h7f:8'hff};
default: data <= {8'h00,8'hff};
endcase
end
WRITE:begin
if(cnt_write >= 6'd33) cnt_write <= 1'b0;
else cnt_write <= cnt_write + 1'b1;
case(cnt_write)
//74HC595是串行转并行的芯片,3路输入可产生8路输出,而且可以级联使用
//74HC595的时序实现,参考74HC595的芯片手册
6'd0: begin seg_sck <= LOW; seg_din <= data[15]; end //SCK下降沿时SER更新数据
6'd1: begin seg_sck <= HIGH; end //SCK上升沿时SER数据稳定
6'd2: begin seg_sck <= LOW; seg_din <= data[14]; end
6'd3: begin seg_sck <= HIGH; end
6'd4: begin seg_sck <= LOW; seg_din <= data[13]; end
6'd5: begin seg_sck <= HIGH; end
6'd6: begin seg_sck <= LOW; seg_din <= data[12]; end
6'd7: begin seg_sck <= HIGH; end
6'd8: begin seg_sck <= LOW; seg_din <= data[11]; end
6'd9: begin seg_sck <= HIGH; end
6'd10: begin seg_sck <= LOW; seg_din <= data[10]; end
6'd11: begin seg_sck <= HIGH; end
6'd12: begin seg_sck <= LOW; seg_din <= data[9]; end
6'd13: begin seg_sck <= HIGH; end
6'd14: begin seg_sck <= LOW; seg_din <= data[8]; end
6'd15: begin seg_sck <= HIGH; end
6'd16: begin seg_sck <= LOW; seg_din <= data[7]; end
6'd17: begin seg_sck <= HIGH; end
6'd18: begin seg_sck <= LOW; seg_din <= data[6]; end
6'd19: begin seg_sck <= HIGH; end
6'd20: begin seg_sck <= LOW; seg_din <= data[5]; end
6'd21: begin seg_sck <= HIGH; end
6'd22: begin seg_sck <= LOW; seg_din <= data[4]; end
6'd23: begin seg_sck <= HIGH; end
6'd24: begin seg_sck <= LOW; seg_din <= data[3]; end
6'd25: begin seg_sck <= HIGH; end
6'd26: begin seg_sck <= LOW; seg_din <= data[2]; end
6'd27: begin seg_sck <= HIGH; end
6'd28: begin seg_sck <= LOW; seg_din <= data[1]; end
6'd29: begin seg_sck <= HIGH; end
6'd30: begin seg_sck <= LOW; seg_din <= data[0]; end
6'd31: begin seg_sck <= HIGH; end
6'd32: begin seg_rck <= HIGH; end //当16位数据传送完成后RCK拉高,输出生效
6'd33: begin seg_rck <= LOW; state <= MAIN; end
default: ;
endcase
end
default: state <= IDLE;
endcase
end
end
endmodule
二进制转BCD码
在得到最后的结果后,我们希望这个数据可以在数码管上显示,最简单的方法是用除法运算来获得百位、十位、个位上的值,但实际上这种方式取模会占用过多资源,底层电路设计十分复杂,通常使用加减3法实现二进制转BCD码。
功能实现
- 输入和输出:模块
bin2bcd16
有一个16位的输入bitcode
和一个16位的输出bcdcode
。输入是要转换的二进制数,输出是转换后的BCD码。 - 中间变量:定义了一个32位的寄存器
data
来存储中间处理结果。但是这里有一个问题:由于data
是一个寄存器类型(reg
),它不能在连续赋值语句(assign
)中被赋值给输出。实际上,这个程序可能无法正常工作,因为assign bcdcode = data[31:16];
这行代码可能不会按预期更新bcdcode
的值。一种解决方法是将bcdcode
也声明为寄存器类型,并在always
块中更新它。 - 处理逻辑:在
always@(bitcode)
块中,当输入bitcode
变化时,执行转换逻辑。首先,将输入的16位二进制数拼接到一个32位值的低16位上(高16位为0)。然后,执行一个16次的循环,每次循环中检查data
的高16位中的每4位是否大于或等于5,如果是,则加上3。然后,将整个32位值左移1位(实际上只关心高16位)。但是,这里的处理逻辑也有问题:由于每次只检查和处理高16位中的4位,并且左移操作会导致处理过的位被丢弃,因此这个程序实际上并没有正确地实现二进制到BCD的转换。正确的实现应该使用一个循环来处理整个二进制数的每一位(从高位到低位或从低位到高位),并且需要保留和处理所有位的结果。
module bin2bcd16 (
input [15:0] bitcode,
output [15:0] bcdcode
);
reg [31:0] data;
assign bcdcode = data[31:16];
always@(bitcode) begin
data = {16'd0,bitcode};
repeat(16) begin //二进制码总共16位,所以循环位数是16
if(data[19:16]>=5)
data[19:16] = data[19:16] + 3;
if(data[23:20]>=5)
data[23:20] = data[23:20] + 3;
if(data[27:24]>=5)
data[27:24] = data[27:24] + 3;
if(data[31:28]>=5)
data[31:28] = data[31:28] + 3;
data = data << 1;
end
end
endmodule
控制和计算
这部分代码负责协调输入模块、显示模块和计算模块的工作。它需要能够根据用户的输入,控制显示模块和计算模块的工作。使用一个状态机来控制整个系统的工作流程。
功能实现
- 输入输出定义:
clk
是时钟信号,rst_n
是复位信号,col
是4位的列输入,row
是4位的行输出,seg_rck
、seg_sck
和seg_din
是用于驱动数码管显示的信号。 - 寄存器定义:
dat_1
到dat_8
是用于存储要显示的数字的寄存器,state
是状态机的状态寄存器,num1
和num2
是用于存储输入的两个操作数,num3
是存储运算结果的寄存器,ope
是存储运算符的寄存器,dat_en
和dot_en
是用于控制数码管显示的使能信号。 - 状态机:这个计算器使用了一个状态机来控制整个运算过程。在
state==0
的时候进行初始化,在state==1
和state==2
的时候读取第一个操作数,在state==3
的时候读取运算符,在state==4
和state==5
的时候读取第二个操作数,在state==6
的时候进行运算并显示结果。 - 键盘扫描:
array_keyboard
模块是用于扫描键盘输入的模块,它会根据col
的输入产生row
的输出,并且产生key_out
和key_pulse
两个信号来表示哪个键被按下。 - 数码管驱动:
segment
模块是用于驱动数码管显示的模块,它会根据dat_1
到dat_8
的输入和dat_en
、dot_en
的使能信号来控制数码管的显示。 - 二进制到BCD的转换:
bin2bcd16
模块是用于将二进制数转换为BCD码的模块,它会将num3
的二进制输入转换为num3_bcd
的BCD码输出。
module top
(
input clk,
input rst_n,
input [3:0]col,
output [3:0]row,
output seg_rck,
output seg_sck,
output seg_din
);
reg [3:0] dat_1=4'b0;
reg [3:0] dat_2=4'b0;
reg [3:0] dat_3=4'b0;
reg [3:0] dat_4=4'b0;
reg [3:0] dat_5=4'b0;
reg [3:0] dat_6=4'b0;
reg [3:0] dat_7=4'b0;
reg [3:0] dat_8=4'b0;
reg [7:0] state=0;
reg [7:0] num1;
reg [7:0] num2;
reg [15:0] num3;
wire [15:0] num3_bcd;
reg [2:0] ope;
reg [7:0] dat_en=8'b00000000;
reg [7:0] dot_en=8'b00000000;
wire seg_rck;
wire seg_sck;
wire seg_din;
wire [15:0]key_out;
wire [15:0]key_pulse;
// assign dot_en[7:0]=8'b11111111;
// assign dat_en[7:0]=8'b00000000;
always@(posedge clk)begin
if(!rst_n)
begin
dot_en[7:0]<=8'b00000000;
dat_en[7:0]<=8'b00000000;
state<=0;
dat_1<=0;dat_2<=0;dat_3<=0;dat_4<=0;dat_5<=0;dat_6<=0;dat_7<=0;dat_8<=0;
end
else if(state==0)begin
dot_en[7:0]<=8'b00000000;
dat_en[7:0]<=8'b00000000;
state<=1;
dat_1<=0;dat_2<=0;dat_3<=0;dat_4<=0;dat_5<=0;dat_6<=0;dat_7<=0;dat_8<=0;
end
else if(state==1)begin
if(key_pulse[0]==1)begin dat_2<=4'd7; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[1]==1)begin dat_2<=4'd8; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[2]==1)begin dat_2<=4'd9; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[4]==1)begin dat_2<=4'd4; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[5]==1)begin dat_2<=4'd5; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[6]==1)begin dat_2<=4'd6; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[8]==1)begin dat_2<=4'd3; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[9]==1)begin dat_2<=4'd2; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[10]==1)begin dat_2<=4'd1; state<=2; dat_en[7:0]<=8'b01000000; end
else if(key_pulse[12]==1)begin dat_2<=4'd0; state<=2; dat_en[7:0]<=8'b01000000; end
end
else if(state==2)begin
if(key_pulse[0]==1)begin dat_1<=dat_2; dat_2<=4'd7; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[1]==1)begin dat_1<=dat_2; dat_2<=4'd8; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[2]==1)begin dat_1<=dat_2; dat_2<=4'd9; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[4]==1)begin dat_1<=dat_2; dat_2<=4'd4; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[5]==1)begin dat_1<=dat_2; dat_2<=4'd5; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[6]==1)begin dat_1<=dat_2; dat_2<=4'd6; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[8]==1)begin dat_1<=dat_2; dat_2<=4'd3; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[9]==1)begin dat_1<=dat_2; dat_2<=4'd2; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[10]==1)begin dat_1<=dat_2; dat_2<=4'd1; state<=3; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[12]==1)begin dat_1<=dat_2; dat_2<=4'd0; state<=3; dat_en[7:0]<=8'b11000000; end
end
else if(state==3)begin
if(key_pulse[3]==1)begin ope<=0; state<=4; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[7]==1)begin ope<=1; state<=4; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[11]==1)begin ope<=2; state<=4; dat_en[7:0]<=8'b11000000; end
else if(key_pulse[15]==1)begin ope<=3; state<=4; dat_en[7:0]<=8'b11000000; end
end
else if(state==4)begin
if(key_pulse[0]==1)begin dat_4<=4'd7; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[1]==1)begin dat_4<=4'd8; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[2]==1)begin dat_4<=4'd9; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[4]==1)begin dat_4<=4'd4; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[5]==1)begin dat_4<=4'd5; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[6]==1)begin dat_4<=4'd6; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[8]==1)begin dat_4<=4'd3; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[9]==1)begin dat_4<=4'd2; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[10]==1)begin dat_4<=4'd1; state<=5; dat_en[7:0]<=8'b11010000; end
else if(key_pulse[12]==1)begin dat_4<=4'd0; state<=5; dat_en[7:0]<=8'b11010000; end
end
else if(state==5)begin
if(key_pulse[0]==1)begin dat_3<=dat_4; dat_4<=4'd7; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[1]==1)begin dat_3<=dat_4; dat_4<=4'd8; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[2]==1)begin dat_3<=dat_4; dat_4<=4'd9; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[4]==1)begin dat_3<=dat_4; dat_4<=4'd4; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[5]==1)begin dat_3<=dat_4; dat_4<=4'd5; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[6]==1)begin dat_3<=dat_4; dat_4<=4'd6; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[8]==1)begin dat_3<=dat_4; dat_4<=4'd3; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[9]==1)begin dat_3<=dat_4; dat_4<=4'd2; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[10]==1)begin dat_3<=dat_4; dat_4<=4'd1; state<=6; dat_en[7:0]<=8'b11110000; end
else if(key_pulse[12]==1)begin dat_3<=dat_4; dat_4<=4'd0; state<=6; dat_en[7:0]<=8'b11110000; end
end
else if(state==6)begin
if(key_pulse[14]==1)begin state<=1; dat_en[7:0]<=8'b11111111; end
num1<=dat_1 * 4'd10+dat_2;
num2<= dat_3 * 4'd10+ dat_4;
if(ope==0) num3<=num1/num2;
else if(ope==1) num3=num1*num2;
else if(ope==2) num3=num1-num2;
else if(ope==3) num3=num1+num2;
dat_8<=num3_bcd[3:0];
dat_7<=num3_bcd[7:4];
dat_6<=num3_bcd[11:8];
dat_5<=num3_bcd[15:12];
end
end
//Array_KeyBoard
array_keyboard u1(
.clk(clk),
.rst_n(rst_n),
.col(col),
.row(row),
.key_out(key_out),
.key_pulse(key_pulse)
);
segment u2 (
.clk(clk),
.rst_n(rst_n), //系统复位 低有效
.dat_1(dat_1), //SEG1 显示的数据输入
.dat_2(dat_2), //SEG2 显示的数据输入
.dat_3(dat_3), //SEG3 显示的数据输入
.dat_4(dat_4), //SEG4 显示的数据输入
.dat_5(dat_5), //SEG5 显示的数据输入
.dat_6(dat_6), //SEG6 显示的数据输入
.dat_7(dat_7), //SEG7 显示的数据输入
.dat_8(dat_8), //SEG8 显示的数据输入
.dat_en(dat_en), //数码管数据位显示使能,[MSB~LSB]=[SEG1~SEG8]
.dot_en(dot_en), //数码管小数点位显示使能,[MSB~LSB]=[SEG1~SEG8]
.seg_rck(seg_rck), //74HC595的RCK管脚
.seg_sck(seg_sck), //74HC595的SCK管脚
.seg_din(seg_din) //74HC595的SER管脚
);
bin2bcd16 bin2bcd(
.bitcode(num3),
.bcdcode(num3_bcd)
);
endmodule
FPGA的资源利用说明
Design Summary:
Number of registers: 199 out of 4635 (4%)
PFU registers: 199 out of 4320 (5%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 409 out of 2160 (19%)
SLICEs as Logic/ROM: 409 out of 2160 (19%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 155 out of 2160 (7%)
Number of LUT4s: 809 out of 4320 (19%)
Number used as logic LUTs: 499
Number used as distributed RAM: 0
Number used as ripple logic: 310
Number used as shift registers: 0
Number of PIO sites used: 13 + 4(JTAG) out of 105 (16%)
Number of block RAMs: 0 out of 10 (0%)
Number of GSRs: 1 out of 1 (100%)
EFB used : No
JTAG used : No
Readback used : No
Oscillator used : No
Startup used : No
POR : On
Bandgap : On
Number of Power Controller: 0 out of 1 (0%)
Number of Dynamic Bank Controller (BCINRD): 0 out of 6 (0%)
Number of Dynamic Bank Controller (BCLVDSO): 0 out of 1 (0%)
Number of DCCA: 0 out of 8 (0%)
Number of DCMA: 0 out of 2 (0%)
Number of PLLs: 0 out of 2 (0%)
Number of DQSDLLs: 0 out of 2 (0%)
Number of CLKDIVC: 0 out of 4 (0%)
Number of ECLKSYNCA: 0 out of 4 (0%)
Number of ECLKBRIDGECS: 0 out of 2 (0%)
软硬件
附件下载
diamond.zip
diamond工程
archive.zip
WEBIDE工程
团队介绍
个人
评论
0 / 100
查看更多
猜你喜欢
2024年寒假练 - 基于小脚丫FPGA套件STEP BaseBoard V4.0实现两位十进制加、减、乘、除计算器该项目使用了小脚丫FPGA套件STEP BaseBoard V4.0,实现了两位十进制加、减、乘、除计算器的设计,它的主要功能为:两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制。。
常钰豪
170
2024年寒假练 - 用小脚丫FPGA套件STEP BaseBoard V4.0制作两位十进制加、减、乘、除计算器该项目使用了小脚丫FPGA套件STEP BaseBoard V4.0,实现了两位十进制加、减、乘、除计算器的设计,它的主要功能为:运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。。
ChanRa1n
89
2024年寒假练 - 基于小脚丫FPGA套件STEP Baseboard V4.0实现两位十进制加、减、乘、除计算器该项目使用了Lattice Diamond软件,Verilog语言,实现了一个十进制计算器的设计,它的主要功能为:1.两位十进制数加、减、乘、除 2.运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。3.输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。。
wapoci
406