【FPGA】课程设计:简单计时器闹钟

这篇具有很好参考价值的文章主要介绍了【FPGA】课程设计:简单计时器闹钟。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文是EDA实验的课程设计
完整源码文件获取方式见文末

演示视频

少废话,先看东西。

EDA实验-闹钟演示视频

一、实验目的

设计一个电子闹钟。要求电路上电后自动计时,到达预置的闹响时刻后,由蜂鸣器发出音乐报警。闹响时刻可利用按键设置,设置范围0~999999。
此次实验除了满足上述基本功能外,额外添置了流水灯功能,当到达预置的闹响时刻后,不仅蜂鸣器会发出音乐报警,并且LED会形成流水灯。

二、实验环境

2.1 硬件环境

本实验采用的开发板是正点原子的开拓者FPGA开发板。

2.2 软件环境

使用软件:Quartus Ⅱ (18.1) 、ModelSim(10.5)
操作系统:Windows 10(64位)

三、方案设计及理论计算

3.1 原理框图

【FPGA】课程设计:简单计时器闹钟

输入:时钟信号,重置信号,按键信号。
输出:数码管位选信号、数码管段选信号、流水灯控制信号、蜂鸣器控制信号。

3.2 分频器

输入:时钟信号,重置信号。
输出:分频时钟信号。
功能:对50MHz分频。
板子上电之后需每隔1s进行计数,板子的时钟频率为50MHz,为满足这一功能,需要设计一个分频器,对板子的50MHz频率进行分频,从而输出一个1Hz的时钟信号。
分频器模值、系统时钟和期望输出时钟频率关系为
【FPGA】课程设计:简单计时器闹钟

所以,把50MHz时钟分频,输出1Hz的时钟,分频器的模值为
【FPGA】课程设计:简单计时器闹钟

为了保证分频器正常工作,计数器寄存器所能表示的最大值必须大于分频器的模值。这里,设置把计数器寄存器的位数设定为26位。
流程图如图 1所示。
【FPGA】课程设计:简单计时器闹钟

数码管的动态显示和流水灯也同样用到分频器,原理一样。其中,数码管对系统时钟频率进行了10分频,流水灯对系统时钟频率进行了5分频。

3.3 按键消抖

输入:时钟信号,重置信号,按键信号。
输出:按键数据有效信号,按键消抖后数据。
功能:消除按键抖动。
由于每次按下按键时存在抖动,容易引起按键的多次触发,因此按键消抖模块就可以解决这个问题。
按键消抖主要通过延时来实现,即当按键的一个状态保持20ms以上,即锁存按键的状态,这样既保证按键消抖的稳定又保证了一定的灵敏性。原理图如图2所示。
【FPGA】课程设计:简单计时器闹钟

3.4 状态机

实验中需要通过按键实现时间显示和时间设定两个模式的切换,本实验采用了一位寄存器作为状态机,当按键按下时,状态机可以从0、1两个状态之间来回切换。原理如图3所示:
【FPGA】课程设计:简单计时器闹钟

为了防止时间设定模式时对蜂鸣器和流水灯的干扰,本实验单独设置了一个寄存器信号,用于隔离两种模式。同时,显示时间和设定时间采用不同的寄存器存储,这样方便比较并且当时间设定时,时间显示会处于暂停的状态。

3.5 数码管动态显示

输入:重置信号,时钟信号,计数时间,设定时间,数码管使能信号、模式信号。
输出:数码管位选信号,数码管段选信号。
功能:将计数时间/设定时间在数码管上进行显示。
在数码管动态显示模块中,首先需要根据模式信号来判断需要显示的时间,即时间显示模式显示计数时间,时间设定模式显示设定时间。之后,通过整除去尾、取模取尾的计算方式,将时间每个位数上的数据提取出来。例如,提取十位数的数据公式为:
【FPGA】课程设计:简单计时器闹钟

将每一位上的数据提取出来后,将其转换成8421BCD码,与每一个数码管进行绑定。通过对系统时钟进行10分频,得到的频率为5MHz的数码管驱动时钟,用来控制数码管的位选信号,使每一个数码管以1ms的时间周期轮流显示。
当数码管需要显示时,通过段选信号将每一位数码管绑定的数据进行转换,从而显示出正确的数值。本实验的开发板采用的是共阳极数码管,段选真值表如图4所示。
【FPGA】课程设计:简单计时器闹钟

3.6 引脚分配

根据开发手册配置相关引脚如图 5所示。
【FPGA】课程设计:简单计时器闹钟

四、波形仿真

4.1 仿真参数

sys_clk:时钟信号
sys_rst_n:复位信号
key0:按键信号,用于切换模式
key1:按键信号,在设定模式下,按一次设定时间加一秒
key2:按键信号,在设定模式下,按一次设定时间减一秒
key3:按键信号,在设定模式下,按一次设定时间加十秒
beep:蜂鸣器信号
seg_sel:数码管位选信号
seg_led:数码管段选信号
led:流水灯信号

4.2 仿真波形图

【FPGA】课程设计:简单计时器闹钟

