北邮22级信通院数电:Verilog-FPGA(4)第三周实验:按键消抖、呼吸灯、流水灯 操作流程&&注意事项

北邮22信通一枚~

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

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

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

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

目录

一.注意事项

二.按键消抖

2.1  LED_debounce代码

2.2debounce.v代码

 2.3管脚分配

三.流水灯

3.1  LED_flash.v代码

3.2 divide.v代码

3.3decode38.v代码

3.4管脚分配

四.呼吸灯

4.1 LED_breath.v代码

 4.2管脚分配


一.注意事项

烧录之前首先检查这几个参数是否调整完毕:

 

没调的赶紧去调!!!

二.按键消抖

2.1  LED_debounce代码

module LED_debounce (clk,rst,key,led);
 
        input             clk;
        input             rst;
        input 	          key;                      				
	output   reg      led;        
 
        wire              key_pulse;
 
        //当按键按下时产生一个高脉冲,翻转一次led
        always @(posedge clk  or  negedge rst)
           begin
             if (!rst) 
		led <= 1'b1;
	     else if (key_pulse)
		led <= ~led;
	     else
                led <= led;
	   end    
         //例化消抖module,这里没有传递参数N,采用了默认的N=1     
         debounce  u1 (                               
                       .clk (clk),
                       .rst (rst),
                       .key (key),
                       .key_pulse (key_pulse)
                       );
 endmodule

2.2debounce.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

 2.3管脚分配

三.流水灯

3.1  LED_flash.v代码

module LED_flash(clk,rst,led);
 
	input clk,rst;						
	output [7:0] led;				
 
 
        reg   [2:0] cnt ; //定义了一个3位的计数器,输出可以作为3-8译码器的输入
 
        wire clk1h;       //定义一个中间变量,表示分频得到的时钟,用作计数器的触发        
 
        //例化module decode38,相当于调用
        decode38 u1 (                                   
			.sw(cnt),     //例化的输入端口连接到cnt,输出端口连接到led  
			.led(led)
			);
 
        //例化分频器模块,产生一个1Hz时钟信号		
        divide #(.WIDTH(32),.N(12000000)) u2 (         //传递参数
			.clk(clk),
			.rst_n(rst),                   //例化的端口信号都连接到定义好的信号
			.clkout(clk1h)
			);                             
 
        //1Hz时钟上升沿触发计数器,循环计数		
        always @(posedge clk1h or negedge rst)
	     if (!rst)
		cnt <= 0;
	     else
		cnt <= cnt +1;
 
        endmodule

3.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     

3.3decode38.v代码

module decode38 (sw,led);
 
	input [2:0] sw;            //开关输入信号,利用了其中3个开关作为3-8译码器的输入
	output [7:0] led;		   //输出信号控制特定LED
 
        reg [7:0] led;          //定义led为reg型变量,在always过程块中只能对reg型变量赋值
 
        //always过程块,括号中sw为敏感变量,当sw变化一次执行一次always中所有语句,否则保持不变
	always @ (sw)
	begin
		case(sw)                        //case语句,一定要跟default语句
			3'b000:	led=8'b0111_1111;   //条件跳转,其中“_”下划线只是为了阅读方便,无实际意义  
			3'b001:	led=8'b1011_1111;   //位宽'进制+数值是Verilog里常数的表达方法,
                                        //进制可以是b、o、d、h(二、八、十、十六进制)
			3'b010:	led=8'b1101_1111;
			3'b011:	led=8'b1110_1111;
			3'b100:	led=8'b1111_0111;
			3'b101:	led=8'b1111_1011;
			3'b110: led=8'b1111_1101;
			3'b111:	led=8'b1111_1110;
			default: ;
		endcase
	end
 
endmodule

3.4管脚分配

四.呼吸灯

4.1 LED_breath.v代码

module LED_breath(clk,rst,led);
 
	input clk;             //系统时钟输入
	input rst;             //复位输出
	output led;            //led输出
 
	reg [24:0] cnt1;       //计数器1
	reg [24:0] cnt2;       //计数器2
	reg flag;              //呼吸灯变亮和变暗的标志位
 
	//parameter   CNT_NUM = 2400;	//计数器的最大值 period = (2400^2)*2 ~= 12000000 = 1s由亮到暗0.5s,由暗到亮0.5s
	parameter   CNT_NUM = 3464;	//计数器的最大值 period = (3464^2)*2 ~= 24000000 = 2s由亮到暗1s,由暗到亮1s
	//产生计数器cnt1
	always@(posedge clk or negedge rst) begin 
		if(!rst) begin
			cnt1<=13'd0;
			end 
                else begin
		     if(cnt1>=CNT_NUM-1) 
                        cnt1<=1'b0;
		     else 
                        cnt1<=cnt1+1'b1; 
                    end
		end
 
	//产生计数器cnt2
	always@(posedge clk or negedge rst) begin 
		if(!rst) begin
			cnt2<=13'd0;
			flag<=1'b0;
			end 
                else begin
		     if(cnt1==CNT_NUM-1) begin   //当计数器1计满时计数器2开始计数加一或减一
			if(!flag) begin              //当标志位为0时计数器2递增计数,表示呼吸灯效果由暗变亮
				if(cnt2>=CNT_NUM-1)      //计数器2计满时,表示亮度已最大,
                                         //标志位变高,之后计数器2开始递减
                                    flag<=1'b1;
				else
                                    cnt2<=cnt2+1'b1;
			end else begin  //当标志位为高时计数器2递减计数
				if(cnt2<=0) //计数器2级到0,表示亮度已最小,标志位变低,之后计数器2开始递增
                                    flag<=1'b0;
				else 
                                    cnt2<=cnt2-1'b1;
			end
			end
                            else cnt2<=cnt2;//计数器1在计数过程中计数器2保持不变
			end
			end
 
	//比较计数器1和计数器2的值产生自动调整占空比输出的信号,输出到led产生呼吸灯效果
	assign	led = (cnt1<cnt2)?1'b0:1'b1;
 
endmodule

 4.2管脚分配

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青山入墨雨如画

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值