1. 程式人生 > >數碼管的動態掃描與驅動

數碼管的動態掃描與驅動

fin 文件 芯片 組成 == test 關於 實現 沒有

數碼管的基本原理

    關於數碼管,一個單個的數碼管可以看做是多個led燈的集合,如下圖所示

技術分享其中的8都是LED組成的,通過引腳上電即可點亮不同的LED然後組成不同的數字,這個過程在數碼管的設計中叫做段選。

    在多個數碼管的情況下,需要選擇哪個數碼管點亮,這個在數碼管設計中稱作位選,多個數碼管可以通過位選和段選完成電子時鐘設計等功能。

技術分享

下面通過項目對於多個數碼管進行點亮,讓其在開發板上顯示不同的數據。

預計實驗現象:

      在quartus的in system source and probes editor 工具,輸入需要顯示在數碼管上的數據,則數碼管顯示對應數據。

相關知識點:

    數碼管動態掃描的實現、in system source and probes editor調試工具的使用。

設計過程:

    1、數碼管動態掃描實現。

    2、In system sources and probes edit (ISSP)調試工具的使用

    3、4輸入查找表,6位輸出。

    4、分頻模塊,從系統時鐘分頻得到1KHz的掃描時鐘

    5、6選一多路選擇器,選擇為當前數碼管的位置。

驅動模塊邏輯電路圖:

技術分享

下面就是照著邏輯電路圖來編寫程序了。

創建工程,添加文件

      技術分享

module segment(disp_data,rst_n,clk,en ,sel,seg);

input clk;//50M
input rst_n;
input en;
input [23:0]disp_data;
output [5:0]sel;//位選(控制哪個數碼管亮)
output reg [6:0]seg;//段選(控制數碼管顯示什麽數據)


//分頻器的代碼,這裏為了完整,不做多個文件來寫模塊了
reg[14:0] diviter_cnt; //25000-1
reg clk_1k;
reg [5:0]sel_r;
reg [3:0]data_temp;//待顯示數據緩存

//生成一個分頻計數器計數
always@(posedge clk or negedge rst_n)


if (!rst_n)
diviter_cnt<=15‘d0;
else if (!en)
diviter_cnt<=15‘d0;
else if (diviter_cnt==24999)
diviter_cnt<=15‘d0;
else
diviter_cnt<=diviter_cnt+1‘b1;

//1k掃描時鐘生成
always@(posedge clk or negedge rst_n)
if (!rst_n)
clk_1k<=1‘b0;
else if (diviter_cnt==24999)
clk_1k<=~clk_1k; //大型設計中,這種產生分頻器的方法是不可以的


//位選移位寄存器
always@(posedge clk_1k or negedge rst_n)
if (!rst_n)
sel_r<=6‘b000_001;
else if(sel_r==6‘b100_000)
sel_r<=6‘b000_001;
else
sel_r<=sel_r<<1;

//設計一個6選一多路器
always@(*)
case(sel_r)
6‘b000_001:data_temp=disp_data[3:0];
6‘b000_010:data_temp = disp_data[7:4];
6‘b000_100:data_temp=disp_data[11:8];
6‘b001_000:data_temp=disp_data[15:12];
6‘b010_000:data_temp=disp_data[19:16];
6‘b100_000:data_temp=disp_data[23:20];
default
data_temp<=4‘b0000;
endcase

//譯碼器
always@(*)
case (data_temp)
4‘h0:seg=7‘b1000000;//這裏按數碼管碼表來
4‘h1:seg=7‘b1111001;
4‘h2:seg=7‘b0100100;
4‘h3:seg=7‘b0110000;
4‘h4:seg=7‘b0011001;
4‘h5:seg=7‘b0010010;
4‘h6:seg=7‘b0000010;
4‘h7:seg=7‘b1111000;
4‘h8:seg=7‘b0000000;
4‘h9:seg=7‘b0010000;
4‘ha:seg=7‘b0001000;
4‘hb:seg=7‘b0000011;
4‘hc:seg=7‘b1000110;
4‘hd:seg=7‘b0100001;
4‘he:seg=7‘b0000110;
4‘hf:seg=7‘b0001110;
endcase

//二選一多路器
assign sel=(en)?sel_r:6‘b000_000;

endmodule

編寫testbench文件來進行仿真

技術分享

`timescale 1ns/1ns

`define clk_period 20

module HXE_tb;

reg Clk; //50M
reg Rst_n;
reg En; //數碼管顯示使能,1使能,0關閉

reg [31:0]disp_data;

wire [7:0] sel;//數碼管位選(選擇當前要顯示的數碼管)
wire [6:0] seg;//數碼管段選(當前要顯示的內容)

HXE8 HXE8(
.Clk(Clk),
.Rst_n(Rst_n),
.En(En),
.disp_data(disp_data),
.sel(sel),
.seg(seg)
);

initial Clk = 1;
always#(`clk_period/2) Clk = ~Clk;

initial begin
Rst_n = 1‘b0;
En = 1;
disp_data = 32‘h12345678;
#(`clk_period*20);
Rst_n = 1;
#(`clk_period*20);
#20000000;
disp_data = 32‘h87654321;
#20000000;
disp_data = 32‘h89abcdef;
#20000000;
$stop;
end

endmodule

點擊仿真運行,可以看到sel和seg的輸出與我們期望的是一樣的,即位選進行移位操作,段選顯示123456和abcdef。

