昨天把johnson流水灯的实验做了,还是脑子清醒的时候好,一会儿就写完了并且成功运行了。
其实johnson流水灯就是普通的流水灯加上一些按键控制而已,这个过程中除了普通的流水灯以外,还有一个就是按键消抖,只有这两个点吧。
要实现的功能是:按键1,控制流水灯是否移动,按键2,控制流水灯向左移动,按键3,控制流水灯向右移动,流动频率设置为0.5s/次。
实现起来还是比较简单的,先上代码。
/*-----------------------------------------------------------------------------------------
Module Name: test_johnson.v
File Author: creation
Finish Time: 2013-07-26
Description: johnson流水灯实验
-------------------------------------------------------------------------------------------
Version Design Coding Simulate Review Rel data
V1.0
-------------------------------------------------------------------------------------------
Version Modified History
V1.0 draft
-----------------------------------------------------------------------------------------*/
`timescale 1ns / 10ps
module test_johnson(
input wire asy_rst ,//复位
input wire clk ,//50MHZ晶振
input wire [2:0] key ,//3位按键,0位控制是否流动,1位控制从低向高,2位控制从高向低
output wire [7:0] o_led //8位led
);
/*---------------------------Internal registers and wires--------------------------------*/
/* Register Output Signals */
reg [7:0] led; //led输出寄存器
/* internal logic use signals */
reg [19:0] cnt_key; //按键消抖计时
reg [2:0] key_r; //上一时钟周期按键状态寄存
reg [2:0] key_p; //按键消抖时20ms前按键状态寄存
reg [2:0] flag_key_p; //按键下降沿标志
reg [2:0] flag_key; //流水灯流动状态
reg [24:0] cnt_led; //流水灯流动计时0.5s/位
/* ----------------------Logical Implementation------------------------------------------*/
/*-----------------------------------------------------------------------------------------
gen the cnt_key signal
-----------------------------------------------------------------------------------------*/
always @ ( posedge clk or negedge asy_rst )
begin
if ( !asy_rst )
cnt_key <= 1'b0;
else if ( flag_key_p != 1'b0 ) //按键出现下降沿时flag_key_p不等于0,将cnt_key赋值1,开始消抖计时
cnt_key <= 1'b1;
else if ( cnt_key == 20'd999999 ) //按键消抖计时满20ms,计时复位等待下次,按键下降沿
cnt_key <= 1'b0;
else if ( cnt_key != 1'b0 ) //flag_key_p出现下降沿后cnt_key才会出现非0值
cnt_key <= cnt_key + 1'b1;
else
cnt_key <= cnt_key;
end
/*-----------------------------------------------------------------------------------------
gen the key_r signal
-----------------------------------------------------------------------------------------*/
always @ ( posedge clk or negedge asy_rst )
begin
if ( !asy_rst )
key_r <= 3'b111; //按键初始状态为高电平
else
key_r <= key; //锁存上一时钟周期按键状态
end
/*-----------------------------------------------------------------------------------------
gen the flag_key_p signal
-----------------------------------------------------------------------------------------*/
always @ ( posedge clk or negedge asy_rst )
begin
if ( !asy_rst )
flag_key_p <= 3'b000; //按键下降沿检测初始状态全为0,表示假
else
flag_key_p <= key_r & ( ~key ); //脉冲边沿检测
end
/*-----------------------------------------------------------------------------------------
gen the flag_key signal
-----------------------------------------------------------------------------------------*/
always @ ( posedge clk or negedge asy_rst )
begin
if ( !asy_rst )
flag_key <= 3'b010; //流水灯控制状态初始不流动,若按下流动则从低向高流动
else if ( cnt_key == 20'd999999 && key == key_p ) //当按键消抖计时加满并且当前key值与20ms前键值相同,
//则开始改变流水灯控制状态
begin
if ( key[0] == 1'b0 ) //0位,控制是否流动,若按下该状态进行反转
flag_key[0] <= ~flag_key[0];
else if ( ~key[1] || ~key[2] ) //1位,控制从低向高,2位控制从高向低
flag_key[2:1] <= ~key[2:1]; //这两位若按下这对flag_key_p高两位进行赋值,此过程中需要对key反转
else
flag_key <= flag_key;
end
else
flag_key <= flag_key;
end
/*-----------------------------------------------------------------------------------------
gen the key_p signal
-----------------------------------------------------------------------------------------*/
always @ ( posedge clk or negedge asy_rst )
begin
if ( !asy_rst )
key_p <= 3'b111;
else if ( flag_key_p != 1'b0 ) //出现按键下降沿对按键状态进行锁存,保持20ms
key_p <= key_r;
else
key_p <= key_p;
end
/*-----------------------------------------------------------------------------------------
gen the cnt_led signal
-----------------------------------------------------------------------------------------*/
always @ ( posedge clk or negedge asy_rst )
begin //0.5s流水灯
if ( !asy_rst )
cnt_led <= 1'b0;
else if ( cnt_led == 25'd24999999 )
cnt_led <= 1'b0;
else
cnt_led <= cnt_led + 1'b1;
end
/*-----------------------------------------------------------------------------------------
gen the led signal
-----------------------------------------------------------------------------------------*/
always @ ( posedge clk or negedge asy_rst )
begin
if ( !asy_rst )
led <= 8'hfe;
else if ( cnt_led == 25'd24999999 && flag_key[0] == 1'b1 ) //0位,为真,开始流动
begin
if ( flag_key[1] == 1'b1 ) //1位,为真,从低向高
led <= {led[6:0],led[7]};
else if ( flag_key [2] == 1'b1) //2位,为真,从高向低
led <= {led[0],led[7:1]};
else
led <= led;
end
else
led <= led;
end
assign o_led = led; //输出寄存器输出
endmodule
这段代码中主要是两个功能
1.使用脉冲边沿检测实现了按键出现下降沿时开始启动消抖计时的功能,并且将初始键值赋值给key_p,用于20ms后与key进行比较看是否20ms前后按键值相同,相同则按键有效,并对flag_key_p进行相应赋值。
2.流水灯根据flag_key的值进行相应流动,这个就太简单了。
虽然程序很简单,但前几天试过一次,不知道是不是那次脑子比较乱,愣是没整理好思路。这次,为了保险起见,我先写了流水灯的部分,然后先写了控制一个按键的代码试试,最后才将多个按键一起控制的代码完善了。至于仿真,这次没做,虽然不符合规范,但这次我也深深深深体会到按功能模块进行代码设计是多么重要,第一次我就是整个过程一起考虑,才成了一锅粥,也肯定有刚开始学,脑子根本转不过来的原因,但我相信,代码看多,写多,自然就好了。
用户1718548 2013-7-27 15:40
飞言走笔 2013-7-27 14:20