北邮22级信通院数电:Verilog-FPGA(11)第十一周实验(2)设计一个24秒倒计时器

北邮22信通一枚~

跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章

持续关注作者 迎接数电实验学习~

获取更多文章,请访问专栏:

北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客

目录

一.代码部分

1.1  counter_24.v

1.2  divide.v

1.3  debounce.v

二.管脚分配

三.实验效果


一.代码部分

1.1  counter_24.v

module counter_24
(
	input clk,rst,hold,
	output [8:0] seg_led_1,
	output [8:0] seg_led_2,
	output reg [7:0] led
);

	wire clk_lh;
	wire hold_pulse;
	reg hold_flag;
	reg back_to_zero_flag;
	reg [6:0] seg [16:0];
	reg [3:0] cnt_ge;
	reg [3:0] cnt_shi;
	
	initial
		begin 
			seg[0] = 7'h3f;
			seg[1] = 7'h06;
			seg[2] = 7'h5b;
			seg[3] = 7'h4f;
			seg[4] = 7'h66;
			seg[5] = 7'h6d;
			seg[6] = 7'h7d;
			seg[7] = 7'h07;
			seg[8] = 7'h7f;
			seg[9] = 7'h6f;
			seg[10] = 7'hf7;
			seg[11] = 7'h7c;
			seg[12] = 7'h39;
			seg[13] = 7'h5e;
			seg[14] = 7'h79;
			seg[15] = 7'h71;
		end
	debounce debounce_1
	(
		.clk(clk),
		.rst(rst),
		.key(hold),
		.key_pulse(hold_pulse)
	);
	
	divide # (.WIDTH(32),.N(12000000)) divide_1
	(
		.clk(clk),
		.rst_n(rst),
		.clkout(clk_lh)
	);
	
	always @ (posedge hold_pulse)
		if(!rst==1)
			hold_flag <= 0;
		else 
			hold_flag <= ~hold_flag;
	always @ (*)
		if(!rst==1)
			back_to_zero_flag <= 0;
		else if( cnt_shi==0 && cnt_ge==0)
			back_to_zero_flag <= 1;
		else 
			back_to_zero_flag <= 0;
			
	always @ (posedge clk_lh or negedge rst)
		begin 
			if(!rst==1)
				begin 
					cnt_ge <= 4'd4;
					cnt_shi <=4'd2;
				end
			else if(hold_flag==1)
				begin 
					cnt_ge <= cnt_ge;
					cnt_shi <= cnt_shi;
				end
			else if(cnt_shi==0 && cnt_ge==0)
				begin 
					cnt_shi <= cnt_shi;
					cnt_ge <=cnt_ge;
				end
			else if(cnt_ge==0)
				begin 
					cnt_ge <=4'd9;
					cnt_shi <= cnt_shi-1;
				end
			else 
				begin cnt_ge <= cnt_ge-1; end
		end
	//计时完成点亮led
	always @ (back_to_zero_flag)
		begin 
			if(back_to_zero_flag == 1)
				led = 8'b0;
			else
				led = 8'b1111_1111;
		end
	
	assign seg_led_1[8:0] = {2'b00,seg[cnt_ge]};
	assign seg_led_2[8:0] = {2'b00,seg[cnt_shi]};

endmodule
				
		

1.2  divide.v

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     

1.3  debounce.v

module debounce (clk,rst,key,key_pulse);
 
        parameter       N  =  1;         //要消除的按键的数量
 
	input             clk;
        input             rst;
        input 	[N-1:0]   key;          //输入的按键					
	output  [N-1:0]   key_pulse;        //按键动作产生的脉冲	
 
        reg     [N-1:0]   key_rst_pre;  //定义一个寄存器型变量存储上一个触发时的按键值
        reg     [N-1:0]   key_rst;      //定义一个寄存器变量储存储当前时刻触发的按键值
 
        wire    [N-1:0]   key_edge;      //检测到按键由高到低变化是产生一个高脉冲
 
        //利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
        always @(posedge clk  or  negedge rst)
          begin
             if (!rst) begin
                 key_rst <= {N{1'b1}}; //初始化时给key_rst赋值全为1,{}中表示N个1
                 key_rst_pre <= {N{1'b1}};
             end
             else begin
                 key_rst <= key;       //第一个时钟上升沿触发之后key的值赋给key_rst,
                                       //同时key_rst的值赋给key_rst_pre
                 key_rst_pre <= key_rst;    //非阻塞赋值。
                                            //相当于经过两个时钟触发,
                                            //key_rst存储的是当前时刻key的值,
                                            //key_rst_pre存储的是前一个时钟的key的值
             end    
           end
 
        assign  key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。
                                                    //当key检测到下降沿时,
                                                    //key_edge产生一个时钟周期的高电平
 
        reg	[17:0]	  cnt;                       //产生延时所用的计数器,系统时钟12MHz,
                                                 //要延时20ms左右时间,至少需要18位计数器     
 
        //产生20ms延时,当检测到key_edge有效是计数器清零开始计数
        always @(posedge clk or negedge rst)
           begin
             if(!rst)
                cnt <= 18'h0;
             else if(key_edge)
                cnt <= 18'h0;
             else
                cnt <= cnt + 1'h1;
             end  
 
        reg     [N-1:0]   key_sec_pre;                //延时后检测电平寄存器变量
        reg     [N-1:0]   key_sec;                    
 
 
        //延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
        always @(posedge clk  or  negedge rst)
          begin
             if (!rst) 
                 key_sec <= {N{1'b1}};                
             else if (cnt==18'h3ffff)
                 key_sec <= key;  
          end
       always @(posedge clk  or  negedge rst)
          begin
             if (!rst)
                 key_sec_pre <= {N{1'b1}};
             else                   
                 key_sec_pre <= key_sec;             
         end      
       assign  key_pulse = key_sec_pre & (~key_sec);     
 
endmodule

二.管脚分配

三.实验效果

数码管显示24秒倒计时,倒计时结束后所有LED灯亮起。

最近更新

  1. TCP协议是安全的吗?

    2023-12-05 15:52:10       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-05 15:52:10       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-05 15:52:10       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-05 15:52:10       20 阅读

热门阅读

  1. 快速学习PyQt5的高级自定义控件

    2023-12-05 15:52:10       34 阅读
  2. 达梦8搭建DataWatch集群

    2023-12-05 15:52:10       25 阅读
  3. MySQL 学习笔记(刷题篇)

    2023-12-05 15:52:10       36 阅读
  4. Shell 脚本自动化备份与恢复实践

    2023-12-05 15:52:10       34 阅读
  5. Spring Boot 内置工具类

    2023-12-05 15:52:10       36 阅读
  6. 自动化任务:探索 Shell 脚本的实际应用

    2023-12-05 15:52:10       36 阅读
  7. c# 责任链模式

    2023-12-05 15:52:10       38 阅读
  8. centos8 redis 6.2.6源码安装+主从哨兵

    2023-12-05 15:52:10       39 阅读