FPGA实战篇——【2】按键控制LED灯闪烁

FPGA实战——按键控制LED灯

实验任务

无按键按下时,LED 灯全灭;
按键 key0 按下时,LED 灯显示自左往右的流水;
按键 key1 按下时,LED 灯显示自右往左的流水;
按键 key2 按下时,四个 LED 灯同时闪烁;
按键 key3 按下时,LED 灯全亮。

硬件电路

在这里插入图片描述

程序设计

1.RTL文件编写

首先我们要了解一下编程的思路。通过按键来实现灯全亮全灭其实很容易,但是要实现流水灯的效果,这就要结合我们上一节的流水灯的知识,就需要一个计数器。我们的思路是这样的:
(由于初学,我们先不进行消抖;然后按键也要一直按着才一直执行,松开就不再执行对应程序段)

  • 流水灯是需要计数器的,所以我们先定义计数器。这个计数器以0.2s为周期。
  • 如果key0被按下,是需要流水灯的,我们就先设置一个led_ctrl变量,来为led目前的状态编码,这个状态每计数器周期就变化一次(也就是一直在流水,就是我们在硬件电路是否调用的问题,调用它就显示出流水)
  • 我们检测按键是否被按下,当key0被按下的时候,我们就对led_ctrl进行判断,对4个状态进行赋值(led_ctrl有4个状态,分别对应4个流水灯亮的状态),一直按着key0就可以实现流水
  • 同理,剩余3个按键的就很简单了。
  • 总结来就是,我们需要一个计数器(一个always块),一个控制led状态的程序(一个always块),一个检测按键,控制led(一个always块)

程序代码如下:

module key_led(
		input            sys_clk,
		input            sys_rst_n,
				   
		input 	   [3:0] key,
				   
		output reg [3:0] led
		);

reg [23:0] cnt;      //计数器 
reg [1:0]  led_ctrl; //led状态变量

