欢迎各位朋友关注“郝旭帅电子设计团队”,本篇为各位朋友介绍基于FPGA的计算器设计---第一版。
功能说明:
1. 计算器的显示屏幕为数码管。
2. 4x4矩阵键盘作为计算器的输入设备。
3. 计算任意两位正整数的加减乘除。
4. 当减法结果出现负数时(一个小的数字减去一个大的数字),数码管需要显示负数。
5. 除法计算时,结果只输出商的整数,小数自动抹去。
注:矩阵键盘输入0-9,表示0-9;10表示加号(数码管显示A);11表示减号(数码管显示b);12表示乘号(数码管显示C);13表示除号(数码管显示d);14表示等于号。
使用平台:本次设计应用Altera的平台设计(芯片:EP4CE10F17C8N)。
作者QQ:746833924
说明:本篇设计中不涉及到IP和原语,代码在其他平台依然可以适用;当其他板卡电路不同时,会导致不同的现象出现,如有需要修改代码请联系作者;如需作者使用的板卡,请联系作者;
设计思想如下:
keyboard4x4_drive模块为4x4矩阵键盘的驱动模块,负责检测4x4矩阵键盘被按下的按键信息;cal_logic模块为计算逻辑和控制显示信息的逻辑,负责根据矩阵键盘传递的按键信息进行计算,根据计算的过程控制输出需要显示的信息;seven_tube_drive(七段数码管驱动)模块负责将产生的数字逻辑显示到数码管上。
keyboard4x4_drive模块的设计思想和具体设计可以参考本公众号,获取方式:关注本公众号,发送“FPGA矩阵键盘驱动第一版”。
cal_logic模块的设计思想:利用状态机设计进行实现。共有八个状态。
localparam STATE_IDLE = 8'b0000_0001; localparam STATE_NUM1_1 = 8'b0000_0010; localparam STATE_NUM1_2 = 8'b0000_0100; localparam STATE_OPCODE = 8'b0000_1000; localparam STATE_NUM2_1 = 8'b0001_0000; localparam STATE_NUM2_2 = 8'b0010_0000; localparam STATE_EQUAL = 8'b0100_0000; localparam STATE_RESULT = 8'b1000_0000;
STATE_IDLE :空闲初始化状态;
STATE_NUM1_1 :输入第一个操作数的第一位。
STATE_NUM1_2 :输入第一个操作数的第二位。
STATE_OPCODE :输入操作符。
STATE_NUM2_1 :输入第二个操作数的第一位。
STATE_NUM2_2 :输入第二个操作数的第二位。
STATE_EQUAL :输入等于号。
STATE_RESULT :计算输出结果。
复位结束后,在IDLE状态,初始化所有的中间变量和输出,之后进入STATE_NUM1_1。
STATE_IDLE : begin state <= STATE_NUM1_1; num1 <= 8'd0; num2 <= 8'd0; opcode <= 4'd0; show_data <= 24'hfffff0; end
在STATE_NUM1_1状态中,当输入的数字0-9,认为是第一个操作数的第一位(显示到最后的数码管),然后进入STATE_NUM1_2,准备接收第一个操作数的第二位;如果输入数字10-13,认为是第一个操作数为0(数码管显示第一个操作数0和操作符),并且存储好操作符,进入STATE_NUM2_1,准备接收第二个操作数的第一位;如果输入为其他数字,则认为无效输入。
STATE_NUM1_1 : begin if (flag == 1'b1) if (press_num < 4'd10) begin state <= STATE_NUM1_2; num1 <= {4'd0, press_num}; show_data <= {20'hfffff, press_num}; end else if (press_num > 4'd9 && press_num < 4'd14) begin num1 <= 8'd0; state <= STATE_NUM2_1; opcode <= press_num; show_data <= {show_data[19:0], press_num}; end else state <= STATE_NUM1_1; else state <= STATE_NUM1_1; end
在STATE_NUM1_2状态中,当输入的数字0-9,认为是第一个操作数的第二位(将第一个操作数的第一位和第二位都显示到数码管上)(计算得出num1),然后进入STATE_OPCODE,准备接收操作符;如果输入数字10-13,认为是第一个操作数为STATE_NUM1_1输入的数字(数码管显示第一个操作数和操作符),并且存储好操作符,进入STATE_NUM2_1,准备接收第二个操作数的第一位;如果输入为其他数字,则认为无效输入。
STATE_NUM1_2 : begin if (flag == 1'b1) if (press_num < 4'd10) begin state <= STATE_OPCODE; num1 <= num1 * 10 + press_num; show_data <= {show_data[19:0], press_num}; end else if (press_num > 4'd9 && press_num < 4'd14) begin state <= STATE_NUM2_1; opcode <= press_num; show_data <= {show_data[19:0], press_num}; end else state <= STATE_NUM1_2; else state <= STATE_NUM1_2; end
在STATE_OPCODE状态中,如果输入数字10-13,认为是第一个操作数为STATE_NUM1_1和STATE_NUM1_2输入的数字(数码管显示第一个操作数和操作符),并且存储好操作符,进入STATE_NUM2_1,准备接收第二个操作数的第一位;如果输入为其他数字,则认为无效输入。
STATE_OPCODE : begin if (flag == 1'b1) if (press_num > 4'd9 && press_num < 4'd14) begin state <= STATE_NUM2_1; opcode <= press_num; show_data <= {show_data[19:0], press_num}; end else state <= STATE_OPCODE; else state <= STATE_OPCODE; end
在STATE_NUM2_1状态中,如果输入数字0-9,认为是第二个操作数的第一个数字(数码管显示第一个操作数、操作符和第二个操作数的第一个数字),进入STATE_NUM2_2,准备接收第二个操作数的第二位;如果输入为其他数字,则认为无效输入。
STATE_NUM2_1 : begin if (flag == 1'b1) if (press_num < 4'd10) begin state <= STATE_NUM2_2; num2 <= press_num; show_data <= {show_data[19:0], press_num}; end else state <= STATE_NUM2_1; else state <= STATE_NUM2_1; end
在STATE_NUM2_2状态中,当输入的数字0-9,认为是第二个操作数的第二位(将第一个操作数、操作符和第二个操作数显示到数码管上)(计算得出num2),然后进入STATE_EQUAL,准备接收等于号;如果输入数字14,认为是等于号,进入STATE_RESULT状态。如果输入为其他数字,则认为无效输入。
STATE_NUM2_2 : begin if (flag == 1'b1) if (press_num < 4'd10) begin state <= STATE_EQUAL; num2 <= num2 * 10 + press_num; show_data <= {show_data[19:0], press_num}; end else if (press_num == 4'd14) begin state <= STATE_RESULT; end else state <= STATE_NUM2_2; else state <= STATE_NUM2_2; end
在STATE_EQUAL状态中,如果输入数字14,认为是等于号,进入STATE_RESULT状态。如果输入为其他数字,则认为无效输入。
STATE_EQUAL : begin if (flag == 1'b1) if (press_num == 4'd14) begin state <= STATE_RESULT; end else state <= STATE_EQUAL; else state <= STATE_EQUAL; end
在STATE_RESULT状态中,只有按下复位才可以再次进行计算;在本状态中根据第一个操作数、操作符和第二个操作数进行计算,并且将计算的结果作为输出。
STATE_RESULT : begin state <= STATE_RESULT; show_data[23:20] <= result[15] ? 4'he : 4'h0; show_data[19:16] <= result[15] ? (-result)/10000 : result/10000; show_data[15:12] <= result[15] ? (-result)/1000 % 10 : result/1000 % 10; show_data[11:8] <= result[15] ? (-result)/100 % 10 : result/100 % 10; show_data[7:4] <= result[15] ? (-result)/10 % 10 : result/10 % 10; show_data[3:0] <= result[15] ? (-result) % 10 : result % 10; end
七段数码管为普通六位一体的共阳极数码,采用动态驱动的方式,在此不再赘述。
仿真时,需要利用4x4矩阵键盘的仿真模型,否则不容易仿真(相关代码,下方链接提供)。
calculator calculator_inst( .clk (clk ), .rst_n (rst_n ), .keyboard4x4_row (keyboard4x4_row), .keyboard4x4_col (keyboard4x4_col), .sel (sel ),// 数码管位选信号 .seg (seg ) // 数码管段选信号 ); keyboard4x4 keyboard4x4_inst( .press_num (press_num ), .keyboard4x4_col (keyboard4x4_col), .keyboard4x4_row (keyboard4x4_row) );
模拟2+8;
press_data(5'd2); press_data(5'd10); press_data(5'd8); press_data(5'd14);
模拟55-8;
press_data(5'd5); press_data(5'd5); press_data(5'd11); press_data(5'd8); press_data(5'd14);
其他模拟情况,可以根据设计者自由模拟;
注意在计算一次完成后,需要进行一次复位(相当于计算器中的清除),然后才可以进行下一次计算。
讲解和演示视频链接如下:
链接:https://pan.baidu.com/s/1WKkfXU7XInPBNCUSvgs5Pw
提取码:s0f8
本篇内容中有部分资源来源于网络,如有侵权,请联系作者。
如果您觉得本公众号还不错的话,可以推给身边的朋友们,感谢并祝好!