FPGA——八位七段数码管驱动
一、设计思路
数码管共阳
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