FPGA基础学习之数字时钟设计1

先说明一下:1、本博文绝对原创,转载请注明。

                        2、小弟刚开始学习FPGA,博文如有什么不对的地方,恳请大家指出。

                        3、本文目的旨在与新手分享交流学习中的一些问题。

一、实验平台说明

硬件平台:使用的网上一块比较便宜的开发板,100多点。Altear EP4CE6E22C8芯片。

软件平台:quarters-ii 11.0 

使用语言:verlog

二、模块层次说明

系统由顶层、数据转换模块、显示模块、时钟分频模块组成。

时钟模块:开发板使用的是50M晶振,在这里我们需要两种时钟,一个用于一秒钟计时(1HZ),一个用于数码管扫描(500HZ);

显示模块:用于在数码管上显示时钟数据,由于只有4位数码管,所以这里其实设计的并不是一个完整的数字时钟,应该叫计时器,;两个数码管显示秒数据,两个数码管显示分钟数据,知道溢出清零,不过大家可以在这个基础上自行添加设计。

数据转换模块:功能是讲0-9的数字转换成数码管显示的段码。

顶层:实例化时钟和显示模块,同时完成一些逻辑任务。这里的实例化大家可以类似的理解成C语言中的函数的调用,但实际上不是的,因为FPGA是硬件,一定要记住并发这个概念。

三、时钟分频模块

 

module clock_div(clk,clk_out1,clk_out2);
input  clk;
output clk_out1;
reg    clk_out1;
reg [27:0] DIV_cnt1 =0;
parameter  period1=50000000;  //频率为1hz

output clk_out2;
reg    clk_out2;
reg [27:0] DIV_cnt2 =0;
parameter  period2=100000;  //100000频率为500hz
//////////////分频1////////////////
always @ (posedge clk)  
 begin
  DIV_cnt1 <= DIV_cnt1 + 1;
  if(DIV_cnt1 == (period1>>1)-1)//高电平
    clk_out1 <= #1 1'b1;
  else if(DIV_cnt1 == period1-1)//低电平
    begin
	  clk_out1  <= #1 1'b0;
	  DIV_cnt1  <= #1 1'b0;
    end
 end
//////////////分频2////////////////
always @ (posedge clk)  
 begin
  DIV_cnt2 <= DIV_cnt2 + 1;
  if(DIV_cnt2 == (period2>>1)-1)//高电平
    clk_out2 <= #1 1'b1;
  else if(DIV_cnt2 == period2-1)//低电平
    begin
	  clk_out2  <= #1 1'b0;
	  DIV_cnt2  <= #1 1'b0;
    end
 end
endmodule


四、数据转换模块

 

 

 

 

//////////////数字转换成数码管编码模块///////////////
module num_to_code(num,code);
input num;      //输入0-9的数据 
output code;    //输出0-9数据的数码管段码
wire[3:0] num;  
reg[7:0] code;

always @ (num)  //当数字变动一次就进行一次转换
 begin
  case (num)
	4'h0 : code <= 8'hc0; //显示"0"
	4'h1 : code <= 8'hf9; //显示"1"
	4'h2 : code <= 8'ha4; //显示"2"
	4'h3 : code <= 8'hb0; //显示"3"
	4'h4 : code <= 8'h99; //显示"4"
	4'h5 : code <= 8'h92; //显示"5"
	4'h6 : code <= 8'h82; //显示"6"
	4'h7 : code <= 8'hf8; //显示"7"
	4'h8 : code <= 8'h80; //显示"8"
	4'h9 : code <= 8'h90; //显示"9"
	default : code <= 8'hff; 
  endcase 
 end
endmodule 


五、数码管显示模块

 

 

 

 

//////////////数码管显示模块//////////////
module display(clk,seg0,seg1,seg2,seg3,seg_d,seg_w);
input seg0,seg1,seg2,seg3;     //输入的4位段码数据
input clk;                     //扫描时钟
output seg_d;                  //输出到管脚的段码数据
output seg_w;                  //输出到管脚的位选数据

wire[7:0] seg0,seg1,seg2,seg3; 
reg[7:0] seg_d;
reg[3:0] seg_w;
reg[3:0] scan_cnt; //位选扫描

