2024寒假练-基于小脚丫FPGA实现秒表
该项目使用了Lattice MXO2的小脚丫FPGA核心板 - Type C、WebIDE平台、verilog语言,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:秒表使用四个按钮输入:开始、停止、增量和清除(重置)。开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次);停止输入使计数器停止递增,但使数码管显示当前计数器值;每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间;复位/清除输入强制计数器值为零。。
标签
FPGA
数字逻辑
显示
开发板
参加活动/培训
邓佳怡
更新2024-04-01
北京理工大学
458

一、项目需求

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

二、完成的功能

秒表使用四个按钮输入:开始、停止、增量和清除(重置)。

1.开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次)。

2.停止输入使计数器停止递增,但使数码管显示当前计数器值。

3.每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间。

4. 复位/清除输入强制计数器值为零。

三、设计思路

1.时钟分频

调用小脚丫示例实验17分频器。

2.计时初始化

设置显示器,整数位、小数位数字分别为0-9时七段显示数码管显示对应数字,并设置显示初值为0.0。为了显示小数点,整数位数值比小数位数值增加二进制数1000 0000。

3.清零重置按键

对重置信号rst进行判断,若有效,则保持显示数字0.0并循环。若重置信号无效,进入计数功能。

4.状态量设置

设置开始、暂停、增量状态量。当相应按键信号有效时,状态量改为相应状态量。特别的是,对增量状态量的判断,需要增加使用增量前状态,用以判断增量状态有效时间位于时钟的位置,确保在时钟下降沿后有效再将状态量设为增量状态量,以对齐时钟,保证在0.1秒计数完成后再增加计数。若增量状态未有效,增量前状态重置(也不能有效);增量状态有效,对时钟判断决定增量前状态是否有效,两个状态均为有效时小数位计数加1。

5.开始按键

重置信号无效时,按下开始按键,状态量设为开始按键对应状态量,按10Hz时钟周期进行计数。每过一个时钟周期,小数位计数从0开始加1;每次计数增加前对小数位和整数位进行判断:若小数位计数增加前数字不为9,则小数位计数+1,整数位不变;若小数位计数增加前数字为9整数位计数增加前数字不是9,则小数位置为0,整数位计数加1;若小数位计数增加前数字为9且整数位计数增加前数字也为9,则小数位和整数位均置为0,即计数满9.9清0重新开始。

6.暂停按键

重置信号无效时,按下暂停按键,状态量设为暂停按键对应状态量,计数不变。

7.增量按键

重置信号无效时,按下增量按键,状态量设为增量按键对应状态量,并对增量前状态进行判断。若增量前状态有效,对小数位和整数位数字进行判断,若小数位计数增加前数字不为9,则小数位计数+1,整数位不变;若小数位计数增加前数字为9整数位计数增加前数字不是9,则小数位置为0,整数位计数加1;若小数位计数增加前数字为9且整数位计数增加前数字也为9,则小数位和整数位均置为0,即计数满9.9清0重新开始。若增量前状态无效,重置增量状态。

8.计时完成后翻转

技术增加时自动判断计数是否达到9.9,达到9.9时自动清零进行下一轮计数。

四、设计过程

1.功能框图

2.代码实现

·初始状态设置

设置显示器,整数位、小数位数字分别为0-9时七段显示数码管显示对应数字,并设置显示初值为0.0。为了显示小数点,整数位数值比小数位数值增加二进制数1000 0000。

input clk,rst;
input start;
input stop;
input   plus;
 output [8:0] seg_led_1,seg_led_2;
 
reg cnt_flag=1'b0;      //计数标志
reg plus_flag=1'b0;     //增量标志
    reg         pre_plus=1'b1;      //增量前标志
reg    [9:0]   seg1 [9:0];
reg    [9:0]   seg2 [9:0];
reg [3:0] cnt_ge;      //个位
reg [3:0] cnt_shi;     //十位
 
initial
begin
seg1[0] = 9'hbf;                                           //对存储器中第一个数赋值9'b00_1011_1111,相当于共阴极接地,DP点变高亮,7段显示数字  0
    seg1[1] = 9'h86;                                           //7段显示数字  1
    seg1[2] = 9'hdb;                                           //7段显示数字  2
    seg1[3] = 9'hcf;                                           //7段显示数字  3
    seg1[4] = 9'he6;                                           //7段显示数字  4
    seg1[5] = 9'hed;                                           //7段显示数字  5
    seg1[6] = 9'hfd;                                           //7段显示数字  6      
    seg1[7] = 9'h87;                                           //7段显示数字  7
    seg1[8] = 9'hff;                                           //7段显示数字  8
    seg1[9] = 9'hef;                                           //7段显示数字  9
