FPGA &&双按键控制&&LED呼吸灯实现
文章目录
首先本次设计要实现的目标: 一个按键控制led闪烁频率的增加,另一个按键控制led闪烁频率的减小
[工程链接:](https://pan.baidu.com/s/137NTleHZP8ss9jTRnAhMhQ
提取码:qb68)
1.按键脉冲设计
众所周知,按键一般采取消抖程序作为按键开关控制使用,这一次我采用了,脉冲信号来作为开关信号使用,本次采取**寄存器打拍实现
1.1按键脉冲信号代码
/*
* @Description: 按键脉冲信号产生
* @Version: 1.0
* @Author: Fangyeye
* @Date: 2022-12-19 20:16:02
* @LastEditors: Fangyeye
* @LastEditTime: 2022-12-19 20:46:01
*/
module key_pulse#(
parameter integer KEY_NUM = 2 //按键的数量
)
(
input wire clk , //系统时钟频率50MHZ
input wire rst_n ,
input wire [KEY_NUM-1 :0] key_in , //按键输入信号
output wire [KEY_NUM-1 :0] key_pulse
);
reg key_r , key_rr ;
// 寄存器打两拍的作用:1.同步时钟域 2.捕获按键按下的下降沿
always @(posedge clk , negedge rst_n) begin
if(!rst_n)begin
key_r <= 'b0;
key_rr <= 'b0;
end
else begin
key_r <= key_in;
key_rr <= key_r;
end
end
assign key_pulse = (~key_r) & key_rr ;
endmodule
1.2.按键脉冲Testbench代码
// 按键脉冲测试tb
`timescale 1ns/1ns
module tb_key_pulse();
reg clk;
reg rst_n;
wire pulse;
reg key;
key_pulse u0(
.clk(clk ),
.rst_n(rst_n),
.key_in(key),
.key_pulse(pulse)
);
always #10 clk = ~ clk;
initial begin
clk = 0;
key = 0;
rst_n = 1;
#5 rst_n = 0;
#5 rst_n = 1;
#105 key = 0;
#50 key = 1;
#200 key = 0;
#100 key = 1;
#2000 $stop;
end
endmodule
1.3.仿真波形:
FPGA &&双按键控制&&LED呼吸灯实现
易知:当按键按下时,key为低电平时立马产生一个脉冲信号,实现开关作用
2. pwm脉宽调制
1.1PWM信号简介:
-
什么是PWM:PWM简称脉冲宽度调制,即在一个周期内存在不同极性的电平状态。
-
PWM频率:是指一秒钟内从高电平时间在到低电平时间,再从低电平跳到高电平的瞬间次数,也就是一秒钟内有多少个PWM的周期。f = T / 1(HZ)。 基本单位为HZ(赫兹)
-
PWM周期:是指一秒钟内从高电平时间在到低电平时间,T = f / 1(s)。
-
PWM占空比:是指一个周期内高电平时间和总时间的比值。
1.2时间比例换算:
1s(秒) =1000毫秒=1000 000 (微秒) =1000 000 000 (纳秒)
上面图表表示:
T(周期) : 10ms/1s = 0.01s
f(频率) : 0.01/1 = 100HZ
占空比 ;4/(4+6) *100% =40
1.3.设计一个周期10KHZ,占空比40%的信号:
按照上面的波形我们来设计个周期10KHZ,占空比40%的方波信号吧
1.3.1verilog代码
/*
* @Description: 输出可调的PWM信号模块
@function : 1.FPGA的可调PWM信号模块
2.可调PWM信号的周期/占空比
3.默认占空比设置:DUTY
4.输出 10KHZ 40%占空比信号
* @Version: 1.0
* @Author: Fangyeye
* @Date: 2022-12-19 21:39:47
* @LastEditors: Fangyeye
* @LastEditTime: 2022-12-20 13:35:50
*/
module led_gard#(
parameter integer CLK_FREQ = 50_000_000 , //输入的时钟频率
parameter integer PERIOD = 1_000 , //PWM信号周期 Unit:HZ
parameter integer DUTY = 40
)
(
//系统接口
input wire clk , //输入时钟50MHZ
input wire rst_n , //系统复位,低电平复位
//输出信号
output reg out //输出信号
);
//reg
reg [31:0] cnt ; //计数器 --用于时钟分频 到 1KHZ
localparam integer CNT_PERIOD = ( CLK_FREQ / PERIOD ) -1 ; //周期计数值
localparam integer CNT_DUTY = CNT_PERIOD * (DUTY *1.0 / 100) - 1 ; //占空比计数值 --高电平计数值
//计数器计数
always @(posedge clk , negedge rst_n) begin
if(!rst_n)begin
cnt <= 32'b0;
end
else if(cnt == CNT_PERIOD ) begin
cnt <= 32'b0;
end
else begin
cnt <= cnt + 1'b1;
end
end
// 根据占空比 动态调节进行电平翻转
always @(posedge clk , negedge rst_n) begin
if(!rst_n)begin
out <= 1'b1; //默认输出高电平
end
else if(cnt >= CNT_PERIOD) begin
out <= 1'b1;
end
else begin
if(cnt >= CNT_DUTY)begin //开始低电平计数
out <= 1'b0;
end
end
end
endmodule
1.3.2Testbench代码
/测试实例
`timescale 1ns/1ns
module tb_led_gard();
//reg
reg clk ;
reg rst_n ;
//wire
wire out ;
parameter integer CLK_FREQ = 50_000_000 ; //输入的时钟频率
parameter integer PERIOD = 1_000 ; //PWM信号周期 Unit:HZ
parameter integer DUTY = 40 ; //PWM的占空比
//实例化输出可调的PWM信号模块
led_gard #(
.CLK_FREQ(CLK_FREQ) ,
.PERIOD(PERIOD) ,
.DUTY(DUTY)
)
led_gard_init(
.clk(clk),
.rst_n(rst_n),
.out(out)
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
#20 //系统开始工作
rst_n = 1'b1;
end
always #10 clk = ~clk;
endmodule
1.3.3仿真波形:
易知 输出信号周期10KHZ,占空比40%,与预期设计相符合
3.双按键控制 LED灯亮度的实现
接下来让我们修改代码来达到预期的设计,实现本次设计吧!
1.3.1双按键控制 LED灯verilog代码
/*
* @Description: 一个按键控制led亮度的增加,另一个按键控制led亮度的减小
* @Version: 1.0
* @Author: Fangyeye
* @Date: 2022-12-20 11:59:10
* @LastEditors: Fangyeye
* @LastEditTime: 2022-12-20 14:13:12
*/
module fpga_top#(
parameter integer CLK_FREQ = 50_000_000 , //输入的时钟频率
parameter integer PERIOD = 1_000 , //PWM信号周期 Unit:HZ
parameter integer DUTY = 40 //默认40%占空比
)
(
//系统接口
input wire clk , //输入时钟50MHZ
input wire rst_n , //系统复位,低电平复位
//用户接口
input wire key_up , //亮度增加按键
input wire key_down, //亮度减小按键
//输出信号
output reg out //输出信号
);
//reg
reg [31:0] cnt ; //计数器 --用于将时钟分频 到 1KHZ
reg [9:0] pulse ; //电平翻转计数器
//wire
wire key_up_pluse ; //亮度增加按键按下脉冲信号
wire key_down_pluse ; //亮度减小按键按下脉冲信号
localparam integer CNT_PERIOD = ( CLK_FREQ / PERIOD ) -1 ; //周期计数值
localparam integer CNT_DUTY = CNT_PERIOD * (DUTY *1.0 / 100) - 1 ; //占空比计数值 --高电平计数值
//计数器计数
always @(posedge clk , negedge rst_n) begin
if(!rst_n)begin
cnt <= 32'b0;
end
else if(cnt == CNT_PERIOD ) begin
cnt <= 32'b0;
end
else begin
cnt <= cnt + 1'b1;
end
end
// 根据占空比 动态调节进行电平翻转
always @(posedge clk , negedge rst_n) begin
if(!rst_n)begin
out <= 1'b1; //默认输出高电平
end
else if(cnt >= CNT_PERIOD) begin
out <= 1'b1;
end
else begin
if(cnt >= pulse )begin //开始低电平计数
out <= 1'b0;
end
end
end
// 2个按键实例化
key_pulse #(
.KEY_NUM(1)
)
key_down_pulse_init (
.clk(clk),
.rst_n(rst_n),
.key_in(key_down),
.key_pulse(key_down_pluse)
);
key_pulse #(
.KEY_NUM(1)
)
key_up_pulse_init (
.clk(clk),
.rst_n(rst_n),
.key_in(key_up),
.key_pulse(key_up_pluse)
);
// 按键功能实现
always @(posedge clk , negedge rst_n ) begin
if(!rst_n)begin
pulse <= CNT_DUTY;
end
else begin
if(key_down_pluse)begin
if(cnt <= CNT_PERIOD)begin
pulse <= pulse + 4'd10;
end
end
else if (key_up_pluse) begin
if(cnt >= 'd10)begin
pulse <= pulse - 4'd10;
end
end
end
end
endmodule
### 1.3.2上板验证
两个按键触发方式为下降沿触发
由硬件原理图确定 (本人硬件是按下IO读取低电平)
FPGA读到数据
亮度增加按键按下:+10 ,亮度减小按键按下:-10
发现实物上板子LED亮度也有相应的变化,说明设计正确✔