一、动态显示驱动模块示意图
二、动态显示驱动模块波形图
1.待显示数据data给其任意值9876;
2.小数点位point选中倒数第二个,数据为000-010,则显示的数据为987.6;
3.符号位sign首先给定低电平,当复位信号sys_rst_n无效(高)时,sign电平拉高,表示要显示负数,即-987.6;
4.使能信号seg_en给定初值低电平,复位信号无效给定高电平,表示数码管一直处于显示状态。
data_reg变量只寄存符号位和数据位,不包含小数点位。(结果为X-9876,使用5个数码位,最高位不显示,用X表示)
每个数码位显示时间为1ms,为了对1ms进行记数,设置一个计数器变量cnt_1ms[15:0],初值为0,一个时钟周期20ns,1ms记数5*104个周期,则计数器最大值为49999(对应二进制为1100 0011 0100 1111),故计数器位宽16。
声明一个一毫秒标志信号flag_1ms用来控制数码位的选择,当计数器为最大值-1时保持高电平,其他时刻为低电平。
为了对扫描周期进行记数,声明cnt_sel变量,6个数码管,最大值为5(二进制101,三位宽)。位选信号sel的000000表示一个数码位都不选中,000001表示选中第一个数码位。
定义一个变量表示即将要显示的数据data_disp,选中第一个数码位时显示6,第二个数码位显示7,第五个数码位显示负号,用10表示(也可以选择除了0-9的其他数值表示),最高位不显示,用11表示(二进制1011)。
声明变量小数点位dot_disp,高电平不显示,在第二个数码位拉低电平,其他时刻拉高。
输出段选信号seg,共阳数码管(段选时低电平点亮),初值全F表示不点亮,数字6对应的十六进制格式为82(查表);7表示f8,但由于小数点位要点亮,故7的dp应该为0,即0111_1000,对应十六进制的78,而不是f8;10表示负号,负号就是g位,即g为0(低有效),其他为1,也就是1011 1111,即bf;11不点亮就是全1,即ff。
但段选信号和位选信号差一个时钟周期,为同步就要对信号进行打拍,将sel信号定义为一个变量sel_reg,使用时序逻辑对其进行打拍生成时序信号sel。
三、代码
module seg_dynamic
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低有效
input wire [19:0] data , //数码管要显示的值
input wire [5:0] point , //小数点显示,高电平有效
input wire seg_en , //数码管使能信号,高电平有效
input wire sign , //符号位,高电平显示负号
output reg [5:0] sel , //数码管位选信号
output reg [7:0] seg //数码管段选信号
);
parameter CNT_MAX = 16'd49_999;
//wire define
wire [3:0] unit ; //个位数
wire [3:0] ten ; //十位数
wire [3:0] hun ; //百位数
wire [3:0] tho ; //千位数
wire [3:0] t_tho ; //万位数
wire [3:0] h_hun ; //十万位数
//reg define
reg [23:0] data_reg ; //待显示数据寄存器
reg [15:0] cnt_1ms ; //1ms计数器
reg flag_1ms ; //1ms标志信号
reg [2:0] cnt_sel ; //数码管位选计数器
reg [5:0] sel_reg ; //位选信号
reg [3:0] data_disp ; //当前数码管显示的数据
reg dot_disp ; //当前数码管显示的小数点
//data_reg:控制数码管显示数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_reg <= 24'b0;
//若显示的十进制数的十万位为非零数据或需显示小数点,则六个数码管全显示
else if((h_hun) || (point[5]))//最高位非零或最高位的符号位为高电平
data_reg <= {h_hun,t_tho,tho,hun,ten,unit};
//若显示的十进制数的万位为非零数据或需显示小数点,则值显示在5个数码管上
//打比方我们输入的十进制数据为20’d12345,我们就让数码管显示12345而不是012345
else if(((t_tho) || (point[4])) && (sign == 1'b1))//显示负号
data_reg <= {4'd10,t_tho,tho,hun,ten,unit};//4'd10我们定义为显示负号
else if(((t_tho) || (point[4])) && (sign == 1'b0))
data_reg <= {4'd11,t_tho,tho,hun,ten,unit};//4'd11我们定义为不显示
//若显示的十进制数的千位为非零数据或需显示小数点,则值显示4个数码管
else if(((tho) || (point[3])) && (sign == 1'b1))
data_reg <= {4'd11,4'd10,tho,hun,ten,unit};
else if(((tho) || (point[3])) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,tho,hun,ten,unit};
//若显示的十进制数的百位为非零数据或需显示小数点,则值显示3个数码管
else if(((hun) || (point[2])) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd10,hun,ten,unit};
else if(((hun) || (point[2])) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit};
//若显示的十进制数的十位为非零数据或需显示小数点,则值显示2个数码管
else if(((ten) || (point[1])) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd11,4'd10,ten,unit};
else if(((ten) || (point[1])) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit};
//若显示的十进制数的个位且需显示负号
else if(((unit) || (point[0])) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,unit};
//若上面都不满足都只显示一位数码管
else
data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit};
//cnt_sel:从0到5循环数,用于选择当前显示的数码管
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_sel <= 3'd0;
else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
cnt_sel <= 3'd0;
else if(flag_1ms == 1'b1)
cnt_sel <= cnt_sel + 1'b1;
else
cnt_sel <= cnt_sel;
//数码管位选信号寄存器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
sel_reg <= 6'b000_000;
else if((cnt_sel == 3'd0) && (flag_1ms == 1'b1))
sel_reg <= 6'b000_001;
else if(flag_1ms == 1'b1)
sel_reg <= sel_reg << 1;
else
sel_reg <= sel_reg;
//控制数码管的位选信号,使六个数码管轮流显示
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_disp <= 4'b0;
else if((seg_en == 1'b1) && (flag_1ms == 1'b1))
case(cnt_sel)
3'd0: data_disp <= data_reg[3:0] ; //给第1个数码管赋个位值
3'd1: data_disp <= data_reg[7:4] ; //给第2个数码管赋十位值
3'd2: data_disp <= data_reg[11:8] ; //给第3个数码管赋百位值
3'd3: data_disp <= data_reg[15:12]; //给第4个数码管赋千位值
3'd4: data_disp <= data_reg[19:16]; //给第5个数码管赋万位值
3'd5: data_disp <= data_reg[23:20]; //给第6个数码管赋十万位值
default:data_disp <= 4'b0 ;//其他情况赋值0
endcase
else
data_disp <= data_disp;
//dot_disp:小数点低电平点亮,需对小数点有效信号取反
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
dot_disp <= 1'b1;
else if(flag_1ms == 1'b1)
dot_disp <= ~point[cnt_sel];//共阳数码管低电平点亮,故取反
else
dot_disp <= dot_disp;
//控制数码管段选信号,显示数字
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
seg <= 8'b1111_1111;
else
case(data_disp)
4'd0 : seg <= {dot_disp,7'b100_0000}; //显示数字0
4'd1 : seg <= {dot_disp,7'b111_1001}; //显示数字1
4'd2 : seg <= {dot_disp,7'b010_0100}; //显示数字2
4'd3 : seg <= {dot_disp,7'b011_0000}; //显示数字3
4'd4 : seg <= {dot_disp,7'b001_1001}; //显示数字4
4'd5 : seg <= {dot_disp,7'b001_0010}; //显示数字5
4'd6 : seg <= {dot_disp,7'b000_0010}; //显示数字6
4'd7 : seg <= {dot_disp,7'b111_1000}; //显示数字7
4'd8 : seg <= {dot_disp,7'b000_0000}; //显示数字8
4'd9 : seg <= {dot_disp,7'b001_0000}; //显示数字9
4'd10 : seg <= 8'b1011_1111 ; //显示负号,只点亮g,低电平
4'd11 : seg <= 8'b1111_1111 ; //不显示任何字符
default:seg <= 8'b1100_0000;
endcase
//sel:数码管位选信号赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
sel <= 6'b000_000;
else
sel <= sel_reg;
bcd bcd_inst
(
.sys_clk (sys_clk ) ,
.sys_rst_n (sys_rst_n ) ,
.data (data ) ,
.unit (unit ),
.ten (ten ),
.hun (hun ),
.tho (tho ),
.t_tho (t_tho),
.h_hun (h_hun)
);
endmodule
四、测试代码
`timescale 1ns/1ns
module tb_seg_dynamic();
reg sys_clk ;
reg sys_rst_n ;
reg [19:0] data ;
reg [5:0] point ;
reg seg_en ;
reg sign ;
wire [5:0] sel ;
wire [7:0] seg ;
initial
begin
sys_clk=1'b1;
sys_rst_n<=1'b0;
data<=20'd0;
point<=6'b0;
sign<=1'b0;
seg_en<=1'b0;
#30
sys_rst_n<=1'b1;
data<=20'd9876;
point<=6'b0000_010;
sign<=1'b1;
seg_en<=1'b1;
end
always #10 sys_clk=~sys_clk;
defparam seg_dynamic_inst.CNT_MAX=20'd5;//CNT_MAX参数重定义
seg_dynamic seg_dynamic_inst
(
.sys_clk (sys_clk ) ,
.sys_rst_n (sys_rst_n) ,
.data (data ) ,
.point (point ) ,
.seg_en (seg_en ) ,
.sign (sign ) ,
. sel ( sel ) ,
. seg ( seg )
);
endmodule