这两天学习xilinx的板子,做了一个数码管显示的数字时钟,本设计实现了数字时钟的数码管显示,没有实现调时间功能,现将实现过程记录下来。供有类似需要的童鞋做个参考。
设计主要分为三个模块:时钟分频模块,时钟位数据产生模块,数码管扫描显示模块。
一、时钟显示模块 clk_div
由于FPGA工作频率很高,我的板子上时钟是50MHz,数码管显示的最佳扫描频率是1KHz,第一个模块是做的一个50K的分频电路,是一个整数分频,这里并不复杂,具体整数分频实现思路可以参考我的博文:http://blog.csdn.net/baijingdong/article/details/20066327
代码:
module clk_div(
clk,
rst,
clk_out
);
input clk;
input rst;
output clk_out;
parameter N2=50000;
reg clk3=1'b0;
reg [16:0]count3=17'd0;
assign clk_out=clk3;
always @(posedge clk or negedge rst)
begin
if (!rst)
begin
count3<=17'd0;
clk3<=1'b0;
end
else
if(count3
该模块前面也有一个时钟分频模块,因为分频后的时钟为1KHz,这个时钟直接做计数时钟频率还是太高,这里前面要先加一个1K分频的整数分频模块。clk1-in(1khz),clk3-out(1Hz).
我的板子上只有四个数码管,因此只实现了秒和分的时间显示,与单片机和其他嵌入式开发不同,FPGA是一个纯硬件的电路,因此,设计相对很不灵活,我们知道数码管显示是扫描显示的,每次只能显示分、秒四个数字中的一个数字,若是按照C语言的思路可以定义一个sec作为一个秒变量,sec%10,sec/10分别可以作为秒的个位和十位的数据,但是在FPGA中却不推荐这么做,因为纯硬件电路做除法等运算直接综合会耗费大量资源,有的运算比如开根号等甚至是无法综合的。
因此,用定义一个计数器分别取其十位和个位的方法设计并不好,我最初的思路也是这样,从我的模块名中就可以看出。但是这种方法起码在fpga设计中不推荐。
这里分别定义了四个寄存器变量,分别代表分和秒的十位和个位数据,利用四层判断语句完成判决。思路并不复杂,但还是很容易让人糊涂的。
模块代码如下:
module clk_dis1_count(clk1,rst,op1,op2,op3,op4);
input clk1,rst; //声明时钟和复位信号,此处时钟为分频后得到的1KHz信号;
output reg [3:0]op1=4'd0;
output reg [3:0]op2=4'd0;
output reg [3:0]op3=4'd0;
output reg [3:0]op4=4'd0; /*op1,op2...为数码管显示时钟,这里分别定义,
op1,op3范围为【0-9】,op2,op4范围为【0-5】*/
//**************************************************************//
//reg [9:0] count_clk1=10'd0;
//parameter N2=500; //1KHz->2Hz,500分频电路;
parameter N2=1000; //更改成为1hz,每一秒输出一次,做电子表使用。 十位数可以达到1024,不会溢出。
reg clk3=1'b0; //输出clk3 ,2Hz
reg [9:0]count_clk1=10'd0;//计数器,足够用了。
always @(posedge clk1 or negedge rst)
begin
if (!rst)
begin
count_clk1<=10'd0;
clk3<=1'b0;
end
else
if(count_clk100.后两位为00分位置加一。
else
begin
if(op3<9)
op3<=op3+1'b1;//由于分和秒都是60进制,因此方法同上。
else
begin
if(op4<5)
op4<=op4+1'b1;
else
op4<='d0;
op3<='d0;
end
op2<='d0;
end
op1<='d0;
end
end
end
endmodule
我的板子上的数码管是共阴极的,建立一个查询表,设计过程中用case(Num)语句改变输出。
数码管片选电平为高时数码管点亮,这里设计一个状态机,设计一个IDLE状态和四个输出状态在对应数码管点亮时输出对应位置的数据,然后切换到下一个输出状态。
这部分的代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: bupt ,mr_bai
// Engineer:
//
// Create Date: 14:08:33 03/01/2014
// Design Name:
// Module Name: clk_display
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module clk_display(
clk,
rst,
sm_seg,
sm_bit
);
input clk;
input rst;
output reg[7:0] sm_seg;
output reg [3:0]sm_bit;
//**********************
//定义线网型连接线;
//*******************
wire clk1;
wire [3:0]op1,op2,op3,op4;
//*********************
//定义状态机
parameter IDLE=5'b0000_1,
SA =5'b0001_0,
SB =5'b0010_0,
SC =5'b0100_0,
SD =5'b1000_0;
reg [4:0]state;
//
reg [3:0]Num;
clk_div M1(
.clk(clk),
.rst(rst),
.clk_out(clk1)
);
clk_dis1_count M2(
.clk1(clk1),
.rst(rst),
.op1(op1),
.op2(op2),
.op3(op3),
.op4(op4)
);
/*always @(posedge clk1)
begin
sm_bit<={sm_bit[2:0],sm_bit[3]};
end*/
always @(posedge clk1 or negedge rst)
begin
if(!rst)
state<=IDLE;
else
begin
case (state)
IDLE:state<=SA ;
SA:
begin
sm_bit<=4'b0001;
Num <=op1;
state <=SB;
end
SB:
begin
sm_bit<=4'b0010;
Num <=op2;
state <=SC;
end
SC:
begin
sm_bit<=4'b0100;
Num <=op3;
state <=SD;
end
SD:
begin
sm_bit<=4'b1000;
Num <=op4;
state <=SA;
end
default state<=IDLE;
endcase
end
end
always @ (Num)//
begin
case (Num)
4'h0 : sm_seg = 8'h3f; // "0"
4'h1 : sm_seg = 8'h06; // "1"
4'h2 : sm_seg = 8'h5b; // "2"
4'h3 : sm_seg = 8'h4f; // "3"
4'h4 : sm_seg = 8'h66; // "4"
4'h5 : sm_seg = 8'h6d; // "5"//共阴极数码管表
4'h6 : sm_seg = 8'h7d; // "6"
4'h7 : sm_seg = 8'h07; // "7"
4'h8 : sm_seg = 8'h7f; // "8"
4'h9 : sm_seg = 8'h6f; // "9"
4'ha : sm_seg = 8'h77; // "a"
4'hb : sm_seg = 8'h7c; // "b"
4'hc : sm_seg = 8'h39; // "c"
4'hd : sm_seg = 8'h5e; // "d"
4'he : sm_seg = 8'h79; // "e"
4'hf : sm_seg = 8'h71; // "f"
endcase
end
endmodule