[FPGA]Verilog 60s秒表计时器(最大可计时间长达9min)

[FPGA]Verilog 60s秒表计时器

1.引述

这次的实验来自于本人本科课程数电结课时的自选题目。由于这次上传是后知后觉,学校已将小脚丫板子回收,所以在这篇文章中没法贴出代码结果的效果图了,但最终效果已经过测试,可放心食用。那么下面就贴上代码并略加讲解供大家参考。

2.分频模块

我们要实现一个秒表,自然要将实验板中的时钟脉冲clk分频为一个周期为1s的脉冲,已知小脚丫板子的晶振为12MHz。下面贴上分频模块的代码。

module divide #
(                            //parameter是verilog里参数定义
parameter WIDTH    =    24,    //计数器的位数,计数的最大值为 2**(WIDTH-1)
parameter N        =    12_000_000 //分频系数,请确保 N<2**(WIDTH-1),否则计数会溢出
)
(
input clk, //clk频率为12MHz
input rst_n, //复位信号,低有效,
output clkout //输出信号,可以连接到LED观察分频的时钟
);
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)
    begin
        if(!rst_n)
            cnt_p <= 1‘b0;
        else if(cnt_p == (N-1))
            cnt_p <= 1‘b0;
        else
            cnt_p <= cnt_p + 1‘b1; //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
    end
//上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_p <= 1‘b0;
        else if(cnt_p < (N>>1)) //N>>1表示右移一位,相当于除以2取商
            clk_p <= 1‘b0;
        else
            clk_p <= 1‘b1; //得到的分频时钟正周期比负周期多一个clk时钟
    end
//下降沿触发时计数器的控制
always @(negedge clk or negedge rst_n)
    begin
        if(!rst_n)
            cnt_n <= 1‘b0;
        else if(cnt_n == (N-1))
            cnt_n <= 1‘b0;
        else
            cnt_n <= cnt_n + 1‘b1;
    end
