FPGA--实战,数字秒钟设计

需求:
4个数码管,可实现分显示和秒显示
每过1s,灯闪烁一下
KEY有三个,分别功能为,分钟调整清零,秒钟调整清零,以及时钟暂停
比较清晰的是我们有三个模块:数码管部分,计数部分,按键检测部分
顶层设计代码如下:

module digital_clock(
CLK_50M,RST_N,KEY,
LED1,SEG_DATA,SEG_EN
);
//外部引脚
input CLK_50M;//晶振信号输入
input RST_N;//复位开关
input [2:0] KEY;
output LED1;//LED 引脚
output  [7:0] SEG_DATA;//数码管数据
output  [3:0] SEG_EN;//数码管使能

//内部引脚
wire [2:0] key_flag;//KEY模块连接到计数模块上
wire [3:0] second_units;//秒钟个位数据
wire [3:0] second_tens;//秒钟十位数据

wire [3:0] minute_units;//分钟个位数据
wire [3:0] minute_tens;//分钟个位数据

Segled                 segled_init(
	.CLK_50M              (CLK_50M),
	.RST_N                (RST_N),
	.input_second_units   (second_units),
	.input_second_tens    (second_tens),
	.input_minute_units   (minute_units),
	.input_minute_tens    (minute_tens),
	.SEG_DATA             (SEG_DATA),
	.SEG_EN               (SEG_EN)
);


key                     key_init(
	.CLK_50M               (CLK_50M),
	.RST_N                 (RST_N),
	.KEY                   (KEY),
	.output_key_flag       (key_flag)
);

Counter                 counter_init(
	.CLK_50M               (CLK_50M),
	.RST_N                 (RST_N),
	.key_flag              (key_flag),
	.second_units          (second_units),
	.second_tens           (second_tens),
	.minute_units          (minute_units),
	.minute_tens           (minute_tens),
	.LED1                  (LED1)
);
endmodule

然后对KEY模块首先进行设计,完成的功能就是对输入按键KEY检测进行消抖,然后通过output_key_flag进行状态输出

//带消抖的按键检测
//要用到的有定时器,按键按下存储状态的寄存器,消抖后的寄存器
module key(
CLK_50M,RST_N,KEY,
output_key_flag
);
input CLK_50M;
input RST_N;
input [2:0] KEY;//输入的按键检测
output [2:0] output_key_flag;//输出给计数模块的指令值

reg [26:0] key_time_cnt;//最基本的计时器
reg [26:0] key_time_cnt_n;

reg [2:0] key_reg;//按键状态保留寄存器
reg [2:0] key_reg_n;



parameter SET_TIME_20MS=27'd1_000_000;//50_000_000是1s,差50倍


//定时器部分,定时时间为20ms
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   key_time_cnt<=27'b0;
	 else
	   key_time_cnt<=key_time_cnt_n;
end
always@(*)
begin
   if(key_time_cnt==SET_TIME_20MS)
	   key_time_cnt_n=27'b0;
	else 
	   key_time_cnt_n=key_time_cnt+27'b1;
end
//按键检测
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   key_reg   <= 3'b0;
	 else
	   key_reg   <= key_reg_n;
	 
end
//每20ms扫描一次,把按键的值赋给key的寄存器
always@(*)
begin
     if(key_time_cnt==SET_TIME_20MS)
      key_reg_n = KEY;
	  else
	   key_reg_n=key_reg;
end
//这个做法可以保证是边沿检测,就是按键松开的瞬间再输出数据
//111,下一刻变成110,然后111&(001)=001就是检测到了
assign output_key_flag =  key_reg & (~key_reg_n);
endmodule

其次是计数模块,它的功能有三个
1.实现1S的LED闪烁
2.读取output_key_flag的数据,分别实现:a.时间暂停,b.秒加一调整 c.分加一调整
3.通过计时获得秒,十秒,分,十分的数据,然后数据传出到数码管模块

module Counter(
CLK_50M,RST_N,key_flag,
second_units,second_tens,minute_units,minute_tens,LED1
);
input CLK_50M;
input RST_N;
input [2:0] key_flag;//key1-3功能分别为暂停,分加1,秒加一

output [3:0] second_units;//秒钟个位
output [3:0] second_tens;//秒钟十位
output [3:0] minute_units;//分钟个位
output [3:0] minute_tens;//分钟十位
output LED1;//led输出

//基本计时器
reg [26:0] time_cnt;
reg [26:0] time_cnt_n;
//秒计数
reg [3:0] second_time_cnt;
reg [3:0] second_time_cnt_n;
//十秒位计数
reg [3:0] ten_second_time_cnt;
reg [3:0] ten_second_time_cnt_n;
//分钟计数
reg [3:0] minute_time_cnt;
reg [3:0] minute_time_cnt_n;
//十分钟计数
reg [3:0] ten_minute_time_cnt;
reg [3:0] ten_minute_time_cnt_n;
//led
reg  led_reg;
reg  led_reg_n;

