FPGA新手入门
前言
新手入门,大概展示下在书上看到的例程,以及自己的思考以及modelsim的仿真结果,希望大家多多批评。
第一天:系统时钟的二分频;
第二天:开发板蜂鸣器驱动
第三天:开发板拨键触发蜂鸣器
第三天:流水灯设计(移位循环)
第四天:3-8译码器驱动led
第四天:按键消抖(非状态机法)
一、系统时钟二分频
师兄毕业留下的Xilinx的开发板一块,看了下特权的例程,今天复现一下系统时钟的二分频。
module test (clk_25m,rst_n,out_clk_12m5)
input clk_25m;//系统时钟25M
input rst_n;//复位端
output out_clk_12m5;//二分频,希望输出时钟12.5M
always@(posedge clk_25m or negedge rst_n)//时序逻辑,在系统时钟上升沿及复位端下降沿时:
if (!rst_n) out_clk_12m5<=1'b0;//复位端(0)有效,输出时钟为低电平;
else out_clk_12m5<= ~out_clk_12m5;//否则:在每个系统时钟上升沿时,输出时钟取反。
endmodule
这是modelsim的仿真图,很简单,例化之后延时一段时间让复位端无效即可
总结
其实没太懂输出信号out_clk最开始为啥是低电平?,再仿真一次,out_clk初始值变成高电平了?有看到的大佬麻烦讲一下
二、系统蜂鸣器驱动
师兄毕业留下的Xilinx的开发板一块,看了下特权的例程,今天复现一下系统蜂鸣器驱动。
改了点数,要不看波形太麻烦
module test2(
input ext_clk_25m,
input ext_rst_n,
output reg beep
);
reg [13:0] cnt;//定义计数器cnt,计数1000000,二进制为20位,满足25hz
always@(posedge ext_clk_25m or negedge ext_rst_n)//定义时序计数器规则
if (!ext_rst_n) cnt<=20'd0;//复位计数器清零
else if (cnt==14'd999_9) cnt<= 20'd0;//计数器到999——999时清零
else cnt<=cnt+1'b1;//其他条件计数器加1操作
always@(posedge ext_clk_25m or negedge ext_rst_n)//定义计数器与beep操作
if (!ext_rst_n) beep<=1'b0; //复位端有效,beep=0,蜂鸣器不响
else if (cnt<14'd499_9) beep<=1'b1;//计数器计数至499999之前,beep=1,
else beep<=1'b0;//计数器计数499999后,计数器为0,完成50%占空比
endmodule
这是modelsim的仿真图,很简单,例化之后延时一段时间让复位端无效即可
总结
注意啊,两个时序逻辑是并发的,不是顺序的,但是always块内的逻辑是顺序的,不然要if_else干嘛,是吧。
三、系统蜂鸣器开关驱动
师兄毕业留下的Xilinx的开发板一块,看了下特权的例程
module test_3_1(input ext_clk_25m,
input ext_rst_n,
input switch,//输入开关量
output reg beep//输出寄存器型蜂鸣器接口
);
always@(posedge ext_clk_25m or negedge ext_rst_n)
if (!ext_rst_n) beep<=1'b0;//复位端低有效,蜂鸣器无声
else if (switch == 1'b1) beep<=1'b0;//开光量高电平,蜂鸣器无声
else beep<=1'b1;//其他情况,蜂鸣器有声
endmodule
总结
输入输出信号默认wire型,beep可认为一触发器。
四、流水灯设计入门
师兄毕业留下的Xilinx的开发板一块,看了下特权的例程
module test4(
input ext_clk_25m,
input ext_rst_n,
output reg [7:0] led//输出8位流水灯
);
reg[4:0] cnt;//设置每800ns换灯亮
always@(posedge ext_clk_25m or negedge ext_rst_n)//定义计数器,达到最大值即重新+1
if (!ext_rst_n) cnt<=5'd0;
else if (cnt<5'd19) cnt<=cnt+1'b1;
else cnt<= 5'd0;
always@(posedge ext_clk_25m or negedge ext_rst_n)//定义led在计数器控制下进行流水灯操作
if (!ext_rst_n) led<=8'b0111_1111;
else if (cnt == 5'd19) led<= {led[6:0],led[7]};
else ;//这里是保持上一步骤信号值不变
endmodule
总结
else if (cnt == 5’d19) led<= {led[6:0],led[7]};
简单说一下这里的理解,这里是一个循环移位操作,用最笨的方法讲一下,有大神可以给我分享下最简单的理解办法。
五、3-8译码器流水灯设计入门
师兄毕业留下的Xilinx的开发板一块,看了下特权的例程
module test5(
input ext_clk_25m,//
input ext_rst_n,
input [3:0] switch,//输入四位拨码开关,低有效
output reg [7:0] led//输出8位led
);
always@(posedge ext_clk_25m or negedge ext_rst_n)
if (!ext_rst_n) led<=8'b1111_1111;//复位端低有效,led全灭
else if (switch[3] == 4'b1) led<=8'b1111_1111;//s3无效,led全灭
else begin
case (switch[2:0])//3-8译码器,led从0-7逐渐变亮
3'b111:led<=8'b1111_1110;
3'b110:led<=8'b1111_1101;
3'b101:led<=8'b1111_1011;
3'b100:led<=8'b1111_0111;
3'b011:led<=8'b1110_1111;
3'b010:led<=8'b1101_1111;
3'b001:led<=8'b1011_1111;
3'b000:led<=8'b0111_1111;
default;
endcase
end
endmodule
总结
这里用case语句,对于多条件分支比if-else 好用。
六、非状态机做按键消抖
师兄毕业留下的Xilinx的开发板一块,看了下特权的例程
module test6(
input ext_clk_25m,
input ext_rst_n,
input [1:0]key,//输入两位按键,按下为0,松开为1
output reg [1:0]led//输出两位led灯
);
wire key1;
assign key1 = key[1]&key[0];//检测两按键是否有按动,有按动为0,无按动为1
reg [3:0] key_r;//定义四位寄存器做打拍使用
always@(posedge ext_clk_25m or negedge ext_rst_n)//定义按键抖动逻辑
if (!ext_rst_n) key_r<=4'b1111;//复位端低有效,寄存器为全1;
else key_r<={key_r[2:0],key1};//其他情况,寄存器记录四个时钟内按键按动情况
assign key_neg = key_r[3]&~key_r[2];//定义下降沿
assign key_pos = ~key_r[3]&key_r[2]; //定义上升沿
reg[19:0]cnt;//定义按键消抖逻辑
always@(posedge ext_clk_25m or negedge ext_rst_n)
if (!ext_rst_n) cnt<=20'd0;//复位端低有效,计数器清零
else if (key_neg||key_pos) cnt<=20'd0;//检测到上升沿或者下降沿,计数器清零
else if (cnt<20'd999_999) cnt<=cnt+1'b1;//计数器小于1000——000,计数器加1
else cnt<=20'd0;//其他情况,计数器清零
reg [1:0] key_value[1:0];//定义两个寄存器,2位x2
always@(posedge ext_clk_25m or negedge ext_rst_n)//采集按键值
if (!ext_rst_n) begin//复位端低有效,两寄存器赋值为1
key_value[1]<=2'b11;
key_value[0]<=2'b11;
end
else begin
key_value[1]<=key_value[0];//0寄存器赋值给1寄存器
key_value[0]<={key[1],key[0]};//0寄存器采集按键情况
end
wire key_press = key_value[1]&~key_value[0];//当仅当按键按下时,key_press=1
always@(posedge ext_clk_25m or negedge ext_rst_n)//根据按键情况赋值led
if (!ext_rst_n) led<=2'b11;//复位端低有效,led全灭
else begin
case(key_press)
2'b10:led[1] =1'b0;
2'b01:led[0] =1'b0;
endcase
end
endmodule
总结
这个方法不好,感觉也就边沿检测有点意思。非状态机做按键消抖主要模块分别是按键的抖动逻辑,按键消抖逻辑,采集按键值,赋值给led。
主要讲讲边沿检测吧,边沿检测关键点设计一个寄存器,存储最近几拍的状态值,(至于几拍效果最好,我觉得有待商榷),一般就是用之前讲的移位赋值的方法打拍。取最后一个和次后的状态,分别做与逻辑,即可生成边沿。至于检测边沿,即将两边沿情况的逻辑或,若为1,则存在上升沿或下降沿,,即检测边沿。
说的不太明白,希望大家多多批评。