要求:数码管以0.5秒为间隔依次显示0——999999
数码管动态显示利用的是人的视觉暂留效应和数码管的余晖效应
每毫秒只有一个数码管亮,显示数据,数码管依次循环亮,形成同时亮的效果。
程序模块主要要分成:
数码管的显示(具体在什么时候显示什么内容),
数码管显示的内容分解在单个数码管上该是怎样(涉及到BCD码)
74hc595的控制
module top(
input wire sys_clk ,
input wire sys_rst ,
output wire ds ,
output wire oe ,
output wire shcp ,
output wire stcp
);
wire [19:0] data ;
wire [5:0] point ;
wire sign ;
wire seg_en ;
seg_595_dynamic seg_595_dynamic_inst(
.sys_clk(sys_clk) ,
.sys_rst(sys_rst) ,
.data (data ) ,
.point (point ) ,
.sign (sign ) ,
.seg_en (seg_en ) ,
.ds (ds ) ,
.oe (oe ) ,
.shcp (shcp ) ,
.stcp (stcp)
);
shumaguan_show
#(
.cnt_100ms_max (23'd4999_999 ) , //100ms 一次 数字变换
. data_max ( 20'd999_999 ) //显示数字的值
)
shumaguan_show_inst
(
.sys_clk (sys_clk) ,
.sys_rst (sys_rst) ,
.data (data ) ,
.point (point ) ,
.sign (sign ) ,
.seg_en (seg_en)
);
endmodule
module seg_595_dynamic(
input wire sys_clk ,
input wire sys_rst ,
input wire [19:0] data ,
input wire [5:0] point ,
input wire sign ,
input wire seg_en ,
output wire ds ,
output wire oe ,
output wire shcp ,
output wire stcp
);
wire [5:0] sel;
wire [7:0] seg;
hc595_ctrl hc595_ctrl_inst(
.sys_clk(sys_clk) ,
.sys_rst(sys_rst) ,
.seg (seg) ,
.sel (sel) ,
.ds (ds ) ,
.shcp (shcp) ,
.stcp (stcp ) , //每一个上升沿就讲移位寄存器数据送入存储寄存��
.oe (oe ) //低电平时将存储寄存器中数据并行输��
);
seg_dynamic
#(
.cnt_1ms_max (16'd49)
//parameter cnt_1ms_max = 16'd49_999
)
seg_dynamic_inst
(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
. data ( data ),
. point ( point ), //��
.sign (sign ), //符号
.seg_en (seg_en ),
.sel (sel ),
.seg (seg)
);
endmodule
module hc595_ctrl(
input wire sys_clk,
input wire sys_rst,
input wire [7:0] seg,
input wire [5:0] sel,
output reg ds, //串行输入
output reg shcp, //每一个上升沿就往移位寄存器送入一位
output reg stcp, //每一个上升沿就讲移位寄存器数据送入存储寄存器
output wire oe //低电平时将存储寄存器中数据并行输出
);
wire [13:0] data; //利用组合形式给它赋值,那就得是wire型啊
assign data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel[5:0]}; //将段选位选数据排列在一起方便串行输入到芯片
reg [1:0] cnt; //分频计数 74HC595对时钟有要求
reg [3:0] cnt_bit;
always @ (posedge sys_clk or negedge sys_rst)
if (!sys_rst)
cnt <= 2'd0;
else if (cnt == 2'd3)
cnt <= 2'd0;
else
cnt <= cnt +2'd1;
always @ (posedge sys_clk or negedge sys_rst) //记录串行输入到多少位了
if (!sys_rst)
cnt_bit <= 4'd0;
else if ((cnt_bit == 4'd13)&&(cnt == 2'd3))
cnt_bit <= 4'd0;
else if (cnt == 2'd3)
cnt_bit <= cnt_bit + 1'd1;
else
cnt_bit <=cnt_bit;
always @ (posedge sys_clk or negedge sys_rst) //
if (!sys_rst)
ds <= 1'b0;
else if (cnt == 4'd0)
ds <= data[cnt_bit];
else
ds <= ds;
always @ (posedge sys_clk or negedge sys_rst) //
if (!sys_rst)
shcp <=1'b0;
else if (cnt == 2'd2)
shcp <= 1'b1;
else if (cnt == 2'd0)
shcp <= 1'b0;
else
shcp <= shcp;
always @ (posedge sys_clk or negedge sys_rst) //
if (!sys_rst)
stcp <= 1'b0;
else if ((cnt_bit == 4'd0) && (cnt == 2'd0))
stcp <= 1'b1;
else if ((cnt_bit == 4'd0) && (cnt == 2'd2))
stcp <= 1'b0;
else
stcp <= stcp;
assign oe = 1'b0;
endmodule
module seg_dynamic
#(
parameter cnt_1ms_max = 16'd5
//parameter cnt_1ms_max = 16'd49_999
)
(
input wire sys_clk,
input wire sys_rst,
input wire [19:0] data,
input wire [5:0] point, //点
input wire sign, //符号
input wire seg_en,
output reg [5:0] sel,
output reg [7:0] seg
);
wire [3:0] shi_wan; //每个位置的具体数据
wire [3:0] wan; //每个位置的具体数据
wire [3:0] qian; //每个位置的具体数据
wire [3:0] bai; //每个位置的具体数据
wire [3:0] shi; //每个位置的具体数据
wire [3:0] ge; //每个位置的具体数据
reg [23:0] data_reg; //数据缓存(BCD码)
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; //小数点显示与否
bcd_8421 bcd_8421_inst (
.sys_clk (sys_clk ) ,
. sys_rst ( sys_rst ) ,
. data ( data ) ,
. ge ( ge ) ,
. shi ( shi ) ,
. bai ( bai ) ,
. qian ( qian ) ,
. wan ( wan ) ,
. shi_wan ( shi_wan)
);
always @(posedge sys_clk or negedge sys_rst) //数据寄存,你要考虑到底用了多少个数码管
if (!sys_rst)
data_reg <= 24'b0;
else if ((shi_wan) || (point[5])) //最前端那个数码管有数值或者有点
data_reg <= {shi_wan,wan ,qian ,bai,shi,ge};
else if (((wan) || (point[4])) && (sign == 1'b1)) // 看第五位有值或有点没,有的话再说第六位(上一位)有符号没
data_reg <= {4'd10,wan ,qian,bai ,shi ,ge }; //4'd10 表示负号
else if (((wan) || (point[4])) && (sign == 1'b0))
data_reg <= {4'd11,wan ,qian,bai ,shi ,ge }; //4'd11 表示没符号
else if (((qian) || (point[3])) && (sign == 1'b1) )
data_reg <= {4'd11,4'd10 ,qian,bai ,shi ,ge };
else if (((qian) || (point[3])) && (sign == 1'b0) )
data_reg <= {4'd11,4'd11,qian,bai ,shi ,ge };
else if (((bai) || (point[2])) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd10,bai ,shi ,ge };
else if (((bai) || (point[2])) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,4'd11,bai ,shi ,ge };
else if (((shi) || (point[1])) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd11,4'd10 ,shi ,ge };
else if (((shi) || (point[1])) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,4'd11,4'd11,shi ,ge };
else if (((ge) || (point[0])) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10 ,ge };
else if (((ge) || (point[0])) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,ge };
else
data_reg <= 24'b0;
always @(posedge sys_clk or negedge sys_rst) //1ms 49_999
if (!sys_rst)
cnt_1ms <= 16'd0;
else if (cnt_1ms == cnt_1ms_max)
cnt_1ms <= 16'd0;
else
cnt_1ms <= cnt_1ms +16'd1;
always @(posedge sys_clk or negedge sys_rst) //1ms标志信号 49_999
if (!sys_rst)
flag_1ms <= 1'b0;
else if (cnt_1ms == cnt_1ms_max-1'b1)
flag_1ms <= 1'b1 ;
else
flag_1ms <= flag_1ms;
always @(posedge sys_clk or negedge sys_rst) //位选,1ms转换一个数码管
if (!sys_rst)
cnt_sel <= 3'd0;
else if ((flag_1ms == 1'b1) && (cnt_sel == 3'd5))
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) //借助flag_1ms对时间分成一份一份,利用cnt_sel知道该谁亮了 ,估计不要cnt_sel,用自己也可以判断
if (!sys_rst)
sel_reg <= 6'b000_000;
else if ((flag_1ms == 1'b1) && (cnt_sel == 3'd0))
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)
if (!sys_rst)
data_disp <= 4'b0;
else if ((seg_en == 1'b1) && (flag_1ms))
case (cnt_sel)
3'd0 : data_disp <= data_reg[3:0];
3'd1 : data_disp <= data_reg[7:4];
3'd2 : data_disp <= data_reg[11:8];
3'd3 : data_disp <= data_reg[15:12];
3'd4 : data_disp <= data_reg[19:16];
3'd5 : data_disp <= data_reg[23:20];
default : data_disp <= 4'b0;
endcase
else
data_disp <= data_disp;
always @(posedge sys_clk or negedge sys_rst) //低电平有效
if (!sys_rst)
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) //对数码管位选赋值
if (!sys_rst)
sel <= 6'b00_0000;
else
sel <= sel_reg; // 打拍操作,时序延迟一个时隙,让段选位选在同一个时间点变换
always @(posedge sys_clk or negedge sys_rst) //数码管共阳
if (!sys_rst)
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 //seg要接收到的也需要是表示数字的二进制形式
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 ; //显示负号
4'd11 : seg <= 8'b1111_1111 ; //不显示任何字符
default:seg <= 8'b1100_0000; //其他情况显示0
endcase
endmodule
module bcd_8421(
input wire sys_clk,
input wire sys_rst,
input wire [19:0] data,
output reg [3:0] ge,
output reg [3:0] shi,
output reg [3:0] bai,
output reg [3:0] qian,
output reg [3:0] wan,
output reg [3:0] shi_wan
);
reg [4:0] cnt_shift; //移位判断计数器
reg [43:0] data_shift; //移位判断数据寄存器
reg shift_flag; //移位判断标志信号
always @(posedge sys_clk or negedge sys_rst) //0到21的循环计数(移位加三的方式实现的话,首位各加一个,999_999十进制换算到二进制有20位)
if(!sys_rst )
cnt_shift <= 5'd0;
else if ((cnt_shift==5'd21) && (shift_flag == 1'b1))
cnt_shift <= 5'd0;
else if (shift_flag == 1'b1)
cnt_shift <= cnt_shift + 1'b1;
else
cnt_shift <= cnt_shift;
always @(posedge sys_clk or negedge sys_rst) //移位判断标志信号,控制移位判断的先后顺序
if(!sys_rst )
shift_flag <= 1'b0;
else
shift_flag <= ~shift_flag;
always @(posedge sys_clk or negedge sys_rst) //data_shift有44位,其中高24位是999_999形成的BCD码需要24位,低20位是999_999的二进制形式是20位
if(!sys_rst )
data_shift <=44'b0;
else if (cnt_shift == 5'd0)
data_shift <= {24'b0, data };
else if ((cnt_shift <= 5'd20)&&(shift_flag==1'b0)) //cnt_shift=0时,还是上次的,因为六位数最大变成二进制有20位,所以考虑有20次判断和移位操作,21次稳定,将它输出,
begin
data_shift[23:20] <= (data_shift[23:20]>4) ? (data_shift[23:20]+2'd3) : (data_shift[23:20]);
data_shift[27:24] <= (data_shift[27:24]>4) ? (data_shift[27:24]+2'd3) : (data_shift[27:24]);
data_shift[31:28] <= (data_shift[31:28]>4) ? (data_shift[31:28]+2'd3) : (data_shift[31:28]);
data_shift[35:32] <= (data_shift[35:32]>4) ? (data_shift[35:32]+2'd3) : (data_shift[35:32]);
data_shift[39:36] <= (data_shift[39:36]>4) ? (data_shift[39:36]+2'd3) : (data_shift[39:36]);
data_shift[43:40] <= (data_shift[43:40]>4) ? (data_shift[43:40]+2'd3) : (data_shift[43:40]);
end
else if ((cnt_shift <= 5'd20)&&(shift_flag==1'b1))
data_shift <= data_shift << 1;
else
data_shift <= data_shift;
always @(posedge sys_clk or negedge sys_rst) //当计数器等于20时,对各位置的赋值(BCD)
if(!sys_rst )
begin
ge <= 4'b0;
shi <= 4'b0;
bai <= 4'b0;
qian <= 4'b0;
wan <= 4'b0;
shi_wan <= 4'b0;
end
else if (cnt_shift == 5'd21)
begin
ge <= data_shift[23:20];
shi <= data_shift[27:24];
bai <= data_shift[31:28];
qian <= data_shift[35:32];
wan <= data_shift[39:36];
shi_wan <= data_shift[43:40];
end
else
begin
ge <= ge ;
shi <= shi ;
bai <= bai ;
qian <= qian ;
wan <= wan ;
shi_wan <= shi_wan ;
end
endmodule
module shumaguan_show
#(
parameter cnt_100ms_max=23'd4999_999, //100ms 一次 数字变换
data_max = 20'd999_999 //显示数字的值
)
(
input wire sys_clk,
input wire sys_rst,
output reg [19:0] data,
output wire [5:0] point,
output wire sign,
output reg seg_en
);
reg [22:0] cnt_100ms;
reg cnt_flag;
always @(posedge sys_clk or negedge sys_rst) //100ms时钟计数
if (!sys_rst)
cnt_100ms <= 23'd0;
else if (cnt_100ms == cnt_100ms_max)
cnt_100ms <= 23'd0;
else
cnt_100ms <= cnt_100ms + 23'd1;
always @(posedge sys_clk or negedge sys_rst) //100ms时钟的标志位
if (!sys_rst)
cnt_flag <= 1'd0;
else if (cnt_100ms == cnt_100ms_max-1'd1)
cnt_flag <= 1'd1;
else
cnt_flag <= 1'd0;
always @(posedge sys_clk or negedge sys_rst) //显示数据
if (!sys_rst)
data <= 20'd0;
else if ( (data == data_max)&&(cnt_flag == 1'd1))
data <= 20'd0;
else if (cnt_flag == 1'd1)
data <= data + 20'd1;
else
data <= data;
assign point = 6'b000000; //六个小数点(用不上)
assign sign = 1'b0; //正负号(用不上)
always @(posedge sys_clk or negedge sys_rst) //段选使能信号(高电平表示有效)
if (!sys_rst)
seg_en <= 1'b0;
else
seg_en <= 1'b1;
endmodule
仿真文件
module vtf_top;
// Inputs
reg sys_clk;
reg sys_rst;
// Outputs
wire ds;
wire oe;
wire shcp;
wire stcp;
// Instantiate the Unit Under Test (UUT)
top uut (
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.ds(ds),
.oe(oe),
.shcp(shcp),
.stcp(stcp)
);
initial begin
// Initialize Inputs
sys_clk = 0;
sys_rst = 0;
// Wait 100 ns for global reset to finish
#100;
sys_rst <= 1'b1;
// Add stimulus here
end
always # 10 sys_clk = ~sys_clk ;
endmodule