技術分享

一般都需要進行後仿,才能得到實際的工作時的數據波形,這裏由於使用的是Cyclone V系列的芯片,而quartus 取消了對該系列的門級仿真,故而此次設計的後仿就不做了。但是我們這裏發現,友晶的開發板的連接模式與下面這種常規的位選段選接法不同,其連接方式為並行接法,每個數碼管連接一個IO管腳,通過IO管腳的設置來決定數據的顯示,這裏兩者的區別是位選的有無

技術分享技術分享

重新編寫segment程序

技術分享

module segment_2(disp_data,rst_n,clk,en ,data_out);
input clk;//50M
input rst_n;
input en;
input [23:0]disp_data;

output reg [41:0]data_out;
reg[24:0]cnt;//定義計數寄存器
reg [3:0]data_temp;//待顯示數據緩存
reg [6:0]seg;//段選(控制數碼管顯示什麽數據)
reg num;
always@(posedge clk or negedge rst_n)
//設置500ms的延時
if(rst_n==1‘b0)
cnt<=25‘d0;
else if (cnt==25‘d24_999_999)//(500_000_000/20) -1的結果。
cnt<=25‘d0;
else
cnt<=cnt+1‘b1;

always@(posedge clk or negedge rst_n)
if (rst_n==1‘b0)
data_temp=4‘b0001;
else if (cnt==25‘d24_999_999)
case (num)
1‘h0: begin data_temp<=disp_data[3:0];num<=num+1; end
1‘h1:begin data_temp<=disp_data[7:4] ; num<=num+1;end
1‘h2:begin data_temp<=disp_data[11:8] ;num<=num+1;end
1‘h3:begin data_temp<=disp_data[15:12] ; num<=num+1;end
1‘h4:begin data_temp<=disp_data[19:16] ; num<=num+1;end
1‘h5:begin data_temp<=disp_data[23:20] ; num<=num+1;end
default
num<=0;
endcase
always@(*)
begin
case (data_temp)
4‘h0:seg=7‘b1000000;//這裏按數碼管碼表來
4‘h1:seg=7‘b1111001;
4‘h2:seg=7‘b0100100;
4‘h3:seg=7‘b0110000;
4‘h4:seg=7‘b0011001;
4‘h5:seg=7‘b0010010;
4‘h6:seg=7‘b0000010;
4‘h7:seg=7‘b1111000;
4‘h8:seg=7‘b0000000;
4‘h9:seg=7‘b0010000;
4‘ha:seg=7‘b0001000;
4‘hb:seg=7‘b0000011;
4‘hc:seg=7‘b1000110;
4‘hd:seg=7‘b0100001;
4‘he:seg=7‘b0000110;
4‘hf:seg=7‘b0001110;
endcase
data_out<=({seg,seg,seg,seg,seg,seg,seg});
end

endmodule

編寫testbench文件並設定路徑

技術分享

`timescale 1ns/1ns
`define clk_period 20
module segment_2_tb;

reg clk;//50M
reg rst_n;
reg en;
reg [23:0]disp_data;
wire [41:0]data_out;//段選(控制數碼管顯示什麽數據)

segment_2 segment0(.disp_data(disp_data),
.rst_n(rst_n),
.clk(clk),
.en (en),
.data_out(data_out)
);

initial clk = 1;
always#(`clk_period/2) clk = ~clk;
initial begin
rst_n=1‘b0;
en=1;
disp_data=24‘h123456;
#(`clk_period*20)
rst_n=1;
#(`clk_period*20)
#20_000_000;
disp_data<=24‘habcdef;
#20_000_000;
$stop;
end
endmodule

仿真波形如圖

技術分享

技術分享

到了這一步就需要一個工具,即之前說到的In sysytem sources and probes editor (ISSP)

打開一個IP核

技術分享

技術分享

技術分享

之後一直next知道finish,ctrl+o將產生的文件添加到工程中。將端口添加到頂層文件中

技術分享

新建一個segment_top文件,將文件端口都鏈接進來

技術分享

module segment_top(clk,rst_n,data_out);

input clk;//50M
input rst_n;
wire [23:0]disp_data;


output [41:0]data_out;

segment_data segment_data0(
.probe(),
.source(disp_data));

segment_2 segment0(.disp_data(disp_data),
.rst_n(rst_n),
.clk(clk),
.en (1‘b1),
.data_out(data_out));

endmodule

至此,工程修改完了,之後就是分配引腳

技術分享

將.sof文件下載到開發板中

技術分享

下載之後,開發板上的數碼管一直顯示00_00_00,這是因為沒有給它需要顯示的值。

技術分享

現在在電腦上調用in system programer 工具

技術分享

將數據格式改為16進制

技術分享

在Data框中輸入希望顯示的數據,會發現數碼管上的數據會隨著輸入的數據的變化而變化。

技術分享

技術分享

技術分享

技術分享

技術分享

技術分享

技術分享

到這裏,數碼管的點亮的試驗的基本目標就已經完成了,但是本次實驗還有很多不足,比如讓不同數碼管顯示不同數據,復位按鈕按下後,顯示不是從頭開始跳變的,只是按下的時候會改變顯示顯示8而已,這點也是一個很大的不足,如果需要做一個電子時鐘的話,程序還需要很多的修改,在下一篇文章中,我會將這些問題一一解決,並將程序及設計步驟發到博客上來。

數碼管的動態掃描與驅動