目录
使用四个按键开关控制四个LED灯。(正点原子开发指南,记录自己学习过程,题目练习,这里程序是自己编写的与指南中不同,如果看到的小伙伴有什么好的建议可以一同分享交流)
一、KEY硬件原理
开发板上的4个按键未按下时,输出高电平,按下后,输出低电平。图中的每个按键都 连接了一个10K电阻,起到限流的作用,以防止按键被按下时电源直接接地造成电路短路。
二、设计输入
1.端口设计
本次实验使用4个按键来控制4颗LED灯,没有按键被按下时,4颗LED保持常灭;如果按键 KEY0被按下, LED灯从低位到高位流水;如果按键KEY1被按下,LED灯从高位到低位流水;如果按键 KEY2被按下,LED灯交替闪烁;KEY3被按下,LED灯常亮。
模块的输入端口包含3个输入端口系统时钟sys_clk、复位信号sys_rst_n、4bit按键输入信号key[3:0],输出为包含4bit的LED端口led[3:0]。
2.波形图绘制
如波形图所示,这里仅展示了当按键0按下时Led[3:0]的状态变化,初始状态给led端口赋值为4’b0000;当按键0按下,key[3:0]此时状态为1110,等待0.5s后,即计数器计数25000000次(板载晶振为50MHz,周期为20ns),给 led 端口赋初始值为4’b0001;等待0.5s后,给led端口赋值为4’b0010;等待0.5s,给led端口赋值为 4’b0100;再次等待0.5s后,给led端口赋值为4’b1000;再次等待0.5s后,给led端口赋值为4’b0001,后面依次类推,四个LED即可实现流水的效果。
其他按键波形图此处没做绘制,详细实现逻辑参考代码,流水灯移位操作原理可看上一篇文章https://blog.csdn.net/qq_45910789/article/details/139297906?spm=1001.2014.3001.5501。
3.按键控制led代码实现
根据波形图使用Verilog编写计数器(key_led.v)代码
代码如下:
//key控制led
module key_led(
input sys_clk, // 系统时钟输入
input sys_rst_n, // 系统复位信号输入
input [3:0] key, // 键盘输入
output reg [3:0] led // LED输出
);
parameter MCNT = 25'd24999999; // 计数器最大计数值,对应0.5秒
reg [24:0] cnt; // 计数器
// 计数器逻辑,用于生成0.5秒的时间间隔
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
cnt <= 25'd0;
else if (cnt == MCNT)
cnt <= 25'd0;
else
cnt <= cnt + 25'd1;
end
// LED控制逻辑,根据键盘输入控制LED状态
//这个思路有个缺陷,判断key状态时需要先判断cnt是否达到最大计数值
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) // 如果系统复位信号为低电平
led <= 4'b0000; // 将LED输出置零
else if (cnt == MCNT) begin // 当计数器计满0.5秒时
case (key) // 根据键盘输入进行不同的操作
4'b1110: begin // 如果键盘输入为 "1110"
if (led == 4'b000) // 如果LED状态为全灭
led <= 4'b0001; // 将LED状态置为最低位亮
else
led <= {led[2:0], led[3]}; // 否则循环左移LED状态
end
4'b1101: begin // 如果键盘输入为 "1101"
if (led == 4'b000) // 如果LED状态为全灭
led <= 4'b1000; // 将LED状态置为最高位亮
else
led <= {led[0], led[3:1]}; // 否则循环右移LED状态
end
4'b1011: begin // 如果键盘输入为 "1011"
led <= ~led; // 取反LED状态
end
4'b0111: begin // 如果键盘输入为 "0111"
led <= 4'b1111; // 将LED状态全部置为亮
end
default: begin // 对于其他键盘输入
led <= 4'b0000; // 将LED状态置为全灭
end
endcase
end
end
//下面这个思路也可以,就是先选择key状态,然后判断计数最大值
/*
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) // 如果系统复位信号为低电平
led <= 4'b0000; // 将LED输出置零
else begin
case(key)
4'b1110:begin
if(led ==4'b000)
led <= 4'b0001;
else if(cnt == MCNT)
led <= {led[2:0],led[3]};
end
4'b1101:begin
if(led ==4'b000)
led <= 4'b1000;
if(cnt == MCNT)
led <= {led[0],led[3:1]};
end
4'b1011:begin
if(cnt == MCNT)
led <= ~led;
else
led <= led;
end
4'b0111:begin led <= 4'b1111;end
default:led <= 4'b0000;
endcase
end
end
*/
endmodule
三、RTL功能仿真
1.RTL原理图
RTL视图如下:
2.仿真测试文件代码
代码如下:
`timescale 1ns / 1ns
module tb_key_led();
reg sys_clk ;
reg sys_rst_n;
reg [3:0]key ;
wire [3:0]led ;
initial begin
sys_clk = 1'b1;
sys_rst_n = 1'b0;
key <= 4'b1111;
#201
sys_rst_n = 1'b1;
#24900
key <= 4'b1110;
#24900
key <= 4'b1111;
#24900
key <= 4'b1101;
#24900
key <= 4'b1111;
#24900
key <= 4'b1011;
#24900
key <= 4'b1111;
#24900
key <= 4'b0111;
#24900
key <= 4'b1111;
end
always #10 sys_clk = ~sys_clk;
key_led
#(
.MCNT(24)
)
key_led_inst(
.sys_clk (sys_clk ),
.sys_rst_n(sys_rst_n),
.key (key ),
.led (led )
);
endmodule
3.仿真结果
仿真整体波形图如上图,仿真时,依次按下按键0后释放按键,按下按键1后释放按键,按下按键2后释放按键,按下按键3后释放按键。
按键0按下的仿真波形与之前所绘制的按下按键0的波形图一致,这里为了减少仿真耗时在仿真文件里将间隔时间设置为500ns,计数器最大次数25。
三、下载验证
在经过分析综合、管脚分配、时序约束后,生成bit流文件进行上板验证实现结果如下所示:
key_led
总结
注意按键按下的状态选择程序编写,按键0和按键1的流水灯由于使用的移位操作进行,所以在判断按键按下后需要先把led初始状态设置为0001(向左流水)和1000(向右流水),还有闪烁操作前也要加上对计数器的判断。