always @ (posedge clk)  //数码管扫描
 begin
  scan_cnt = scan_cnt+1;
  if (scan_cnt == 3'd4)
   scan_cnt <=  0;
 end
always @ (scan_cnt)   //数码管位选
 begin
  case (scan_cnt)
   3'd0 : seg_w<=4'b0111;
	3'd1 : seg_w<=4'b1011;
	3'd2 : seg_w<=4'b1101;
	3'd3 : seg_w<=4'b1110;
	default :seg_w<=4'b1111;
  endcase
 end 
always @ (scan_cnt)  //数码管显示数据
 begin
  case (scan_cnt)
   3'd0 : seg_d<=seg3; //
	3'd1 : seg_d<=seg2;//
	3'd2 : seg_d<=seg1;
	3'd3 : seg_d<=seg0;//8'hf9
	default :seg_d<=8'hff;
  endcase  
 end
endmodule 

六、顶层模块

 

module seg_clock(clock,led,seg_d,seg_w); 
input  clock;     //系统时钟50MHZ晶振输入
output led;       //LED
output seg_d;     //数码管的段选
output seg_w;     //数码管的位选
reg[3:0]    led;  //4位led灯
wire[7:0] seg_d;  //段选8位
wire[3:0] seg_w;  //位选4位

wire   clk1;      //分频后的时钟1 因为分频器输出为reg类型,故这里用wire
wire   clk2;      //同上

reg[3:0]  tim[0:3];    //4位数时间数据
wire[7:0] tim_d[0:3];  //4位时间数据的段码

initial   //初始化时钟数据
 begin
  tim[0]<=4'd0;
  tim[1]<=4'd0;
  tim[2]<=4'd0;
  tim[3]<=4'd0;
 end

////////////实例化分频模块////////////////
clock_div    clock_div(
                       .clk(clock),
		       .clk_out1(clk1),
		       .clk_out2(clk2)
			);
////////////实例化数据转换模块////////////////							 
num_to_code  num_to_code0(
                         .num(tim[0]),
			 .code(tim_d[0])
			  );
num_to_code  num_to_code1(
                         .num(tim[1]),
			 .code(tim_d[1])
			  );
num_to_code  num_to_code2(
                         .num(tim[2]),
			 .code(tim_d[2])
		         );								
num_to_code  num_to_code3(
                         .num(tim[3]),
			 .code(tim_d[3])
			 );		
////////////实例化显示模块////////////////								
display      display(
                     .clk(clk2),
		     .seg0(tim_d[0]),
		     .seg1(tim_d[1]),
		     .seg2(tim_d[2]),  
		     .seg3(tim_d[3]),   
		     .seg_d(seg_d),
		     .seg_w(seg_w)
		     );								
							 
always @ (posedge clk1)  //clk为1hz 此处更新时间
 begin
  led<=~led;
  tim[0]<=tim[0]+4'd1;
  if(tim[0]==4'd9) //秒的个位
   begin
	 tim[0]<=4'd0;
	 tim[1]<=tim[1]+ 4'd1; //秒的十位
	 if(tim[1]==4'd5)
	  begin
	   tim[1]<=4'd0;
		tim[2]<=tim[2]+ 4'd1; //分的个位
		if(tim[2]==4'd9)
		 begin
		  tim[2]<=4'd0;
		  tim[3]<=tim[3]+ 4'd1;//分的十位
		  if(tim[3]==4'd9)
		   begin
			 tim[3]<=4'd0;
			end
		 end
	  end
	end
 end
endmodule

七、实验效果及系统结构图

 

下载后的效果图

 

FPGA基础学习之数字时钟设计1_第1张图片
 

系统整体机构图,这个是我用另一种方式做的,顶层用的原理图的方式设计的,发现这种方式有许多的优点,我会在下一篇文章张中分享。

 

FPGA基础学习之数字时钟设计1_第2张图片

 

八、心得分享与注意事项

最后说说过程中遇到的问题,也许也有人会遇到,这样可以帮助大家更快的解决问题。

1、并行!并行!并行!说三遍,使用硬件描述语言相当于是你在搭建电路一样,这个可以通过RTL图看出。

2、大家要注意“<<=”和“=”的使用,以及reg和wire,比如模块一得输出是reg类型了,那么输入到模块2中的输入就要是wire类型了。

3、verlog貌似暂时还不只是数组类型端口,这点要注意。(数组类型和位宽是两回事,不要搞混了哦)。

4、一定要注意数据宽度,特别是是在模块之间传递的时候。

5、暂时就想到这些,第一次写,有什么不足的地方往大家多多指点,下一篇文章使用原理图的方式来设计顶层。

 

PS:现从事汽车电子MCU相关工作,已经不懂FPGA了 2020

 

 

 

 

你可能感兴趣的:(FPGA学习)