2024寒假练-基于小脚丫FPGA实现秒表
该项目使用了Lattice MXO2的小脚丫FPGA核心板 - Type C接口,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。。
标签
FPGA
烜月xy
更新2024-04-01
长江师范学院
324

任务要求:

  • 通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。
  • 使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。
  • 秒表使用三个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。

硬件介绍:

该项目使用硬件平台为:基于Lattice MXO2的小脚YFPGA核心板 - Type C接口。

系统框图如下:


3 完成的功能及资源占用情况:

3.1 完成的功能:

  • 首先,上电后数码管显示值为0.0,并在点击“开始”按键之前保持0.0。
  • 按下“开始”按键后,秒表开始以10Hz时钟速率递增,每次递增0.1s,当计时到9.9时,会翻转回0.0重新开始计时。
  • 按下“停止”按键时,秒表停止计时并锁定在当前时间,此时点击“增量”按键可使计时值增加0.1,同样当计时超过9.9时,会翻转回0.0重新开始计时。
  • 在自动计时或者停止计时的状态下,按下“清零”按键,秒表将清零显示0.0,并锁定在0.0,等待再次按下“开始”按键或“增量”按键。

3.2 资源占用情况:

Design Summary:
Number of registers: 64 out of 4635 (1%)
PFU registers: 64 out of 4320 (1%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 50 out of 2160 (2%)
SLICEs as Logic/ROM: 50 out of 2160 (2%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 21 out of 2160 (1%)
Number of LUT4s: 99 out of 4320 (2%)
Number used as logic LUTs: 57
Number used as distributed RAM: 0
Number used as ripple logic: 42
Number used as shift registers: 0
Number of PIO sites used: 23 + 4(JTAG) out of 105 (26%)
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%)


4 实现思路:

1、核心部分的计时部分,需要以10Hz的频率工作,故首先需要将系统时钟12MHz进行分频处理以得到10Hz的时钟信号;

2、控制秒表开始和停止计时部分可以采取一个标志位,通过标志位判断当前处于暂停状态还是继续状态;

3、由于需要将计时数值显示在数码管上,故需要构建一个数码管驱动模块,将计时时的BCD码值转换成数码管显示的段码值;

4、“增量”按键的需求为无论按住增量按钮多长时间都只会增加一次,所以需要为其编写按键消抖代码。

5 实现过程:

5.1 程序架构图:

  • 主模块stopwatch9_9.v负责对按键的检测、时钟分频和计时功能的实现;
  • 副模块digital_tube_drive.v负责将计时的BCD码值转换成数码管显示段码值。
  • 副模块debounce.v负责按键消抖;


5.2 主模块stopwatch9_9.v:

首先定义一系列输入和输出的引脚分别为:clk(时钟信号输入)、key_start(开始按键输入)、key_stop(停止按键输入)、key_add(增量按键的输入)、key_rst(复位/清零按键的输入)、seg1(数码管1段码输出)、seg1_dig(数码管1公共端输出)、seg2(数码管2段码输出)、seg2_dig(数码管2公共端输出)。

input   wire    clk;
input   wire    key_start;
input   wire    key_stop;
input   wire    key_add;
input   wire    key_rst;
output  [7:0]   seg1;
output         seg1_dig;
output  [7:0]   seg2;
output         seg2_dig;


然后是获得10Hz的时钟信号,对于系统时钟12MHz的情况下,当计数到927C0H时将产生10Hz频率的寄存器进行取反,即可得到需要的时钟信号。

定义一个常量存储计数的最大值:

parameter COUNT_MAX = 20'h927C0;


927C0H转换成二进制后的长度是20位,下面分别定义clk_10作为产生10Hz频率的计数器,以及clk_10_out作为10Hz频率输出寄存器:

reg [19:0] clk_10;
reg         clk_10_out = 1'b0;


然后在always块中执行产生10Hz时钟频率的代码:

always @(posedge clk) begin
    if(clk_10 == COUNT_MAX) begin
        clk_10 <= 20'd0;
        clk_10_out = ~clk_10_out;
    end
    else begin
        clk_10 <= clk_10 + 1'b1;
    end
end

 

然后是按键检测的部分,这里定义start_flag用于判断当前是处于自动计时模式还是暂停模式,其为1表示处于自动计时模式,为0表示处于暂停模式,上电复位后默认为暂停模式:

reg start_flag = 1'b0;
always @(negedge key_start, negedge key_stop, negedge key_rst) begin
    if(!key_rst) begin
        start_flag <= 1'b0;
    end
    else if(!key_stop) begin
        start_flag <= 1'b0;
    end
    else if(!key_start) begin
        start_flag <= 1'b1;
    end
    else begin
        start_flag <= start_flag;
    end
end


然后是整个项目的核心,秒表的计时部分功能实现,这里有两个位宽为4的寄存器time1和time2,以及一个时钟信号counter_clk。时钟信号counter_clk根据start_flag的值不同分别选择使用clk_10_out计时或key_add_out计时。

在always块中,根据counter_clk和key_rst信号的状态进行操作。如果key_rst为低电平,即按下了复位按键,就将time1和time2清零。否则,当time2计数达到9时,将其清零并检查time1,如果time1也达到9,则将其清零,否则将其递增。如果time2未达到9,则递增time2。整个逻辑实现了time1作为计时的低位,time2作为计时的高位,两者组合达到了0到99的计数。

reg [3:0]   time1;
reg [3:0]   time2;
wire        counter_clk;
assign counter_clk = start_flag ? clk_10_out : !key_add_out;
always @(posedge counter_clk, negedge key_rst)
begin
    if(!key_rst) begin
        time1 <= 4'd0;
        time2 <= 4'd0;
    end
    else begin
        if(time2 == 9) begin
            time2 <= 4'd0;
            if(time1 == 9)
                time1 <= 4'd0;
            else
                time1 <= time1 + 1'b1;
        end
        else
            time2 <= time2 + 1'b1;
    end
end

5.3 副模块digital_tube_drive.v:

该模块接收一个4位BCD码输入和一个小数点输入,输出控制数码管的段输出和公共端输出。

数码管的段输出值根据输入的BCD数字进行更新,在always块中使用case语句对所有可能的输入进行了处理,并将相应的段输出值赋给seg_out_val寄存器。

由于使用的核心板上的数码管为共阴极数码管,所以公共端输出被固定为低电平,小数点的输出直接由输入dp控制。

module digital_tube_drive(
    input       [3:0]   bcd_num , // 输入的BCD数字
    input               dp      , // 小数点
    output wire [7:0]   seg_out , // 数码管段输出
    output wire         seg_dig   // 数码管公共端输出
);
reg             [6:0]   seg_out_val; // 数码管段输出值
assign seg_dig = 1'b0; // 数码管公共端输出为低电平
assign seg_out[0] = dp; // 小数点输出
assign seg_out[7:1] = seg_out_val; // 数码管段输出
always @ (bcd_num) // 根据输入的BCD数字更新数码管段输出值
begin
    case (bcd_num)
        4'd0:  seg_out_val <= 7'b111_1110; // 数字0
        4'd1:  seg_out_val <= 7'b011_0000; // 数字1
        4'd2:  seg_out_val <= 7'b110_1101; // 数字2
        4'd3:  seg_out_val <= 7'b111_1001; // 数字3
        4'd4:  seg_out_val <= 7'b011_0011; // 数字4
        4'd5:  seg_out_val <= 7'b101_1011; // 数字5
        4'd6:  seg_out_val <= 7'b101_1111; // 数字6
        4'd7:  seg_out_val <= 7'b111_0000; // 数字7
        4'd8:  seg_out_val <= 7'b111_1111; // 数字8
        4'd9:  seg_out_val <= 7'b111_1011; // 数字9
        4'd10: seg_out_val <= 7'b111_0111; // 字母A
        4'd11: seg_out_val <= 7'b001_1111; // 字母b
        4'd12: seg_out_val <= 7'b100_1111; // 字母C
        4'd13: seg_out_val <= 7'b011_1101; // 字母d
        4'd14: seg_out_val <= 7'b100_1111; // 字母E
        4'd15: seg_out_val <= 7'b100_0111; // 字母F
    endcase
end
endmodule


5.4 副模块debounce.v:

按键消抖内容参考了电子森林的代码,网址如下:

按键消抖: https://www.eetree.cn/wiki/7._%E6%8C%89%E9%94%AE%E6%B6%88%E6%8A%96

这个按键消抖模块十分强大,调用时可自定义按键个数,仅例化一个模块即可实现多个按键的消抖操作。


6.模块仿真

6.1 主模块stopwatch9_9.v:

在正常计数期间,可以看到time1和time2按照预期的功能依次进行递增;

这里可以分别看到,当key_stop按下后,time1和time2停止递增,counter_clk改为由key_add决定,key_add每按下一次,time2的值加1,同时在后面key_rst按下后,time1和time2全部清零,并在接下来的key_start按下时重新开始计数。

由此,整个主模块工作正常。

6.2 副模块digital_tube_drive.v:

可以看出,在bcd_num依次为不同值时,seg_out_val都为对应的共阴数码管的段码值,成功实现了将BCD码译码为数码管的段码值。


6.3 副模块debounce.v:

该模块取自电子森林的代码,进行仿真时也看到了在抖动期间的低电平都被消除了,只有低电平持续一定时间的状态才判断为有效状态,key_pulse才进行输出。


7.AI大模型的使用

7.1 副模块digital_tube_drive.v:

副模块digital_tube_drive的代码主要由大模型生成,本人只对其进行了较小的改动。

7.1 按键检测的always块:

按键检测的always块也主要由大模型生成,本人同样在调试过程中对其进行了适当的修改。


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

遇到的主要难题在于由于是初学FPGA,在编程思维上依旧是普通单片机的思维,这里的变量不能在不同的always里面进行赋值操作,在初次编写代码时碰了不少壁,后面自己花了一点时间去仔细学了一下Verilog的编写方式方法,以及通过本次的项目练习,现在已经了解了一些Verilog的编程技巧了。

9. 未来的计划或建议

未来计划继续在这个项目的基础上增加功能,比如目前的的秒表计时只能记到9.9秒,我希望到9.9.后下一步不是清零,而是改变数码管显示的精度然后从10秒继续计时,一直记到59秒后再清零,同时由于数码管显示变为秒十位和秒个位,所以递增按键按下后的递增值需要从0.1秒变为1秒才行,然后还可以连接蜂鸣器,当计时值满时让蜂鸣器发出1s左右的提示声音。




软硬件
元器件
LCMXO2-4000HC-4MG132C
逻辑单元数:4320 逻辑阵列块数:540
LPC11U35FHI33
32位ARM Cortex-M0微控制器;高达128 kB闪存;高达12 kB SRAM和4 kB EEPROM;USB设备;USART接口
附件下载
archive.zip
基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口实现具有启动、停止、递增和清除功能的秒表
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号