本文為明德揚原創及錄用文章,轉載請注明出處!
1.1 總體設計
1.1.1 概述
LED數碼管以發光二極管作為發光單元,顏色有單紅,黃,藍,綠,白,黃綠等效果,并且可以構造成“8”字形。數碼管根據LED的接法不同分為共陰和共陽兩類,它們的發光原理是一樣的,只是它們的電源極性不同而已。
數碼管可以通過驅動電路來驅動內部的各個段碼,從而顯示出需要的數字。根據數碼管驅動方式的不同,可以將其分為靜態式和動態式兩類。
1.1.2 設計目標
完成數碼管的顯示,具體功能要求如下:
1. 間隔1s切換數碼管位選,做到數碼管從左到右流動顯示的效果;
2. 數碼管顯示的數值從0開始,每切換一位位選數值加一;
1.1.3信號列表

1.1.4設計思路
數碼管顯示原理
數碼管的8個顯示字段”a、b、c、d、e、f、g、h”對應顯示面板的位置如下圖所示。

數碼管顯示數字0到9對應的gfedcba值如下表所示。
表5- 1 數碼管顯示數字與字段值的對應關系

數碼管靜態驅動是指每個數碼管的每一個段碼都通過一個I/O端口進行驅動,或使用如BCD碼二-十進制譯碼器譯碼進行驅動,也稱直流驅動。靜態驅動編程簡單,顯示亮度高,但占用的I/O端口多,這里不使用這種方法。
數碼管動態驅動是將所有數碼管的8個顯示字段"a、b、c、d、e、f、g、h"的同名端連接在一起,此外每個數碼管的公共極COM需增加由各自獨立I/O線控制的位選通控制電路。當要輸出某一字形碼時,所有數碼管都會接收到相同的字形碼,但究竟是哪個數碼管會顯示出字形取決于單片機對位選通COM端電路的控制。只需將顯示數碼管的選通控制打開,該位就會顯示出字形,而沒有選通的數碼管并不會點亮。數碼管特定的發光二極管段加上電壓后,這些特定的段就會發亮,并且當每位元數碼管的點亮時間為1~20ms,由于人的視覺暫留現象及發光二極體的余輝效應,盡管實際上各位數碼管并非同時點亮,但只要掃描的速度足夠快,給人的印象就是一組穩定的顯示資料,不會有閃爍感,使得動態顯示的效果和靜態顯示是一樣的,這樣能夠節省大量的I/O口,而且功耗更低。
以下是MP801開發板對應的數碼管原理圖,并且是共陽極的數碼管:

工程架構
根據設計目標將現象翻譯成信號表示如下:
第1秒,數碼管0顯示數字“0”,即seg_sel的值為8’b1111_1110,seg_ment的值為8’b1100_0000;
第2秒,數碼管1顯示數字“1”,即seg_sel的值為8’b1111_1101,seg_ment的值為8'b1111_1001;
第3秒,數碼管2顯示數字“2”,即seg_sel的值為8’b1111_1011,seg_ment的值為8'b1010_0100;
第4秒,數碼管3顯示數字“3”,即seg_sel的值為8’b1111_0111,seg_ment的值為8'b1011_0000;
第5秒,數碼管4顯示數字“4”,即seg_sel的值為8’b1110_1111,seg_ment的值為8'b1001_1001;
第6秒,數碼管5顯示數字“5”,即seg_sel的值為8’b1101_1111,seg_ment的值為8'b1001_0010;
第7秒,數碼管6顯示數字“6”,即seg_sel的值為8’b1011_1111,seg_ment的值為8'b1000_0010;
第8秒,數碼管7顯示數字“7”,即seg_sel的值為8’b0111_1111,seg_ment的值為8'b1111_1000;
第九秒,回到數碼管0顯示數字“0”,以此進行循環。
總結發現,數碼管每隔1秒進行變化,且8個數碼管輪流顯示。
因此本工程用到了三個計數器的架構,具體架構如下圖所示:

