stepfpga的初级项目1

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_33946416/article/details/95886797

1. led流水灯的实现

硬件说明**
方法一:模块化设计:在之前的实验中我们做了3-8译码器和时钟分频,如果把这两个结合起来,我们就能搭建一个自动操作的流水LED显示。框图如下:
在这里插入图片描述
方法二:循环赋值:这是一种很简洁的实现流水灯效果逻辑,就是定义一个8位的变量,在每个时钟上升沿将最低位赋值给最高位,其他位右移一位,这就实现了循环赋值。这8位输出到LED就能实现流水灯。
verilog的实现
方法一实现:

module flashled (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

方法二实现:

module flashled (clk,rst,led);
 
	input clk,rst;						
	output [7:0] led;				
 
        wire clk1h;                                     //定义一个中间变量,表示分频得到的时钟,用作计数器的触发        
 
        //例化分频器模块,产生一个1Hz时钟信号		
        divide #(.WIDTH(32),.N(12000000)) u2 (         //传递参数
			.clk(clk),
			.rst_n(rst),                   //例化的端口信号都连接到定义好的信号
			.clkout(clk1h)
			);                             
 
        //1Hz时钟上升沿触发循环赋值
        reg [7:0] led;	
        always@(posedge clk1h or negedge rst)
	begin
		if(!rst)
			led <= 8'b11111110;            // <=为非阻塞赋值
		else 
			led <= {led[0],led[7:1]};      //当时钟上升沿来一次,执行一次赋值,赋值内容是led[0]与led[7:1]重新拼接成8位赋给led,相当于循环右移
	end	
 endmodule

引脚分配
按照下面表格定义输入输出信号

信号 引脚 信号 引脚
clk C1 led[3] M11
rst L14 led[4] P11
led[0] N13 led[5] N10
led[1] M12 led[6] N9
led[2] P12 led[7] p9

2.按键消抖

硬件说明
按键抖动的原理
在这里插入图片描述
抖动的产生 :通常的按键所用的开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。
消除抖动的措施:一般我们采用软件方法消抖。即检测到按键按下动作之后进行10ms-20ms左右的延时,当前沿的抖动消失之后再一次检测按键的状态。如果仍然是按下的电平状态,则认为这是一次真正的按键按下;同样检测到按键释放,也要做10ms~20ms延时,检测到后沿抖动消失后认为是一个完整的按键弹起过程。
消抖的好处:执行按键消抖有两个好处,
消除误触发:我们想通过按键来翻转信号(例如按下一次led亮,在按一次led灭),如果没有进行消抖,则会产生很多误触发造成不必要的翻转。
记录按键次数:执行按键消抖可以让我们记录按键动作的次数,在很多应用里这非常有用。

  • [1 ] 要消除按键的抖动,我们需要去扫描按键,也就是不断的去采集按键的状态。软件消抖时我们一般只考虑按键按下时的抖动,而放弃对释放时抖动的消除。用系统时钟(频率较高)去采集按键状态,当检测到按下时用计数器延时20ms,再去检测按键状态,如果这时仍为按下状态,确认是一次按下动作,否侧的话认为无按键按下。如何检测按键状态变化就需要用到脉冲边沿检测的方法。
    脉冲边沿检测

  • [ 1] 检测按键按下时要用到脉冲边沿检测的方法,捕捉信号的突变、捕捉时钟的上升下降沿等经常会用到这种方法。简单地说就是用一个频率更高的时钟去触发要检测的信号,用两个寄存器去储存相邻两个时钟采集到的值,然后进行异或运算,如果不为零,代表发生了上升沿或者下降沿。
    -[2] 在按键消抖的过程中,同样运用了脉冲边沿检测。用两个寄存器储存相邻时钟采集的值(例如datapre,data),然后将data取反与前一个值相与(state=datapre&(~data)),如果为1,则判断有下降沿既按键按下由高到低;否则无变化。
    将一个信号由连续时钟采集,相邻两个钟触发的值存入两个寄存器
    Verilog代码
    当按下一次led变亮,再按下一次led变暗。首先做个试验,对按键不做处理通过按键来控制led翻转。

    module top(
     		key,          //按键输入
     		rst,          //复位输入
     		led           //led输出
     		);
     	input key,rst;
     	output reg led;
      
     	always @(key or rst)
     		if (!rst)             //复位时led熄灭
     			led = 1;
     		else if(key == 0)     
     			led = ~led;   //按键按下时led翻转
     		else
     			led = led;
     endmodule
    

