问题叙述:
当拨码开关SW3处于OFF时, LED停止不动,只有一个LED处于点亮,并且点亮的LED不会变化;而SW3处于ON状态时,流水灯处于流动状态。导航按键S2被按下后,LED流动方向是从上到下(D9到D2方向);导航按键S3被按下后,LED流动方向是从下到上(D2到D9)。
这里面用到了拨码开关,按键,以及led灯,那么我把这三种外设的电路图给出来,并简单解释:
拨码开关:
当拨码开关处于“ON”状态时,SW0输出低电平;当拨码开关处于“OFF”状态时,SW0输出高电平。
led灯:
可见,低电平led灯亮;
按键:
独立按键一般有2组管脚,这2组管脚在按键未被按下时是断开的,在按键被按下时则是导通的。
基于此原理,我们一般会把按键的一个管脚接地,另一个管脚上拉到VCC,并且也连接到GPIO。这样,在按键未被按下时,GPIO的连接状态为上拉到VCC,则键值为1;按键被按下时,GPIO虽然还是上拉到VCC,但同时被导通的另一个管脚拉到地了,所以它的键值实际上是0。
其他没什么好说的,就是按键这个问题,既然用到了按键,就要对按键的消抖问题进行处理,本实验也不例外,在用Verilog HDL语言设计硬件电路时,必须考虑按键抖动的问题:
关于按键抖动的消除,上篇博文如果明白了的话,稍加改动,就可以直接拿过来用了。
贴出博文地址:《按键消抖与LED控制》实验的个人思考与总结
一开始,我也没想到把上篇博文中的按键抖动代码拿过来改动,准备过一遍实验,参考特权同学的代码,可是发现,代码我看不懂,这样的话,马克思大概是说过这么一句话,忘了,大概意思是需求是催生生产力。特权同学的代码看了好久,就是不明白为什么?我得自己找办法呀,于是突然想到了为什么非要用你的代码呢?我改一下上篇博文的按键消抖的代码,再添加一些控制led等的东西不就好了。于是产生了如下的Verilog HDL设计代码:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 19:47:47 08/16/2018
// Design Name:
// Module Name: led_flow
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module led_flow(
clk,rst_n,
switch,
sw1_n,sw2_n,
led
);
input clk; //主时钟信号,50MHz
input rst_n; //复位信号,低有效
input switch; //switch表示拨码开关,低电平表示on,高电平表示off
input sw1_n,sw2_n; //2个独立按键,低表示按下
output[7:0] led; //8个发光二极管
reg [7:0] led;
//---------------------------------------------------------------------------
reg[1:0] key_rst;
always @(posedge clk or negedge rst_n)
if (!rst_n) key_rst <= 2'b11;
else key_rst <= {sw2_n,sw1_n};
reg[1:0] key_rst_r; //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中
always @ ( posedge clk or negedge rst_n )
if (!rst_n) key_rst_r <= 2'b11;
else key_rst_r <= key_rst;
//当寄存器key_rst由1变为0时,key_an的值变为高,维持一个时钟周期
wire[2:0] key_an = key_rst_r & (~key_rst);
/*
key_rst 1 1 1 0 0 1
~key_rst 0 0 0 1 1 0
key_rst_r 1 1 1 0 0 1
key_an 0 0 1 0 0
*/
//---------------------------------------------------------------------------
reg[19:0] cnt; //计数寄存器
always @ (posedge clk or negedge rst_n)
if (!rst_n) cnt <= 20'd0; //异步复位
else if(key_an) cnt <=20'd0;
else cnt <= cnt + 1'b1;
reg[1:0] low_sw;
always @(posedge clk or negedge rst_n)
if (!rst_n) low_sw <= 2'b11;
else if (cnt == 20'hfffff) //满20ms,将按键值锁存到寄存器low_sw中 cnt == 20'hfffff
low_sw <= {sw2_n,sw1_n};
//---------------------------------------------------------------------------
reg [1:0] low_sw_r; //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中
always @ ( posedge clk or negedge rst_n )
if (!rst_n) low_sw_r <= 2'b11;
else low_sw_r <= low_sw;
/*
low_sw 11 11 11 10 10 10
~low_sw 00 00 00 01 01 01
low_sw_r 11 11 11 10 10 10
led_ctrl 00 00 00 01 00 00
*/
//当寄存器low_sw由1变为0时,led_ctrl的值变为高,维持一个时钟周期
wire[1:0] led_ctrl = low_sw_r[1:0] & ( ~low_sw[1:0]);
reg dir;
always @ (posedge clk or negedge rst_n)
if (!rst_n) begin
dir <= 1'b0; //复位信号有效时,led灯方向控制寄存器置0,也就是默认为向左流动
end
else begin
if(led_ctrl[0] == 1'b1) dir <= 1'b1; //控制led灯向右流动
if(led_ctrl[1] == 1'b1) dir <= 1'b0; //控制led灯向左流动
end
reg [23:0] delay; //led灯延迟计数器
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
delay <= 24'b0;
else
delay <= delay + 24'b1;
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
led <= 8'b0111_1111; //复位信号有效时,led灯处于静止状态,且只有一个灯led7亮
else if(delay == 24'hffffff) //否则,延迟到达24'hffffff,进行如下操作
begin
if(switch == 1'b1) //如果拨码开关处于off状态时,则led灯处于静止状态,且只有一个等led7亮
led <= 8'b0111_1111;
else //否则,也就是拨码开关处于on状态时,则led做出如下动作:
begin
case(dir) //dir是控制方向的状态,如果dir <= 1'b1,则从左向右流动,如果dir <= 'b0,则从右向左流动
1'b1: led <= {led[0],led[7:1]};
1'b0: led <= {led[6:0],led[7]};
default: ;
endcase
end
end
end
endmodule
我注释也写的很清楚,我就不多解释了,如果你真的想了解,那就认真研读代码,肯定能懂;不过前提是度过了按键消抖的那篇博文。
实验结果十分完美,和我设想的一样。
如何用ISE进行整个流程,我以前也用一个简单的实例说过:全过程实现一个最简单的FPGA项目之PWM蜂鸣器控制
那纯理论上也说过:XIlinx FPGA开发基本流程(一)(总介绍)