一、设计思路
数码管共阳
SEL:位选
SEG:段选
数码管1显示0:sel = 3\'b001,seg = 8\'b0000_0011;
数码管2显示0:sel = 3\'b010,seg = 8\'b0000_0011;
数码管3显示0:sel = 3\'b100,seg = 8\'b0000_0011;
想要数码管1,2,3看着全亮,就要让位选的变化非常快(<=20ms),以至于肉眼观察不到
所以需要设计一个计算20ms的计数器,一个位选8个数码管的计数器,等到计数器计满一次就循环移位以达到位选的目的
至于段选,8个数码管,要输入指定数据,每一个指定数据占用4位,4*8=32位
输入的数据如何如何变成数码管上显示的数据,首先需要一个8选1的多路选择器,选择器的控制端由位选计数器来控制,输出的即为指定数码管的指定数据,但是输入的数据是十进制不是数码管所表示的数据,因此要建立一个查找表,以输出指定的值
二、代码
module hex8(
clk ,
rst_n ,
disp_data,
sel , //片选
seg , //段选
en //现在用不到,是为了接芯片驱动的时候,在数据加载完成后,使能芯片驱动输入数据
);
parameter DATA_W = 32;
parameter SEL_W = 8;
parameter SEG_W = 8;
parameter CNTIME_N = 50000; //1ms
parameter HEX_N = 8; //8个数码管
parameter CNTIME_W = 16;
parameter HEX_W = 4;
input clk;
input rst_n;
input [DATA_W-1:0] disp_data;
output [SEL_W-1:0] sel;
output [SEG_W-1:0] seg;
output en;
reg [SEL_W-1:0] sel;
reg [SEG_W-1:0] seg;
reg [CNTIME_W-1:0] cnt_time;
wire add_cnt_time;
wire end_cnt_time;
reg [HEX_W-1:0] cnt_hex;
wire add_cnt_hex;
wire end_cnt_hex;
reg [HEX_W-1:0] hex_code;
reg en;
//1ms计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_time <= 0;
else if(add_cnt_time)begin
if(end_cnt_time)
cnt_time <= 0;
else
cnt_time <= cnt_time + 1\'b1;
end
end
assign add_cnt_time = 1;
assign end_cnt_time = add_cnt_time && cnt_time == CNTIME_N - 1;
//片选数码管计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_hex <= 0;
else if(add_cnt_hex)begin
if(end_cnt_hex)
cnt_hex <= 0;
else
cnt_hex <= cnt_hex + 1\'b1;
end
end
assign add_cnt_hex = end_cnt_time;
assign end_cnt_hex = add_cnt_hex && cnt_hex == HEX_N - 1;
//片选
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
sel <= 8\'h01;
else if(end_cnt_time)
sel <= {sel[6:0],sel[7]};
end
always @(*)begin
case(cnt_hex)
4\'d0:hex_code = disp_data[3:0] ;
4\'d1:hex_code = disp_data[7:4] ;
4\'d2:hex_code = disp_data[11:8] ;
4\'d3:hex_code = disp_data[15:12];
4\'d4:hex_code = disp_data[19:16];
4\'d5:hex_code = disp_data[23:20];
4\'d6:hex_code = disp_data[27:24];
4\'d7:hex_code = disp_data[31:28];
default:hex_code = 0;
endcase
end
//共阳编码
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
seg <= 8\'hFF;
else begin
case(hex_code)
4\'d0 :seg <= 8\'hC0;
4\'d1 :seg <= 8\'hF9;
4\'d2 :seg <= 8\'hA4;
4\'d3 :seg <= 8\'hB0;
4\'d4 :seg <= 8\'h99;
4\'d5 :seg <= 8\'h92;
4\'d6 :seg <= 8\'h82;
4\'d7 :seg <= 8\'hF8;
4\'d8 :seg <= 8\'h80;
4\'d9 :seg <= 8\'h90;
4\'d10:seg <= 8\'h88;
4\'d11:seg <= 8\'h83;
4\'d12:seg <= 8\'hC6;
4\'d13:seg <= 8\'hA1;
4\'d14:seg <= 8\'h86;
4\'d15:seg <= 8\'h8E;
default:seg <= 8\'hFF;
endcase
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
en <= 0;
else if(cnt_time == CNTIME_N - 2 && add_cnt_time)
en <= 1;
else
en <=0;
end
endmodule
三、仿真代码
`timescale 1ns / 1ns
module hex8_tb();
parameter CYCLE = 20;
parameter DATA_W = 32;
parameter SEL_W = 8;
parameter SEG_W = 8;
reg clk;
reg rst_n;
reg [DATA_W-1:0] disp_data;
wire [SEL_W-1:0] sel;
wire [SEG_W-1:0] seg;
wire en;
hex8 hex8(
clk ,
rst_n ,
disp_data,
sel , //片选
seg , //段选
en
);
initial clk = 1;
always #(CYCLE/2) clk = ~clk;
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(10*CYCLE);
rst_n = 1;
end
initial begin
disp_data = 0;
#(15*CYCLE)
disp_data = {4\'d1,4\'d2,4\'d3,4\'d4,4\'d5,4\'d6,4\'d7,4\'d8};
#(10_000_000);
disp_data = {4\'d9,4\'d10,4\'d11,4\'d12,4\'d13,4\'d14,4\'d15,4\'d16};
#(16_000_000);
$stop;
end
endmodule
四、改进
增加一个HC595芯片,串转并输出,减少输入端口,使用HC595仅需clk,seg,sel三根线即可实现以上数码管的功能
- HC595(移位寄存器)原理
移位寄存器时钟打四拍,即可把四位串行数据存储在寄存器里,存储寄存器打一拍就可以把锁存的数据并行输出
- HC595时序图
这里取125℃,3.3V,12.5MHz
五、595驱动代码
module hc595_driver(
clk ,
rst_n ,
en , //数据使能信号
data , //要输入的数据
sh_cp , //移位寄存器时钟
st_cp , //存储寄存器时钟
ds //串行数据输入
);
parameter DATA_W = 16;
parameter CNTSH_N = 4;
parameter CNTSH_W = 3;
parameter CNTB_N = 16;
parameter CNTB_W = 4;
input clk;
input rst_n;
input [DATA_W-1:0] data;
input en;
output sh_cp;
output st_cp;
output ds;
reg sh_cp;
reg st_cp;
reg ds;
reg [CNTSH_W-1:0] cnt_sh;
wire add_cnt_sh;
wire end_cnt_sh;
reg [CNTB_W-1:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
reg add_sh_flag;
reg [DATA_W-1:0] data_tmp;
//12.5M时钟计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_sh <= 0;
else if(add_cnt_sh)begin
if(end_cnt_sh)
cnt_sh <= 0;
else
cnt_sh <= cnt_sh + 1\'b1;
end
end
assign add_cnt_sh = add_sh_flag;
assign end_cnt_sh = add_cnt_sh && cnt_sh == CNTSH_N - 1;
//计算移位寄存器移动了多少位
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_bit <= 0;
else if(add_cnt_bit)begin
if(end_cnt_bit)
cnt_bit <= 0;
else
cnt_bit <= cnt_bit + 1\'b1;
end
end
assign add_cnt_bit = end_cnt_sh;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == CNTB_N - 1);
//实现移位寄存器时钟
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
sh_cp <= 0;
else if(cnt_sh == (CNTSH_N/2 - 1) && add_cnt_sh)
sh_cp <= 1;
else if(end_cnt_sh)
sh_cp <= 0;
end
//实现存储寄存器时钟,当移位寄存器移位了16位数码管信号,就输出数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
st_cp <= 0;
else if(end_cnt_bit)
st_cp <= 1;
else if(cnt_bit == (CNTB_N/2 - 1) && add_cnt_bit)
st_cp <= 0;
end
//并转串输出数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
ds <= 0;
else if(cnt_sh==(CNTSH_N/4 - 1) && add_cnt_sh)
ds <= data_tmp[(CNTB_N - 1)- cnt_bit];
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
add_sh_flag <= 0;
else if(en)
add_sh_flag <= 1;
else if(end_cnt_bit)
add_sh_flag <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_tmp <= 0;
else if(en && !add_sh_flag)
data_tmp <= data;
end
endmodule
六、仿真文件代码
`timescale 1ns / 1ns
module hc595_driver_tb();
parameter CYCLE = 20;
parameter DATA_W = 16;
reg clk;
reg rst_n;
reg en;
reg [DATA_W-1:0] data;
wire sh_cp;
wire st_cp;
wire ds;
hc595_driver hc595_driver(
clk ,
rst_n ,
en ,
data , //要输入的数据
sh_cp , //移位寄存器时钟
st_cp , //存储寄存器时钟
ds //串行数据输入
);
initial clk = 1;
always #(CYCLE/2) clk = ~clk;
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(10*CYCLE);
rst_n = 1;
end
initial begin
data = 0;
en = 0;
#(15*CYCLE);
data = 16\'b1010_1010_1001_1001;
en = 1;
#(CYCLE);
en = 0;
#1280;
data = 0;
#5000;
$stop;
end
endmodule
七、板级验证代码
module hex8_test(
clk ,
rst_n ,
sh_cp ,
st_cp ,
ds
);
input clk;
input rst_n;
output sh_cp;
output st_cp;
output ds;
wire sh_cp;
wire st_cp;
wire ds;
wire en;
wire [15:0] data;
wire [7:0] sel;
wire [7:0] seg;
wire [31:0] disp_data;
hc595_driver hc595_driver(
.clk (clk),
.rst_n (rst_n),
.en (en), //数据使能信号
.data (data), //要输入的数据
.sh_cp (sh_cp), //移位寄存器时钟
.st_cp (st_cp), //存储寄存器时钟
.ds (ds) //串行数据输入
);
hex8 hex8(
.clk (clk),
.rst_n (rst_n),
.disp_data (disp_data),
.sel (sel), //片选
.seg (seg), //段选
.en (en)
);
assign data = {seg,sel};
assign disp_data = 32\'h12345678;
endmodule
八、仿真代码
`timescale 1ns / 1ns
module hex8_test_tb();
reg clk;
reg rst_n;
wire sh_cp;
wire st_cp;
wire ds;
hex8_test hex8_test(
clk ,
rst_n ,
sh_cp ,
st_cp ,
ds
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(10 * 20)
rst_n = 1;
end
endmodule