项目需求
现有一块小脚丫FPGA核心板,板载资源有8个红色LED,2个RGB灯,4个按键,4个拨码开关和两个一位共阴数码管。现在需要使用按键和数码管制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。
具体要求如下:
1、按键作为输入设备。
2、七段显示器作为输出设备。
3、秒表应从0.0秒计数到9.9秒,然后翻转,计数值每0.1秒精确更新一次。
4、开始按键:按下按键后,开始计时,每0.1秒计数一次。
5、停止按键:按下按键后,暂停计时,并且保持当前状态。
6、增量按键:按下按键后,显示数值+0.1秒。
7、清除按键:按下按键后,显示0.0准备下次计数。
需求分析
根据用户需求,我们需要设计一个使用小脚丫FPGA核心板实现的秒表。这个秒表将通过按键控制功能,并在数码管上显示数值。具体设计如下:
1.硬件资源分配:
-4个按键分别作为开始、停止、增量和清除按键。
-两个一位的数码管用于显示计时数值。
2.软件设计:
-使用Verilog语言编写FPGA逻辑,实现按键控制和数码管显示功能。
-编写状态机,处理按键事件,并根据当前状态更新数码管显示。
-实现一个计时器模块,每0.1秒产生一个时钟信号,用于更新计时值。
-编写一个显示模块,将计时值转换为适合数码管显示的格式。
3.测试和调试:
-在FPGA开发环境中编译和综合设计,并下载到小脚丫FPGA核心板。
-使用示波器和逻辑分析仪观察按键和数码管的输出,确保设计符合需求。
4.文档和说明书:
-编写设计文档,包括硬件连接图、电路原理图以及软件代码的介绍。
硬件介绍
1、共阴极数码管两个
2、波动开关四个
3、R_LED八个
4、RGB灯两个
5、按键四个
6、Type C一个
7、2*20排针(引出电源、地和IO)
剩下来的那个就是FPGA主控。
实现的方式
前期需要学习一些FPGA相关的语法,比如always、assign、连续赋值、阻塞赋值等相关理论的知识。随后根据官方的指导手册就可以在WebIDE环境下尝试创建工程、添加文件、编写代码、逻辑综合、管教分配、FPGA映射、仿真、文件下载就完成了完整的操作。配合GPT大模型来进行辅助生成,验证、修改,不断让实验结果接近项目的需求从而完成任务。
功能框图和引脚分配
代码
Main.v包括全部的流程代码,Display.v是数码管驱动的代码,Divide.v是时钟分频的代码
Main.v
module Main
(
output [8:0] seg_1, // output of 7-segment display 1
output [8:0] seg_2, // output of 7-segment display 2
input clk, // clock signal
input rst, // reset signal
input start, // start signal
input stop, // stop signal
input increase // increase signal
);
wire clk1h; // 1Hz clock signal
reg dis_statr; // display status register
reg dis_increase; //Display increment register
reg [3:0] segdata_1; // data for 7-segment display 1
reg [3:0] segdata_2; // data for 7-segment display 2
initial
begin
segdata_1 <= 4'd0; // initialize data for 7-segment display 1 to 0
segdata_2 <= 4'd0; // initialize data for 7-segment display 2 to 0
dis_statr <= 1'd0; // initialize display status to 0 (stop display)
dis_increase <= 1'd0; // initialize display status to 0 (stop increase)
end
Display display (
.seg_data_1(segdata_1), // input for 7-segment display 1
.seg_data_2(segdata_2), // input for 7-segment display 2
.seg_led_1(seg_1), // output of 7-segment display 1
.seg_led_2(seg_2) // output of 7-segment display 2
);
divide #(.WIDTH(32),.N(1200000)) u2 (
.clk(clk), // input clock signal
.rst_n(rst), // input reset signal
.clkout(clk1h) // output 1Hz clock signal
);
always @ (posedge clk1h or negedge rst or negedge start or negedge stop)
begin
if (!rst) // if reset signal is low
begin
segdata_1 <= 4'd0; // clear data for 7-segment display 1
segdata_2 <= 4'd0; // clear data for 7-segment display 2
dis_statr <= 1'd0; // set display status to 0 (stop display)
dis_increase <= 1'd0; // set display status to 0 (stop increase)
end
else if(!start) // if start signal is low
begin
dis_statr <= 1'd1; // set display status to 1 (start display)
end
else if(!stop) // if stop signal is low
begin
dis_statr <= 1'd0; // set display status to 0 (stop display)
end
else if(!increase) // if stop increase is low
begin
dis_increase <= 1'd1; // set display status to 1 (start increase)
end
else if(dis_increase == 1 && dis_statr == 0) // if increase signal is low and display status is 0 (stop display)
begin
if(segdata_2 != 9) // if data for 7-segment display 2 is not equal to 9
begin
segdata_2 <= segdata_2 + 1; // increment data for 7-segment display 2
dis_increase <= 1'd0; // set display status to 0 (stop increase)
end
else
begin
segdata_2 <= 0; // clear data for 7-segment display 2
segdata_1 <= segdata_1 + 1; // increment data for 7-segment display 1
dis_increase <= 1'd0; // set display status to 0 (stop increase)
end
end
else if(segdata_1 == 9 && segdata_2 == 9) // if display status is 1 and data for 7-segment display 1 is 9 and data for 7-segment display 2 is 9
begin
dis_statr = 0; // set display status to 0 (stop display)
segdata_1 = 0; // clear data for 7-segment display 1
segdata_2 = 0; // clear data for 7-segment display 2
dis_increase <= 1'd0; // set display status to 0 (stop increase)
end
else if(dis_statr == 1 && segdata_2 == 9) // if display status is 1 and data for 7-segment display 2 is 9
begin
segdata_2 <= 4'd0; // clear data for 7-segment display 2
segdata_1 <= segdata_1 + 1; // increment data for 7-segment display 1
end
else if(dis_statr == 1) // if display status is 1
begin
segdata_2 <= segdata_2 + 1; // increment data for 7-segment display 2
end
end
endmodule
Display.v
module Display
(
input [3:0] seg_data_1, // digit displayed on 7-segment display 1
input [3:0] seg_data_2, // digit displayed on 7-segment display 2
output [8:0] seg_led_1, // segment selection for 7-segment display 1
output [8:0] seg_led_2 // segment selection for 7-segment display 2
);
// define two arrays
reg [8:0] seg1 [9:0];
reg [8:0] seg2 [9:0];
initial // initialization
begin
// common anode, DP on
seg1[0] = 9'hbf; // 7-segment display digit 0 with DP
seg1[1] = 9'h86; // 7-segment display digit 1 with DP
seg1[2] = 9'hdb; // 7-segment display digit 2 with DP
seg1[3] = 9'hcf; // 7-segment display digit 3 with DP
seg1[4] = 9'he6; // 7-segment display digit 4 with DP
seg1[5] = 9'hed; // 7-segment display digit 5 with DP
seg1[6] = 9'hfd; // 7-segment display digit 6 with DP
seg1[7] = 9'h87; // 7-segment display digit 7 with DP
seg1[8] = 9'hff; // 7-segment display digit 8 with DP
seg1[9] = 9'hef; // 7-segment display digit 9 with DP
// common anode, DP off
seg2[0] = 9'h3f; // 7-segment display digit 0
seg2[1] = 9'h06; // 7-segment display digit 1
seg2[2] = 9'h5b; // 7-segment display digit 2
seg2[3] = 9'h4f; // 7-segment display digit 3
seg2[4] = 9'h66; // 7-segment display digit 4
seg2[5] = 9'h6d; // 7-segment display digit 5
seg2[6] = 9'h7d; // 7-segment display digit 6
seg2[7] = 9'h07; // 7-segment display digit 7
seg2[8] = 9'h7f; // 7-segment display digit 8
seg2[9] = 9'h6f; // 7-segment display digit 9
end
// illuminate the segments of the 7-segment displays
assign seg_led_1 = seg1[seg_data_1];
assign seg_led_2 = seg2[seg_data_2];
endmodule
Divide.v
// ********************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// ********************************************************************
// File name : divide.v
// Module name : divide
// Author : STEP
// Description : clock divider
// Web : www.stepfpga.com
//
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date: |Changes Made:
// V1.0 |2017/03/02 |Initial ver
// --------------------------------------------------------------------
// Module Function:任意整数时钟分频
module divide ( clk,rst_n,clkout);
input clk,rst_n; // input signals, where clk is connected to the C1 pin of the FPGA with a frequency of 12MHz
output clkout; // output signal, can be connected to an LED to observe the divided clock
// parameter statement in Verilog defines constants
parameter WIDTH = 24; // width of the counter, the maximum count value is 2**WIDTH-1
parameter N = 12_000_000; // division factor, please ensure N < 2**WIDTH-1, otherwise the counter will overflow
reg [WIDTH-1:0] cnt_p,cnt_n; // cnt_p represents the counter triggered on the rising edge, cnt_n represents the counter triggered on the falling edge
reg clk_p,clk_n; // clk_p represents the divided clock triggered on the rising edge, clk_n represents the divided clock triggered on the falling edge
// control of the counter triggered on the rising edge
always @ (posedge clk or negedge rst_n ) // posedge and negedge are Verilog expressions for rising and falling edges of a signal
// execute the statements inside always whenever there is a rising edge on clk or rst_n becomes low
begin
if(!rst_n)
cnt_p<=0;
else if (cnt_p==(N-1))
cnt_p<=0;
else cnt_p<=cnt_p+1; // the counter keeps counting, and when it counts to N-1, it resets to 0. This is a modulo-N counter
end
// output of the divided clock triggered on the rising edge; if N is odd, the duty cycle of the clock is not 50%; if N is even, the duty cycle is 50%
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_p<=0;
else if (cnt_p<(N>>1)) // N>>1 is equivalent to right shifting by 1, which is equivalent to dividing by 2 and discarding the remainder
clk_p<=0;
else
clk_p<=1; // the divided clock has one positive cycle more than the negative cycle
end
// control of the counter triggered on the falling edge
always @ (negedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_n<=0;
else if (cnt_n==(N-1))
cnt_n<=0;
else cnt_n<=cnt_n+1;
end
// output of the divided clock triggered on the falling edge; it is half a clock cycle behind clk_p
always @ (negedge clk)
begin
if(!rst_n)
clk_n<=0;
else if (cnt_n<(N>>1))
clk_n<=0;
else
clk_n<=1; // the divided clock has one positive cycle more than the negative cycle
end
assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;
// conditional expression
// when N=1, clk is directly output
// when N is even, i.e., the least significant bit of N is 0, N[0]=0, output clk_p
// when N is odd, i.e., the least significant bit of N is 1, N[0]=1, output clk_p&clk_n. Positive cycle is more, so it is ANDed
endmodule
GPT使用
检查语法错误和修改
代码注释(中译英)
主要问题和解决方法
主要问题
1、Verilog语法不会
2、还是没有从C语法的方式转变过来
3、FPGA编程软件不会使用,逻辑综合和FPGA映射结果看不懂(仿真暂时用不了)
解决方法(对应上面)
1、看教程、问AI、看视频
2、摁着头皮多次调试、参考别人的代码
3、暂未解决、每次出现问题就出下载看看现象呗
FPGA的资源利用说明
Mian.v:两个9位的输出信号,五个1位的输入信号。两个1位变量,两个4位变量
Display.v:两个9位的输出信号,两个4位的输入信号。两个9位10元素的变量
Divide.v:两个1位的输入信号,一个1位的输出信号。6个变量
下面是小脚丫软件中综合逻辑和映射中,感觉和资源有关的截图:
实物演示
见:视频代码处,这里就放一些照片啦。
未来的计划或建议
在活动开始附近,我正计划购买一块开发板,以学习FPGA的相关知识。没想到,幸运的是,我碰到了一个非常NICE活动。这个活动不仅让我可以免费申请开发板(采用先付后退的方式),而且还结识了一群志同道合的朋友,我们可以一起学习、互相帮助。此外,活动中还提供了多种丰富的示例程序和教程,非常适合初学者入门。
在未来的规划中,我打算基于核心板设计一块拓展板,以便学习更多的外设知识。虽然感觉FPGA适用于高端、快速信号的领域,对于即将毕业的普通电子爱好者来说,可能平常使用的场合比较少,但是是一个不错学习数电和就业的方向。我会尝试设计一些简单的项目,以巩固所学内容。随着技能的提高,我会进一步挑战更具难度的项目,不断提高自己的技术水平。
总的来说,这次活动为我提供了一个难得的学习机会,让我对FPGA技术产生了浓厚的兴趣。在未来的日子里,我将以此次活动为契机,努力学习,不断提高自己的技能水平。
最后一点点点点点小小的建议就是:优化一下在线IDE,其他的FPGA编程软件我虽然没有用过,但是在这次学习的过程看到很多那些软件的使用,感觉非常的复杂和麻烦。这个在线IDE对我入门起到了非常重要的地位,无需安装任何驱动就可以完成编程,而且使用非常方便,但是仿真还是有一点点小问题,希望还能继续优化。