end
     initial                                                                                                                          
 begin
          seg2[0] = 9'h3f;                                           //对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字  0
      seg2[1] = 9'h06;                                           //7段显示数字  1
      seg2[2] = 9'h5b;                                           //7段显示数字  2
      seg2[3] = 9'h4f;                                           //7段显示数字  3
      seg2[4] = 9'h66;                                           //7段显示数字  4
      seg2[5] = 9'h6d;                                           //7段显示数字  5
      seg2[6] = 9'h7d;                                           //7段显示数字  6
      seg2[7] = 9'h07;                                           //7段显示数字  7
      seg2[8] = 9'h7f;                                           //7段显示数字  8
      seg2[9] = 9'h6f;                                           //7段显示数字  9
            end

 ·时钟分频

分出10Hz频率(0.1s)时钟。

//用于分出一个10Hz的频率
divide #(.WIDTH(32),.N(1200000)) U1 (
.clk(clk),
.rst_n(rst),      
.clkout(clk10h)
);

 分频模块(调用小脚丫网站示例17)

    // ********************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>> 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;                    //输入信号,其中clk连接到FPGA的C1脚,频率为12MHz
        output clkout;                      //输出信号,可以连接到LED观察分频的时钟
 
        //parameter是verilog里常数语句
parameter WIDTH = 3;             //计数器的位数,计数的最大值为2**WIDTH-1
parameter N = 5;           //分频系数,请确保N < 2**WIDTH-1,否则计数会溢出
 
reg [WIDTH-1:0] cnt_p,cnt_n;  //cnt_p为上升沿触发时的计数器,cnt_n为下降沿触发时的计数器
reg clk_p,clk_n;     //clk_p为上升沿触发时分频时钟,clk_n为下降沿触发时分频时钟
 
//上升沿触发时计数器的控制
always @ (posedge clk or negedge rst_n )     //posedge和negedge是verilog表示信号上升沿和下降沿
    //当clk上升沿来临或者rst_n变低的时候执行一次always里的语句
begin
if(!rst_n)
cnt_p<=0;
else if (cnt_p==(N-1))
cnt_p<=0;
else cnt_p<=cnt_p+1;             //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
end
 
         //上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
         always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_p<=0;
else if (cnt_p<(N>>1))          //N>>1表示右移一位,相当于除以2去掉余数
clk_p<=0;
else
clk_p<=1;               //得到的分频时钟正周期比负周期多一个clk时钟
end
 
        //下降沿触发时计数器的控制        
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
 
        //下降沿触发的分频时钟输出,和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;                //得到的分频时钟正周期比负周期多一个clk时钟
end
 
        assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;      //条件判断表达式
       //当N=1时,直接输出clk
       //当N为偶数也就是N的最低位为0,N(0)=0,输出clk_p
       //当N为奇数也就是N最低位为1,N(0)=1,输出clk_p&clk_n。正周期多所以是相与
endmodule

·判断重置信号

对重置信号rst进行判断,若有效,则保持显示数字0.0并循环。

always @(posedge clk10h or negedge rst)begin
        if (!rst == 1)
        begin
     cnt_ge <= 4'd0;
     cnt_shi <= 4'd0;
     end

  若重置信号无效,进入计数功能。

·状态量设置