//下降沿触发的分频时钟输出,和clk_p相差半个clk时钟
always @(negedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_n <= 1‘b0;
        else if(cnt_n < (N>>1))
            clk_n <= 1‘b0;
        else
            clk_n <= 1‘b1;    //得到的分频时钟正周期比负周期多一个clk时钟
    end
wire    clk1 = clk; //当N=1时,直接输出clk
wire    clk2 = clk_p; //当N为偶数也就是N的最低位为0,N[0]=0,输出clk_p
wire    clk3 = clk_p & clk_n; //当N为奇数也就是N最低位为1,N[0]=1,输出clk_p&clk_n。正周期多所以是相与
assign clkout = (N==1)? clk1:(N[0]? clk3:clk2);    //条件判断表达式
endmodule

3.八位数码管显示模块

小脚丫板子上有两个八位数码管显示,本实验中用来显示从00s到59s的显示。下面贴上数码管显示模块的代码。

module segment
(
input  wire [3:0] seg_data_1, //四位输入数据信号
input  wire [3:0] seg_data_2, //四位输入数据信号
output wire [8:0] segment_led_1, //数码管1,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
output wire [8:0] segment_led_2  //数码管2,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
);
reg[8:0] seg [15:0]; //存储7段数码管译码数据
initial
    begin
        seg[0] = 9‘h3f;   //  0
        seg[1] = 9‘h06;   //  1
        seg[2] = 9‘h5b;   //  2
        seg[3] = 9‘h4f;   //  3
        seg[4] = 9‘h66;   //  4
        seg[5] = 9‘h6d;   //  5
        seg[6] = 9‘h7d;   //  6
        seg[7] = 9‘h07;   //  7
        seg[8] = 9‘h7f;   //  8
        seg[9] = 9‘h6f;   //  9
        seg[10]= 9‘h77;   //  A
        seg[11]= 9‘h7C;   //  b
        seg[12]= 9‘h39;   //  C
        seg[13]= 9‘h5e;   //  d
        seg[14]= 9‘h79;   //  E
        seg[15]= 9‘h71;   //  F
    end
   assign segment_led_1 = seg[seg_data_1];
   assign segment_led_2 = seg[seg_data_2];
endmodule

4.功能讲解

在主模块中除了要例化上述的两个模块之外,还需给这个秒表添砖加瓦一下!标题中提到这是一个60s秒表,而我们数码管显示只从00到59,但最大计时量程却达到了9min,这是怎么办到的呢?这里我们就用到了小脚丫上的一排八位LED灯,每当计到59s时,下一秒数码管显示回到00,点亮八位LED灯中的一个,达到表示已计过了1min的作用。一共有八位LED灯,当八个灯都被点亮后,数码管还有一次从00到59的显示机会,这样我们就的得到了一个最大计时量程为9min的秒表啦!下面贴上八位LED灯显示部分的代码。

always@(posedge clk)
     if(cnt1==4‘b0)
         LED[7:0]<=8‘b11111111;
     else if(cnt1==4‘b0001)
         LED[7:0]<=8‘b11111110;
     else if(cnt1==4‘b0010)
         LED[7:0]<=8‘b11111100;
     else if(cnt1==4‘b0011)
         LED[7:0]<=8‘b11111000;
     else if(cnt1==4‘b0100)
         LED[7:0]<=8‘b11110000;
     else if(cnt1==4‘b0101)
         LED[7:0]<=8‘b11100000;
     else if(cnt1==4‘b0110)
         LED[7:0]<=8‘b11000000;
     else if(cnt1<=4‘b0111)
         LED[7:0]<=8‘b10000000;
     else if(cnt1<=4‘b1000)
         LED[7:0]<=8‘b00000000;

此外作为一个秒表自然就要有暂停和开始计时的功能(当然清零功能也是有哒!主模块中就用rst复位键来实现,这里不多赘述。)暂停和开始计时这里我就用同一个按键实现。小脚丫板子上还有两个RGB三色灯,既然有这么好的资源存在,我们就要物尽其用!按下开始计时键时,秒表开始计时,数码管显示开始变化,此处我们让RGB三色灯中的一个等亮绿灯,表示处于正常计时状态中;当再次按键开启键时,秒表暂停,我们让另一个RGB三色灯亮红色,表示处于暂停状态。下面贴上包含三色灯点亮的部分代码。

always @(posedge clk1h or negedge rst) //产生60进制计数器
    begin    //数码管显示要按照十进制的方式显示
        if(!rst)
        begin
            cnt <= 8‘h00; //复位初值显示00
            cnt1<=4‘b0;
        end
        else if(flag)
            begin
            G_LED2<=1‘b0;
            R_LED1<=1‘b1;
                if(cnt[3:0] == 4‘d9) //个位满九?
                    begin
                        cnt[3:0] <= 4‘d0; //个位清零
                        if(cnt[7:4] == 4‘d5 ) //十位满五?
                        begin
                            cnt[7:4] <= 4‘d0; //十位清零
                            cnt1<=cnt1+1;
                        end
                        else
                        begin
                            cnt[7:4] <= cnt[7:4] + 1‘b1; //十位加一
                            cnt1<=cnt1;
                        end
                    end
                else cnt[3:0] <= cnt[3:0] + 1‘b1; //个位加一
            end
        else
        begin
            cnt <= cnt;
            G_LED2<=1‘b1;
            R_LED1<=1‘b0;
        end
    end

5.主模块

最后贴上主模块的代码,完成整个秒表的实现。

module counter60
(
input clk,rst, //时钟和复位输入
input key, //启动暂停按键
output wire [8:0] segment_led_1,segment_led_2, //数码管输出
output reg [7:0] LED, //八位LED灯
output reg R_LED1,G_LED2 //RGB三色灯,此处用红色表示处于暂停状态中,绿色表示处于正常计时中
);
wire clk1h;    //1秒时钟
reg    [7:0] cnt;    //计时计数器
reg [3:0] cnt1; //分钟计数器
reg    flag; //启动暂停标志

divide #  //例化分频器产生1秒时钟信号
(
.WIDTH(24),
.N(12_000_000)
) u1
(
.clk(clk),
.rst_n(rst),
.clkout(clk1h)
);
segment u2 //例化数码管显示模块
(
.seg_data_1        (cnt[7:4]),  //seg_data input
.seg_data_2        (cnt[3:0]),  //seg_data input
.segment_led_1    (segment_led_1),  //MSB~LSB = SEG,DP,G,F,E,D,C,B,A
.segment_led_2    (segment_led_2)   //MSB~LSB = SEG,DP,G,F,E,D,C,B,A
);
always @(posedge clk or negedge rst) //产生标志信号
begin
if(!rst)
        flag = 1‘b0;
else if(!key)
     begin
        flag = ~flag;
        end
    else
    begin
        flag = flag;
        end
end
always @(posedge clk1h or negedge rst) //产生60进制计数器
    begin    //数码管显示要按照十进制的方式显示
        if(!rst)
        begin
            cnt <= 8‘h00; //复位初值显示00
            cnt1<=4‘b0;
        end
        else if(flag)
            begin
            G_LED2<=1‘b0;
            R_LED1<=1‘b1;
                if(cnt[3:0] == 4‘d9) //个位满九?
                    begin
                        cnt[3:0] <= 4‘d0; //个位清零
                        if(cnt[7:4] == 4‘d5 ) //十位满五?
                        begin
                            cnt[7:4] <= 4‘d0; //十位清零
                            cnt1<=cnt1+1;
                        end
                        else
                        begin
                            cnt[7:4] <= cnt[7:4] + 1‘b1; //十位加一
                            cnt1<=cnt1;
                        end
                    end
                else cnt[3:0] <= cnt[3:0] + 1‘b1; //个位加一
            end
        else
        begin
            cnt <= cnt;
            G_LED2<=1‘b1;
            R_LED1<=1‘b0;
        end
    end
always@(posedge clk)
     if(cnt1==4‘b0)
         LED[7:0]<=8‘b11111111;
     else if(cnt1==4‘b0001)
         LED[7:0]<=8‘b11111110;
     else if(cnt1==4‘b0010)
         LED[7:0]<=8‘b11111100;
     else if(cnt1==4‘b0011)
         LED[7:0]<=8‘b11111000;
     else if(cnt1==4‘b0100)
         LED[7:0]<=8‘b11110000;
     else if(cnt1==4‘b0101)
         LED[7:0]<=8‘b11100000;
     else if(cnt1==4‘b0110)
         LED[7:0]<=8‘b11000000;
     else if(cnt1<=4‘b0111)
         LED[7:0]<=8‘b10000000;
     else if(cnt1<=4‘b1000)
         LED[7:0]<=8‘b00000000;
endmodule

module divide #
(                            //parameter是verilog里参数定义
parameter WIDTH    =    24,    //计数器的位数,计数的最大值为 2**(WIDTH-1)
parameter N        =    12_000_000 //分频系数,请确保 N<2**(WIDTH-1),否则计数会溢出
)
(
input clk, //clk频率为12MHz
input rst_n, //复位信号,低有效,
output clkout //输出信号,可以连接到LED观察分频的时钟
);
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)
    begin
        if(!rst_n)
            cnt_p <= 1‘b0;
        else if(cnt_p == (N-1))
            cnt_p <= 1‘b0;
        else
            cnt_p <= cnt_p + 1‘b1; //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
    end
