基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口制作的简易秒表
该项目使用了小脚丫FPGA核心板,实现了简易秒表的设计,它的主要功能为:显示计数值,开始、暂停、增量、复位。
标签
FPGA
数字逻辑
开发板
参加活动
鲍飞.
更新2024-04-02
143

项目需求

现有一块小脚丫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.文档和说明书:

   -编写设计文档,包括硬件连接图、电路原理图以及软件代码的介绍。

 

硬件介绍

image.png

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个变量

下面是小脚丫软件中综合逻辑和映射中,感觉和资源有关的截图:

资源使用情况.png

资源使用情况(3).png资源使用情况(1).png资源使用情况(2).png

实物演示

见:视频代码处,这里就放一些照片啦。

实物图 (1).jpg

实物图 (3).jpg

实物图 (2).jpg

未来的计划或建议

在活动开始附近,我正计划购买一块开发板,以学习FPGA的相关知识。没想到,幸运的是,我碰到了一个非常NICE活动。这个活动不仅让我可以免费申请开发板(采用先付后退的方式),而且还结识了一群志同道合的朋友,我们可以一起学习、互相帮助。此外,活动中还提供了多种丰富的示例程序和教程,非常适合初学者入门。

在未来的规划中,我打算基于核心板设计一块拓展板,以便学习更多的外设知识。虽然感觉FPGA适用于高端、快速信号的领域,对于即将毕业的普通电子爱好者来说,可能平常使用的场合比较少,但是是一个不错学习数电和就业的方向。我会尝试设计一些简单的项目,以巩固所学内容。随着技能的提高,我会进一步挑战更具难度的项目,不断提高自己的技术水平。

总的来说,这次活动为我提供了一个难得的学习机会,让我对FPGA技术产生了浓厚的兴趣。在未来的日子里,我将以此次活动为契机,努力学习,不断提高自己的技能水平。

最后一点点点点点小小的建议就是:优化一下在线IDE,其他的FPGA编程软件我虽然没有用过,但是在这次学习的过程看到很多那些软件的使用,感觉非常的复杂和麻烦。这个在线IDE对我入门起到了非常重要的地位,无需安装任何驱动就可以完成编程,而且使用非常方便,但是仿真还是有一点点小问题,希望还能继续优化。

附件下载
archive.zip
编译好的代码和源文件
团队介绍
个人即为团队,但是是菜鸡。是一名在校大学生,趁寒假学习了FPGA的基本语法和使用,很感谢官方对本活动的赞助。
评论
0 / 100
查看更多
猜你喜欢
2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口制作具有启动、停止、递增和清除功能的秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板 - Type C接口,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:具有启动、停止、递增和清除功能的秒表。
学分+1
236
2024年寒假练-基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口实现具有启动、停止、递增和清除功能的秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板 - Type C接口,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。 秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。。
Aurora.
206
2024寒假在家一起练-基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口设计具有启动、停止、递增和清除功能的秒表该项目使用了FPGA编程,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:具有启动、停止、递增和清除功能的秒表。
碧24可以
174
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号