//0.2秒计数器
always @ (posedge sys_clk or negedge sys_rst_n) begin
		if(!sys_rst_n)
			cnt <= 24'd0;
		else if(cnt < 24'd1000_0000)
			cnt <= cnt + 1'b1;
		else
			cnt <= 24'd0; //这是已经数到最大值了,就赋值0	
end

//led状态赋值  ,这是控制led的状态的,只有流水的时候有用
always @ (posedge sys_clk or negedge sys_rst_n) begin
		if(!sys_rst_n)
			led_ctrl <= 2'b00;
		else if(cnt == 24'd1000_0000)   //计数器周期0.2s,所以led每0.2s状态加一
			led_ctrl <= led_ctrl + 1'b1;
		else
			led_ctrl <= led_ctrl;
end

//按键控制led状态
always @ (posedge sys_clk or negedge sys_rst_n) begin
		if(!sys_rst_n)
			led <= 4'b0000;
		else if(key[0] == 0)  //如果key0按下,实现从左向右的流水灯
			case(led_ctrl)   
				2'b00  :  led <= 4'b0001;
				2'b01  :  led <= 4'b0010;
				2'b10  :  led <= 4'b0100;
				2'b11  :  led <= 4'b1000;
				default:  led <= 4'b0000;
			endcase
		else if(key[1] == 0)  //如果key1按下,实现从右向左的流水灯
			case(led_ctrl)   
				2'b11  :  led <= 4'b0001;
				2'b10  :  led <= 4'b0010;
				2'b01  :  led <= 4'b0100;
				2'b00  :  led <= 4'b1000;
				default:  led <= 4'b0000;
			endcase	
		else if(key[2] == 0)  //如果key2按下,所有灯同时闪烁
			case(led_ctrl)   
				2'b00  :  led <= 4'b1111;
				2'b01  :  led <= 4'b0000;
				2'b10  :  led <= 4'b1111;
				2'b11  :  led <= 4'b0000;
				default:  led <= 4'b0000;
			endcase	
		else if(key[3] == 0)  //如果key3按下,所有灯都亮。
			led <= 4'b1111;
		else                
			led <= 4'b0000;	
end
endmodule

2.约束文件ucf

NET sys_clk            TNM_NET = sys_clk_pin;
TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 20ns HIGH 50%;

NET sys_clk     LOC = T8 | IOSTANDARD = LVCMOS33;
NET sys_rst_n   LOC = L3 | IOSTANDARD = LVCMOS33;

NET led<0>   	LOC = P4 | IOSTANDARD = LVCMOS33;
NET led<1>   	LOC = N5 | IOSTANDARD = LVCMOS33;
NET led<2>   	LOC = P5 | IOSTANDARD = LVCMOS33;
NET led<3>   	LOC = M6 | IOSTANDARD = LVCMOS33;

NET key<0>   	LOC = C3 | IOSTANDARD = LVCMOS33;
NET key<1>   	LOC = D3 | IOSTANDARD = LVCMOS33;
NET key<2>   	LOC = E4 | IOSTANDARD = LVCMOS33;
NET key<3>   	LOC = E3 | IOSTANDARD = LVCMOS33;

两次实验都出的一个错误。报错如下
key_led.ucf(1): Syntax error. Ensure that the previous constraint specification was terminated with ‘;’.

实际上,不用看后面的 ;提示,问题出在,没有加等号=
TNM_NET = sys_clk_pin; 被写成了
TNM_NET sys_clk_pin; 就报错了

而且有意思的是,这个错误两次都犯过,都是刚开始没意识到的时候,第一次编译总是没有报错,第二次编译才没通过,很奇怪,如果有大佬看见,希望告诉一下原因。

3.下载程序

编译完成之后,就可以生成bit流文件然后下载到开发板了。

RTL图

在这里插入图片描述
综合之后就可以看,但是实验来看,需要编译成功,才能看到我们期望看到的那个图。如下图设置好端口,点击右下角的Create Schematic即可。
在这里插入图片描述
生成RTL图如下
在这里插入图片描述

仿真tb文件编写

因为按键检测,硬件上需要按键去按下松开,是我们人的操作,那tb文件去仿真的时候如何实现呢。我们的思路就是每延时几秒,通过程序让key[i] = 0,通过软件赋值来实现。
//注意,我们要写的操作是在initial中就完成,而不是在always这里,always这是生成时钟。
//新建文件后, 从头开始修改、编写代码!!!修改模块名,initial语句块的赋值等。。。。

`timescale 1ns / 1ns

module tb_key_led();

	// Inputs
	reg sys_clk;
	reg sys_rst_n;
	reg [3:0] key;

	// Outputs
	wire [3:0] led;

	// Instantiate the Unit Under Test (UUT)
	key_led u_key_led (
		.sys_clk(sys_clk), 
		.sys_rst_n(sys_rst_n), 
		.key(key), 
		.led(led)
	);

	initial begin
		// Initialize Inputs
		sys_clk    <= 1'b0;
		sys_rst_n  <= 1'b0;
		key        <= 4'b1111;
		
		// Wait 100 ns for global reset to finish
		#100;
        sys_rst_n  <= 1'b1;
		// Add stimulus here
		// 1ns * 800_000_000 = 0.8s 
		#2000_0000 key[0] <= 0;    
		#2000_0000 key[0] <= 1;
                   key[1] <= 0; 
		#2000_0000 key[1] <= 1;
                   key[2] <= 0; 
		#2000_0000 key[2] <= 1;
                   key[3] <= 0; 
		#2000_0000 key[3] <= 1;		
	end
	//注意,我们要写的操作是在initial中就完成,而不是在always这里,always这是生成时钟
	always #10 sys_clk = ~sys_clk;
	
endmodule

出现了一个错误
(vopt-2133) Instantiating ‘u_key_led’ has exceeded the recursion depth limit of 200.
在这里插入图片描述
就是这里重名了,导致嵌套死循环。修改如下在这里插入图片描述
最终1s时间内的仿真如图:
在这里插入图片描述

以上就是按键控制LED闪烁的工程,包括例化、按键消抖等还未设计,将在之后的工程练习中学习。

  • 5
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值