设置开始、暂停、增量状态量。当相应按键信号有效时,状态量改为相应状态量。特别的是,对增量状态量的判断,需要增加使用增量前状态,用以判断增量状态有效时间位于时钟的位置,确保在时钟下降沿后有效再将状态量设为增量状态量,以对齐时钟,保证在0.1秒计数完成后再增加计数。若增量状态未有效,增量前状态重置(也不能有效);增量状态有效,对时钟判断决定增量前状态是否有效,两个状态均为有效时小数位计数加1。

 else
        begin
            //开始状态设置
            if (start==1'b0)
            begin
                cnt_flag<=1'b1;
            end
            //暂停状态设置
            else if (stop==1'b0)
            begin
                cnt_flag<=1'b0;
            end
            //增量状态设置
            else if (plus==1'b0)
            begin
                //确保是时钟下降沿
                if(pre_plus==1'b1)
                begin
                    plus_flag<=1'b1;
                end
                pre_plus<=1'b0;
            end
            //如果没有进行增量功能,重置增量前标志
            else if (plus==1'b1)
            begin
                if(pre_plus==1'b0)
                begin
                    pre_plus<=1'b1;
                end
            end     

 ·计数功能

开始按键对应状态量有效时,按10Hz时钟周期进行计数。每过一个时钟周期,小数位计数从0开始加1;每次计数增加前对小数位和整数位进行判断:若小数位计数增加前数字不为9,则小数位计数+1,整数位不变;若小数位计数增加前数字为9整数位计数增加前数字不是9,则小数位置为0,整数位计数加1;若小数位计数增加前数字为9且整数位计数增加前数字也为9,则小数位和整数位均置为0,即计数满9.9清0重新开始。

//计数
            if (cnt_flag==1'b1)
            begin
                if(cnt_ge==4'd9)
                begin
                    cnt_ge<=4'd0;
                    if(cnt_shi==4'd9)
                    begin
                        cnt_shi<=4'd0;
                        cnt_ge<=4'd0;
                    end
                    else
                    begin
                        cnt_shi<=cnt_shi+4'd1;
                    end
                end
                else
                begin
                    cnt_ge<=cnt_ge+4'd1;
                end
            end

·暂停功能

暂停按键对应状态量有效时,计数不变。暂停状态中可启用增量功能。

·增量功能

增量按键对应状态量有效时,对小数位和整数位数字进行判断,若小数位计数增加前数字不为9,则小数位计数+1,整数位不变;若小数位计数增加前数字为9整数位计数增加前数字不是9,则小数位置为0,整数位计数加1;若小数位计数增加前数字为9且整数位计数增加前数字也为9,则小数位和整数位均置为0,即计数满9.9清0重新开始。若增量前状态无效,重置增量状态。

//暂停   
            else
            begin
                //暂停时使用增量功能
                if (plus_flag==1'b1)
                begin
                    plus_flag<=1'b0;
                    if(cnt_ge==4'd9)
                    begin
                        cnt_ge<=4'd0;
                        if(cnt_shi==4'd9)
                        begin
                            cnt_shi<=4'd0;
                            cnt_ge<=4'd0;
                        end
                        else
                        begin
                            cnt_shi<=cnt_shi+4'd1;
                        end
                    end
                    else
                    begin
                        cnt_ge<=cnt_ge+4'd1;
                    end
                end
            end
        end
    end

·输出数字至七段显示数码管,显示相应数字。

assign seg_led_1[8:0] = {2'b00,seg2[cnt_ge]};
assign seg_led_2[8:0] = {2'b00,seg1[cnt_shi]};   

五、gpt设计模块参考

module stopwatch_display(
input clk,
input start,
input pause,
input reset,
input inc,
output reg [3:0] sec_high = 0,
output reg [3:0] sec_low = 0,
output reg [6:0] display1, // 显示高位的数码管
output reg [6:0] display2 // 显示低位的数码管
);

// 数码管显示从0到9的逻辑段编码(假定共阳极配置,0为亮,1为灭)
// 顺序是 gfedcba
wire [6:0] segment_map[9:0] = {
7'b1000000, // 0
7'b1111001, // 1
7'b0100100, // 2
7'b0110000, // 3
7'b0011001, // 4
7'b0010010, // 5
7'b0000010, // 6
7'b1111000, // 7
7'b0000000, // 8
7'b0010000 // 9
};

reg [25:0] counter;
reg running = 0;
reg inc_pressed_pre = 0;

always @(posedge clk) begin
if (reset) begin
sec_high <= 0;
sec_low <= 0;
counter <= 0;
running <= 0;
end else if (start) begin
running <= 1;
end else if (pause) begin
running <= 0;
end else if (inc && !inc_pressed_pre) begin
if (sec_low < 9) begin
sec_low <= sec_low + 1;
end else begin
sec_low <= 0;
if (sec_high < 9) begin
sec_high <= sec_high + 1;
end else begin
sec_high <= 0;
end
end
end
inc_pressed_pre <= inc;

if (running) begin
if (counter >= 5000000 - 1) begin
counter <= 0;
if (sec_low < 9) begin
sec_low <= sec_low + 1;
end else begin
sec_low <= 0;
if (sec_high < 9) begin
sec_high <= sec_high + 1;
end else begin
sec_high <= 0;
end
end
end else begin
counter <= counter + 1;
end
end
// 更新数码管显示
display1 <= segment_map[sec_high];
display2 <= segment_map[sec_low];
end

endmodule

六、仿真波形图

七、FPGA资源利用说明

ad75db40640af4b3faa8de077874ead.png

八、遇到的困难及解决方法

1.增量功能

起初我并未使用状态量,判断按键有效后直接进行计数/暂停/重置,但是无法实现增量功能。经与同学讨论后设置状态量,增加了状态判断分支,每次执行功能前根据状态量来确定功能。

2.如何使得不论按住增量按钮多久计数都只增加一次

设置增量前状态,在一个时钟有效沿结束并完成一次增量后增量前状态重置,增量状态也不再置为有效。

九、未来的计划

1.可以增加至更多的计数上限

2.不仅有增量功能,还有减量功能,以便增加计数加多了可以取消

3.增加倒计时功能,由拨动开关决定启用正计时还是倒计时功能

附件下载
项目文件.zip
项目文件
可直接烧录文件.jed
可直接烧录文件
团队介绍
北京理工大学
团队成员
邓佳怡
北京理工大学信息与电子学院21级
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号