//上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_p <= 1‘b0;
        else if(cnt_p < (N>>1)) //N>>1表示右移一位,相当于除以2取商
            clk_p <= 1‘b0;
        else
            clk_p <= 1‘b1; //得到的分频时钟正周期比负周期多一个clk时钟
    end
//下降沿触发时计数器的控制
always @(negedge clk or negedge rst_n)
    begin
        if(!rst_n)
            cnt_n <= 1‘b0;
        else if(cnt_n == (N-1))
            cnt_n <= 1‘b0;
        else
            cnt_n <= cnt_n + 1‘b1;
    end
//下降沿触发的分频时钟输出,和clk_p相差半个clk时钟
always @(negedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_n <= 1‘b0;
        else if(cnt_n < (N>>1))
            clk_n <= 1‘b0;
        else
            clk_n <= 1‘b1;    //得到的分频时钟正周期比负周期多一个clk时钟
    end
wire    clk1 = clk; //当N=1时,直接输出clk
wire    clk2 = clk_p; //当N为偶数也就是N的最低位为0,N[0]=0,输出clk_p
wire    clk3 = clk_p & clk_n; //当N为奇数也就是N最低位为1,N[0]=1,输出clk_p&clk_n。正周期多所以是相与
assign clkout = (N==1)? clk1:(N[0]? clk3:clk2);    //条件判断表达式
endmodule

module segment
(
input  wire [3:0] seg_data_1, //四位输入数据信号
input  wire [3:0] seg_data_2, //四位输入数据信号
output wire [8:0] segment_led_1, //数码管1,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
output wire [8:0] segment_led_2  //数码管2,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
);
reg[8:0] seg [15:0]; //存储7段数码管译码数据
initial
    begin
        seg[0] = 9‘h3f;   //  0
        seg[1] = 9‘h06;   //  1
        seg[2] = 9‘h5b;   //  2
        seg[3] = 9‘h4f;   //  3
        seg[4] = 9‘h66;   //  4
        seg[5] = 9‘h6d;   //  5
        seg[6] = 9‘h7d;   //  6
        seg[7] = 9‘h07;   //  7
        seg[8] = 9‘h7f;   //  8
        seg[9] = 9‘h6f;   //  9
        seg[10]= 9‘h77;   //  A
        seg[11]= 9‘h7C;   //  b
        seg[12]= 9‘h39;   //  C
        seg[13]= 9‘h5e;   //  d
        seg[14]= 9‘h79;   //  E
        seg[15]= 9‘h71;   //  F
    end
   assign segment_led_1 = seg[seg_data_1];
   assign segment_led_2 = seg[seg_data_2];
endmodule

6.总结

到这里整个秒表就完成啦。最后再次向读者们道歉,不能贴上实验效果图了。身边有实验板的读者们可以将代码烧录进板子观察现象。本人编程水平、时间有限,这篇文章到这里就要结束啦,欢迎广大读者评论留言,更欢迎大家指出本人的不足,希望能通过交流自身得到提高。最后感谢大家的耐心阅读!

原文地址:https://www.cnblogs.com/RDJLM/p/12044385.html

时间: 2024-11-08 04:30:06

[FPGA]Verilog 60s秒表计时器(最大可计时间长达9min)的相关文章

期中考试秒表计时器

制作一个秒表计时器,记录0-15秒时间.要求精准,可控. 要求: 能够用按键控制秒表开始 能够用按键控制秒表停止 能够用按键控制秒表清零 使用定时器控制时间,要求精确. 使用1个数码管和多个按键实现功能. 1.首先根据要求把电路图加上2个按钮,如图下图所示 2.根据要求写出代码,下面是参考代码 #include<reg52.h> #define LED P0 unsigned int i = 0; unsigned int j =0; sbit KEY1 = P1^0; //暂停,开始按钮 s

开发板制作秒表计时器---我太难了丶

制作一个秒表计时器,记录0-15秒时间.要求精准,可控. 1.能够用按键控制秒表开始 2.能够用按键控制秒表停止 3.能够用按键控制秒表清零 4.使用定时器控制时间,要求精确. 5.使用1个数码管和多个按键实现功能. 将下面代码复制粘贴到C51软件里,编译运行生成.hex文件,再将文件添加到下面电路中,运行.(软件文件,安装方法,创建工程,电路图在我前面博客里有) 右边数码管呈现从1到9数字一次增加,鼠标点击连接接口P1的按钮,数码管启动,再次按下数码管数字停止:点击连接接口P2的按钮,数码管数

FPGA Verilog HDL 系列实例--------步进电机驱动控制

[连载] FPGA Verilog HDL 系列实例 Verilog HDL 之 步进电机驱动控制 步进电机的用途还是非常广泛的,目前打印机,绘图仪,机器人等等设备都以步进电机为动力核心.那么,下面我们就了解下什么是步进电机,它是怎么控制的. 一.步进电机相关知识简介 1.步进电机概述 步进电机是一种能够将电脉冲信号转换成角位移或线位移的机电元件,它实际上是一种单相或多相同步电动机.单相步进电动机有单路电脉冲驱动,输出功率一般很小,其用途为微小功率驱动.多相步进电动机有多相方波脉冲驱动,用途很广

秒表计时器

制作一个秒表计时器,记录0-15秒时间.要求精准,可控. 1.能够用按键控制秒表开始 2.能够用按键控制秒表停止 3.能够用按键控制秒表清零 4.使用定时器控制时间,要求精确. 5.使用1个数码管和多个按键实现功能. 先根据需求判断所需的功能,再改变电路图 然后根据要求开始写代码 1 #include<reg52.h> 2 sbit an = P1^0; 3 sbit qing = P1^1; 4 unsigned int i = 0; 5 unsigned int s = 0; 6 code

期中测试( 制作一个秒表计时器 )

制作一个秒表计时器,记录0-15秒时间.要求精准,可控. 1.能够用按键控制秒表开始 2.能够用按键控制秒表停止 3.能够用按键控制秒表清零 4.使用定时器控制时间,要求精确. 5.使用1个数码管和多个按键实现功能. #include<reg52.h> #define LED P0 sbit KEY1 =P1^0; sbit KEY2 =P1^4; unsigned int i = 0; unsigned char n = 0; unsigned char LED_NUM[] = {0xc0,

[FPGA] Verilog 燃气灶控制器的设计与实现

燃气灶控制器的设计与实现 一.引述 本次实验所用可编程器件型号为MAXII EPM1270T144C5(其引脚表见本人另一博文,链接为 https://www.cnblogs.com/RDJLM/p/12075584.html),通过可编程实验板实现一个基本的模拟燃气灶. 二.设计课题的基本要求 1. 燃气灶的控制按键有三个:点火/关闭按键 BTN7.火力调节按键 BTN6(火力增大) 和 BTN5(火力减小) 2. 用 8×8 双色点阵模拟显示燃气灶的灶眼,用如图 1 所示的四个点阵显示状态分

使用setTimeout setInterval实现秒表计时器( 开始/暂停 停止计时)

<!doctype html>   <html lang="en">   <head>   <meta charset="UTF-8">   <meta name="Generator" content="EditPlus®">   <meta name="Author" content="">   <meta

IOS之秒表计时器

1.利用xib方式新建一个秒表计数器. 功能实现: 该应用有三个按钮:开始,记次,复位 开始:开始控制着秒表的开始,点击后,按钮文字变成停止,在没有开始的情况下不允许记次操作 记次:可以记录多个你点击的时间, 复位:复位可以让秒表归零 实现效果图如下:                                                   1. 新建一个类,勾选xib,然后新建一个试图监控器,然后加载到根视图中 在AppDelegate.m中代码如下: //新建一个视图控制器 Mai

不用计时器也可以计时

public async void awit(int time=1000)        {            while(true)            {                await Task.Delay(time);                timenow.CellText  = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");            }        } .net framework4.5以上版