可以看到上电之后,程序开始正常计时,首先按下模式切换按键key0,切换到设定时间模式,之后按下key1,设定时间加一,再按下key2,设定时间减一,再按下key3,设定时间加十,共设计十秒时间。最后再按一次key0,重新切换到计时模式。当计时时间到达设定时间时,蜂鸣器的beep信号变为高电平,流水灯开始工作。按下复位信号后,蜂鸣器的beep信号变为低电平,停止工作,流水灯也回复到初始状态。

五、实验结果

将程序通过驱动下载到开发板上后,开发板开始自动计数,按下key0进入设定模式,通过其它三个按键设定好闹钟时间。再按key0,返回计数模式。当时间到达设定时间时,蜂鸣器播放音乐,流水灯开始工作,达到预期效果。
实验效果图如图所示。
【FPGA】课程设计:简单计时器闹钟

六、完整代码

顶层模块:

1.	module top_alarm(  
2.	    //global clock  
3.	    input            sys_clk  ,       // 全局时钟信号  
4.	    input            sys_rst_n,       // 复位信号(低有效)  
5.	    input           key0,           //按键信号  
6.	    input           key1,  
7.	    input           key2,  
8.	    input           key3,  
9.	    //seg_led interface  
10.	    output    [5:0]  seg_sel  ,       // 数码管位选信号  
11.	    output    [7:0]  seg_led  ,        // 数码管段选信号  
12.	    output   [3:0]  led ,       //4个LED灯  
13.	    output beep  
14.	);  
15.	  
16.	//wire define  
17.	wire    [19:0]  data;                 // 数码管正常计数显示的数值  
18.	wire    [19:0]  set_data;         // 数码管设定闹钟显示的数值  
19.	wire   en;                                // 数码管显示使能信号  
20.	wire    en_beep;                                // 蜂鸣器使能信号  
21.	wire   key_value0;  
22.	wire   key_value1;  
23.	wire   key_value2;  
24.	wire   key_value3;  
25.	wire   key_flag0;  
26.	wire   key_flag1;  
27.	wire   key_flag2;  
28.	wire   key_flag3;  
29.	  
30.	// 对四个按键分别进行消抖  
31.	key_debounce k0 (  
32.	    .sys_clk(sys_clk),          //外部50M时钟  
33.	    .sys_rst_n(sys_rst_n),        //外部复位信号,低有效  
34.	    .key(key0),              //外部按键输入  
35.	    .key_flag(key_flag0),         //按键数据有效信号  
36.	    .key_value(key_value0)       //按键消抖后的数据    
37.	    );  
38.	    
39.	key_debounce k1(  
40.	    .sys_clk(sys_clk),          //外部50M时钟  
41.	    .sys_rst_n(sys_rst_n),        //外部复位信号,低有效  
42.	    .key(key1),              //外部按键输入  
43.	    .key_flag(key_flag1),         //按键数据有效信号  
44.	    .key_value(key_value1)       //按键消抖后的数据    
45.	);  
46.	      
47.	 key_debounce k2(  
48.	    .sys_clk(sys_clk),          //外部50M时钟  
49.	    .sys_rst_n(sys_rst_n),        //外部复位信号,低有效  
50.	    .key(key2),              //外部按键输入  
51.	    .key_flag(key_flag2),         //按键数据有效信号  
52.	    .key_value(key_value2)       //按键消抖后的数据    
53.	 );  
54.	      
55.	 key_debounce k3(  
56.	    .sys_clk(sys_clk),          //外部50M时钟  
57.	    .sys_rst_n(sys_rst_n),        //外部复位信号,低有效  
58.	    .key(key3),              //外部按键输入  
59.	    .key_flag(key_flag3),         //按键数据有效信号  
60.	    .key_value(key_value3)       //按键消抖后的数据    
61.	 );  
62.	      
63.	//计数器模块,产生数码管需要显示的数据  
64.	count u_count(  
65.	    .clk           (sys_clk  ),       // 时钟信号  
66.	    .rst_n         (sys_rst_n),       // 复位信号  
67.	    .key_flag0   (key_flag0) ,    //按键有效信号  
68.	    .key_value0 (key_value0),   //消抖后的按键信号    
69.	    .key_flag1   (key_flag1) ,    //按键有效信号  
70.	    .key_value1 (key_value1),   //消抖后的按键信号    
71.	    .key_flag2   (key_flag2) ,    //按键有效信号  
72.	    .key_value2 (key_value2),   //消抖后的按键信号    
73.	    .key_flag3   (key_flag3) ,    //按键有效信号  
74.	    .key_value3 (key_value3),   //消抖后的按键信号    
75.	    .set_data(set_data) ,      // 设定0~999999时间  
76.	    .data          (data),       // 6位数码管要显示的数值  
77.	    .en            (en),       // 数码管使能信号  
78.	    .en_beep (en_beep ),  
79.	    .mode   (mode)  
80.	);  
81.	  
82.	  
83.	    
84.	//数码管动态显示模块  
85.	seg_led u_seg_led(  
86.	    .clk           (sys_clk  ),       // 时钟信号  
87.	    .rst_n         (sys_rst_n),       // 复位信号  
88.	      
89.	    .original_data           (data),       // 显示的数值  
90.	    .set_data(set_data),  
91.	    .en            (en       ),       // 数码管使能信号  
92.	    .mode   (mode),  
93.	      
94.	    .seg_sel       (seg_sel  ),       // 位选  
95.	    .seg_led       (seg_led  )        // 段选  
96.	);  
97.	  
98.	// 蜂鸣器音乐模块  
99.	alarm_music alarm(  
100.	    .clk           (sys_clk  ),           // 时钟信号  
101.	    .rst_n         (sys_rst_n),       // 复位信号  
102.	    .data          (data) ,              // 实际的时间  
103.	    .set_data (set_data ),      // 设定的时间  
104.	    .en_beep (en_beep ),  
105.	    .beep(beep)  
106.	);  
107.	  
108.	flow_light light(  
109.	    .sys_clk  (sys_clk) ,  //系统时钟  
110.	    .sys_rst_n (sys_rst_n) ,  //系统复位,低电平有效  
111.	    .data          (data) ,              // 实际的时间  
112.	    .set_data (set_data ),      // 设定的时间  
113.	    .en_beep (en_beep ),  
114.	    .led (led)         //4个LED灯  
115.	    );  
116.	endmodule  

