FPGA之没事敲例程

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仿真图
这是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仿真图
这是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

modlesim仿真图

总结

输入输出信号默认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

modelsim仿真图

总结

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

modelsim仿真图

总结

这里用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,则存在上升沿或下降沿,,即检测边沿。
说的不太明白,希望大家多多批评。

  • 10
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值