数码管分七段数码管和八段数码管。七段和八段的区别在于,是否包括小数点DP(Digital Point)。本实验中使用的是数码管是8段数码管,每段是由led组成。通过控制每段led的亮灭,来控制数码管显示不同的数字和字母。
,a—dp为输入端,全部在二极管的正极,二极管的负极共同接地。只有当a—dp输入为高电平的时候,二极管才导通,然后对应的段发亮。
所示,a—dp为输入端,全部在二极管的负极,二极管的正极极共同接+5v(高电平)。只有当a—dp输入为低电平的时候,二极管才导通,然后对应的段发亮。
要显示不同的数字或者字母,就要选择点亮对应的led段。图5中对应的是cyclone IV开发板上数码管的真值表,可以通过查找该表来显示我们想要的数字或者字母。
在静态显示中,只考虑段选信号。在不同的时刻,各个位选信号保持不变,并根据真值表,选择要显示的数字或者字母。
在动态显示中,需要将位选信号考虑进来。在不同的时刻,各个位的位选信号随时改变,并根据真值表,选择显示不同的数字或者字母。
Cyclone IV开发板中的数码管是共阳极,所以数码管中需要给低电平,对应的led段才会亮。位选信号原理图如图8所示,位选信号也是需要低电平有效。
创建time_count文件,并编写 time_count模块代码。
module time_count(
input clk ,//50MHz时钟信号
input rst_n,//复位信号
output reg flag//一个时钟周期的脉冲信号
);
parameter MAX_NUM = 25'd25_000_000;//计数器最大计数值
reg [24:0] cnt ; //时钟分频计数器
//计数器对时钟计数,每0.5s,输出一个时钟周期脉冲信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin//按复位时
flag <= 1'b0;//信号为0
cnt <= 25'd0;//计数器清零
end
else if(cnt < MAX_NUM - 1'b1)begin//如果没到时间
flag <= 1'b0;//信号为0
cnt <= cnt + 1'b1;//计数器正常累计+1
end
else begin //否则到时间
flag <= 1'b1;//信号变为1
cnt <= 25'd0;
end
end
endmodule
创建seg_led_static文件,并编写 seg_led_static模块代码。
module seg_led_static(
input clk ,
input rst_n ,
input flag ,
output reg [5:0] sel ,//数码管位选信号
output reg [7:0] seg //数码管段选信号
);
reg [3:0] num;//数码管显示十六进制数
//控制数码管位选信号(注:低电平有效),选中所有的数码管
always @(posedge clk or negedge rst_n)begin
if(!rst_n)//如果按复位键0
sel <= 6'b111111;//则默认为高电平
else
sel <= 6'b000000;//否则为低电平
end
//每次通知信号flag到达时,数码管计数加1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
num <= 4'h0;
else if(flag)begin
if(num < 4'hf)
num <= num + 1'h1;
else
num <= 4'h0;
end
else begin
num <= num;
end
end
//根据数码管显示的数值,控制段选信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
seg <= 8'b0;
else begin
case(num)//匹配16进制数
4'h0: seg <= 8'b1100_0000;//匹配到后参考共阳极真值表
4'h1: seg <= 8'b1111_1001;
4'h2: seg <= 8'b1010_0100;
4'h3: seg <= 8'b1011_0000;
4'h4: seg <= 8'b1001_1001;
4'h5: seg <= 8'b1001_0010;
4'h6: seg <= 8'b1000_0010;
4'h7: seg <= 8'b1111_1000;
4'h8: seg <= 8'b1000_0000;
4'h9: seg <= 8'b1001_0000;
4'ha: seg <= 8'b1000_1000;
4'hb: seg <= 8'b1000_0011;
4'hc: seg <= 8'b1100_0110;
4'hd: seg <= 8'b1010_0001;
4'he: seg <= 8'b1000_0110;
4'hf: seg <= 8'b1000_1110;
default : seg <= 8'b1100_0000;
endcase
end
end
endmodule
创建top_seg_led_static文件,并编写top_seg_led_static模块代码:
module top_seg_led_static(
input clk ,//50MHz系统时钟
input rst_n,//系统复位信号(低有效)
output [5:0] sel ,//数码管位选
output [7:0] seg//数码管段选
);
parameter MAX_NUM = 25'd25_000_000;// 数码管变化的时间间隔0.5s
wire add_flag ;// 数码管变化的通知信号
//每隔0.5s产生一个时钟周期的脉冲信号
time_count #(.MAX_NUM(MAX_NUM)) u_time_count(
.clk (clk) ,//50MHz时钟信号
.rst_n (rst_n),//复位信号
.flag (add_flag)//一个时钟周期的脉冲信号
);
//每当脉冲信号到达时,使数码管显示的数值加1
seg_led_static u_seg_led_static(
.clk (clk) ,
.rst_n (rst_n) ,
.flag (add_flag),
.sel (sel) ,
.seg (seg)
);
endmodule
计数器模块
module counter(
input wire clk ,//时钟
input wire rst_n,//复位信号
output reg [4:0] hour ,//小时
output reg [5:0] min ,//分钟
output reg [5:0] sec//秒
);
parameter MAX_NUM = 26'd49_999_999;//1s
parameter TOTAL_SEC = 17'd86399 ;//24x60x60s
reg [25:0] cnt_sec ;//秒计数器
reg [16:0] cnt_time;//计时器
//计时1s秒钟模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_sec <= 26'd0;
end
else if(cnt_sec == MAX_NUM)begin
cnt_sec <= 26'd0;
end
else begin
cnt_sec <= cnt_sec + 1'd1;
end
end
//时间模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_time <= 17'd0;
end
else if(cnt_time == TOTAL_SEC && cnt_sec == MAX_NUM)begin
cnt_time <= 17'd0;
end
else if(cnt_sec == MAX_NUM)begin
cnt_time <= cnt_time + 1'd1;
end
else begin
cnt_time <= cnt_time;
end
end
//取出时分秒模块
always@(*)begin
hour = cnt_time / 12'd3600 ;//小时
min = (cnt_time % 12'd3600) / 6'd60;//分钟
sec = (cnt_time % 12'd3600) % 6'd60;//秒
end
endmodule
数码管驱动模块
module seg_driver(
input wire clk ,//时钟
input wire rst_n,//复位信号
input wire [4:0] hour ,//小时
input wire [5:0] min ,//分钟
input wire [5:0] sec ,//秒
output reg [5:0] seg_sel,
output reg [7:0] seg_ment
);
parameter CNT_20US = 10'd999;//20微秒
wire [3:0] sec_low ;//秒的低位
wire [2:0] sec_high ;//秒的高位
wire [3:0] min_low ;//分钟的低位
wire [2:0] min_high ;//分钟的高位
wire [1:0] hour_low ;//小时的低位
wire [1:0] hour_high;//小时的高位
reg [9:0] cnt ;//计数器,计20us时间
reg [3:0] number ;//显示时分秒寄存器
//计时模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 10'd0;
end
else if(cnt == CNT_20US)begin
cnt <= 10'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
//位选信号切换模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seg_sel <= 6'b011111;//初始化第一个数码管亮
end
else if(cnt == CNT_20US)begin
seg_sel <= {seg_sel[0],seg_sel[5:1]};//每隔20us进行位移操作
end
else begin
seg_sel <= seg_sel;//其他时间保持不变
end
end
//位选信号译码模块
always@(*)begin
case(seg_sel)
6'b011111: number = sec_low ;//秒的低位给第一个数码管显示
6'b101111: number = sec_high ;//秒的高位给第二个数码管显示
6'b110111: number = min_low ;//分钟的低位给第三个数码管显示
6'b111011: number = min_high ;//分钟的高位给第四个数码管显示
6'b111101: number = hour_low ;//小时的低位给第五个数码管显示
6'b111110: number = hour_high;//小时的高位给第六个数码管显示
default: number = sec_low ;
endcase
end
//段选信号译码模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seg_ment <= 8'b1100_0000;//初始化显示0
end
else begin
case(number)
4'd0: seg_ment <= 8'b1100_0000;//数码管显示0
4'd1: seg_ment <= 8'b1111_1001;//数码管显示1
4'd2: seg_ment <= 8'b1010_0100;//数码管显示2
4'd3: seg_ment <= 8'b1011_0000;//数码管显示3
4'd4: seg_ment <= 8'b1001_1001;//数码管显示4
4'd5: seg_ment <= 8'b1001_0010;//数码管显示5
4'd6: seg_ment <= 8'b1000_0010;//数码管显示6
4'd7: seg_ment <= 8'b1111_1000;//数码管显示7
4'd8: seg_ment <= 8'b1000_0000;//数码管显示8
4'd9: seg_ment <= 8'b1001_0000;//数码管显示9
default: seg_ment <= 8'b1100_0000;//数码管显示0
endcase
end
end
assign sec_low = sec % 4'd10 ;//秒的低位如59秒--->9
assign sec_high = sec / 4'd10 ;//秒的高位如59秒--->5
assign min_low = min % 4'd10 ;//分钟的低位如59--->9
assign min_high = min / 4'd10 ;//分钟的高位如59--->5
assign hour_low = hour % 4'd10;//小时的低位如23--->3
assign hour_high = hour / 4'd10;//小时的高位如23--->2
endmodule
顶层模块
module digital_clock(
input wire clk ,//时钟
input wire rst_n,//复位信号
output wire [5:0] seg_sel,//数码管位选信号
output wire [7:0] seg_ment//数码管段选信号
);
parameter MAX_NUM = 26'd49_999_999;//1s
parameter TOTAL_SEC = 17'd86399 ;//60x24x24s
parameter CNT_20US = 10'd999 ;//20us
wire [4:0] hour;//小时
wire [5:0] min ;//分钟
wire [5:0] sec ;//秒
//实例化计数器模块
counter#(.MAX_NUM(MAX_NUM),
.TOTAL_SEC(TOTAL_SEC)) u_counter(
.clk (clk) ,
.rst_n (rst_n),
.hour (hour) ,
.min (min) ,
.sec (sec)
);
//实例化数码管驱动模块
seg_driver#(.CNT_20US(CNT_20US)) u_seg_driver(
.clk (clk) ,
.rst_n (rst_n) ,
.hour (hour) ,
.min (min) ,
.sec (sec) ,
.seg_sel (seg_sel),
.seg_ment (seg_ment)
);
endmodule