//停止位(第一个KEY)
reg  stop_reg;
reg  stop_reg_n;
parameter SET_TIME_1S=27'd50_000_000;

//定时器部分,定时时间为1s
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   time_cnt<=27'b0;
	 else
	   time_cnt<=time_cnt_n;
end
//	else if(key_flag[0]==1'b1)不可以这么写,因为按键边缘检测,只会是一瞬间的按起来的时候改变,如同一个很小的脉冲,因此我们需要的是检测这个脉冲并保留
always@(*)
begin
   if(time_cnt==SET_TIME_1S)
	   time_cnt_n=27'b0;
	else if(stop_reg==1'b1)//第一个按键用来暂停
	   time_cnt_n=time_cnt;
	else 
	   time_cnt_n=time_cnt+27'b1;
end
//按键使得时间停止的部分
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   stop_reg<=1'b0;
	 else
	   stop_reg<=stop_reg_n;
end

always@(*)
begin
    if(key_flag[0]==1'b1)
	   stop_reg_n=~stop_reg;
	 else
	   stop_reg_n=stop_reg;
end

//1s计数部分
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   second_time_cnt<=4'b0;
	 else
	   second_time_cnt<=second_time_cnt_n;
end

always@(*)
begin
   if(time_cnt==SET_TIME_1S|key_flag[1]==1'b1)//每当计数到1s后,计数器加1,或者第二个按键秒数加一
	   second_time_cnt_n=second_time_cnt+4'b1;
	else if(second_time_cnt==4'd10)//当计数到10,计数器归零
	   second_time_cnt_n=4'd0;
	else 
	   second_time_cnt_n=second_time_cnt;//不然保持不变即可
end
//10s计数部分
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   ten_second_time_cnt<=4'b0;
	 else
	   ten_second_time_cnt<=ten_second_time_cnt_n;
end

always@(*)
begin
   if(second_time_cnt==4'd10)//每当秒计数到10后,十位计数器加1
	   ten_second_time_cnt_n=ten_second_time_cnt+4'b1;
	else if(ten_second_time_cnt==4'd6)//当计数到6,计数器归零
	   ten_second_time_cnt_n=4'd0;
	else 
	   ten_second_time_cnt_n=ten_second_time_cnt;//不然保持不变即可
end
//1minute计数部分
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   minute_time_cnt<=4'b0;
	 else
	   minute_time_cnt<=minute_time_cnt_n;
end

always@(*)
begin
   if(ten_second_time_cnt==4'd6|key_flag[2]==1'b1)//每当十秒计数到6后,分钟计数器加1,或者第三个按键按下
	   minute_time_cnt_n=minute_time_cnt+4'b1;
	else if(minute_time_cnt==4'd10)//当计数到10,计数器归零
	   minute_time_cnt_n=4'd0;
	else 
	   minute_time_cnt_n=minute_time_cnt;//不然保持不变即可
end
//10minute计数部分
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   ten_minute_time_cnt<=4'b0;
	 else
	   ten_minute_time_cnt<=ten_minute_time_cnt_n;
end

always@(*)
begin
   if(minute_time_cnt==4'd10)//每当分钟计数到10后,十位计数器加1
	   ten_minute_time_cnt_n=ten_minute_time_cnt+4'b1;
	else if(ten_minute_time_cnt==4'd6)//当计数到6,计数器归零
	   ten_minute_time_cnt_n=4'd0;
	else 
	   ten_minute_time_cnt_n=ten_minute_time_cnt;//不然保持不变即可
end
//led 部分
always@(posedge CLK_50M,negedge RST_N)
begin
    if(!RST_N)
	   led_reg<=1'b1;
	 else
	   led_reg<=led_reg_n;
end

always@(*)
begin
    if(time_cnt==SET_TIME_1S)
	   led_reg_n=~led_reg;
	 else
	   led_reg_n=led_reg;
end


assign second_units  =  second_time_cnt;
assign second_tens   =  ten_second_time_cnt;
assign minute_units  =  minute_time_cnt;
assign minute_tens   =  ten_minute_time_cnt;
assign LED1           =  led_reg;
endmodule

最后就是单纯的数码管动态显示部分

/*
动态数码管原理就是,分别对每个管子使能,一个使能的时候其他不使能,只要切换的够快,人眼看起来就是同时的
这个原因是因为数据位的7位是共用的
*/
module Segled(
CLK_50M,RST_N,input_second_units,input_second_tens,input_minute_units,input_minute_tens,
SEG_DATA,SEG_EN
);

input CLK_50M;
input RST_N;
input [3:0] input_second_units;
input [3:0] input_second_tens;
input [3:0] input_minute_units;
input [3:0] input_minute_tens;
output  reg	[ 3:0] 	SEG_EN;				//数码管使能端口
output  reg	[ 7:0] 	SEG_DATA;			//数码管数据端口(查看管脚分配文档或者原理图)

reg [26:0] segled_time_cnt;//计时器
reg [26:0] segled_time_cnt_n;
reg [1:0] tenms_cnt;//计数器,每10ms加一
reg [1:0] tenms_cnt_n;

parameter SET_TIME_10MS=27'd50_000;

parameter SEG_EN1=4'b1110;
parameter SEG_EN2=4'b1101;
parameter SEG_EN3=4'b1011;
parameter SEG_EN4=4'b0111;
parameter SEG_DISEN=4'b1111;
/*读取txt文件中的对应于数码管0-9的16进制值
bf
86
db
cf
e6
ed
fd
87
ff
ef
*/
reg [7:0] SEG_NUM [9:0];
initial
begin
    $readmemh("SEG_NUM.txt",SEG_NUM);
end
/*
parameter	SEG_NUM0 = 8'hbf,   //数字0
				SEG_NUM1 = 8'h86,   //数字1
				SEG_NUM2 = 8'hdb,   //数字2
				SEG_NUM3 = 8'hcf,   //数字3
				SEG_NUM4 = 8'he6,   //数字4
				SEG_NUM5 = 8'hed,   //数字5
				SEG_NUM6 = 8'hfd,   //数字6
				SEG_NUM7 = 8'h87,   //数字7
				SEG_NUM8 = 8'hff,   //数字8
				SEG_NUM9 = 8'hef,   //数字9
				SEG_NUMA = 8'hf7,   //数字A
				SEG_NUMB = 8'hfc,   //数字B
				SEG_NUMC = 8'hb9,   //数字C
				SEG_NUMD = 8'hde,   //数字D
				SEG_NUME = 8'hf9,   //数字E
				SEG_NUMF = 8'hf1;   //数字F
*/
always@(posedge CLK_50M,negedge RST_N)
begin
if(!RST_N)
  segled_time_cnt<=27'b0;
else
  segled_time_cnt<=segled_time_cnt_n;
end

always@(*)
begin
  if(segled_time_cnt==SET_TIME_10MS)
    segled_time_cnt_n=27'b0;
  else
    segled_time_cnt_n=segled_time_cnt+27'b1;
end
//每10ms后,进行计数
always@(posedge CLK_50M,negedge RST_N)
begin
if(!RST_N)
  tenms_cnt<=2'b0;
else
  tenms_cnt<=tenms_cnt_n;
end

always@(*)
begin
  if(segled_time_cnt==SET_TIME_10MS)
    tenms_cnt_n=tenms_cnt+2'b1;
  else
    tenms_cnt_n=tenms_cnt;
end
//每10ms切换一个数码管使能
always@(*)
begin
 case(tenms_cnt)
   2'b00 : SEG_EN=SEG_EN4;
   2'b01 : SEG_EN=SEG_EN3;
   2'b10 : SEG_EN=SEG_EN2;
   2'b11 : SEG_EN=SEG_EN1;
 endcase
end
//每10ms更新一下输出数据
always@(*)
begin
 case(tenms_cnt)
	 2'b00 : SEG_DATA=SEG_NUM[input_second_units];//将计数模块的数据赋值给数码管
	 2'b01 : SEG_DATA=SEG_NUM[input_second_tens];
	 2'b10 : SEG_DATA=SEG_NUM[input_minute_units];
	 2'b11 : SEG_DATA=SEG_NUM[input_minute_tens];
 endcase
end
endmodule
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"advanced-fpga-design.pdf" 是一本关于FPGA(现场可编程门阵列)设计的高级指南的PDF文件。FPGA是一种灵活可编程的集成电路芯片,它能够根据需求进行逻辑门电路的重新配置。这本指南面向有一定FPGA设计经验的工程师或学者,提供了深入的FPGA设计知识和技巧。 在这本指南中,读者将了解到更高级的FPGA设计概念和技术。例如,他们将学习如何使用高级语言(如VHDL或Verilog)来描述复杂的逻辑电路,并将其转化为可以在FPGA上实现的逻辑单元。他们还将了解如何使用FPGA内部资源(如Look-Up Tables和Flip-Flops)配置高级功能,以实现更复杂的设计。 此外,该指南还提供了优化和测试FPGA设计的方法。读者将了解如何通过减少功耗、提高时序等方面来优化设计,以满足特定的性能需求。此外,他们还将学习到如何进行功能验证和时序仿真,以确保他们的设计在实际应用中的正确性和可靠性。 这本指南还介绍了一些最新的FPGA设计趋势和发展。它讨论了使用高级综合工具(如Vivado和Quartus)进行设计的好处,并介绍了一些用于加速设计过程的现代化工具和技术。此外,该指南还围绕FPGA的应用领域(如通信、图像处理和机器学习等)进行了讨论,旨在启发读者将FPGA应用于不同的实际问题。 总之,"advanced-fpga-design.pdf" 是一本深入了解FPGA设计的高级指南,为已经具有一定FPGA设计经验的读者提供了更多的知识和技巧。无论是优化设计、测试验证还是探索最新的FPGA设计趋势,读者都能从中获得有益的信息和实践指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值