2024年寒假练-基于Lattice MXO2的小脚丫FPGA核心板设计具有启动、停止、递增和清除功能的秒表
该项目使用了Lattice MXO2的小脚丫FPGA核心板,实现了秒表的设计,它的主要功能为:启动、停止、递增和清除功能。
标签
FPGA
开发板
水波不兴
更新2024-04-01
北京理工大学
236

1. 项目功能介绍

使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。

秒表使用四个按钮输入:开始、停止、增量和清除(重置)。开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。

2. 设计思路

由于需要时钟信号为10Hz,参考时钟信号为12MHz,所以需要设计分频器。项目要求“增量输入都会导致显示值增加一次,无论按住增量按钮多长时间”,所以需要按键防抖模块。需要使用数码管显示,需要用到译码器。将几个模块输入ChatGPT使其生成代码,在此基础上修改代码完成项目。

3. 硬件框图

硬件为基于Lattice MXO2的小脚丫FPGA核心板,小脚丫FPGA团队最新推出的FPGA核心模块。

4. 实现的功能及图片展示

实现的功能有启动按钮开始计时,暂停按钮停止计时,增量按钮使秒表显示值增加一次,清除按钮使秒表清零。

5. 功能框图

我使用了chatGPT来生成功能框图。

6.仿真结果图

这是分频器模块仿真结果,发现分频器能实现12MHz到10Hz的分频。(使用Vivado仿真)

下面是秒表主程序仿真结果

清除按键功能仿真结果,使数码管显示变成0(我觉得间接体现了译码器模块功能)


启动按键功能仿真结果。使数码管开始计数。


暂停按键功能仿真结果。使数码管暂停计数。

增量按键功能没有仿真成功。

6. FPGA资源占用报告

· 寄存器位数:51个寄存器位中使用了1%,共计51个寄存器位。

· PFU寄存器:在4320个PFU寄存器中使用了1%,共计51个寄存器。

· PIO寄存器:在315个PIO寄存器中没有使用。

· SLICE数量:在2160个SLICE中使用了2%,共计48个SLICE。

· LUT4数量:在4320个LUT4中使用了2%,共计95个LUT4,其中55个用作逻辑LUTs,40个用作Ripple Logic。

· RAM数量:未使用任何SLICE作为RAM。

· Carry数量:在2160个SLICE中使用了1%,共计20个SLICE用作Carry。

· PIO sites使用情况:在105个PIO sites中使用了23个,占比26%。

· Block RAM数量:未使用任何Block RAM。

· GSR数量:在1个GSR中使用了100%。

 

7. 主要代码段及说明

module debounce(

    input clk,          //时钟信号

    input rst,          //复位信号

    input button_in,    //按键输入信号

    output reg button_out   //防抖输出信号

);

 

reg [15:0]  cnt;        //计数器

 