未经过消抖的程序下载到小脚丫上会发现按键有时不能够控制led翻转,这是因为按键的抖动造成了led状态变化不可控,所以我们必须将抖动消除。下面是一种延时去抖的程序

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

以上就是一个N位按键的消抖程序,如果有按键按下会输出一个时钟周期的高脉冲。下面我们可以试试用这个按键消抖的输出来触发LED的显示,既按键一次LED翻转。你也可以不加按键消抖试试用按键来控制LED(按一次变亮,再按一次灭掉)。
下面的程序是例化调用debounce模块来控制LED

module top (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

引脚分配
在这里插入图片描述

3.计时控制

实现一个篮球赛场上常见的24秒计时器
硬件说明
实现由数码管作为显示模块,按键作为控制信号的输入(包含复位信号和暂停信号),Lattice MXO2 4000HC作为控制核心的篮球读秒系统,实现框图如下:
在这里插入图片描述
Verilog代码

module counter
(
	clk				,    //时钟
	rst				,    //复位
	hold			,    //启动暂停按键
	seg_led_1		,    //数码管1
	seg_led_2		,    //数码管2
	led                  //led
);
 
	input 	clk,rst;
	input	hold;
 
	output 	[8:0]	seg_led_1,seg_led_2;
	output 	reg	[7:0]	led;
 
	wire		clk1h;        //1Hz时钟
	wire		hold_pulse;   //按键消抖后信号
	reg			hold_flag;    //按键标志位
	reg			back_to_zero_flag	; //计时完成信号
	reg   		[6:0]   seg		[9:0];  
	reg			[3:0]	cnt_ge;      //个位
	reg			[3:0]	cnt_shi;     //十位
 
	initial 
	begin
		seg[0] = 7'h3f;	   //  0
		seg[1] = 7'h06;	   //  1
		seg[2] = 7'h5b;	   //  2
		seg[3] = 7'h4f;	   //  3
		seg[4] = 7'h66;	   //  4
		seg[5] = 7'h6d;	   //  5
		seg[6] = 7'h7d;	   //  6
		seg[7] = 7'h07;	   //  7
		seg[8] = 7'h7f;	   //  8
		seg[9] = 7'h6f;	   //  9
/*若需要显示A-F,
		seg[10]= 7'hf7;	   //  A
		seg[11]= 7'h7c;	   //  b
		seg[12]= 7'h39;    //  C
		seg[13]= 7'h5e;    //  d
		seg[14]= 7'h79;    //  E
		seg[15]= 7'h71;    //  F
	end
	// 启动/暂停按键进行消抖
	debounce  U2 (
				.clk(clk),
				.rst(rst),
				.key(hold),
				.key_pulse(hold_pulse)
				);
	// 用于分出一个1Hz的频率	
	divide #(.WIDTH(32),.N(12000000)) U1 ( 
			.clk(clk),
			.rst_n(rst),      
			.clkout(clk1h)
			);
    //按键动作标志信号产生
	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;
    //24秒倒计时控制
	always @ (posedge clk1h 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
			cnt_ge <= cnt_ge -1;
		end
	//计时完成点亮led
	always @ ( back_to_zero_flag)begin
		if (back_to_zero_flag==1)
			led = 8'b0;
		else
			led = 8'b11111111;
		end
 
	assign seg_led_1[8:0] = {2'b00,seg[cnt_ge]};
 
	assign seg_led_2[8:0] = {2'b00,seg[cnt_shi]};
 
 
endmodule

引脚分配
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_33946416/article/details/95886797