【原创】FPGA应用(四)——电子钟
0赞
发表于 7/26/2015 5:45:12 PM
阅读(3883)
一、设计需求
设计一个可以显示分、秒的电子钟模块并在红色飓风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:
/**********************************************版权申明************************************************* ** 电子技术应用网站, CrazyBird ** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird ** **--------------------------------------------文件信息-------------------------------------------------- ** 文件名: 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 ** http://www.chinaaet.com, http://blog.chinaaet.com/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:
/**********************************************版权申明************************************************* ** 电子技术应用网站, CrazyBird ** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird ** **--------------------------------------------文件信息-------------------------------------------------- ** 文件名: 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流文件以及下载到板子上,便可以在数码管上看到电子钟的效果。