FPGA基于 DE2-115 8路彩灯控制
前言
第一次写博客,第一次自主设计fpga,verilog语法只是粗略了解,做出来的东西只是能跑,深层次的理解和优化还在学习中。
设计要求
1、 8 路彩灯能演示三种花型(花型自拟);
2.、要求用 Verilog HDL 语言写出代码,用 QuartusPrime 平台编译并生成原理图,并能够用软件实现仿真;
3、彩灯可以实现多种节拍的变换;
4、 最终设计要求在 DE2-115 实验开发板上实现:彩灯用开发板上的发光二极管模拟,花型
控制开关用开发板上的按键进行选择。
设计思路
花型选择状态参量及花型状态按钮(key0-2)状态寄存
将花型状态按钮(key0-2)状态传递进寄存器 key_state,并以此寄存器状态作为不同花型的触发条件。这里对 key0-2 的状态进行传递,是为了演示更方便。
时钟信号分频及 ledr 状态参量寄存器 cnt
系统晶振为 50MHZ,及一次时钟为 20ns,频率过高,肉眼不能分辨。使用计数器,对系统时钟分频,达到肉眼可分辨闪烁频率。通过频率状态参量 sw,控制 ledr 闪烁状态参量 cnt
的变化频率,以此实现 ledr 不同频率闪烁。
彩灯 ledr 控制设计
通过 case 语句实现不同 cnt 状态的 ledr 输出,同时通过判断寄存器 key_state 状态,,
实现不同 ledr 花型输出。
上代码
/* ┌─┐ ┌─┐
# ┌──┘ ┴───────┘ ┴──┐
# │ │
# │ ─── │
# │ ─┬┘ └┬─ │
# │ │
# │ ─┴─ │
# │ │
# └───┐ ┌───┘
# │ │
# │ │
# │ │
# │ └──────────────┐
# │ │
# │ ├─┐
# │ ┌─┘
# │ │
# └─┐ ┐ ┌───────┬──┐ ┌──┘
# │ ─┤ ─┤ │ ─┤ ─┤
# └──┴──┘ └──┴──┘
# 神兽保佑
# 代码无BUG!
*/
module top_led(
input clk,
input rst_n, //复位,低电平触发
input wire[2:0]key, //花型按钮,低电平输入
input wire[2:0]sw, //频率状态选择
output reg [7:0] led_g, //复位状态指示灯
output reg [7:0] led_r //led花型
);
reg[2:0]key_state; //花型按钮状态寄存器
reg[31:0]cnt_clk; //分频计数器
reg[3:0]cnt; //ledr状态参量
//按钮信号寄存
always@(posedge clk or negedge rst_n)
begin
if (!rst_n)
key_state<=3'b0;
else if(key ==3'b110)
key_state<=3'b001;
else if(key ==3'b101)
key_state<=3'b010;
else if(key ==3'b011)
key_state<=3'b100;
else begin
key_state<=key_state;
end
end
//分频
always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
cnt<=4'd0;
cnt_clk<=32'd0;
end
else
begin
case (sw)
3'b000 :begin
if(cnt_clk <32'd4_999_999) // 0.1s一周期
begin
cnt_clk<=cnt_clk +32'd1;
cnt<=cnt;
end
else if(cnt==4'd7)
cnt<=4'd0;
else
begin
cnt<=cnt +4'd1;
cnt_clk<=32'd0;
end
end
3'b001 :begin
if(cnt_clk <32'd9_999_999) // 0.2s一周期
begin
cnt_clk<=cnt_clk +32'd1;
cnt<=cnt;
end
else if(cnt==4'd8)
cnt<=4'd0;
else
begin
cnt<=cnt +4'd1;
cnt_clk<=32'd0;
end
end
3'b011 :begin
if(cnt_clk <32'd19_999_999) // 0.4s一周期
begin
cnt_clk<=cnt_clk +32'd1;
cnt<=cnt;
end
else if(cnt==4'd8)
cnt<=4'd0;
else
begin
cnt<=cnt +4'd1;
cnt_clk<=32'd0;
end
end
3'b111 :begin
if(cnt_clk <32'd49_999_999) // 0.8s一周期
begin
cnt_clk<=cnt_clk +32'd1;
cnt<=cnt;
end
else if(cnt==4'd8)
cnt<=4'd0;
else
begin
cnt<=cnt +4'd1;
cnt_clk<=32'd0;
end
end
endcase
end
end
//led
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
led_g<=9'b11_111_111;//9'b00_000_000;
led_r<=8'b00_000_000;
end
else if(key_state==3'b001)//流水灯
begin
case (cnt)
4'd1 : led_r<=8'b00_000_001;
4'd2 : led_r<=8'b00_000_010;
4'd3 : led_r<=8'b00_000_100;
4'd4 : led_r<=8'b00_001_000;
4'd5 : led_r<=8'b00_010_000;
4'd6 : led_r<=8'b00_100_000;
4'd7 : led_r<=8'b01_000_000;
4'd8 : led_r<=8'b10_000_000;
default : led_r<=8'b00_000_000;
endcase
end
else if(key_state==3'b010)//跳变
begin
case (cnt)
4'd1 : led_r<=8'b11_001_100;
4'd2 : led_r<=8'b11_001_100;
4'd3 : led_r<=8'b00_110_011;
4'd4 : led_r<=8'b00_110_011;
4'd5 : led_r<=8'b11_001_100;
4'd6 : led_r<=8'b11_001_100;
4'd7 : led_r<=8'b00_110_011;
4'd8 : led_r<=8'b00_110_011;
default : led_r<=8'b00_000_000;
endcase
end
else if(key_state==3'b100) //累加
begin
case (cnt)
4'd1 : led_r<=8'b00_000_001;
4'd2 : led_r<=8'b00_000_011;
4'd3 : led_r<=8'b00_000_111;
4'd4 : led_r<=8'b00_001_111;
4'd5 : led_r<=8'b00_011_111;
4'd6 : led_r<=8'b00_111_111;
4'd7 : led_r<=8'b01_111_111;
4'd8 : led_r<=8'b11_111_111;
default : led_r<=8'b00_000_000;
endcase
end
else begin
led_g<=9'b00_000_000;//9'b11_111_111;
end
end
endmodule
结语
在时序逻辑和组合逻辑的设计上,参考两段式状态机模型,即一个 always 语块处理时钟信号和输入并进行 状态传递,一个 always 语块控制输出,使代码更加简洁,层次清晰。 后续的改进上面,可以使用 PLL 管理时钟的方式来控制频率选择,可以使用更少的 logic elements 实现功能。在使用PLL实现不同频率后,logic elements使用量只需要不到一半,但我还没跑出来。害,继续学吧。
因为全是自学的,对语法的理解和运用上可能有错误,大家仁者见仁,智者见智吧。
有大佬愿意给我指出的话,不胜感激!
改进版本
http://t.csdn.cn/Jd5U6