1秒計數器cnt_1s:用于計算1s的時間,加一條件為1,表示一直在計數;數到50,000,000下,表示數到1s就結束。
位選計數器sel_cnt:用于區分選通的數碼管,加一條件為end_cnt_1s,表示每間隔1秒的時間后,切換選通下一個數碼管;數到8下,表示8個數碼管都選通過一輪了。
1.1.5參考代碼
module seg_disp(
rst_n ,
clk ,
seg_sel ,
segment
);
parameter TIME_1S = 50_000_000 ;
parameter SEG_WID = 8 ;
parameter SEG_NUM = 8 ;
parameter CNT_WID = 10 ;
parameter TIME_20US = 10'd1000 ;
parameter NUM_0 = 8'b1100_0000;
parameter NUM_1 = 8'b1111_1001;
parameter NUM_2 = 8'b1010_0100;
parameter NUM_3 = 8'b1011_0000;
parameter NUM_4 = 8'b1001_1001;
parameter NUM_5 = 8'b1001_0010;
parameter NUM_6 = 8'b1000_0010;
parameter NUM_7 = 8'b1111_1000;
parameter NUM_8 = 8'b1000_0000;
parameter NUM_9 = 8'b1001_0000;
parameter NUM_ERR = 8'b1111_1111;
input clk ;
input rst_n ;
output [SEG_WID - 1:0] seg_sel ;
output [SEG_WID - 1:0] segment ;
reg [SEG_WID - 1:0] seg_sel ;
reg [SEG_WID - 1:0] segment ;
reg [ 31 : 0] cnt_1s ;
reg [SEG_NUM - 1:0] sel_cnt ;
reg [ 4 - 1 : 0] seg_tmp ;
wire add_cnt_1s ;
wire end_cnt_1s ;
wire add_sel_cnt ;
wire end_sel_cnt ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_1s <= 0;
end
else if(add_cnt_1s) begin
if(end_cnt_1s)
cnt_1s <= 0;
else
cnt_1s <= cnt_1s+1 ;
end
end
assign add_cnt_1s = 1;
assign end_cnt_1s = add_cnt_1s && cnt_1s == TIME_1S-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
sel_cnt <= 0;
end
else if(add_sel_cnt)begin
if(end_sel_cnt)
sel_cnt <= 0;
else
sel_cnt <= sel_cnt + 1;
end
end
assign add_sel_cnt = end_cnt_1s;
assign end_sel_cnt = add_sel_cnt && sel_cnt == SEG_NUM-1;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
seg_sel <= {SEG_NUM{1'b1}};
end
else begin
seg_sel <= ~(1'b1 << sel_cnt);
end
end
always @(*)begin
if(rst_n==1'b0)
seg_tmp = {SEG_NUM{1'b1}};
else
seg_tmp = sel_cnt ;
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
segment<=NUM_0;
end
else begin
case (seg_tmp)
0 : segment <= NUM_0;
1 : segment <= NUM_1;
2 : segment <= NUM_2;
3 : segment <= NUM_3;
4 : segment <= NUM_4;
5 : segment <= NUM_5;
6 : segment <= NUM_6;
7 : segment <= NUM_7;
8 : segment <= NUM_8;
9 : segment <= NUM_9;
default : segment <= NUM_ERR;
endcase
end
end
endmodule
1.2 效果和總結
下圖是該工程在db603開發板上的現象

下圖是該工程在mp801開發板上的現象

下圖是該工程在ms980試驗箱上的現象

溫馨提示:明德揚2023推出了全新課程——邏輯設計基本功修煉課,降低學習FPGA門檻的同時,增加了學習的趣味性,并組織了考試贏積分活動
http://www.cqqtmy.cn/ffkc/415.html
(點擊→了解課程詳情?)感興趣請聯系易老師:13112063618(微信同步)