always @(posedge clk or negedge rst) begin

    if(!rst) begin

        cnt<=0;         //复位使计数器清零

        button_out<=0;  //复位使输出信号置零

    end else begin

        if(button_in==button_out) begin

            cnt<=0;     //输入信号与输出信号相同时,计数器清零

        end else if(cnt<16'hffff) begin

            cnt <= cnt+1;   //计数器计数

            if (cnt==16'hfffe) begin

                button_out <= button_in;    //计数器将计满时更新输出信号

            end

        end

    end

end

 

endmodule               //防抖输入模块

该代码段为防抖输入模块,确保按键信号稳定后再输出。其中cnt是一个16位计数器,最后的输出信号button_out是经过防抖后的按键信号。


 module Segment_led

(

    input [3:0] cnt_ge,         //个位

    input [3:0] cnt_shi,        //十位

    output [8:0] seg_led_1,     //个位数码管灯

    output [8:0] seg_led_2      //十位数码管灯

);

 

reg [6:0] seg [9:0];

initial

    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

end

 

assign seg_led_1[8:0] = {2'b00,seg[cnt_ge]};

assign seg_led_2[8:0] = {2'b01,seg[cnt_shi]};

 

endmodule                   //译码器模块

该段代码为译码器模块,实现了数码管的译码功能,根据输入的个位和十位数字,选择对应的数码管段亮起,以显示相应数字。


module divide(

    input clk,          //12MHz时钟信号

    output reg clk_10h //分频后10Hz时钟信号

);

 

reg [31:0]  counter;    //计数器,用于分频

 

always @(posedge clk) begin

    if (counter == 600000-1) begin // 12MHz分频到10Hz需要计数600000个周期

        counter <= 0;

        clk_10h <= ~clk_10h; //每计满240000个周期翻转一次输出信号

    end else

        counter <= counter + 1;

end

 

endmodule           //分频器模块

该段代码为分频器模块,将FPGA开发板自带的12MHz时钟信号分频为10Hz时钟信号。


module miaobiao (clk, start, stop, inc, rst, seg_led_1, seg_led_2);

 

    input clk, start, stop, inc, rst;

    output [8:0] seg_led_1;

    output [8:0] seg_led_2;

    

    reg inc_out = 0;    //上一时刻增量输出信号

    reg [3:0] cnt_ge;

    reg [3:0] cnt_shi;

    reg start_flag = 0; //启动按键标志位

    reg stop_flag = 0;  //暂停按键标志位

    reg start_out = 0;  //上一时刻启动输出信号

    reg stop_out = 0;   //上一时刻暂停输出信号

    wire clk_10h;  //10Hz时钟信号

    wire inc_pulse; //增量按键消抖后信号

 

    //调用译码器模块

    Segment_led u1(

        .cnt_ge(cnt_ge),

        .seg_led_1(seg_led_1),

        .seg_led_2(seg_led_2),

        .cnt_shi(cnt_shi)

    );

    

    //调用分频器模块

    divide u2(

        .clk(clk),

        .clk_10h(clk_10h)

    );

    

    //对增量键进行防抖处理

    debounce db_inc(

        .clk(clk),

        .rst(rst),

        .button_in(inc),

        .button_out(inc_pulse)

    );

    

always @(posedge clk or negedge rst) begin

    if(!rst) begin

        //复位信号低有效

        start_flag <= 0;    //启动按键标志位置零

        stop_flag <= 0;     //暂停按键标志位置零

        start_out <= 0;     //启动输出信号置零

        stop_out <= 0;      //暂停输出信号置零

    end else begin

        start_out <= start; //更新启动输出信号

        stop_out <= stop;   //更新暂停输出信号

        

        if(start && !start_out) begin

            //第一次按下启动键,没有连续按下

            //确保计数器开始计数

            start_flag <= 1;    //将启动标志位置1,保证计数器计数

            stop_flag <= 0;     //将暂停标志位置零,使计数器不停止

        end

        

        if(stop && !stop_out) begin

            //第一次按下暂停键,没有连续按下

            //确保计数器暂停计数

            stop_flag <= 1;     //将暂停标志位置1,使计数器暂停计数

        end

    end

end

该段always语句使开发板可以判断启动按键或暂停按键是否按下

 //在10Hz时钟上计数

always @(posedge clk_10h or negedge rst) begin

    if(!rst) begin

        //复位信号低有效

        cnt_ge <= 0;    //使个位计数器置零

        cnt_shi <= 0;   //使十位计数器置零

        inc_out <= 0;   //使增量输出信号置零

    end else begin

        inc_out <= inc_pulse;   //更新增量输出信号

        

        if(start_flag && !stop_flag) begin

            //当启动标志位为1,暂停标志位为0时,在10Hz时钟上计数

            //启动计数功能

            if(cnt_ge >= 9) begin

                cnt_ge <= 0;

                if(cnt_shi >= 9)

                    cnt_shi <= 0;

                else

                    cnt_shi <= cnt_shi +1;

            end else

                cnt_ge <= cnt_ge + 1;

        end

        

        if(inc_pulse && !inc_out) begin

            //当增量按键按下时计数器计数加1

            if(cnt_ge>=9) begin

                cnt_ge <= 0;

            

                if(cnt_shi >= 9)

                    cnt_shi <= 0;

                else

                    cnt_shi <= cnt_shi + 1;

            end else

                cnt_ge <= cnt_ge + 1;

        end

    end

end

endmodule

该段always语句使秒表在清除键按下时计数清零,启动键按下正常计数,暂停按键按下时暂停计数及增量按键按下时使计数增加1次。

整段代码是实现秒表计时功能,其中包含了按键的防抖处理、启动/暂停逻辑、10Hz时钟上的计数逻辑、增量按键的处理逻辑等。根据按键的操作控制计数器的计数,实现秒表的计时功能。同时,根据计数器的值,通过译码器模块显示在数码管上。

8. 遇到的主要难题及解决办法

由于是新学FPGA,很多东西不懂。最主要的是Verilog语言不懂。

解决办法:使用chatGPT和项目示例来实现不同模块,买了一本Verilog数字系统设计教程来学习。

这是ChatGPT给出的防抖输入模块,我将其中的一些信号名称缩写改成更方便我理解的名称。

 

这是chatGPT给出的主模块,同样将一些信号名改成和自己其他模块对应的信号名,并修改一些不对的地方。


这是ChatGPT给出的分频器模块,但我发现他这里面存在错误,应该是技术600000个周期翻转一次输出信号。同样改了信号名。


        initial                                                         //在过程块中只能给reg型变量赋值,Verilog中有两种过程块always和initial
//initial和always不同,其中语句只执行一次
begin
seg[0] = 9'h3f; //对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字 0
seg[1] = 9'h06; //7段显示数字 1
seg[2] = 9'h5b; //7段显示数字 2
seg[3] = 9'h4f; //7段显示数字 3
seg[4] = 9'h66; //7段显示数字 4
seg[5] = 9'h6d; //7段显示数字 5
seg[6] = 9'h7d; //7段显示数字 6
seg[7] = 9'h07; //7段显示数字 7
seg[8] = 9'h7f; //7段显示数字 8
seg[9] = 9'h6f; //7段显示数字 9
end
assign seg_led_1 = seg[seg_data_1];
assign seg_led_2 = seg[seg_data_2];


译码器模块参照stepfpga中的示例项目驱动数码管,由于秒表计数需要小数点。所以采用下段计时控制示例中的代码


	initial 
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
/*若需要显示A-F,解除此段注释即可
seg[10]= 7'hf7; // 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

assign seg_led_1[8:0] = {2'b00,seg[cnt_ge]};

assign seg_led_2[8:0] = {2'b00,seg[cnt_shi]};

除此之外没有解决的问题是我使用清零按钮时,按下去秒表清零,当松开时,秒表显示0.1。

由于对仿真不是很明白,简单仿真波形貌似显示一切正常。

秒表显示0.1原因:在按下清除按键时增量输出信号置为零。

解决办法:

if(!rst) begin
//复位信号低有效
cnt_ge <= 0; //使个位计数器置零
cnt_shi <= 0; //使十位计数器置零
inc_out <= 1; //使增量输出信号置1
end

所以我发现只要用心,还是能找到解决办法的。


9. 未来的计划或建议

在这次活动中我觉得我最大的收获应该是对AI的使用更加熟练,由于我平常使用的是MATLAB或ADS等软件,对FPGA应该只是一次感兴趣的尝试。由于这次活动一开始没有做,后面开始做时又被学校的事耽误,所以完成的不是很好,所以未来或许会完整地参加一次FPGA活动。

 

 

 

 

 

附件下载
archive (2).zip
FPGA代码及jed文件
分频器代码.txt
仿真时分频器模块主程序代码
分频器仿真代码.txt
仿真时分频器模块仿真代码
秒表代码.txt
仿真时秒表功能模块主程序代码
秒表仿真代码.txt
仿真时秒表功能模块仿真代码
团队介绍
大四学生
团队成员
邓振勋
北京理工大学信息与电子学院大四学生
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号