按键消抖模块

1.	module key_debounce(  
2.	    input            sys_clk,          //外部50M时钟      
3.	    input            sys_rst_n,        //外部复位信号,低有效  
4.	      
5.	    input            key,              //外部按键输入  
6.	    output reg       key_flag,         //按键数据有效信号  
7.	    output reg       key_value         //按键消抖后的数据    
8.	    );  
9.	  
10.	//reg define      
11.	reg [31:0] delay_cnt;  
12.	reg        key_reg;  
13.	  
14.	  
15.	always @(posedge sys_clk or negedge sys_rst_n) begin   
16.	    if (!sys_rst_n) begin   
17.	        key_reg   <= 1'b1;  
18.	        delay_cnt <= 32'd0;  
19.	    end  
20.	    else begin  
21.	        key_reg <= key;  
22.	        if(key_reg != key)             //一旦检测到按键状态发生变化(有按键被按下或释放)  
23.	            delay_cnt <= 32'd1000000;  //给延时计数器重新装载初始值(计数时间为20ms)  
24.	        else if(key_reg == key) begin  //在按键状态稳定时,计数器递减,开始20ms倒计时  
25.	                 if(delay_cnt > 32'd0)  
26.	                     delay_cnt <= delay_cnt - 1'b1;  
27.	                 else  
28.	                     delay_cnt <= delay_cnt;  
29.	             end             
30.	    end     
31.	end  
32.	  
33.	always @(posedge sys_clk or negedge sys_rst_n) begin   
34.	    if (!sys_rst_n) begin   
35.	        key_flag  <= 1'b0;  
36.	        key_value <= 1'b1;            
37.	    end  
38.	    else begin  
39.	        if(delay_cnt == 32'd1) begin   //当计数器递减到1时,说明按键稳定状态维持了20ms  
40.	            key_flag  <= 1'b1;         //此时消抖过程结束,给出一个时钟周期的标志信号  
41.	            key_value <= key;          //并寄存此时按键的值  
42.	        end  
43.	        else begin  
44.	            key_flag  <= 1'b0;  
45.	            key_value <= key_value;   
46.	        end    
47.	    end     
48.	end  
49.	      
50.	endmodule   

计数模块

1.	module count(  
2.	    //mudule clock  
3.	    input                   clk  ,      // 时钟信号  
4.	    input                   rst_n,      // 复位信号  
5.	    input        key_flag0,    //按键有效信号  
6.	    input        key_value0,    //消抖后的按键信号  
7.	    input        key_flag1,    //按键有效信号  
8.	    input        key_value1,    //消抖后的按键信号    
9.	    input        key_flag2,    //按键有效信号  
10.	    input        key_value2,    //消抖后的按键信号  
11.	    input        key_flag3,    //按键有效信号  
12.	    input        key_value3,    //消抖后的按键信号  
13.	      
14.	    //user interface  
15.	    output   reg [19:0]     data ,      // 6个数码管要显示的数值  
16.	    output   reg [19:0]     set_data ,      // 设定0~999999时间  
17.	    output   reg     en  ,     // 数码管使能信号  
18.	    output   reg     en_beep  ,     // 蜂鸣器使能信号  
19.	    output   reg     mode                //为了控制不同状态的显示,同时需输出当前模式  
20.	    );  
21.	  
22.	//parameter define  
23.	parameter  MAX_NUM = 26'd5000_0000;      // 计数器计数的最大值 1s/20ns  
24.	  
25.	//reg define  
26.	reg    [25:0]   cnt ;                   // 计数器,用于计时1s  
27.	reg             flag;                   // 标志信号  
28.	reg             flag_key;                   // 标志信号  
29.	  
30.	  
31.	//模式选择  
32.	always @ (posedge clk or negedge rst_n) begin  
33.	    if(!rst_n)  
34.	         mode <= 1'd0;  
35.	    else if(key_flag0&& (~key_value0)) begin //判断按键是否有效按下  
36.	         mode <= mode + 1'b1;   // 一位状态机,0、1循环  
37.	         end  
38.	    else begin  
39.	            mode <= mode;  
40.	     end     
41.	 end  
42.	   
43.	//计数器对系统时钟计数达1s时,输出一个时钟周期的脉冲信号  
44.	always @ (posedge clk or negedge rst_n) begin  
45.	    if (!rst_n) begin  
46.	        cnt <= 26'b0;  
47.	        flag<= 1'b0;  
48.	    end  
49.	    else if (cnt < MAX_NUM - 1'b1) begin  
50.	        cnt <= cnt + 1'b1;  
51.	        flag<= 1'b0;  
52.	    end  
53.	    else begin  
54.	        cnt <= 26'b0;  
55.	        flag <= 1'b1;  
56.	    end  
57.	end   
58.	  
59.	  
60.	//数码管需要显示的数据,从0累加到999999  
61.	always @ (posedge clk or negedge rst_n) begin  
62.	    if (!rst_n)begin  
63.	        data  <= 20'b0;  
64.	        set_data <=20'b0;  
65.	        en    <= 1'b0;  
66.	    end   
67.	    // 按键0切换状态,mode=0时正常计数  
68.	    else if ( mode == 1'b0) begin  
69.	        en    <= 1'b1;                  //打开数码管使能信号  
70.	        en_beep    <= 1'b1;               
71.	        if (flag) begin                 //显示数值每隔1s累加一次  
72.	            if(data < 20'd999999)   
73.	                data <= data +1'b1;       
74.	            else  
75.	                data <= 20'b0;  
76.	        end   
77.	    else   
78.	           data <= data;  
79.	    end   
80.	    // 按键0切换状态,mode=1时进入设定模式  
81.	    else if (mode == 1'b1) begin  
82.	        en    <= 1'b1;     
83.	        en_beep    <= 1'b0;     
84.	        //按下按键1设定数+1  
85.	        if (key_flag1&& (~key_value1))  begin  
86.	                if(set_data < 20'd999999)   
87.	                        set_data <= set_data + 1'b1;  
88.	                 else  
89.	                        set_data <= 20'b0;  
90.	                 end  
91.	         //按下按键3设定数+10  
92.	        else if (key_flag3&& (~key_value3))  begin  
93.	                if(set_data < 20'd999999)   
94.	                        set_data <= set_data + 4'd10;  
95.	                 else  
96.	                        set_data <= 20'b0;  
97.	                 end  
98.	        //按下按键2设定数-1  
99.	        else if (key_flag2&& (~key_value2))  begin  
100.	                if(set_data > 20'd0)   
101.	                        set_data <= set_data - 1'b1;  
102.	                 else  
103.	                        set_data <= 20'b0;  
104.	                 end  
105.	         //不按键的时设定数维持不变  
106.	        else   
107.	                 set_data <=  set_data;  
108.	        end   
109.	end   
110.	  
111.	endmodule   

数码管显示模块

1.	module seg_led(  
2.	    input                   clk    ,        // 时钟信号  
3.	    input                   rst_n  ,        // 复位信号  
4.	  
5.	    input         [19:0]    original_data   ,        // 6位数码管要显示的计数时间数值  
6.	    input         [19:0]    set_data  ,        // 6位数码管要显示的设定时间数值  
7.	    input                   en     ,        // 数码管使能信号  
8.	    input         [1:0] mode ,  
9.	  
10.	    output   reg  [5:0]     seg_sel,        // 数码管位选,最左侧数码管为最高位  
11.	    output   reg  [7:0]     seg_led         // 数码管段选  
12.	    );  
13.	  
14.	//parameter define  
15.	localparam  CLK_DIVIDE = 4'd10     ;        // 时钟分频系数  
16.	localparam  MAX_NUM    = 13'd5000  ;        // 对数码管驱动时钟(5MHz)计数1ms所需的计数值  
17.	  
18.	//reg define  
19.	reg    [ 3:0]             clk_cnt  ;        // 时钟分频计数器  
20.	reg                       dri_clk  ;        // 数码管的驱动时钟,5MHz  
21.	reg    [23:0]             num      ;        // 24位bcd码寄存器  
22.	reg    [12:0]             cnt0     ;        // 数码管驱动时钟计数器  
23.	reg                       flag     ;        // 标志信号(标志着cnt0计数达1ms)  
24.	reg    [2:0]              cnt_sel  ;        // 数码管位选计数器  
25.	reg    [3:0]              num_disp ;        // 当前数码管显示的数据  
26.	  
27.	  
28.	  
29.	//wire define  
30.	wire   [19:0]              data    ;        // 需要生成的data  
31.	wire   [3:0]              data0    ;        // 个位数  
32.	wire   [3:0]              data1    ;        // 十位数  
33.	wire   [3:0]              data2    ;        // 百位数  
34.	wire   [3:0]              data3    ;        // 千位数  
35.	wire   [3:0]              data4    ;        // 万位数  
36.	wire   [3:0]              data5    ;        // 十万位数  
37.	  
38.	  
39.	//确定选择的数据  
40.	  
41.	assign data = (mode== 1'b0)? original_data : set_data;  
42.	  
43.	//提取显示数值所对应的十进制数的各个位  
44.	assign  data0 = data % 4'd10;               // 个位数  
45.	assign  data1 = data / 4'd10 % 4'd10   ;    // 十位数  
46.	assign  data2 = data / 7'd100 % 4'd10  ;    // 百位数  
47.	assign  data3 = data / 10'd1000 % 4'd10 ;   // 千位数  
48.	assign  data4 = data / 14'd10000 % 4'd10;   // 万位数  
49.	assign  data5 = data / 17'd100000;          // 十万位数  
50.	   
51.	//对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk  
52.	always @(posedge clk or negedge rst_n) begin  
53.	   if(!rst_n) begin  
54.	       clk_cnt <= 4'd0;  
55.	       dri_clk <= 1'b1;  
56.	   end  
57.	   else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin  
58.	       clk_cnt <= 4'd0;  
59.	       dri_clk <= ~dri_clk;  
60.	   end  
61.	   else begin  
62.	       clk_cnt <= clk_cnt + 1'b1;  
63.	       dri_clk <= dri_clk;  
64.	   end  
65.	end  
66.	  
67.	//将20位2进制数转换为8421bcd码(即使用4位二进制数表示1位十进制数)  
68.	always @ (posedge dri_clk or negedge rst_n) begin  
69.	    if (!rst_n)  
70.	        num <= 24'b0;  
71.	    else begin  
72.	        if (data5) begin     //如果显示数据为6位十进制数,  
73.	            num[23:20] <= data5;         //则依次给6位数码管赋值  
74.	            num[19:16] <= data4;  
75.	            num[15:12] <= data3;  
76.	            num[11:8]  <= data2;  
77.	            num[ 7:4]  <= data1;  
78.	            num[ 3:0]  <= data0;  
79.	        end  
80.	        else begin                           
81.	            if (data4) begin //如果显示数据为5位十进制数,则给低5位数码管赋值  
82.	                num[19:0] <= {data4,data3,data2,data1,data0};  
83.	                num[23:20] <= 4'd10; //不需要显示负号时,则第6位不显示任何字符  
84.	            end  
85.	            else begin                   //如果显示数据为4位十进制数,则给低4位数码管赋值  
86.	                if (data3) begin  
87.	                    num[15: 0] <= {data3,data2,data1,data0};  
88.	                    num[23:20] <= 4'd10; //第6位不显示任何字符  
89.	                    num[19:16] <= 4'd10;  
90.	                end  
91.	                else begin               //如果显示数据为3位十进制数,则给低3位数码管赋值  
92.	                    if (data2) begin  
93.	                        num[11: 0] <= {data2,data1,data0};  
94.	                                         //第6、5位不显示任何字符  
95.	                        num[23:16] <= {2{4'd10}};  
96.	                        num[15:12] <= 4'd10;  
97.	                    end  
98.	                    else begin           //如果显示数据为2位十进制数,则给低2位数码管赋值  
99.	                        if (data1) begin  
100.	                            num[ 7: 0] <= {data1,data0};  
101.	                                         //第6、5、4位不显示任何字符  
102.	                            num[23:12] <= {3{4'd10}};  
103.	                            num[11:8] <=  4'd10;  
104.	                        end  
105.	                        else begin       //如果显示数据为1位十进制数,则给最低位数码管赋值  
106.	                            num[3:0] <= data0;  
107.	                                         //第6、5位不显示任何字符  
108.	                            num[23:8] <= {4{4'd10}};  
109.	                            num[7:4] <= 4'd10;  
110.	                        end  
111.	                    end  
112.	                end  
113.	            end  
114.	        end  
115.	    end  
116.	end  
117.	  
118.	//每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号  
119.	always @ (posedge dri_clk or negedge rst_n) begin  
120.	    if (rst_n == 1'b0) begin  
121.	        cnt0 <= 13'b0;  
122.	        flag <= 1'b0;  
123.	     end  
124.	    else if (cnt0 < MAX_NUM - 1'b1) begin  
125.	        cnt0 <= cnt0 + 1'b1;  
126.	        flag <= 1'b0;  
127.	     end  
128.	    else begin  
129.	        cnt0 <= 13'b0;  
130.	        flag <= 1'b1;  
131.	     end  
132.	end  
133.	  
134.	//cnt_sel从0计数到5,用于选择当前处于显示状态的数码管  
135.	always @ (posedge dri_clk or negedge rst_n) begin  
136.	    if (rst_n == 1'b0)  
137.	        cnt_sel <= 3'b0;  
138.	    else if(flag) begin  
139.	        if(cnt_sel < 3'd5)  
140.	            cnt_sel <= cnt_sel + 1'b1;  
141.	        else  
142.	            cnt_sel <= 3'b0;  
143.	    end  
144.	    else  
145.	        cnt_sel <= cnt_sel;  
146.	end  
147.	  
148.	//控制数码管位选信号,使6位数码管轮流显示  
149.	always @ (posedge dri_clk or negedge rst_n) begin  
150.	    if(!rst_n) begin  
151.	        seg_sel  <= 6'b111111;              //位选信号低电平有效  
152.	        num_disp <= 4'b0;             
153.	    end  
154.	    else begin  
155.	        if(en) begin  
156.	            case (cnt_sel)  
157.	                3'd0 :begin  
158.	                    seg_sel  <= 6'b111110;  //显示数码管最低位  
159.	                    num_disp <= num[3:0] ;  //显示的数据  
160.	                end  
161.	                3'd1 :begin  
162.	                    seg_sel  <= 6'b111101;  //显示数码管第1位  
163.	                    num_disp <= num[7:4] ;  
164.	                end  
165.	                3'd2 :begin  
166.	                    seg_sel  <= 6'b111011;  //显示数码管第2位  
167.	                    num_disp <= num[11:8];  
168.	                end  
169.	                3'd3 :begin  
170.	                    seg_sel  <= 6'b110111;  //显示数码管第3位  
171.	                    num_disp <= num[15:12];  
172.	                end  
173.	                3'd4 :begin  
174.	                    seg_sel  <= 6'b101111;  //显示数码管第4位  
175.	                    num_disp <= num[19:16];  
176.	                end  
177.	                3'd5 :begin  
178.	                    seg_sel  <= 6'b011111;  //显示数码管最高位  
179.	                    num_disp <= num[23:20];  
180.	                end  
181.	                default :begin  
182.	                    seg_sel  <= 6'b111111;  
183.	                    num_disp <= 4'b0;  
184.	                end  
185.	            endcase  
186.	        end  
187.	        else begin  
188.	            seg_sel  <= 6'b111111;          //使能信号为0时,所有数码管均不显示  
189.	            num_disp <= 4'b0;  
190.	        end  
191.	    end  
192.	end  
193.	         
194.	  
195.	         
196.	//控制数码管段选信号,显示字符  
197.	always @ (posedge dri_clk or negedge rst_n) begin  
198.	    if (!rst_n )  
199.	        seg_led <= 8'hc0;  
200.	    else begin  
201.	        case (num_disp)  
202.	            4'd0 : seg_led <= 8'b11000000; //显示数字 0  
203.	            4'd1 : seg_led <= 8'b11111001; //显示数字 1  
204.	            4'd2 : seg_led <= 8'b10100100; //显示数字 2  
205.	            4'd3 : seg_led <= 8'b10110000; //显示数字 3  
206.	            4'd4 : seg_led <= 8'b10011001; //显示数字 4  
207.	            4'd5 : seg_led <= 8'b10010010; //显示数字 5  
208.	            4'd6 : seg_led <= 8'b10000010; //显示数字 6  
209.	            4'd7 : seg_led <= 8'b11111000; //显示数字 7  
210.	            4'd8 : seg_led <= 8'b10000000; //显示数字 8  
211.	            4'd9 : seg_led <= 8'b10010000; //显示数字 9  
212.	            4'd10: seg_led <= 8'b111111111;           //不显示任何字符  
213.	            default:   
214.	                   seg_led <= 8'b11000000;  
215.	        endcase  
216.	    end  
217.	end  
218.	  
219.	endmodule   

蜂鸣器模块

1.	module alarm_music(  
2.	    input clk,  
3.	    input rst_n,  
4.	    input   [19:0]data ,      // 6个数码管要显示的数值  
5.	    input   [19:0]set_data ,      // 设定0~999999时间  
6.	    input  en_beep,  
7.	    output  beep  
8.	);  
9.	  
10.	reg  music_flag        ;       // 标志音频产生信号  
11.	  
12.	always @ (posedge clk or negedge rst_n) begin  
13.	    if(!rst_n)  
14.	        music_flag  <= 1'b0;  
15.	    else if((data ==set_data&& set_data>1'b0)&& (en_beep==1'b1))  //判断按键是否有效按下  
16.	        music_flag  <= 1'b1;          
17.	end  
18.	  
19.	//参数定义  
20.	          
21.	localparam    M1 = 95600,  
22.	                        M2 = 85150,  
23.	                        M3 = 75850,  
24.	                        M4 = 71600,  
25.	                        M5 = 63750,  
26.	                        M6 = 56800,  
27.	                        M7 = 50600;  
28.	                          
29.	        parameter   TIMES   = 500;  
30.	          
31.	//信号定义  
32.	    reg             buzzer_r    ;  
33.	      
34.	    reg [16:0]  cnt0    ;//计数每个音符对应的周期  
35.	    reg [8:0]       cnt1    ;//计数每个音符重复多少次  
36.	    reg [5:0]       cnt2    ;//一共有多少个音符  
37.	      
38.	    reg [16:0]  pre_set;//预装载值  
39.	    reg     [16:0]  pre_div;//设定占空比  
40.	  
41.	    always@(posedge clk or negedge rst_n)begin   
42.	        if(~rst_n)begin   
43.	            cnt0 <= 0;  
44.	        end   
45.	        else begin   
46.	            if(cnt0 == pre_set-1)  
47.	                cnt0 <= 0;  
48.	            else   
49.	                cnt0 <= cnt0 + 1;  
50.	        end   
51.	    end   
52.	      
53.	    always@(posedge clk or negedge rst_n)begin   
54.	        if(~rst_n)begin   
55.	            cnt1 <= 0;  
56.	        end   
57.	        else if(cnt0 == pre_set-1)begin   
58.	            if(cnt1 == TIMES-1)  
59.	                cnt1 <= 0;  
60.	            else   
61.	                cnt1 <= cnt1 + 1;  
62.	        end   
63.	    end  
64.	      
65.	    always@(posedge clk or negedge rst_n)begin   
66.	        if(~rst_n)begin   
67.	            cnt2 <= 0;  
68.	        end   
69.	        else if(cnt1 == TIMES-1 && cnt0 == pre_set-1)begin   
70.	            if(cnt2 == 32-1)  
71.	                cnt2 <= 0;  
72.	            else   
73.	                cnt2 <= cnt2 + 1;  
74.	        end   
75.	    end  
76.	      
77.	    //pre_set  查找表 选择每个音频对应的计数周期  
78.	    always@(posedge clk or negedge rst_n)begin   
79.	        if(~rst_n)begin   
80.	            pre_set <= 0;  
81.	        end   
82.	        else begin   
83.	            case(cnt2)  
84.	            0 :pre_set <= M1;  
85.	            1 :pre_set <= M2;  
86.	            2 :pre_set <= M3;  
87.	            3 :pre_set <= M1;  
88.	            4 :pre_set <= M1;  
89.	            5 :pre_set <= M2;  
90.	            6 :pre_set <= M3;  
91.	            7 :pre_set <= M1;  
92.	            8 :pre_set <= M3;  
93.	            9 :pre_set <= M4;  
94.	            10:pre_set <= M5;  
95.	            11:pre_set <= M3;  
96.	            12:pre_set <= M4;  
97.	            13:pre_set <= M5;  
98.	            14:pre_set <= M5;  
99.	            15:pre_set <= M6;  
100.	            16:pre_set <= M5;  
101.	            17:pre_set <= M4;  
102.	            18:pre_set <= M3;  
103.	            19:pre_set <= M1;  
104.	            20:pre_set <= M5;  
105.	            21:pre_set <= M6;  
106.	            22:pre_set <= M5;  
107.	            23:pre_set <= M4;  
108.	            24:pre_set <= M3;  
109.	            25:pre_set <= M1;  
110.	            26:pre_set <= M2;  
111.	            27:pre_set <= M5;  
112.	            28:pre_set <= M1;  
113.	            29:pre_set <= M2;  
114.	            30:pre_set <= M5;  
115.	            31:pre_set <= M1;  
116.	            default:pre_set <= M1;  
117.	            endcase  
118.	        end   
119.	    end   
120.	      
121.	      
122.	    always @ (posedge clk or negedge rst_n)begin   
123.	        if(~rst_n)begin   
124.	            pre_div <= 0;  
125.	        end   
126.	        else begin   
127.	            pre_div <= pre_set>>1;  
128.	        end   
129.	    end   
130.	      
131.	    //assign pre_div = pre_set>>1;//  /2; 设定占空比为50%  
132.	  
133.	    always @ (posedge clk or negedge rst_n)begin   
134.	        if(~rst_n)begin   
135.	            buzzer_r <= 1'b1;  
136.	        end   
137.	        else if(cnt0 < pre_div)begin   
138.	            buzzer_r <= 1'b1;  
139.	        end   
140.	        else begin   
141.	            buzzer_r <= 1'b0;  
142.	        end   
143.	    end   
144.	  
145.	assign beep = music_flag ? buzzer_r : 1'b0;  
146.	      
147.	  
148.	  
149.	  
150.	  
151.	endmodule   

流水灯模块

1.	module flow_light(  
2.	    input               sys_clk  ,  //系统时钟  
3.	    input               sys_rst_n,  //系统复位,低电平有效  
4.	    input   [19:0]data ,      // 6个数码管要显示的数值  
5.	    input   [19:0]set_data ,      // 设定0~999999时间  
6.	    input  en_beep,  
7.	    output  reg  [3:0]  led         //4个LED灯  
8.	    );  
9.	  
10.	//reg define  
11.	reg [23:0] counter;  
12.	reg led_flag;                                                                                                                                      
13.	//计数器对系统时钟计数,计时0.2秒  
14.	always @(posedge sys_clk or negedge sys_rst_n) begin  
15.	    if (!sys_rst_n)  
16.	        counter <= 24'd0;  
17.	    else if (counter < 24'd1000_0000 )  
18.	        counter <= counter + 1'b1;  
19.	    else  
20.	        counter <= 24'd0;  
21.	end  
22.	  
23.	always @(posedge sys_clk or negedge sys_rst_n) begin  
24.	    if (!sys_rst_n)  
25.	        led_flag <= 1'd0;  
26.	    else if ((data ==set_data&& set_data>1'b0) && (en_beep==1'b1))  
27.	        led_flag <= 1'b1;  
28.	    else  
29.	        led_flag <= led_flag;  
30.	end  
31.	  
32.	//通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态  
33.	always @(posedge sys_clk or negedge sys_rst_n) begin  
34.	    if (!sys_rst_n)  
35.	        led <= 4'b0001;  
36.	    else if((counter == 24'd1000_0000 )&& (led_flag == 1'b1))   
37.	        led[3:0] <= {led[2:0],led[3]};  
38.	    else  
39.	        led <= led;  
40.	end  
41.	  
42.	endmodule   

源码文件获取

项目开源在我的Github:https://github.com/zstar1003/XDU_Homework/tree/main/EDA%E5%AE%9E%E9%AA%8C文章来源地址https://www.toymoban.com/news/detail-440193.html

到了这里,关于【FPGA】课程设计:简单计时器闹钟的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Vivado设计秒表计时器实现00分00.00秒到59分59.99秒的计时(verilog语言)

    目录 0.写在最前 一、课程设计要求: 三、名词说明解释 四、Vivado代码实现部分 五、仿真测试程序 六、约束文件 七、开发板结果展示 八、关于改进/扩展 ① 增加秒与 0.1s 之间的分隔符“.”号的点亮: ② 取消 0.1s,0.01s 显示,增加小时形成“时分.秒”的显示方式 ③ 其它改

    2024年02月06日
    浏览(17)
  • 基于AT89C51单片机的倒数计时器设计

    1.设计任务 利用AT89C51单片机为核心控制元件,设计一个简易的数字电压表,设计的系统实用性强、操作简单,实现了智能化、数字化。 本设计采用单片机为主控芯片,结合 周边 电路组成 LED 彩灯的 闪烁 控制系统器,用来控制 红色 的LED 闪烁 发光,实现 LED灯在倒计时结束后

    2024年02月03日
    浏览(20)
  • 51单片机通过计时器实现倒计时

    软件 : Keil5+Proteus7 元件 : AT89C51 * 1,7SEG-MPX2-CA * 1

    2024年02月16日
    浏览(22)
  • RIP四大计时器

    RIP 计时器(以下均以华为 ensp 中信息为参考) 希望有需要的小伙伴可以参考参考,如有误解、请指正! 一、实验原理 1. 更新计时器( Update Timer ) Update time(更新时间):指运行RIP协议的路由器向其连接口广播自己的路由信息的时间间隔(用于更新RIP路由表信息),控制

    2024年02月03日
    浏览(13)
  • 555计时器原理

    以Multisim上的555计时器为例: 图0.0 555计时器包含八个引脚 分别为: RST - Reset 复位引脚(低电平有效) DIS - Discharge 三极管集电极Collector输入引脚 THR - Threshold 上阈值电压引脚 TRI - Trigger 触发引脚 CON - Control voltage 1 电压控制引脚 OUT - Output 信号输出引脚 VCC GND 555定时器内部功能图

    2024年02月05日
    浏览(16)
  • WPF计时器功能

    本文实现WPF的计时器功能是通过system.timers.timer这个组件实现的。现在网上相关的资料有很多,我只是在自己的工作中刚好遇到要实现这个功能,中间也走了很多的弯路,不停的参考网上现有的资源,终于实现了基本的定时功能。希望本文可以帮助到您,让您花更少的时间来完

    2024年02月05日
    浏览(20)
  • 24秒计时器

    方案一:采用计数器(74LS192)作为核心部分。同时选择(74LS47)作为BCD码译码器来对7段数码显示管进行译码驱动,两个七段共阳数码显示管进行显示。采用计时器(NE555)制成的多谐振荡器,进行秒脉冲的输入。因为我们需要对其进行暂停、清零、报警和自动清零等控制,所

    2024年02月06日
    浏览(20)
  • java计时器

      在 Java中,我们有一个重要的概念:同步和异步。同步就是 Java中的线程安全,异步就是 Java中的线程非安全。 在使用 JVM时,我们一般都是用 start ()方法启动一个线程,然后设置时间,比如定时器,定时器是在某个指定的时间执行相应的任务。但是,在实际应用中,我们

    2023年04月18日
    浏览(21)
  • Qt实现计时器

    一、样图 二、代码 mainwidow.h mainwindow.cpp main.cpp ui_mainwindow.h

    2024年02月07日
    浏览(12)
  • Spring计时器StopWatch

    StopWatch类是Spring框架中用于测量代码执行时间的工具类,它提供了一系列属性来记录监测信息。 本文基于spring-boot-starter-web:2.2.2RELEASE版本。 源码: 属性 描述 id 表示该StopWatch对象的唯一标识符,可以通过构造函数传入。 keepTaskList 表示是否保留所有记录的监测任务,,默认为

    2024年02月11日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包