一、设计需求
设计一个可以显示分、秒的电子钟模块并在红色飓风E45开发板的四个的数码管进行显示。
二、设计思路
首先,我们得了解板上四个八段数码管的特性进行了解。图1所示为数码管的原理图,从中可以知道数码是共阴的,即当LED_AN0~LED_AN3为高电平时三极管导通,LED_S0~LED_S3为低电平,数码管被选中,其中LED_AN0~LED_AN3、LED_A~LED_G和LED_DP是直接连接到FPGA管脚上的。
图1 数码管原理图
其次,根据数码管的原理图给出数码管的编码列表,如表1所示。其中的点号DP根据需要进行亮与灭的选择。
表1 数码管编码对应表
接着,我们要对设计的功能进行合理的划分。根据需求电子钟的功能包括计数和编码显示两部分,故本设计的功能模块组成如图2所示。
图2 定时器设计框架
最后,再补充一点,由于数码管是动态扫描显示的,利用的人眼的视觉暂留效应及发光二极管的余晖效应,只要使扫描速度足够快(低于0.1秒),就不会看到由于数码管切换显示时的闪烁感。
三、设计实现
timer.v:
/**********************************************版权申明*************************************************
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: timer.v
** 创建者: CrazyBird
** 创建日期: 2015-7-26
** 版本号: v1.0
** 功能描述: 电子钟顶层模块
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module timer(
rst_n,
clk,
seg,
sel
);
//******************************************************************************
// 参数定义
//******************************************************************************
// 修改以下参数以满足需求
parameter CLK_CYCLE = 20; // 时钟周期,单位ns
parameter T0 = 1000_000; // 1ms延时
// 修改以上参数以满足需求
//******************************************************************************
// 端口定义
//******************************************************************************
input rst_n; // 全局复位,低电平有效
input clk; // 全局时钟,50MHz
output [7:0] seg; // 编码后的数码管输出
output [3:0] sel; // 数码管的位选
//******************************************************************************
// 变量定义
//******************************************************************************
wire [2:0] min_h; // 分的十位数
wire [3:0] min_l; // 分的个位数
wire [2:0] sec_h; // 秒的十位数
wire [3:0] sec_l; // 秒的个位数
wire display_flag; // 数码管动态显示标志位
//******************************************************************************
// 模块例化
//******************************************************************************
// 例化time_counter模块
time_counter #(
.CLK_CYCLE ( CLK_CYCLE ),
.T0 ( T0 )
)
u_time_counter (
.rst_n ( rst_n ),
.clk ( clk ),
.min_h ( min_h ),
.min_l ( min_l ),
.sec_h ( sec_h ),
.sec_l ( sec_l ),
.display_flag ( display_flag)
);
// 例化display模块
display u_display(
.rst_n ( rst_n ),
.clk ( clk ),
.min_h ( min_h ),
.min_l ( min_l ),
.sec_h ( sec_h ),
.sec_l ( sec_l ),
.display_flag ( display_flag),
.seg ( seg ),
.sel ( sel )
);
//******************************************************************************
endmodule
//*********************************************文件结束*****************************************************time_counter.v:
/**********************************************版权申明*************************************************
** 电子技术应用网站, CrazyBird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: time_counter.v
** 创建者: CrazyBird
** 创建日期: 2015-7-26
** 版本号: v1.0
** 功能描述: 时间计数
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module time_counter(
rst_n,
clk,
min_h,
min_l,
sec_h,
sec_l,
display_flag
);
//******************************************************************************
// 参数定义
//******************************************************************************
// 修改以下参数以满足需求
parameter CLK_CYCLE = 20; // 时钟周期,单位ns
parameter T0 = 1000_000; // 1ms延时
// 修改以上参数以满足需求
// 不要修改以下参数
parameter T0_VAL = T0/CLK_CYCLE-1; // 1ms延时
// 不要修改以上参数
//******************************************************************************
// 端口定义
//******************************************************************************
input rst_n; // 全局复位,低电平有效
input clk; // 全局时钟,50MHz
output reg [2:0] min_h; // 分的十位数
output reg [3:0] min_l; // 分的个位数
output reg [2:0] sec_h; // 秒的十位数
output reg [3:0] sec_l; // 秒的个位数
output display_flag; // 数码管动态扫描标志位
//******************************************************************************
// 1ms延时
//******************************************************************************
reg [15:0] cnt;
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
cnt <= (0);
else if(cnt < T0_VAL)
cnt <= cnt + 1'b1;
else
cnt <= (0);
end
assign delay_1ms = (cnt == T0_VAL); // 1ms延时完成标志位
assign display_flag = delay_1ms; // 数码管动态扫描标志位
//******************************************************************************
// 1s延时
//******************************************************************************
reg [9:0] mse;
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
mse <= (0);
else
begin
if(delay_1ms == 1'b1)
begin
if(mse < 10'd9)
mse <= mse + 1'b1;
else
mse <= (0);
end
end
end
wire sec_l_flag = ((mse == 10'd9) && (delay_1ms == 1'b1)); // 1s延时完成标志位
//******************************************************************************
// 秒计数实现
//******************************************************************************
// 秒个位数计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
sec_l <= 0;
else
begin
if(sec_l_flag == 1'b1)
begin
if(sec_l < 4'd9)
sec_l <= sec_l + 1'b1;
else
sec_l <= 0;
end
end
end
wire sec_h_flag = ((sec_l == 4'd9) && (sec_l_flag == 1'b1)); // 秒个位数进位标志位
// 秒十位数计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
sec_h <= 0;
else
begin
if(sec_h_flag == 1'b1)
begin
if(sec_h < 3'd5)
sec_h <= sec_h + 1'b1;
else
sec_h <= 0;
end
end
end
wire min_l_flag = ((sec_h == 3'd5) && (sec_h_flag == 1'b1)); // 秒十位数进位标志位
//******************************************************************************
// 分计数实现
//******************************************************************************
// 分个位数计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
min_l <= 0;
else
begin
if(min_l_flag == 1'b1)
begin
if(min_l < 4'd9)
min_l <= min_l + 1'b1;
else
min_l <= 0;
end
end
end
wire min_h_flag = ((min_l == 4'd9) && (min_l_flag == 1'b1)); // 分个位数进位标志位
// 分十位数计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
min_h <= 0;
else
begin
if(min_h_flag == 1'b1)
begin
if(min_h < 3'd5)
min_h <= min_h + 1'b1;
else
min_h <= 0;
end
end
end
//******************************************************************************
endmodule
//*********************************************文件结束*****************************************************display.v:
/**********************************************版权申明*************************************************
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: display.v
** 创建者: CrazyBird
** 创建日期: 2015-7-26
** 版本号: v1.0
** 功能描述: 编码显示
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module display(
rst_n,
clk,
min_h,
min_l,
sec_h,
sec_l,
display_flag,
seg,
sel
);
//******************************************************************************
// 端口定义
//******************************************************************************
input rst_n; // 全局复位,低电平有效
input clk; // 全局时钟,50MHz
input [2:0] min_h; // 分的十位数
input [3:0] min_l; // 分的个位数
input [2:0] sec_h; // 秒的十位数
input [3:0] sec_l; // 秒的个位数
input display_flag; // 数码管动态显示标志位
output reg [7:0] seg; // 编码后的数码管输出
output reg [3:0] sel; // 数码管的位选
//******************************************************************************
// 编码函数
//******************************************************************************
function [7:0] seg_data;
input [3:0] din; // 待编码数据
input dp; // 决定数码管点号是否点亮,1为点亮
begin
case(din)
4'd0 : seg_data = {7'b1111110,dp};
4'd1 : seg_data = {7'b0110000,dp};
4'd2 : seg_data = {7'b1101101,dp};
4'd3 : seg_data = {7'b1111001,dp};
4'd4 : seg_data = {7'b0110011,dp};
4'd5 : seg_data = {7'b1011011,dp};
4'd6 : seg_data = {7'b1011111,dp};
4'd7 : seg_data = {7'b1110000,dp};
4'd8 : seg_data = {7'b1111111,dp};
4'd9 : seg_data = {7'b1111011,dp};
endcase
end
endfunction
//******************************************************************************
// 数码管动态显示的计数器
//******************************************************************************
reg [1:0] cnt; // 由于只有四个数码管,故只需两位
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
cnt <= (0);
else if(display_flag == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
//******************************************************************************
// 编码输出
//******************************************************************************
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
seg <= (0);
sel <= (0);
end
else
begin
case(cnt)
2'b00 : // 显示秒个位数
begin
seg <= seg_data(sec_l,1'b0);
sel <= 4'b0001;
end
2'b01 : // 显示秒十位数
begin
seg <= seg_data({1'b0,sec_h},1'b0);
sel <= 4'b0010;
end
2'b10 : // 显示分个位数
begin
seg <= seg_data(min_l,1'b1);
sel <= 4'b0100;
end
2'b11 : // 显示分十位数
begin
seg <= seg_data({1'b0,min_h},1'b0);
sel <= 4'b1000;
end
endcase
end
end
//******************************************************************************
endmodule
//*********************************************文件结束*****************************************************电子钟的modelsim仿真结果如图3~图8所示。其中,为了减少仿真时间以及可以在较短时间内验证功能的正确性,将设计中的延时参数改小。
图3
图4
图5
图6
图7
图8
最后,经过综合、实现、生成bit流文件以及下载到板子上,便可以在数码管上看到电子钟的效果。









QQ好友和群
QQ空间
腾讯微博
腾讯朋友
收藏
淘帖
顶
踩
