1.驅動原理
其中8段數碼管的動態顯示驅動如圖1所示,動態顯示的特點是將所有位數碼管的段選線 並聯在一起,由位選線控制是哪一位數碼管有效。選亮數碼管採用動態掃描顯示。所謂動態掃描顯示即輪流向各位數碼管送出字形碼和相應的位選,利用發光管的餘輝和人眼視覺暫留作用,使人的感覺好像各位數碼管同時都在顯示。
而在實際的使用中,由於FPGA出來的引腳資源寶貴,因此在FPGA外部用了兩個74HC595的芯片進行控制。其主要的作用就是將FPGA輸出的串行信號轉化爲數碼管需要的sel信號以及段信號(seg)。74HC595的芯片示意圖如下。
可以看出該芯片需要3個信號,分別爲DIO(串行數據),RCLK(移位寄存時鐘),SCLK(存儲時鐘),以下分別稱爲DS,SH_CP,ST_CP。
數碼管的驅動如下所示。即,在FPGA內部產生動態的掃描seg和sel。其中的En沒有在設計中使用,所以在代碼中去掉。
然後將產生的sel和seg如何送入到74HC595芯片?通過設計出其需要的端口及串行信號以及另外的兩個時鐘信號即可。模塊原理圖如下。
因此總共包括兩個部分:1,掃描信號驅動,2,模塊接口驅動。
2.RTL部分
(1).產生計數信號用於sel的翻轉
module divider(clk, rst_n,divider_cnt,clk_1k);
input clk;
input rst_n;
output reg [31:0] divider_cnt;
output reg clk_1k;
parameter CNT_MAX = 28'd49_999;
//對divider_cnt進行計數,計數到24999就歸零
always@(posedge clk or negedge rst_n)
if(!rst_n)
divider_cnt <= 0;
else if(divider_cnt == CNT_MAX )
divider_cnt <= 0;
else
divider_cnt <= divider_cnt + 1'b1;
//產生clk_1k的信號
always@(posedge clk or negedge rst_n)
if(!rst_n)
clk_1k <= 0;
else if(divider_cnt == CNT_MAX)
clk_1k = ~clk_1k;
else
clk_1k <= clk_1k;
endmodule
(2)sel信號的產生
module move_choice(clk,rst_n,divider_cnt,sel);
input clk;
input rst_n;
input wire [31:0] divider_cnt;
output reg [7:0] sel;
parameter CNT_MAX = 32'd49_999;
always@(posedge clk or negedge rst_n)
if(!rst_n)
sel <= 8'b0000_0001;
else if(divider_cnt == CNT_MAX)
begin
if(sel == 8'b1000_0000)
sel <= 8'b0000_0001;
else
sel <= sel << 1;
end
else
sel <= sel;
endmodule
(3)根據sel信號分段讀取data信號
module pass_choice(clk,rst_n,sel,disp_data,data_tmp);
input clk;
input rst_n;
input wire [7:0] sel;
input wire [31:0] disp_data;
output reg [4:0] data_tmp;
always@(*)
begin
case(sel)
8'b0000_0001: begin data_tmp <= disp_data[31:28]; end
8'b0000_0010: begin data_tmp <= disp_data[27:24]; end
8'b0000_0100: begin data_tmp <= disp_data[23:20]; end
8'b0000_1000: begin data_tmp <= disp_data[19:16]; end
8'b0001_0000: begin data_tmp <= disp_data[15:12]; end
8'b0010_0000: begin data_tmp <= disp_data[11:8]; end
8'b0100_0000: begin data_tmp <= disp_data[7:4]; end
8'b1000_0000: begin data_tmp <= disp_data[3:0]; end
default: begin data_tmp = 4'b0000;end
endcase
end
endmodule
(4)譯碼器
module translate(clk,rst_n,data_tmp,seg);
input clk;
input rst_n;
input wire [4:0] data_tmp;
output reg [7:0] seg;
always@(*)
begin
case(data_tmp)
4'b0000:seg[7:0] =8'b11000000;
4'b0001:seg[7:0] =8'b11111001;
4'b0010:seg[7:0] =8'b10100100;
4'b0011:seg[7:0] =8'b10110000;
4'b0100:seg[7:0] =8'b10011001;
4'b0101:seg[7:0] =8'b10010010;
4'b0110:seg[7:0] =8'b10000010;
4'b0111:seg[7:0] =8'b11111000;
4'b1000:seg[7:0] =8'b10000000;
4'b1001:seg[7:0] =8'b10010000;
default:seg[7:0] = 8'b01000_0000;
endcase
end
endmodule
2.模塊驅動信號
(1)HC595
module HC595(
clk,
rst_n,
sel,
seg,
ds,
stcp,
shcp,
cnt,
sclk,
sclk_cnt
);
input clk;
input rst_n;
input [7:0] seg;
input [7:0] sel;
output reg ds;
output reg stcp;
output reg shcp;
output reg [3:0]cnt;
output wire sclk;
output reg [6:0]sclk_cnt;
parameter CNT_MAX2 = 2'd3;
//對時鐘進行分頻
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 0;
else if(1'b1)
begin
if(cnt >= CNT_MAX2)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= 0;
//形成了sclk這個分頻後的時鐘
assign sclk = (cnt == CNT_MAX2);
always@(posedge clk or negedge rst_n)
if(!rst_n)
sclk_cnt <= 0;
else if(sclk)
begin
if(sclk_cnt >= 15'd34)
sclk_cnt <= 0;
else
sclk_cnt <= sclk_cnt + 1'b1;
end
else
sclk_cnt <= sclk_cnt;
//設置輸出的激勵順序以及製造時鐘
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
ds <= 0;
shcp <= 0;
stcp <= 0;
end
else
begin
case(sclk_cnt)
15'd0: begin shcp <= 0; end
15'd1: begin shcp <= 1; stcp <= 0; end
15'd2: begin shcp <= 0; ds <= seg[7];end
15'd3: begin shcp <= 1; end
15'd4: begin shcp <= 0; ds <= seg[6];end
15'd5: begin shcp <= 1; end
15'd6: begin shcp <= 0; ds <= seg[5];end
15'd7: begin shcp <= 1; end
15'd8: begin shcp <= 0; ds <= seg[4];end
15'd9: begin shcp <= 1; end
15'd10: begin shcp <= 0; ds <= seg[3];end
15'd11: begin shcp <= 1; end
15'd12: begin shcp <= 0; ds <= seg[2];end
15'd13: begin shcp <= 1; end
15'd14: begin shcp <= 0; ds <= seg[1];end
15'd15: begin shcp <= 1; end
15'd16: begin shcp <= 0; ds <= seg[0];end
15'd17: begin shcp <= 1; end
15'd18: begin shcp <= 0; ds <= sel[7];end
15'd19: begin shcp <= 1; end
15'd20: begin shcp <= 0; ds <= sel[6];end
15'd21: begin shcp <= 1; end
15'd22: begin shcp <= 0; ds <= sel[5];end
15'd23: begin shcp <= 1; end
15'd24: begin shcp <= 0; ds <= sel[4];end
15'd25: begin shcp <= 1; end
15'd26: begin shcp <= 0; ds <= sel[3];end
15'd27: begin shcp <= 1; end
15'd28: begin shcp <= 0; ds <= sel[2];end
15'd29: begin shcp <= 1; end
15'd30: begin shcp <= 0; ds <= sel[1];end
15'd31: begin shcp <= 1; end
15'd32: begin shcp <= 0; ds <= sel[0];end
15'd33: begin shcp <= 1; end
15'd34: begin stcp <= 1; end
default:;
endcase
end
endmodule
3.頂層1
module dis_top(clk,rst_n,disp_data,ds,shcp,stcp);
input clk;
input rst_n;
input wire [31:0] disp_data;
wire [7:0] seg;
wire [31:0] divider_cnt;
wire [7:0] sel;
wire clk_1k;
wire [4:0] data_tmp;
output wire ds;
output wire shcp;
output wire stcp;
wire sclk;
wire [6:0]sclk_cnt;
wire [3:0] cnt;
divider divider(
.clk(clk),
.rst_n(rst_n),
.divider_cnt(divider_cnt),
.clk_1k(clk_1k)
);
move_choice move_choice(
.clk(clk),
.rst_n(rst_n),
.sel(sel),
.divider_cnt(divider_cnt)
);
pass_choice pass_choice(
.clk(clk),
.rst_n(rst_n),
.sel(sel),
.disp_data(disp_data),
.data_tmp(data_tmp)
);
translate translate(
.clk(clk),
.rst_n(rst_n),
.seg(seg),
.data_tmp(data_tmp)
);
HC595 HC595_1(
.clk(clk),
.rst_n(rst_n),
.seg(seg),
.sel(sel),
.ds(ds),
.shcp(shcp),
.stcp(stcp),
.cnt(cnt),
.sclk(sclk),
.sclk_cnt(sclk_cnt)
);
endmodule
4.通過讀取輸入信號得到硬件顯示的值
module hex_top(clk,rst_n,ds,shcp,stcp);
input clk;
input rst_n;
output wire ds;
output wire shcp;
output wire stcp;
wire [31:0] disp_data;
dis_top dis_top(
.clk(clk),
.rst_n(rst_n),
.ds(ds),
.shcp(shcp),
.stcp(stcp),
.disp_data(disp_data)
);
hex_data hex_data_inst (
.probe (),
.source (disp_data)
);
endmodule