FPGA和數碼管
1.1.1 數碼管基礎知識
數碼管由8個發光二極管(以下簡稱字段)構成,通過不同的組合可顯示數字0~9、字符A~F、H、L、P、R、U、Y、符號“”及小數點“”。數碼管的外型結構如圖所示。數碼管又分爲共陰極和共陽極兩種結構,分別如圖所示。
圖1‑94共陰極和共陽極數碼管
(a)共陰極(b)共陽極
共陽極數碼管的8個發光二極管的陽極(二極管正端)連接在一起,通常接高電平(一般接電源),其它管腳接段驅動電路輸出端。當某段驅動電路的輸出端爲低電平時,該端所連接的字段導通並點亮,根據發光字段的不同組合可顯示出各種數字或字符。此時,要求段驅動電路能吸收額定的段導通電流,還需根據外接電源及額定段導通電流來確定相應的限流電阻。
共陰極數碼管的8個發光二極管的陰極(二極管負端)連接在一起,通常接低電平(一般接地),其它管腳接段驅動電路輸出端。當某段驅動電路的輸出端爲高電平時,該端所連接的字段導通並點亮,根據發光字段的不同組合可顯示出各種數字或字符。此時,要求段驅動電路能提供額定的段導通電流,還需根據外接電源及額定段導通電流來確定相應的限流電阻。
數據線D0與a字段對應,D1字段與b字段對應……,依此類推。如使用共陽極數碼管,則數據爲0表示對應字段亮,數據爲1表示對應字段暗;如使用共陰極數碼管,則相反。
表1‑38數碼管字型編碼表
數碼管工作方式有兩種:靜態顯示方式和動態顯示方式。
1、靜態顯示接口
從下圖可以看出,靜態顯示方式的特點是各位數碼管相互獨立,公共端恆定接地(共陰極)或接正電源(共陽極)。每個數碼管的8個字段分別與一個8位I/O口地址相連,I/O口只要有段碼輸出,相應字符即顯示出來並保持不變,直到I/O口輸出新的段碼。
圖1‑95兩位的LED數碼管靜態顯示示意圖
採用靜態顯示方式時,用較小的電流即可獲得較高的亮度,且佔用CPU時間少,編程簡單,顯示便於監測和控制,但其佔用的口線多,硬件電路複雜,成本高,只適用於顯示位數較少的場合。
2、動態顯示
當需要顯示的位數較多時,爲了節省硬件接口,往往採用動態顯示的方式。
動態顯示的特點是將所有位數碼管的段選線並聯在一起,由位選線控制是哪一位數碼管有效。選亮數碼管採用動態掃描顯示。所謂動態掃描顯示即輪流向各位數碼管送出字形碼和相應的位選,利用發光管的餘輝和人眼視覺暫留作用,使人的感覺好像各位數碼管同時都在顯示。動態顯示的亮度比靜態顯示要差一些,所以在選擇限流電阻時應略小於靜態顯示電路中的。
動態顯示是指一位一位地輪流點亮各位數碼管,這種逐位點亮顯示器的方式稱爲位掃描。通常,各位數碼管的段選線相應並聯在一起,由一個8位的I/O口控制;各位的位選線(公共陰極或公共陽極)由另外的I/O口線控制。以動態方式顯示時,各數碼管分時輪流選通。要使其穩定顯示,必須採用掃描方式,即在某一時刻只選通一位數碼管,並送出相應的段碼,在另一時刻選通另一位數碼管,並送出相應的段碼。依此規律循環,即可使各位數碼管顯示將要顯示的字符,雖然這些字符是在不同的時刻分別顯示的,但由於人眼存在視覺暫留效應,因此只要每位顯示間隔足夠短就可以給人以同時顯示的感覺。
圖1‑96四位的LED數碼管動態顯示示意圖
1.1.2 FPGA和數碼管驅動
使用動態掃描還是靜態掃描其實取決於硬件設計,並不取決於驅動程序,一旦硬件確定下來那麼就需要驅動去適配硬件,本次設計使用的數碼管爲共陽極數碼管,動態掃描加載,電路如下:
圖1‑97動態數碼管設計電路
表1‑39數碼管設計資源
設計的資源如表1‑39所示,其中掃描頻率是1s掃描的頻率,即1/6ms≈166.67Hz。
數碼管主要考慮輸入調用它的輸入。即如何充分減少資源以實現數碼管動態顯示。
圖1‑98數碼管電路設計建模圖
表 1‑40建模圖中模塊間信號定義
整個系統設計結構如圖1‑98所示,上圖的組合模塊-數碼管接口 smg_interface.v 中,輸入信號 Number_Sig 佔了 24 位寬,然而 Number_Sig 的位分配如下表:
表1‑42 Number_Sig的位分配表
整個系統設計結構如圖1‑98所示,上圖的組合模塊-數碼管接口smg_interface.v 中,輸入信號 Number_Sig 佔了 24 位寬,然而Number_Sig 的位分配如下表:
爲什麼每一位數字數碼管,都用 4 位位寬來代表呢?
每數碼管可以支持顯示 0~F,正是因爲如此,如果我們用每 4 位位寬來代表某一個數碼管顯示的信號,那麼可以避免“使用除法或者求餘運算符執行十進制的取位操作”。一來方便設計,而來減少資源。
舉個例子: 24’h123456, 亦即 00010010 0011 0100 0101 0110。
smg_encode_module.v 在這裏的功能就是就是將數字 0~F 加碼爲數碼管碼。然而,比較特別的是, smg_control_module.v 和 smg_scan_module.v 有並行操作的性質。
smg_interface.v 的大致操作如下:
假設我往 Number_Sig 輸入 24’h123456
(1)在 T1,smg_control_module.v 會將 Number_Sig[23:20] 送往至smg_encode_module.v 加碼並且送往數碼管。在同一時間 smg_scan_module.v 會掃描第一位數碼管(使能)。
(2)在 T2,smg_control_module.v 會將 Number_Sig[19:16] 送往至smg_encode_module.v 加碼並且送往數碼管,在同一時間smg_scan_module.v 會掃描第二位數碼管(使能)。
(3)在 T3, smg_control_module.v會將 Number_Sig[15:12]送往至smg_encode_module.v 加碼並且送往數碼管,在同一時間 smg_scan_module.v 會掃描第三位數碼管(使能)。
(4)在 T4,smg_control_module.v 會將 Number_Sig[11:8] 送往至 smg_encode_module.v 加碼並且送往數碼管,在同一時間 smg_scan_module.v 會掃描第四位數碼管(使能)。
(5)在 T5,smg_control_module.v 會將 Number_Sig[7:4] 送往至 smg_encode_module.v 加碼並且送往數碼管,在同一時間 smg_scan_module.v 會掃描第五位數碼管(使能)。
(6)在 T6,smg_control_module.v 會將 Number_Sig[3:0] 送往至 smg_encode_module.v 加碼並且送往數碼管,在同一時間 smg_scan_module.v 會掃描第六位數碼管(使能)。
在 T1 的時候第一位數碼管會顯示1。在 T2 的時候第二位數碼管會顯示 2,其他的依此類推。最後在 T6 的時候,第六位數碼管會顯示 6。就這樣一次性的掃描(六位數碼管全掃描)就完成。啊,別忘了!每位數碼管掃描停留的時間(使能的時間)大約是 1ms。所以一次性掃描所需要的時間大約是6ms,亦即在每一秒內,一組 6 位的數碼管會掃描 166 次左右。
圖1‑100數碼管控制模塊結構圖
代碼1‑13數碼管控制模塊(smg_control_module)代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-18 23:59:39
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-05-19 01:27:11
7. //# Description:
8. //# @Modification History: 2019-05-19 01:27:11
9. //# Date By Version Change Description:
10.//# ========================================================================= #
11.//# 2019-05-19 01:27:11
12.//# ========================================================================= #
13.//# | | #
14.//# | OpenFPGA | #
15.//****************************************************************************//
16.module smg_control_module
17.(
18. input CLOCK,
19. input RST_n,
20. input [23:0]Number_Sig,
21. output [3:0]Number_Data
22.);
23.
24. /******************************************/
25.
26. parameter T1MS = 16'd49999; //定義 1ms 的常量
27.
28. /******************************************/
29. //1ms 的定時器
30. reg [15:0]C1;
31.
32. always @ ( posedge CLOCK or negedge RST_n )
33. if( !RST_n )
34. C1 <= 16'd0;
35. else if( C1 == T1MS )
36. C1 <= 16'd0;
37. else
38. C1 <= C1 + 1'b1;
39.
40. /******************************************/
41.
42. reg [3:0]i;
43. reg [3:0]rNumber;
44.
45. always @ ( posedge CLOCK or negedge RST_n )
46. if( !RST_n )
47. begin
48. i <= 4'd0;
49. rNumber <= 4'd0;
50. end
51. else
52. case( i )
53.
54. 0:
55. if( C1 == T1MS ) i <= i + 1'b1;
56. else rNumber <= Number_Sig[23:20];
57.
58. 1:
59. if( C1 == T1MS ) i <= i + 1'b1;
60. else rNumber <= Number_Sig[19:16];
61.
62. 2:
63. if( C1 == T1MS ) i <= i + 1'b1;
64. else rNumber <= Number_Sig[15:12];
65.
66. 3:
67. if( C1 == T1MS ) i <= i + 1'b1;
68. else rNumber <= Number_Sig[11:8];
69.
70. 4:
71. if( C1 == T1MS ) i <= i + 1'b1;
72. else rNumber <= Number_Sig[7:4];
73.
74. 5:
75. if( C1 == T1MS ) i <= 4'd0;
76. else rNumber <= Number_Sig[3:0];
77.
78. endcase
79.
80. /******************************************/
81.
82. assign Number_Data = rNumber;
83.
84. /******************************************/
85.
86. endmodule
rNumber 是每一位數字的暫存器(43 行)用來驅動Number_Data(82 行)。(52~78 行)每隔 1ms該控制模塊就會將不同位的數字往 Number_Data 輸出
圖1‑101數碼管加碼模塊結構圖
代碼 1‑14數碼管加碼模塊(smg_encode_module)代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-18 23:59:39
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-07-21 03:32:41
7. //# Description:
8. //# @Modification History: 2019-07-20 20:54:39
9. //# Date By Version Change Description:
10.//# ========================================================================= #
11.//# 2019-07-20 20:54:39
12.//# ========================================================================= #
13.//# | | #
14.//# | OpenFPGA | #
15.//****************************************************************************//
16.module smg_encode_module
17.(
18. input CLOCK,
19. input RST_n,
20. input [3:0]Number_Data,
21. output [7:0]SMG_Data
22.);
23.
24. /***************************************/
25.
26. parameter _0 = 8'b1100_0000, _1 = 8'b1111_1001, _2 = 8'b1010_0100,
27. _3 = 8'b1011_0000, _4 = 8'b1001_1001, _5 = 8'b1001_0010,
28. _6 = 8'b1000_0010, _7 = 8'b1111_1000, _8 = 8'b1000_0000,
29. _9 = 8'b1001_0000, _A = 8'b1000_1000, _B = 8'b1000_0011,
30. _C = 8'b1100_0110, _D = 8'b1010_0001, _E = 8'b1000_0110,
31. _F = 8'b1000_1110;
32.
33. /***************************************/
34.
35. reg [7:0]rSMG;
36.
37. always @ ( posedge CLOCK or negedge RST_n )
38. if( !RST_n )
39. begin
40. rSMG <= 8'b1111_1111;
41. end
42. else
43. case( Number_Data )
44.
45. 4'd0 : rSMG <= _0;
46. 4'd1 : rSMG <= _1;
47. 4'd2 : rSMG <= _2;
48. 4'd3 : rSMG <= _3;
49. 4'd4 : rSMG <= _4;
50. 4'd5 : rSMG <= _5;
51. 4'd6 : rSMG <= _6;
52. 4'd7 : rSMG <= _7;
53. 4'd8 : rSMG <= _8;
54. 4'd9 : rSMG <= _9;
55. 4'd10 : rSMG <= _A;
56. 4'd11 : rSMG <= _B;
57. 4'd12 : rSMG <= _C;
58. 4'd13 : rSMG <= _D;
59. 4'd14 : rSMG <= _E;
60. 4'd15 : rSMG <= _F;
61.
62. endcase
63.
64. /***************************************/
65.
66. assign SMG_Data = rSMG;
67.
68. /***************************************/
69.
70.endmodule
第 26~31 行聲明瞭 SMG 碼的常量。第 43~62 行是針對“十位數”的加碼操作,但是是針對“個位數”(58 行)。第66 行是輸出。
圖1‑102數碼管掃描模塊結構圖
代碼 1‑15數碼管掃描模塊(smg_scan_module)代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-18 23:59:39
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-07-21 03:37:17
7. //# Description:
8. //# @Modification History: 2019-05-19 01:41:56
9. //# Date By Version Change Description:
10.//# ========================================================================= #
11.//# 2019-05-19 01:41:56
12.//# ========================================================================= #
13.//# | | #
14.//# | OpenFPGA | #
15.//****************************************************************************//
16.module smg_scan_module
17.(
18. input CLOCK,
19. input RST_n,
20. output [5:0]Scan_Sig
21.);
22.
23. /*****************************/
24.
25. parameter T1MS = 16'd49999;
26.
27. /*****************************/
28.
29. reg [15:0]C1;
30.
31. always @ ( posedge CLOCK or negedge RST_n )
32. if( !RST_n )
33. C1 <= 16'd0;
34. else if( C1 == T1MS )
35. C1 <= 16'd0;
36. else
37. C1 <= C1 + 1'b1;
38.
39. /*******************************/
40.
41. reg [3:0]i;
42. reg [5:0]rScan;
43.
44. always @ ( posedge CLOCK or negedge RST_n )
45. if( !RST_n )
46. begin
47. i <= 4'd0;
48. rScan <= 6'b100_000;
49. end
50. else
51. case( i )
52.
53. 0:
54. if( C1 == T1MS ) i <= i + 1'b1;
55. else rScan <= 6'b011_111;
56.
57. 1:
58. if( C1 == T1MS ) i <= i + 1'b1;
59. else rScan <= 6'b101_111;
60.
61. 2:
62. if( C1 == T1MS ) i <= i + 1'b1;
63. else rScan <= 6'b110_111;
64.
65. 3:
66. if( C1 == T1MS ) i <= i + 1'b1;
67. else rScan <= 6'b111_011;
68.
69. 4:
70. if( C1 == T1MS ) i <= i + 1'b1;
71. else rScan <= 6'b111_101;
72.
73. 5:
74. if( C1 == T1MS ) i <= 4'd0;
75. else rScan <= 6'b111_110;
76.
77.
78. endcase
79.
80. /******************************/
81.
82. assign Scan_Sig = rScan;
83.
84. /******************************/
85.
86.
87.endmodule
第 25 行是 1ms 的常量聲明,在 31~37 行是 1ms 的定時器。該模塊和smg_control_module.v一樣,都是每隔 1ms 都有一個動作。smg_scan_module.v 每隔 1ms就會使能不同的數碼管(56~78行)。然而數碼管實際的掃描順序是自左向右。在位操作的角度上,邏輯 0 從最高位到最低位交替移位。
接下來將上訴模塊進行封裝,詳細的連接圖見圖1‑98,綜合後的RTL電路圖如下:
圖1‑103數碼管電路綜合後的RTL圖
結果和圖1‑98一樣。
下面進行模塊的調用和驗證,驗證結構圖如下:
圖1‑104數碼管電路驗證結構圖
驗證中會建立一個名爲demo_control_module.v輸出 24’h000000 ~ 24’h999999 用來驅動smg_interface.v 的輸入。具體的內容還是直接看代碼:
代碼 1‑16數碼管電路驗證代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-18 23:59:39
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-07-20 21:04:39
7. //# Description:
8. //# @Modification History: 2019-07-20 21:04:39
9. //# Date By Version Change Description:
10. //# ========================================================================= #
11. //# 2019-07-20 21:04:39
12. //# ========================================================================= #
13. //# | | #
14. //# | OpenFPGA | #
15. //****************************************************************************//
16. module demo_control_module
17. (
18. input CLOCK,
19. input RST_n,
20. output [23:0]Number_Sig
21. );
22.
23. /******************************/
24.
25. parameter T100MS = 23'd4_999_999;
26.
27. /******************************/
28.
29. reg [22:0]C1;
30.
31. always @ ( posedge CLOCK or negedge RST_n )
32. if( !RST_n )
33. C1 <= 23'd0;
34. else if( C1 == T100MS )
35. C1 <= 23'd0;
36. else
37. C1 <= C1 + 1'b1;
38.
39. /*******************************************************/
40.
41. reg [3:0]i;
42. reg [23:0]rNum;
43. reg [23:0]rNumber;
44.
45. always @ ( posedge CLOCK or negedge RST_n )
46. if( !RST_n )
47. begin
48. i <= 4'd0;
49. rNum <= 24'd0;
50. rNumber <= 24'd0;
51. end
52. else
53. case( i )
54.
55. 0:
56. if( C1 == T100MS ) begin rNum[3:0] <= rNum[3:0] + 1'b1; i <= i + 1'b1; end
57.
58. 1:
59. if( rNum[3:0] > 4'd14 ) begin rNum[7:4] <= rNum[7:4] + 1'b1; rNum[3:0] <= 4'd0; i <= i + 1'b1; end
60. else i <= i + 1'b1;
61.
62. 2:
63. if( rNum[7:4] > 4'd14 ) begin rNum[11:8] <= rNum[11:8] + 1'b1; rNum[7:4] <= 4'd0; i <= i + 1'b1; end
64. else i <= i + 1'b1;
65.
66. 3:
67. if( rNum[11:8] > 4'd14 ) begin rNum[15:12] <= rNum[15:12] + 1'b1; rNum[11:8] <= 4'd0; i <= i + 1'b1; end
68. else i <= i + 1'b1;
69.
70. 4:
71. if( rNum[15:12] > 4'd14 ) begin rNum[19:16] <= rNum[19:16] + 1'b1; rNum[15:12] <= 4'd0; i <= i + 1'b1; end
72. else i <= i + 1'b1;
73.
74. 5:
75. if( rNum[15:12] > 4'd14 ) begin rNum[19:16] <= rNum[19:16] + 1'b1; rNum[15:12] <= 4'd0; i <= i + 1'b1; end
76. else i <= i + 1'b1;
77.
78. 6:
79. if( rNum[19:16] > 4'd14 ) begin rNum[23:20] <= rNum[23:20] + 1'b1; rNum[19:16] <= 4'd0; end
80. else i <= i + 1'b1;
81.
82. 7:
83. if( rNum[23:20] > 4'd14 ) begin rNum <= 24'd0; i <= i + 1'b1; end
84. else i <= i + 1'b1;
85.
86. 8:
87. begin rNumber <= rNum; i <= 4'd0; end
88.
89. endcase
90.
91. /*******************************************************/
92.
93. assign Number_Sig = rNumber;
94.
95. /*******************************************************/
96.
97. endmodule
第 20~37 行之間,包含了100ms 定時的常量(25 行)和 100ms 的定時器(31~37行)。
在 41~89 就是該模塊的核心部分。寄存器rNum 操作空間(42 行),然而 rNumber 是用於驅動Number_Sig(93 行)。每隔 100ms 的定時都會是rNum 遞增(56 行), 58~84行之間就會執行“4 位寬”數字之間的“進位操作”。
我們假設一個情況,當rNum 的值是 24’h000009,然後在下一個100ms 的定時鐘, rNum的值就會 +1 操作。在59行, if條件就會成立,rNum[3:0]就會被賦值位零,然後rNum[7:4]就會執行 +1 操作,rNum 的值成爲 24’h000010。接下來的幾個步驟也會執行類似的操作。
在 72~84 行表示了當 rNum 的值超過24’h999999 的時候,就會恢復爲 24’h000000。
在這裏我們有一個問題?爲什麼不直接使用rNum 驅動 Number_Sig 而是選擇使用rNumber寄存器來驅動Number_Sig。如果我們把 rNum 當着 Number_Sig 的驅動對象,在 i 步驟 16~22 之間,由於“進位操作”的關係,會使得Number_Sig 的輸出產生許多毛刺,因此才使用 rNumber 驅動Number_Sig。當 rNum 完成“進位操作”以後,再賦值與rNumber,由 rNumber 驅動 Number_Sig ( 87行)。
然後按照圖1‑104進行頂層模塊的設計,代碼如下:
代碼 1‑17數碼管電路驗證代碼的頂層代碼
1. //****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-05-18 23:59:39
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-07-21 03:49:46
7. //# Description:
8. //# @Modification History: 2019-05-19 01:41:52
9. //# Date By Version Change Description:
10.//# ========================================================================= #
11.//# 2019-05-19 01:41:52
12.//# ========================================================================= #
13.//# | | #
14.//# | OpenFPGA | #
15.//****************************************************************************//
16.module smg_interface_demo
17.(
18. input CLOCK,
19. input RST_n,
20. output [7:0]SMG_Data,
21. output [5:0]Scan_Sig
22.);
23.
24. /******************************/
25.
26. wire [23:0]Number_Sig;
27.
28. demo_control_module U1
29. (
30. .CLOCK( CLOCK ),
31. .RST_n( RST_n ),
32. .Number_Sig( Number_Sig ) // output - to U2
33. );
34.
35. /******************************/
36.
37. smg_interface U2
38. (
39. .CLOCK( CLOCK ),
40. .RST_n( RST_n ),
41. .Number_Sig( Number_Sig ), // input - from U1
42. .SMG_Data( SMG_Data ), // output - to top
43. .Scan_Sig( Scan_Sig ) // output - to top
44. );
45.
46. /******************************/
47.
48.endmodule
綜合後的RTL如下:
圖1‑105數碼管電路驗證代碼的頂層代碼綜合後RTL圖
基本和圖1‑104一樣,將代碼下載到目標板上就可以看到數碼管已經變成了一個“秒錶”了。
代碼地址
github
你笑一次,我就可以高興好幾天;可看你哭一次,我就難過了好幾年…
https://github.com/suisuisi/FPGA/tree/master/FPGAandPI